From ea64f2e46ee6cd36335ae3b8680ec3dd0dffa527 Mon Sep 17 00:00:00 2001 From: Daniel James Date: Thu, 27 Apr 2017 18:22:43 +0100 Subject: [PATCH 1/8] Remove the grouped node stuff --- .travis.yml | 8 - .../boost/unordered/detail/implementation.hpp | 221 ------------------ include/boost/unordered/detail/map.hpp | 8 - include/boost/unordered/detail/set.hpp | 8 - include/boost/unordered/unordered_map.hpp | 8 - include/boost/unordered/unordered_set.hpp | 8 - test/helpers/test.hpp | 2 - test/unordered/insert_stable_tests.cpp | 6 +- test/unordered/merge_tests.cpp | 2 - 9 files changed, 3 insertions(+), 268 deletions(-) diff --git a/.travis.yml b/.travis.yml index 03f49936..db516156 100644 --- a/.travis.yml +++ b/.travis.yml @@ -38,14 +38,6 @@ matrix: env: | label="clang 32 bit"; user_config="using clang : : clang++ -m32 -Werror --std=c++03 ;" - - compiler: gcc - env: | - label="gcc C++03 interopable1"; - user_config="using gcc : : g++-4.8 -fsanitize=address -Werror --std=c++03 -DBOOST_UNORDERED_INTEROPERABLE_NODES=1 ;" - - compiler: clang - env: | - label="gcc C++11 interopable2"; - user_config="using clang : : clang++ -fsanitize=address -Werror --std=c++03 -DBOOST_UNORDERED_INTEROPERABLE_NODES=2 ;" before_script: - cd ${TRAVIS_BUILD_DIR} diff --git a/include/boost/unordered/detail/implementation.hpp b/include/boost/unordered/detail/implementation.hpp index 50baa8ff..f92f3ec5 100644 --- a/include/boost/unordered/detail/implementation.hpp +++ b/include/boost/unordered/detail/implementation.hpp @@ -66,19 +66,6 @@ #define BOOST_UNORDERED_EMPLACE_LIMIT 10 #endif -// BOOST_UNORDERED_INTEROPERABLE_NODES - Use the same node type for -// containers with unique and equivalent keys. -// -// 0 = Use different nodes -// 1 = Use ungrouped nodes everywhere -// 2 = Use grouped nodes everywhere -// -// Might add an extra value to use grouped nodes everywhere later. - -#if !defined(BOOST_UNORDERED_INTEROPERABLE_NODES) -#define BOOST_UNORDERED_INTEROPERABLE_NODES 0 -#endif - // BOOST_UNORDERED_USE_ALLOCATOR_TRAITS - Pick which version of // allocator_traits to use. // @@ -190,10 +177,6 @@ template struct node; template struct ptr_node; template struct node_algo; -template struct grouped_node; -template struct grouped_ptr_node; -template struct grouped_node_algo; - static const float minimum_max_load_factor = 1e-3f; static const std::size_t default_bucket_count = 11; @@ -4565,210 +4548,6 @@ template struct pick_node typedef typename pick::link_pointer link_pointer; typedef boost::unordered::detail::node_algo node_algo; }; - -//////////////////////////////////////////////////////////////////////// -// Grouped nodes - -template -struct grouped_node : boost::unordered::detail::value_base -{ - typedef typename ::boost::unordered::detail::rebind_wrap >::type allocator; - typedef typename ::boost::unordered::detail::allocator_traits< - allocator>::pointer node_pointer; - typedef node_pointer link_pointer; - typedef typename ::boost::unordered::detail::rebind_wrap >::type bucket_allocator; - typedef typename ::boost::unordered::detail::allocator_traits< - bucket_allocator>::pointer bucket_pointer; - - link_pointer next_; - node_pointer group_prev_; - std::size_t hash_; - - grouped_node() : next_(), group_prev_(), hash_(0) {} - - void init(node_pointer self) { group_prev_ = self; } - - private: - grouped_node& operator=(grouped_node const&); -}; - -template -struct grouped_ptr_node : boost::unordered::detail::ptr_bucket -{ - typedef T value_type; - typedef boost::unordered::detail::ptr_bucket bucket_base; - typedef grouped_ptr_node* node_pointer; - typedef ptr_bucket* link_pointer; - typedef ptr_bucket* bucket_pointer; - - node_pointer group_prev_; - std::size_t hash_; - boost::unordered::detail::value_base value_base_; - - grouped_ptr_node() : bucket_base(), group_prev_(0), hash_(0) {} - - void init(node_pointer self) { group_prev_ = self; } - - void* address() { return value_base_.address(); } - value_type& value() { return value_base_.value(); } - value_type* value_ptr() { return value_base_.value_ptr(); } - - private: - grouped_ptr_node& operator=(grouped_ptr_node const&); -}; - -template struct grouped_node_algo -{ - typedef typename N::node_pointer node_pointer; - typedef typename N::link_pointer link_pointer; - typedef typename N::bucket_pointer bucket_pointer; - - static node_pointer next_node(link_pointer n) - { - return static_cast(n->next_); - } - - static node_pointer next_for_find(node_pointer n) - { - return static_cast(n->group_prev_->next_); - } - - static link_pointer next_for_erase(link_pointer prev) - { - return static_cast(prev->next_)->group_prev_; - } - - static node_pointer last_for_rehash(link_pointer prev) - { - return static_cast(prev->next_)->group_prev_; - } - - // The 'void*' arguments are pointers to the table, which we - // will ignore, but without groups they could be used to - // access the various functions for dealing with values and keys. - static node_pointer next_group(node_pointer n, void const*) - { - return static_cast(n->group_prev_->next_); - } - - static std::size_t count(node_pointer n, void const*) - { - std::size_t x = 0; - node_pointer it = n; - do { - it = it->group_prev_; - ++x; - } while (it != n); - - return x; - } - - // Adds node 'n' to the group containing 'pos'. - // If 'pos' is the first node in group, add to the end of the group, - // otherwise add before 'pos'. Other versions will probably behave - // differently. - static inline void add_to_node(node_pointer n, node_pointer pos) - { - n->next_ = pos->group_prev_->next_; - n->group_prev_ = pos->group_prev_; - pos->group_prev_->next_ = n; - pos->group_prev_ = n; - } - - static inline node_pointer extract_first_node(link_pointer prev) - { - node_pointer n = next_node(prev); - if (n->group_prev_ != n) { - node_pointer next = next_node(n); - next->group_prev_ = n->group_prev_; - n->group_prev_ = n; - } - prev->next_ = n->next_; - return n; - } - - // Split the groups containing 'i' and 'j' so that they can - // be safely erased/extracted. - static link_pointer split_groups(node_pointer i, node_pointer j) - { - node_pointer prev = i->group_prev_; - if (prev->next_ != i) - prev = node_pointer(); - - if (j) { - node_pointer first = j; - while (first != i && first->group_prev_->next_ == first) { - first = first->group_prev_; - } - - boost::swap(first->group_prev_, j->group_prev_); - if (first == i) - return prev; - } - - if (prev) { - node_pointer first = prev; - while (first->group_prev_->next_ == first) { - first = first->group_prev_; - } - boost::swap(first->group_prev_, i->group_prev_); - } - - return prev; - } -}; - -// If the allocator uses raw pointers use grouped_ptr_node -// Otherwise use grouped_node. - -template -struct pick_grouped_node2 -{ - typedef boost::unordered::detail::grouped_node node; - - typedef typename boost::unordered::detail::allocator_traits< - typename boost::unordered::detail::rebind_wrap::type>::pointer - node_pointer; - - typedef boost::unordered::detail::bucket bucket; - typedef node_pointer link_pointer; -}; - -template -struct pick_grouped_node2*, - boost::unordered::detail::ptr_bucket*> -{ - typedef boost::unordered::detail::grouped_ptr_node node; - typedef boost::unordered::detail::ptr_bucket bucket; - typedef bucket* link_pointer; -}; - -template struct pick_grouped_node -{ - typedef typename boost::remove_const::type nonconst; - - typedef boost::unordered::detail::allocator_traits< - typename boost::unordered::detail::rebind_wrap >::type> - tentative_node_traits; - - typedef boost::unordered::detail::allocator_traits< - typename boost::unordered::detail::rebind_wrap::type> - tentative_bucket_traits; - - typedef pick_grouped_node2 - pick; - - typedef typename pick::node node; - typedef typename pick::bucket bucket; - typedef typename pick::link_pointer link_pointer; - typedef boost::unordered::detail::grouped_node_algo node_algo; -}; } } } diff --git a/include/boost/unordered/detail/map.hpp b/include/boost/unordered/detail/map.hpp index 9b059d18..13e223f3 100644 --- a/include/boost/unordered/detail/map.hpp +++ b/include/boost/unordered/detail/map.hpp @@ -23,11 +23,7 @@ template struct map typedef boost::unordered::detail::allocator_traits value_allocator_traits; -#if BOOST_UNORDERED_INTEROPERABLE_NODES != 2 typedef boost::unordered::detail::pick_node pick; -#else - typedef boost::unordered::detail::pick_grouped_node pick; -#endif typedef typename pick::node node; typedef typename pick::bucket bucket; typedef typename pick::link_pointer link_pointer; @@ -69,11 +65,7 @@ struct multimap typedef boost::unordered::detail::allocator_traits value_allocator_traits; -#if BOOST_UNORDERED_INTEROPERABLE_NODES != 1 - typedef boost::unordered::detail::pick_grouped_node pick; -#else typedef boost::unordered::detail::pick_node pick; -#endif typedef typename pick::node node; typedef typename pick::bucket bucket; typedef typename pick::link_pointer link_pointer; diff --git a/include/boost/unordered/detail/set.hpp b/include/boost/unordered/detail/set.hpp index 6de4423c..edb98ece 100644 --- a/include/boost/unordered/detail/set.hpp +++ b/include/boost/unordered/detail/set.hpp @@ -23,11 +23,7 @@ template struct set typedef boost::unordered::detail::allocator_traits value_allocator_traits; -#if BOOST_UNORDERED_INTEROPERABLE_NODES != 2 typedef boost::unordered::detail::pick_node pick; -#else - typedef boost::unordered::detail::pick_grouped_node pick; -#endif typedef typename pick::node node; typedef typename pick::bucket bucket; typedef typename pick::link_pointer link_pointer; @@ -68,11 +64,7 @@ template struct multiset typedef boost::unordered::detail::allocator_traits value_allocator_traits; -#if BOOST_UNORDERED_INTEROPERABLE_NODES != 1 - typedef boost::unordered::detail::pick_grouped_node pick; -#else typedef boost::unordered::detail::pick_node pick; -#endif typedef typename pick::node node; typedef typename pick::bucket bucket; typedef typename pick::link_pointer link_pointer; diff --git a/include/boost/unordered/unordered_map.hpp b/include/boost/unordered/unordered_map.hpp index 5c941f75..ab20642c 100644 --- a/include/boost/unordered/unordered_map.hpp +++ b/include/boost/unordered/unordered_map.hpp @@ -740,14 +740,12 @@ template class unordered_map void merge(boost::unordered_map&& source); #endif -#if BOOST_UNORDERED_INTEROPERABLE_NODES template void merge(boost::unordered_multimap& source); #if !defined(BOOST_NO_CXX11_RVALUE_REFERENCES) template void merge(boost::unordered_multimap&& source); -#endif #endif // observers @@ -1277,14 +1275,12 @@ template class unordered_multimap void merge(boost::unordered_multimap&& source); #endif -#if BOOST_UNORDERED_INTEROPERABLE_NODES template void merge(boost::unordered_map& source); #if !defined(BOOST_NO_CXX11_RVALUE_REFERENCES) template void merge(boost::unordered_map&& source); -#endif #endif // observers @@ -1644,7 +1640,6 @@ void unordered_map::merge( } #endif -#if BOOST_UNORDERED_INTEROPERABLE_NODES template template void unordered_map::merge( @@ -1662,7 +1657,6 @@ void unordered_map::merge( table_.merge_unique(source.table_); } #endif -#endif // observers @@ -2152,7 +2146,6 @@ void unordered_multimap::merge( } #endif -#if BOOST_UNORDERED_INTEROPERABLE_NODES template template void unordered_multimap::merge( @@ -2174,7 +2167,6 @@ void unordered_multimap::merge( } } #endif -#endif // lookup diff --git a/include/boost/unordered/unordered_set.hpp b/include/boost/unordered/unordered_set.hpp index 36645b99..674d4e67 100644 --- a/include/boost/unordered/unordered_set.hpp +++ b/include/boost/unordered/unordered_set.hpp @@ -457,14 +457,12 @@ template class unordered_set void merge(boost::unordered_set&& source); #endif -#if BOOST_UNORDERED_INTEROPERABLE_NODES template void merge(boost::unordered_multiset& source); #if !defined(BOOST_NO_CXX11_RVALUE_REFERENCES) template void merge(boost::unordered_multiset&& source); -#endif #endif // observers @@ -962,14 +960,12 @@ template class unordered_multiset void merge(boost::unordered_multiset&& source); #endif -#if BOOST_UNORDERED_INTEROPERABLE_NODES template void merge(boost::unordered_set& source); #if !defined(BOOST_NO_CXX11_RVALUE_REFERENCES) template void merge(boost::unordered_set&& source); -#endif #endif // observers @@ -1322,7 +1318,6 @@ void unordered_set::merge( } #endif -#if BOOST_UNORDERED_INTEROPERABLE_NODES template template void unordered_set::merge( @@ -1340,7 +1335,6 @@ void unordered_set::merge( table_.merge_unique(source.table_); } #endif -#endif // lookup @@ -1732,7 +1726,6 @@ void unordered_multiset::merge( } #endif -#if BOOST_UNORDERED_INTEROPERABLE_NODES template template void unordered_multiset::merge( @@ -1754,7 +1747,6 @@ void unordered_multiset::merge( } } #endif -#endif // lookup diff --git a/test/helpers/test.hpp b/test/helpers/test.hpp index 84e900f2..4f21f0b9 100644 --- a/test/helpers/test.hpp +++ b/test/helpers/test.hpp @@ -93,8 +93,6 @@ static inline void run_tests() << BOOST_UNORDERED_HAVE_PIECEWISE_CONSTRUCT << "\n" \ << "BOOST_UNORDERED_EMPLACE_LIMIT: " \ << BOOST_UNORDERED_EMPLACE_LIMIT << "\n" \ - << "BOOST_UNORDERED_INTEROPERABLE_NODES: " \ - << BOOST_UNORDERED_INTEROPERABLE_NODES << "\n" \ << "BOOST_UNORDERED_USE_ALLOCATOR_TRAITS: " \ << BOOST_UNORDERED_USE_ALLOCATOR_TRAITS << "\n" \ << "BOOST_UNORDERED_CXX11_CONSTRUCTION: " \ diff --git a/test/unordered/insert_stable_tests.cpp b/test/unordered/insert_stable_tests.cpp index c85dd188..afba2239 100644 --- a/test/unordered/insert_stable_tests.cpp +++ b/test/unordered/insert_stable_tests.cpp @@ -47,9 +47,9 @@ std::size_t hash_value(insert_stable::member const& x) } } -// This is now only supported when using grouped nodes. I can't see any -// efficient way to do it otherwise. -#if !BOOST_UNORDERED_INTEROPERABLE_NODES +// This is no longer supported, as there's no longer an efficient way to get to +// the end of a group of equivalent nodes. +#if 0 UNORDERED_AUTO_TEST(stable_insert_test1) { diff --git a/test/unordered/merge_tests.cpp b/test/unordered/merge_tests.cpp index 15755ab8..3d5e7f97 100644 --- a/test/unordered/merge_tests.cpp +++ b/test/unordered/merge_tests.cpp @@ -99,7 +99,6 @@ UNORDERED_AUTO_TEST(merge_multiset) test::check_equivalent_keys(y); } -#if BOOST_UNORDERED_INTEROPERABLE_NODES UNORDERED_AUTO_TEST(merge_set_and_multiset) { boost::unordered_set x; @@ -139,7 +138,6 @@ UNORDERED_AUTO_TEST(merge_set_and_multiset) test::check_equivalent_keys(x); test::check_equivalent_keys(y); } -#endif template void merge_empty_test(X*, test::random_generator generator) { From e9c4696544f11376d7dfe23dcc171d2852b4437c Mon Sep 17 00:00:00 2001 From: Daniel James Date: Thu, 27 Apr 2017 18:22:43 +0100 Subject: [PATCH 2/8] Get rid of node_algo --- .../boost/unordered/detail/implementation.hpp | 257 ++++++++---------- include/boost/unordered/detail/map.hpp | 2 - include/boost/unordered/detail/set.hpp | 2 - include/boost/unordered/unordered_map.hpp | 24 +- include/boost/unordered/unordered_set.hpp | 14 +- 5 files changed, 124 insertions(+), 175 deletions(-) diff --git a/include/boost/unordered/detail/implementation.hpp b/include/boost/unordered/detail/implementation.hpp index f92f3ec5..13c549e1 100644 --- a/include/boost/unordered/detail/implementation.hpp +++ b/include/boost/unordered/detail/implementation.hpp @@ -175,7 +175,6 @@ struct ptr_bucket; template struct node; template struct ptr_node; -template struct node_algo; static const float minimum_max_load_factor = 1e-3f; static const std::size_t default_bucket_count = 11; @@ -2660,7 +2659,6 @@ struct table : boost::unordered::detail::functions @@ -2701,6 +2699,42 @@ struct table : boost::unordered::detail::functions(n->next_); + } + + static node_pointer next_for_find(link_pointer n) + { + return static_cast(n->next_); + } + + static link_pointer next_for_erase(link_pointer prev) + { + return prev->next_; + } + + node_pointer next_group(node_pointer n) const + { + node_pointer n1 = n; + do { + n1 = next_node(n1); + } while (n1 && this->key_eq()(get_key(n), get_key(n1))); + return n1; + } + + std::size_t group_count(node_pointer n) const + { + std::size_t x = 0; + node_pointer it = n; + do { + ++x; + it = next_node(it); + } while (it && this->key_eq()(get_key(n), get_key(it))); + + return x; + } + bucket_allocator const& bucket_alloc() const { return allocators_.first(); } node_allocator const& node_alloc() const { return allocators_.second(); } @@ -2734,8 +2768,7 @@ struct table : boost::unordered::detail::functionshash_) == index) { ++count; - n = node_algo::next_node(n); + n = next_node(n); } return count; @@ -3235,7 +3268,7 @@ struct table : boost::unordered::detail::functionsnext_) { return link_pointer(); } - std::size_t node_hash = node_algo::next_node(prev)->hash_; + std::size_t node_hash = next_node(prev)->hash_; if (this->hash_to_bucket(node_hash) != bucket_index) { return link_pointer(); } if (node_hash == key_hash && - this->key_eq()(k, this->get_key(node_algo::next_node(prev)))) { + this->key_eq()(k, this->get_key(next_node(prev)))) { return prev; } - prev = node_algo::next_for_erase(prev); + prev = next_for_erase(prev); } } @@ -3277,7 +3310,8 @@ struct table : boost::unordered::detail::functionsnext_ = n->next_; --this->size_; this->fix_bucket(bucket_index, prev); n->next_ = link_pointer(); @@ -3302,8 +3336,7 @@ struct table : boost::unordered::detail::functionssize_ != other.size_) return false; - for (node_pointer n1 = this->begin(); n1; - n1 = node_algo::next_node(n1)) { + for (node_pointer n1 = this->begin(); n1; n1 = next_node(n1)) { node_pointer n2 = other.find_node(other.get_key(n1)); if (!n2 || n1->value() != n2->value()) @@ -3325,8 +3358,8 @@ struct table : boost::unordered::detail::functionsget_previous_start(); if (start_node->next_) { - this->get_bucket(this->hash_to_bucket( - node_algo::next_node(start_node)->hash_)) + this->get_bucket( + this->hash_to_bucket(next_node(start_node)->hash_)) ->next_ = n; } @@ -3551,7 +3584,7 @@ struct table : boost::unordered::detail::functionsnext_) { - node_pointer n = other_table::node_algo::next_node(prev); + node_pointer n = other_table::next_node(prev); const_key_type& k = this->get_key(n); std::size_t key_hash = this->hash(k); node_pointer pos = this->find_node(key_hash, k); @@ -3560,8 +3593,7 @@ struct table : boost::unordered::detail::functionsreserve_for_insert(this->size_ + 1); - other_table::node_algo::split_groups( - n, other_table::node_algo::next_node(n)); + // other_table::split_groups(n, other_table::next_node(n)); prev->next_ = n->next_; --other.size_; other.fix_bucket(other.hash_to_bucket(n->hash_), prev); @@ -3670,7 +3702,7 @@ struct table : boost::unordered::detail::functionsfind_previous_node(k, key_hash, bucket_index); if (!prev) return 0; - link_pointer end = node_algo::next_node(prev)->next_; + link_pointer end = next_node(prev)->next_; this->delete_nodes(prev, end); this->fix_bucket(bucket_index, prev); return 1; @@ -3699,7 +3731,7 @@ struct table : boost::unordered::detail::functionscreate_buckets(this->bucket_count_); - for (node_pointer n = src.begin(); n; n = node_algo::next_node(n)) { + for (node_pointer n = src.begin(); n; n = next_node(n)) { this->add_node_unique( boost::unordered::detail::func::construct_node( this->node_alloc(), n->value()), @@ -3712,7 +3744,7 @@ struct table : boost::unordered::detail::functionscreate_buckets(this->bucket_count_); - for (node_pointer n = src.begin(); n; n = node_algo::next_node(n)) { + for (node_pointer n = src.begin(); n; n = next_node(n)) { this->add_node_unique( boost::unordered::detail::func::construct_node( this->node_alloc(), boost::move(n->value())), @@ -3723,7 +3755,7 @@ struct table : boost::unordered::detail::functions holder(*this); - for (node_pointer n = src.begin(); n; n = node_algo::next_node(n)) { + for (node_pointer n = src.begin(); n; n = next_node(n)) { this->add_node_unique(holder.copy_of(n->value()), n->hash_); } } @@ -3731,7 +3763,7 @@ struct table : boost::unordered::detail::functions holder(*this); - for (node_pointer n = src.begin(); n; n = node_algo::next_node(n)) { + for (node_pointer n = src.begin(); n; n = next_node(n)) { this->add_node_unique(holder.move_copy_of(n->value()), n->hash_); } } @@ -3750,8 +3782,8 @@ struct table : boost::unordered::detail::functionsvalue() != n2->value()) break; - n1 = node_algo::next_node(n1); - n2 = node_algo::next_node(n2); + n1 = next_node(n1); + n2 = next_node(n2); if (n1 == end1) return n2 == end2; @@ -3777,8 +3809,8 @@ struct table : boost::unordered::detail::functionsvalue(); if (!find_equiv(start, n1, v)) { std::size_t matches = count_equal_equiv(n2, end2, v); if (!matches) return false; - if (matches != - 1 + count_equal_equiv(node_algo::next_node(n1), end1, v)) + if (matches != 1 + count_equal_equiv(next_node(n1), end1, v)) return false; } } @@ -3810,7 +3841,7 @@ struct table : boost::unordered::detail::functionsvalue() == v) return true; return false; @@ -3820,7 +3851,7 @@ struct table : boost::unordered::detail::functionsvalue() == v) ++count; return count; @@ -3833,10 +3864,11 @@ struct table : boost::unordered::detail::functionshash_ = key_hash; if (pos) { - node_algo::add_to_node(n, pos); + n->next_ = pos->next_; + pos->next_ = n; if (n->next_) { std::size_t next_bucket = - this->hash_to_bucket(node_algo::next_node(n)->hash_); + this->hash_to_bucket(next_node(n)->hash_); if (next_bucket != this->hash_to_bucket(key_hash)) { this->get_bucket(next_bucket)->next_ = n; } @@ -3849,8 +3881,7 @@ struct table : boost::unordered::detail::functionsnext_) { this->get_bucket( - this->hash_to_bucket( - node_algo::next_node(start_node)->hash_)) + this->hash_to_bucket(next_node(start_node)->hash_)) ->next_ = n; } @@ -3869,10 +3900,10 @@ struct table : boost::unordered::detail::functionshash_ = hint->hash_; - node_algo::add_to_node(n, hint); + n->next_ = hint->next_; + hint->next_ = n; if (n->next_ != hint && n->next_) { - std::size_t next_bucket = - this->hash_to_bucket(node_algo::next_node(n)->hash_); + std::size_t next_bucket = this->hash_to_bucket(next_node(n)->hash_); if (next_bucket != this->hash_to_bucket(n->hash_)) { this->get_bucket(next_bucket)->next_ = n; } @@ -4006,21 +4037,19 @@ struct table : boost::unordered::detail::functionshash_to_bucket(i->hash_); // Split the groups containing 'i' and 'j'. // And get the pointer to the node before i while // we're at it. - link_pointer prev = node_algo::split_groups(i, j); + // link_pointer prev = split_groups(i, j); // If we don't have a 'prev' it means that i is at the // beginning of a block, so search through the blocks in the // same bucket. - if (!prev) { - prev = this->get_previous_start(bucket_index); - while (prev->next_ != i) { - prev = node_algo::next_for_erase(prev); - } + link_pointer prev = this->get_previous_start(bucket_index); + while (prev->next_ != i) { + prev = next_for_erase(prev); } prev->next_ = i->next_; @@ -4047,8 +4076,8 @@ struct table : boost::unordered::detail::functionsdelete_nodes(prev, end); this->fix_bucket(bucket_index, prev); @@ -4062,16 +4091,14 @@ struct table : boost::unordered::detail::functionsget_previous_start(bucket_index); - while (prev->next_ != i) { - prev = node_algo::next_for_erase(prev); - } + link_pointer prev = this->get_previous_start(bucket_index); + while (prev->next_ != i) { + prev = next_for_erase(prev); } // Delete the nodes. @@ -4093,13 +4120,12 @@ struct table : boost::unordered::detail::functionshash_; - node_pointer group_end(node_algo::next_group(n, this)); + node_pointer group_end(next_group(n)); node_pointer pos = this->add_node_equiv( boost::unordered::detail::func::construct_node( this->node_alloc(), n->value()), key_hash, node_pointer()); - for (n = node_algo::next_node(n); n != group_end; - n = node_algo::next_node(n)) { + for (n = next_node(n); n != group_end; n = next_node(n)) { this->add_node_equiv( boost::unordered::detail::func::construct_node( this->node_alloc(), n->value()), @@ -4114,13 +4140,12 @@ struct table : boost::unordered::detail::functionshash_; - node_pointer group_end(node_algo::next_group(n, this)); + node_pointer group_end(next_group(n)); node_pointer pos = this->add_node_equiv( boost::unordered::detail::func::construct_node( this->node_alloc(), boost::move(n->value())), key_hash, node_pointer()); - for (n = node_algo::next_node(n); n != group_end; - n = node_algo::next_node(n)) { + for (n = next_node(n); n != group_end; n = next_node(n)) { this->add_node_equiv( boost::unordered::detail::func::construct_node( this->node_alloc(), boost::move(n->value())), @@ -4134,11 +4159,10 @@ struct table : boost::unordered::detail::functions holder(*this); for (node_pointer n = src.begin(); n;) { std::size_t key_hash = n->hash_; - node_pointer group_end(node_algo::next_group(n, this)); + node_pointer group_end(next_group(n)); node_pointer pos = this->add_node_equiv( holder.copy_of(n->value()), key_hash, node_pointer()); - for (n = node_algo::next_node(n); n != group_end; - n = node_algo::next_node(n)) { + for (n = next_node(n); n != group_end; n = next_node(n)) { this->add_node_equiv(holder.copy_of(n->value()), key_hash, pos); } } @@ -4149,11 +4173,10 @@ struct table : boost::unordered::detail::functions holder(*this); for (node_pointer n = src.begin(); n;) { std::size_t key_hash = n->hash_; - node_pointer group_end(node_algo::next_group(n, this)); + node_pointer group_end(next_group(n)); node_pointer pos = this->add_node_equiv( holder.move_copy_of(n->value()), key_hash, node_pointer()); - for (n = node_algo::next_node(n); n != group_end; - n = node_algo::next_node(n)) { + for (n = next_node(n); n != group_end; n = next_node(n)) { this->add_node_equiv( holder.move_copy_of(n->value()), key_hash, pos); } @@ -4209,7 +4232,21 @@ inline void table::rehash_impl(std::size_t num_buckets) this->create_buckets(num_buckets); link_pointer prev = this->get_previous_start(); while (prev->next_) { - node_pointer group_last = node_algo::last_for_rehash(prev); + // Group together all nodes with equal hash value, this may + // include nodes with different keys, but that's okay because + // they will end up in the same bucket. + // + // TODO: Don't do this for unique keys? + node_pointer group_last = next_node(prev); + std::size_t hash = group_last->hash_; + for (;;) { + node_pointer next = next_node(group_last); + if (!next || next->hash_ != hash) { + break; + } + group_last = next; + } + bucket_pointer b = this->get_bucket(this->hash_to_bucket(group_last->hash_)); if (!b->next_) { @@ -4419,87 +4456,6 @@ template struct ptr_node : boost::unordered::detail::ptr_bucket ptr_node& operator=(ptr_node const&); }; -template struct node_algo -{ - typedef typename N::node_pointer node_pointer; - typedef typename N::link_pointer link_pointer; - typedef typename N::bucket_pointer bucket_pointer; - - static node_pointer next_node(link_pointer n) - { - return static_cast(n->next_); - } - - static node_pointer next_for_find(node_pointer n) - { - return static_cast(n->next_); - } - - static link_pointer next_for_erase(link_pointer prev) - { - return prev->next_; - } - - // Group together all nodes with equal hash value, this may - // include nodes with different keys, but that's okay because - // they will end up in the same bucket. - static node_pointer last_for_rehash(link_pointer prev) - { - node_pointer n = next_node(prev); - std::size_t hash = n->hash_; - for (;;) { - node_pointer next = next_node(n); - if (!next || next->hash_ != hash) { - return n; - } - n = next; - } - } - - template - static node_pointer next_group(node_pointer n, Table const* t) - { - node_pointer n1 = n; - do { - n1 = next_node(n1); - } while (n1 && t->key_eq()(t->get_key(n), t->get_key(n1))); - return n1; - } - - template - static std::size_t count(node_pointer n, Table const* t) - { - std::size_t x = 0; - node_pointer it = n; - do { - ++x; - it = next_node(it); - } while (it && t->key_eq()(t->get_key(n), t->get_key(it))); - - return x; - } - - // Add node 'n' after 'pos'. - // This results in a different order to the grouped implementation. - static inline void add_to_node(node_pointer n, node_pointer pos) - { - n->next_ = pos->next_; - pos->next_ = n; - } - - static inline node_pointer extract_first_node(link_pointer prev) - { - node_pointer n = next_node(prev); - prev->next_ = n->next_; - return n; - } - - static link_pointer split_groups(node_pointer, node_pointer) - { - return link_pointer(); - } -}; - // If the allocator uses raw pointers use ptr_node // Otherwise use node. @@ -4546,7 +4502,6 @@ template struct pick_node typedef typename pick::node node; typedef typename pick::bucket bucket; typedef typename pick::link_pointer link_pointer; - typedef boost::unordered::detail::node_algo node_algo; }; } } diff --git a/include/boost/unordered/detail/map.hpp b/include/boost/unordered/detail/map.hpp index 13e223f3..30d8151c 100644 --- a/include/boost/unordered/detail/map.hpp +++ b/include/boost/unordered/detail/map.hpp @@ -27,7 +27,6 @@ template struct map typedef typename pick::node node; typedef typename pick::bucket bucket; typedef typename pick::link_pointer link_pointer; - typedef typename pick::node_algo node_algo; typedef boost::unordered::detail::table table; typedef boost::unordered::detail::map_extractor extractor; @@ -69,7 +68,6 @@ struct multimap typedef typename pick::node node; typedef typename pick::bucket bucket; typedef typename pick::link_pointer link_pointer; - typedef typename pick::node_algo node_algo; typedef boost::unordered::detail::table table; typedef boost::unordered::detail::map_extractor extractor; diff --git a/include/boost/unordered/detail/set.hpp b/include/boost/unordered/detail/set.hpp index edb98ece..edeeaa86 100644 --- a/include/boost/unordered/detail/set.hpp +++ b/include/boost/unordered/detail/set.hpp @@ -27,7 +27,6 @@ template struct set typedef typename pick::node node; typedef typename pick::bucket bucket; typedef typename pick::link_pointer link_pointer; - typedef typename pick::node_algo node_algo; typedef boost::unordered::detail::table table; typedef boost::unordered::detail::set_extractor extractor; @@ -68,7 +67,6 @@ template struct multiset typedef typename pick::node node; typedef typename pick::bucket bucket; typedef typename pick::link_pointer link_pointer; - typedef typename pick::node_algo node_algo; typedef boost::unordered::detail::table table; typedef boost::unordered::detail::set_extractor extractor; diff --git a/include/boost/unordered/unordered_map.hpp b/include/boost/unordered/unordered_map.hpp index ab20642c..b268cb19 100644 --- a/include/boost/unordered/unordered_map.hpp +++ b/include/boost/unordered/unordered_map.hpp @@ -1569,7 +1569,7 @@ unordered_map::erase(iterator position) { node_pointer node = table::get_node(position); BOOST_ASSERT(node); - node_pointer next = table::node_algo::next_node(node); + node_pointer next = table::next_node(node); table_.erase_nodes_unique(node, next); return iterator(next); } @@ -1580,7 +1580,7 @@ unordered_map::erase(const_iterator position) { node_pointer node = table::get_node(position); BOOST_ASSERT(node); - node_pointer next = table::node_algo::next_node(node); + node_pointer next = table::next_node(node); table_.erase_nodes_unique(node, next); return iterator(next); } @@ -1723,8 +1723,7 @@ std::pair::iterator, unordered_map::equal_range(const key_type& k) { node_pointer n = table_.find_node(k); - return std::make_pair( - iterator(n), iterator(n ? table::node_algo::next_node(n) : n)); + return std::make_pair(iterator(n), iterator(n ? table::next_node(n) : n)); } template @@ -1733,8 +1732,8 @@ std::pair::const_iterator, unordered_map::equal_range(const key_type& k) const { node_pointer n = table_.find_node(k); - return std::make_pair(const_iterator(n), - const_iterator(n ? table::node_algo::next_node(n) : n)); + return std::make_pair( + const_iterator(n), const_iterator(n ? table::next_node(n) : n)); } template @@ -2054,7 +2053,7 @@ unordered_multimap::erase(iterator position) { node_pointer node = table::get_node(position); BOOST_ASSERT(node); - node_pointer next = table::node_algo::next_node(node); + node_pointer next = table::next_node(node); table_.erase_nodes_equiv(node, next); return iterator(next); } @@ -2065,7 +2064,7 @@ unordered_multimap::erase(const_iterator position) { node_pointer node = table::get_node(position); BOOST_ASSERT(node); - node_pointer next = table::node_algo::next_node(node); + node_pointer next = table::next_node(node); table_.erase_nodes_equiv(node, next); return iterator(next); } @@ -2209,7 +2208,7 @@ typename unordered_multimap::size_type unordered_multimap::count(const key_type& k) const { node_pointer n = table_.find_node(k); - return n ? table::node_algo::count(n, &table_) : 0; + return n ? table_.group_count(n) : 0; } template @@ -2218,8 +2217,7 @@ std::pair::iterator, unordered_multimap::equal_range(const key_type& k) { node_pointer n = table_.find_node(k); - return std::make_pair(iterator(n), - iterator(n ? table::node_algo::next_group(n, &table_) : n)); + return std::make_pair(iterator(n), iterator(n ? table_.next_group(n) : n)); } template @@ -2228,8 +2226,8 @@ std::pair::const_iterator, unordered_multimap::equal_range(const key_type& k) const { node_pointer n = table_.find_node(k); - return std::make_pair(const_iterator(n), - const_iterator(n ? table::node_algo::next_group(n, &table_) : n)); + return std::make_pair( + const_iterator(n), const_iterator(n ? table_.next_group(n) : n)); } template diff --git a/include/boost/unordered/unordered_set.hpp b/include/boost/unordered/unordered_set.hpp index 674d4e67..b5775a77 100644 --- a/include/boost/unordered/unordered_set.hpp +++ b/include/boost/unordered/unordered_set.hpp @@ -1242,7 +1242,7 @@ typename unordered_set::iterator unordered_set::erase( { node_pointer node = table::get_node(position); BOOST_ASSERT(node); - node_pointer next = table::node_algo::next_node(node); + node_pointer next = table::next_node(node); table_.erase_nodes_unique(node, next); return iterator(next); } @@ -1368,8 +1368,8 @@ std::pair::const_iterator, unordered_set::equal_range(const key_type& k) const { node_pointer n = table_.find_node(k); - return std::make_pair(const_iterator(n), - const_iterator(n ? table::node_algo::next_node(n) : n)); + return std::make_pair( + const_iterator(n), const_iterator(n ? table::next_node(n) : n)); } template @@ -1646,7 +1646,7 @@ unordered_multiset::erase(const_iterator position) { node_pointer node = table::get_node(position); BOOST_ASSERT(node); - node_pointer next = table::node_algo::next_node(node); + node_pointer next = table::next_node(node); table_.erase_nodes_equiv(node, next); return iterator(next); } @@ -1772,7 +1772,7 @@ typename unordered_multiset::size_type unordered_multiset::count(const key_type& k) const { node_pointer n = table_.find_node(k); - return n ? table::node_algo::count(n, &table_) : 0; + return n ? table_.group_count(n) : 0; } template @@ -1781,8 +1781,8 @@ std::pair::const_iterator, unordered_multiset::equal_range(const key_type& k) const { node_pointer n = table_.find_node(k); - return std::make_pair(const_iterator(n), - const_iterator(n ? table::node_algo::next_group(n, &table_) : n)); + return std::make_pair( + const_iterator(n), const_iterator(n ? table_.next_group(n) : n)); } template From 408ebd0a0ad5f29b6a89f0d05f2f9756458eb37a Mon Sep 17 00:00:00 2001 From: Daniel James Date: Thu, 27 Apr 2017 18:22:44 +0100 Subject: [PATCH 3/8] Add node_bucket function --- .../boost/unordered/detail/implementation.hpp | 65 ++++++++----------- 1 file changed, 28 insertions(+), 37 deletions(-) diff --git a/include/boost/unordered/detail/implementation.hpp b/include/boost/unordered/detail/implementation.hpp index 13c549e1..0763b567 100644 --- a/include/boost/unordered/detail/implementation.hpp +++ b/include/boost/unordered/detail/implementation.hpp @@ -2735,6 +2735,11 @@ struct table : boost::unordered::detail::functionshash_to_bucket(n->hash_); + } + bucket_allocator const& bucket_alloc() const { return allocators_.first(); } node_allocator const& node_alloc() const { return allocators_.second(); } @@ -2791,7 +2796,7 @@ struct table : boost::unordered::detail::functionshash_) == index) { + while (n && node_bucket(n) == index) { ++count; n = next_node(n); } @@ -3090,8 +3095,7 @@ struct table : boost::unordered::detail::functions(end)->hash_); + bucket_index2 = node_bucket(static_cast(end)); // If begin and end are in the same bucket, then // there's nothing to do. @@ -3259,13 +3263,10 @@ struct table : boost::unordered::detail::functionshash_; - if (key_hash == node_hash) { - if (eq(k, this->get_key(n))) - return n; - } else { - if (this->hash_to_bucket(node_hash) != bucket_index) - return node_pointer(); + if (eq(k, this->get_key(n))) { + return n; + } else if (this->node_bucket(n) != bucket_index) { + return node_pointer(); } n = next_for_find(n); @@ -3273,8 +3274,7 @@ struct table : boost::unordered::detail::functionsget_previous_start(bucket_index); if (!prev) { @@ -3285,12 +3285,9 @@ struct table : boost::unordered::detail::functionsnext_) { return link_pointer(); } - std::size_t node_hash = next_node(prev)->hash_; - if (this->hash_to_bucket(node_hash) != bucket_index) { + if (node_bucket(next_node(prev)) != bucket_index) { return link_pointer(); - } - if (node_hash == key_hash && - this->key_eq()(k, this->get_key(next_node(prev)))) { + } else if (this->key_eq()(k, this->get_key(next_node(prev)))) { return prev; } prev = next_for_erase(prev); @@ -3306,7 +3303,7 @@ struct table : boost::unordered::detail::functionshash(k); std::size_t bucket_index = this->hash_to_bucket(key_hash); - link_pointer prev = this->find_previous_node(k, key_hash, bucket_index); + link_pointer prev = this->find_previous_node(k, bucket_index); if (!prev) { return node_pointer(); } @@ -3358,9 +3355,7 @@ struct table : boost::unordered::detail::functionsget_previous_start(); if (start_node->next_) { - this->get_bucket( - this->hash_to_bucket(next_node(start_node)->hash_)) - ->next_ = n; + this->get_bucket(node_bucket(next_node(start_node)))->next_ = n; } b->next_ = start_node; @@ -3596,7 +3591,7 @@ struct table : boost::unordered::detail::functionsnext_ = n->next_; --other.size_; - other.fix_bucket(other.hash_to_bucket(n->hash_), prev); + other.fix_bucket(other.node_bucket(n), prev); this->add_node_unique(n, key_hash); } } @@ -3675,8 +3670,7 @@ struct table : boost::unordered::detail::functionshash_; - std::size_t bucket_index = this->hash_to_bucket(key_hash); + std::size_t bucket_index = this->node_bucket(n); link_pointer prev = this->get_previous_start(bucket_index); while (prev->next_ != n) { prev = prev->next_; @@ -3699,7 +3693,7 @@ struct table : boost::unordered::detail::functionshash(k); std::size_t bucket_index = this->hash_to_bucket(key_hash); - link_pointer prev = this->find_previous_node(k, key_hash, bucket_index); + link_pointer prev = this->find_previous_node(k, bucket_index); if (!prev) return 0; link_pointer end = next_node(prev)->next_; @@ -3710,7 +3704,7 @@ struct table : boost::unordered::detail::functionshash_to_bucket(i->hash_); + std::size_t bucket_index = this->node_bucket(i); // Find the node before i. link_pointer prev = this->get_previous_start(bucket_index); @@ -3867,8 +3861,7 @@ struct table : boost::unordered::detail::functionsnext_ = pos->next_; pos->next_ = n; if (n->next_) { - std::size_t next_bucket = - this->hash_to_bucket(next_node(n)->hash_); + std::size_t next_bucket = this->node_bucket(next_node(n)); if (next_bucket != this->hash_to_bucket(key_hash)) { this->get_bucket(next_bucket)->next_ = n; } @@ -3880,8 +3873,7 @@ struct table : boost::unordered::detail::functionsget_previous_start(); if (start_node->next_) { - this->get_bucket( - this->hash_to_bucket(next_node(start_node)->hash_)) + this->get_bucket(this->node_bucket(next_node(start_node))) ->next_ = n; } @@ -3903,8 +3895,8 @@ struct table : boost::unordered::detail::functionsnext_ = hint->next_; hint->next_ = n; if (n->next_ != hint && n->next_) { - std::size_t next_bucket = this->hash_to_bucket(next_node(n)->hash_); - if (next_bucket != this->hash_to_bucket(n->hash_)) { + std::size_t next_bucket = this->node_bucket(next_node(n)); + if (next_bucket != this->node_bucket(n)) { this->get_bucket(next_bucket)->next_ = n; } } @@ -4038,7 +4030,7 @@ struct table : boost::unordered::detail::functionshash_to_bucket(i->hash_); + std::size_t bucket_index = this->node_bucket(i); // Split the groups containing 'i' and 'j'. // And get the pointer to the node before i while // we're at it. @@ -4072,7 +4064,7 @@ struct table : boost::unordered::detail::functionshash(k); std::size_t bucket_index = this->hash_to_bucket(key_hash); - link_pointer prev = this->find_previous_node(k, key_hash, bucket_index); + link_pointer prev = this->find_previous_node(k, bucket_index); if (!prev) return 0; @@ -4086,7 +4078,7 @@ struct table : boost::unordered::detail::functionshash_to_bucket(i->hash_); + std::size_t bucket_index = this->node_bucket(i); // Split the groups containing 'i' and 'j'. // And get the pointer to the node before i while @@ -4247,8 +4239,7 @@ inline void table::rehash_impl(std::size_t num_buckets) group_last = next; } - bucket_pointer b = - this->get_bucket(this->hash_to_bucket(group_last->hash_)); + bucket_pointer b = this->get_bucket(this->node_bucket(group_last)); if (!b->next_) { b->next_ = prev; prev = group_last; From a1b1df84a0014cb5f0d148f72614c692d1307ce5 Mon Sep 17 00:00:00 2001 From: Daniel James Date: Thu, 27 Apr 2017 18:22:44 +0100 Subject: [PATCH 4/8] Store bucket + whether first in group in node Instead of the hash value. --- .../boost/unordered/detail/implementation.hpp | 216 ++++++++++++------ test/helpers/invariants.hpp | 34 +-- 2 files changed, 162 insertions(+), 88 deletions(-) diff --git a/include/boost/unordered/detail/implementation.hpp b/include/boost/unordered/detail/implementation.hpp index 0763b567..1f4c9166 100644 --- a/include/boost/unordered/detail/implementation.hpp +++ b/include/boost/unordered/detail/implementation.hpp @@ -1935,7 +1935,7 @@ struct l_iterator : public std::iterator(ptr_->next_); - if (ptr_ && Policy::to_bucket(bucket_count_, ptr_->hash_) != bucket_) + if (ptr_ && ptr_->get_bucket() != bucket_) ptr_ = node_pointer(); return *this; } @@ -1999,7 +1999,7 @@ struct cl_iterator cl_iterator& operator++() { ptr_ = static_cast(ptr_->next_); - if (ptr_ && Policy::to_bucket(bucket_count_, ptr_->hash_) != bucket_) + if (ptr_ && ptr_->get_bucket() != bucket_) ptr_ = node_pointer(); return *this; } @@ -2706,7 +2706,11 @@ struct table : boost::unordered::detail::functions(n->next_); + node_pointer n2 = static_cast(n); + do { + n2 = next_node(n2); + } while (n2 && !n2->is_first_in_group()); + return n2; } static link_pointer next_for_erase(link_pointer prev) @@ -2719,7 +2723,7 @@ struct table : boost::unordered::detail::functionskey_eq()(get_key(n), get_key(n1))); + } while (n1 && !n1->is_first_in_group()); return n1; } @@ -2730,15 +2734,12 @@ struct table : boost::unordered::detail::functionskey_eq()(get_key(n), get_key(it))); + } while (it && !it->is_first_in_group()); return x; } - std::size_t node_bucket(node_pointer n) const - { - return this->hash_to_bucket(n->hash_); - } + std::size_t node_bucket(node_pointer n) const { return n->get_bucket(); } bucket_allocator const& bucket_alloc() const { return allocators_.first(); } @@ -3282,15 +3283,17 @@ struct table : boost::unordered::detail::functionsnext_) { + node_pointer n = next_node(prev); + if (!n) { return link_pointer(); + } else if (n->is_first_in_group()) { + if (node_bucket(n) != bucket_index) { + return link_pointer(); + } else if (this->key_eq()(k, this->get_key(n))) { + return prev; + } } - if (node_bucket(next_node(prev)) != bucket_index) { - return link_pointer(); - } else if (this->key_eq()(k, this->get_key(next_node(prev)))) { - return prev; - } - prev = next_for_erase(prev); + prev = n; } } @@ -3308,7 +3311,11 @@ struct table : boost::unordered::detail::functionsnext_ = n->next_; + node_pointer n2 = next_node(n); + if (n2) { + n2->set_first_in_group(); + } + prev->next_ = n2; --this->size_; this->fix_bucket(bucket_index, prev); n->next_ = link_pointer(); @@ -3347,9 +3354,12 @@ struct table : boost::unordered::detail::functionshash_ = key_hash; + std::size_t bucket = this->hash_to_bucket(key_hash); + bucket_pointer b = this->get_bucket(bucket); - bucket_pointer b = this->get_bucket(this->hash_to_bucket(key_hash)); + // TODO: Do this need to set_first_in_group ? + n->bucket_info_ = bucket; + n->set_first_in_group(); if (!b->next_) { link_pointer start_node = this->get_previous_start(); @@ -3567,6 +3577,8 @@ struct table : boost::unordered::detail::functions void merge_unique(boost::unordered::detail::table& other) { @@ -3588,8 +3600,10 @@ struct table : boost::unordered::detail::functionsreserve_for_insert(this->size_ + 1); - // other_table::split_groups(n, other_table::next_node(n)); prev->next_ = n->next_; + if (prev->next_ && n->is_first_in_group()) { + next_node(prev)->set_first_in_group(); + } --other.size_; other.fix_bucket(other.node_bucket(n), prev); this->add_node_unique(n, key_hash); @@ -3726,10 +3740,11 @@ struct table : boost::unordered::detail::functionscreate_buckets(this->bucket_count_); for (node_pointer n = src.begin(); n; n = next_node(n)) { + std::size_t key_hash = this->hash(this->get_key(n)); this->add_node_unique( boost::unordered::detail::func::construct_node( this->node_alloc(), n->value()), - n->hash_); + key_hash); } } @@ -3739,10 +3754,11 @@ struct table : boost::unordered::detail::functionscreate_buckets(this->bucket_count_); for (node_pointer n = src.begin(); n; n = next_node(n)) { + std::size_t key_hash = this->hash(this->get_key(n)); this->add_node_unique( boost::unordered::detail::func::construct_node( this->node_alloc(), boost::move(n->value())), - n->hash_); + key_hash); } } @@ -3750,7 +3766,8 @@ struct table : boost::unordered::detail::functions holder(*this); for (node_pointer n = src.begin(); n; n = next_node(n)) { - this->add_node_unique(holder.copy_of(n->value()), n->hash_); + std::size_t key_hash = this->hash(this->get_key(n)); + this->add_node_unique(holder.copy_of(n->value()), key_hash); } } @@ -3758,7 +3775,8 @@ struct table : boost::unordered::detail::functions holder(*this); for (node_pointer n = src.begin(); n; n = next_node(n)) { - this->add_node_unique(holder.move_copy_of(n->value()), n->hash_); + std::size_t key_hash = this->hash(this->get_key(n)); + this->add_node_unique(holder.move_copy_of(n->value()), key_hash); } } @@ -3856,18 +3874,21 @@ struct table : boost::unordered::detail::functionshash_ = key_hash; + std::size_t bucket = this->hash_to_bucket(key_hash); + n->bucket_info_ = bucket; + if (pos) { n->next_ = pos->next_; pos->next_ = n; if (n->next_) { std::size_t next_bucket = this->node_bucket(next_node(n)); - if (next_bucket != this->hash_to_bucket(key_hash)) { + if (next_bucket != bucket) { this->get_bucket(next_bucket)->next_ = n; } } } else { - bucket_pointer b = this->get_bucket(this->hash_to_bucket(key_hash)); + n->set_first_in_group(); + bucket_pointer b = this->get_bucket(bucket); if (!b->next_) { link_pointer start_node = this->get_previous_start(); @@ -3891,7 +3912,8 @@ struct table : boost::unordered::detail::functionshash_ = hint->hash_; + n->bucket_info_ = hint->bucket_info_; + n->reset_first_in_group(); n->next_ = hint->next_; hint->next_ = n; if (n->next_ != hint && n->next_) { @@ -4031,20 +4053,16 @@ struct table : boost::unordered::detail::functionsnode_bucket(i); - // Split the groups containing 'i' and 'j'. - // And get the pointer to the node before i while - // we're at it. - // link_pointer prev = split_groups(i, j); - // If we don't have a 'prev' it means that i is at the - // beginning of a block, so search through the blocks in the - // same bucket. link_pointer prev = this->get_previous_start(bucket_index); while (prev->next_ != i) { prev = next_for_erase(prev); } prev->next_ = i->next_; + if (j && i->is_first_in_group()) { + j->set_first_in_group(); + } --this->size_; this->fix_bucket(bucket_index, prev); i->next_ = link_pointer(); @@ -4080,14 +4098,6 @@ struct table : boost::unordered::detail::functionsnode_bucket(i); - // Split the groups containing 'i' and 'j'. - // And get the pointer to the node before i while - // we're at it. - // link_pointer prev = split_groups(i, j); - - // If we don't have a 'prev' it means that i is at the - // beginning of a block, so search through the blocks in the - // same bucket. link_pointer prev = this->get_previous_start(bucket_index); while (prev->next_ != i) { prev = next_for_erase(prev); @@ -4095,10 +4105,16 @@ struct table : boost::unordered::detail::functionsis_first_in_group(); this->delete_node(prev); bucket_index = this->fix_bucket(bucket_index, prev); } while (prev->next_ != j); + if (j && includes_first) { + j->set_first_in_group(); + } return prev; } @@ -4111,7 +4127,7 @@ struct table : boost::unordered::detail::functionscreate_buckets(this->bucket_count_); for (node_pointer n = src.begin(); n;) { - std::size_t key_hash = n->hash_; + std::size_t key_hash = this->hash(this->get_key(n)); node_pointer group_end(next_group(n)); node_pointer pos = this->add_node_equiv( boost::unordered::detail::func::construct_node( @@ -4131,7 +4147,7 @@ struct table : boost::unordered::detail::functionscreate_buckets(this->bucket_count_); for (node_pointer n = src.begin(); n;) { - std::size_t key_hash = n->hash_; + std::size_t key_hash = this->hash(this->get_key(n)); node_pointer group_end(next_group(n)); node_pointer pos = this->add_node_equiv( boost::unordered::detail::func::construct_node( @@ -4150,7 +4166,7 @@ struct table : boost::unordered::detail::functions holder(*this); for (node_pointer n = src.begin(); n;) { - std::size_t key_hash = n->hash_; + std::size_t key_hash = this->hash(this->get_key(n)); node_pointer group_end(next_group(n)); node_pointer pos = this->add_node_equiv( holder.copy_of(n->value()), key_hash, node_pointer()); @@ -4164,7 +4180,7 @@ struct table : boost::unordered::detail::functions holder(*this); for (node_pointer n = src.begin(); n;) { - std::size_t key_hash = n->hash_; + std::size_t key_hash = this->hash(this->get_key(n)); node_pointer group_end(next_group(n)); node_pointer pos = this->add_node_equiv( holder.move_copy_of(n->value()), key_hash, node_pointer()); @@ -4223,33 +4239,43 @@ inline void table::rehash_impl(std::size_t num_buckets) this->create_buckets(num_buckets); link_pointer prev = this->get_previous_start(); - while (prev->next_) { - // Group together all nodes with equal hash value, this may - // include nodes with different keys, but that's okay because - // they will end up in the same bucket. - // - // TODO: Don't do this for unique keys? - node_pointer group_last = next_node(prev); - std::size_t hash = group_last->hash_; - for (;;) { - node_pointer next = next_node(group_last); - if (!next || next->hash_ != hash) { - break; - } - group_last = next; - } + BOOST_TRY + { + while (prev->next_) { + node_pointer n = next_node(prev); + std::size_t key_hash = this->hash(this->get_key(n)); + std::size_t bucket_index = this->hash_to_bucket(key_hash); - bucket_pointer b = this->get_bucket(this->node_bucket(group_last)); - if (!b->next_) { - b->next_ = prev; - prev = group_last; - } else { - link_pointer next = group_last->next_; - group_last->next_ = b->next_->next_; - b->next_->next_ = prev->next_; - prev->next_ = next; + n->bucket_info_ = bucket_index; + n->set_first_in_group(); + + // Iterator through the rest of the group of equal nodes, + // setting the bucket. + for (;;) { + node_pointer next = next_node(n); + if (!next || next->is_first_in_group()) { + break; + } + n = next; + n->bucket_info_ = bucket_index; + // n->reset_first_in_group(); + } + + // n is now the last node in the group + bucket_pointer b = this->get_bucket(bucket_index); + if (!b->next_) { + b->next_ = prev; + prev = n; + } else { + link_pointer next = n->next_; + n->next_ = b->next_->next_; + b->next_->next_ = prev->next_; + prev->next_ = next; + } } } + BOOST_CATCH(...) { delete_nodes(prev, link_pointer()); } + BOOST_CATCH_END } #if defined(BOOST_MSVC) @@ -4414,12 +4440,32 @@ struct node : boost::unordered::detail::value_base bucket_allocator>::pointer bucket_pointer; link_pointer next_; - std::size_t hash_; + std::size_t bucket_info_; - node() : next_(), hash_(0) {} + node() : next_(), bucket_info_(0) {} void init(node_pointer) {} + std::size_t get_bucket() const + { + return bucket_info_ & ((std::size_t)-1 >> 1); + } + + std::size_t is_first_in_group() const + { + return bucket_info_ & ~((std::size_t)-1 >> 1); + } + + void set_first_in_group() + { + bucket_info_ = bucket_info_ | ~((std::size_t)-1 >> 1); + } + + void reset_first_in_group() + { + bucket_info_ = bucket_info_ & ((std::size_t)-1 >> 1); + } + private: node& operator=(node const&); }; @@ -4432,10 +4478,10 @@ template struct ptr_node : boost::unordered::detail::ptr_bucket typedef ptr_bucket* link_pointer; typedef ptr_bucket* bucket_pointer; - std::size_t hash_; + std::size_t bucket_info_; boost::unordered::detail::value_base value_base_; - ptr_node() : bucket_base(), hash_(0) {} + ptr_node() : bucket_base(), bucket_info_(0) {} void init(node_pointer) {} @@ -4443,6 +4489,26 @@ template struct ptr_node : boost::unordered::detail::ptr_bucket value_type& value() { return value_base_.value(); } value_type* value_ptr() { return value_base_.value_ptr(); } + std::size_t get_bucket() const + { + return bucket_info_ & ((std::size_t)-1 >> 1); + } + + std::size_t is_first_in_group() const + { + return bucket_info_ & ~((std::size_t)-1 >> 1); + } + + void set_first_in_group() + { + bucket_info_ = bucket_info_ | ~((std::size_t)-1 >> 1); + } + + void reset_first_in_group() + { + bucket_info_ = bucket_info_ & ((std::size_t)-1 >> 1); + } + private: ptr_node& operator=(ptr_node const&); }; diff --git a/test/helpers/invariants.hpp b/test/helpers/invariants.hpp index ffc5de4e..06091b94 100644 --- a/test/helpers/invariants.hpp +++ b/test/helpers/invariants.hpp @@ -63,20 +63,28 @@ template void check_equivalent_keys(X const& x1) BOOST_DEDUCED_TYPENAME X::size_type bucket = x1.bucket(key); BOOST_DEDUCED_TYPENAME X::const_local_iterator lit = x1.begin(bucket), lend = x1.end(bucket); - for (; lit != lend && !eq(get_key(*lit), key); ++lit) - continue; - if (lit == lend) + + unsigned int count_checked = 0; + for (; lit != lend && !eq(get_key(*lit), key); ++lit) { + ++count_checked; + } + + if (lit == lend) { BOOST_ERROR("Unable to find element with a local_iterator"); - unsigned int count2 = 0; - for (; lit != lend && eq(get_key(*lit), key); ++lit) - ++count2; - if (count != count2) - BOOST_ERROR("Element count doesn't match local_iterator."); - for (; lit != lend; ++lit) { - if (eq(get_key(*lit), key)) { - BOOST_ERROR("Non-adjacent element with equivalent key " - "in bucket."); - break; + std::cerr << "Checked: " << count_checked << " elements" + << std::endl; + } else { + unsigned int count2 = 0; + for (; lit != lend && eq(get_key(*lit), key); ++lit) + ++count2; + if (count != count2) + BOOST_ERROR("Element count doesn't match local_iterator."); + for (; lit != lend; ++lit) { + if (eq(get_key(*lit), key)) { + BOOST_ERROR("Non-adjacent element with equivalent key " + "in bucket."); + break; + } } } }; From 20b0c0a6d8cf70faefe5dac5cea23218a76344d3 Mon Sep 17 00:00:00 2001 From: Daniel James Date: Thu, 27 Apr 2017 18:22:44 +0100 Subject: [PATCH 5/8] Only consider one node from each group in merge_unique --- .../boost/unordered/detail/implementation.hpp | 55 ++++++++++++------- 1 file changed, 36 insertions(+), 19 deletions(-) diff --git a/include/boost/unordered/detail/implementation.hpp b/include/boost/unordered/detail/implementation.hpp index 1f4c9166..25aa1f34 100644 --- a/include/boost/unordered/detail/implementation.hpp +++ b/include/boost/unordered/detail/implementation.hpp @@ -3577,8 +3577,6 @@ struct table : boost::unordered::detail::functions void merge_unique(boost::unordered::detail::table& other) { @@ -3587,26 +3585,45 @@ struct table : boost::unordered::detail::functions::value)); BOOST_ASSERT(this->node_alloc() == other.node_alloc()); - if (other.size_) { - link_pointer prev = other.get_previous_start(); + if (!other.size_) { + return; + } - while (prev->next_) { - node_pointer n = other_table::next_node(prev); - const_key_type& k = this->get_key(n); - std::size_t key_hash = this->hash(k); - node_pointer pos = this->find_node(key_hash, k); + link_pointer prev = other.get_previous_start(); + node_pointer n = other_table::next_node(prev); + while (n) { + const_key_type& k = this->get_key(n); + std::size_t key_hash = this->hash(k); + node_pointer pos = this->find_node(key_hash, k); - if (pos) { + if (pos) { + prev = n; + n = other_table::next_node(prev); + } else { + this->reserve_for_insert(this->size_ + 1); + + prev->next_ = n->next_; + --other.size_; + other.fix_bucket(other.node_bucket(n), prev); + this->add_node_unique(n, key_hash); + n = other_table::next_node(prev); + + // If the next node was from the same group, it's now + // the first node in the group. + if (n && !n->is_first_in_group()) { + n->set_first_in_group(); prev = n; - } else { - this->reserve_for_insert(this->size_ + 1); - prev->next_ = n->next_; - if (prev->next_ && n->is_first_in_group()) { - next_node(prev)->set_first_in_group(); - } - --other.size_; - other.fix_bucket(other.node_bucket(n), prev); - this->add_node_unique(n, key_hash); + n = other_table::next_node(prev); + } + } + + // Skip over rest of group of nodes with equivalent keys, + // as we know there's already one in the container. + while (n && !n->is_first_in_group()) { + prev = n; + n = other_table::next_node(prev); + if (!n) { + return; } } } From f1435d53d4b421b8692cc3903de9db2a2a2db777 Mon Sep 17 00:00:00 2001 From: Daniel James Date: Thu, 27 Apr 2017 18:22:44 +0100 Subject: [PATCH 6/8] Remove 'init' method from nodes --- include/boost/unordered/detail/implementation.hpp | 6 ------ 1 file changed, 6 deletions(-) diff --git a/include/boost/unordered/detail/implementation.hpp b/include/boost/unordered/detail/implementation.hpp index 25aa1f34..0f5f6807 100644 --- a/include/boost/unordered/detail/implementation.hpp +++ b/include/boost/unordered/detail/implementation.hpp @@ -1691,7 +1691,6 @@ template void node_constructor::create_node() BOOST_UNORDERED_CALL_CONSTRUCT0( node_allocator_traits, alloc_, boost::addressof(*node_)); - node_->init(node_); node_constructed_ = true; } @@ -2182,7 +2181,6 @@ template struct node_holder { node_pointer n = nodes_; nodes_ = static_cast(nodes_->next_); - n->init(n); n->next_ = link_pointer(); return n; } @@ -4461,8 +4459,6 @@ struct node : boost::unordered::detail::value_base node() : next_(), bucket_info_(0) {} - void init(node_pointer) {} - std::size_t get_bucket() const { return bucket_info_ & ((std::size_t)-1 >> 1); @@ -4500,8 +4496,6 @@ template struct ptr_node : boost::unordered::detail::ptr_bucket ptr_node() : bucket_base(), bucket_info_(0) {} - void init(node_pointer) {} - void* address() { return value_base_.address(); } value_type& value() { return value_base_.value(); } value_type* value_ptr() { return value_base_.value_ptr(); } From 03baef8b2865bf897061fa11db40c6398447cb52 Mon Sep 17 00:00:00 2001 From: Daniel James Date: Thu, 27 Apr 2017 18:22:44 +0100 Subject: [PATCH 7/8] Remove Types::is_unique --- .../boost/unordered/detail/implementation.hpp | 34 ++++++++++--------- include/boost/unordered/detail/map.hpp | 8 ----- include/boost/unordered/detail/set.hpp | 8 ----- include/boost/unordered/unordered_map.hpp | 16 ++++----- include/boost/unordered/unordered_set.hpp | 16 ++++----- 5 files changed, 34 insertions(+), 48 deletions(-) diff --git a/include/boost/unordered/detail/implementation.hpp b/include/boost/unordered/detail/implementation.hpp index 0f5f6807..ae4ba6ca 100644 --- a/include/boost/unordered/detail/implementation.hpp +++ b/include/boost/unordered/detail/implementation.hpp @@ -3116,16 +3116,17 @@ struct table : boost::unordered::detail::functions:: - propagate_on_container_copy_assignment::value>()); + assign(x, is_unique, + boost::unordered::detail::integral_constant:: + propagate_on_container_copy_assignment::value>()); } } - void assign(table const& x, false_type) + void assign(table const& x, bool is_unique, false_type) { // Strong exception safety. set_hash_functions new_func_this(*this, x); @@ -3140,18 +3141,18 @@ struct table : boost::unordered::detail::functions:: - propagate_on_container_move_assignment::value>()); + x, is_unique, + boost::unordered::detail::integral_constant:: + propagate_on_container_move_assignment::value>()); } } - void move_assign(table& x, true_type) + void move_assign(table& x, bool /* is_unique */, true_type) { delete_buckets(); set_hash_functions new_func_this(*this, x); @@ -3197,7 +3199,7 @@ struct table : boost::unordered::detail::functions struct map typedef boost::unordered::detail::table table; typedef boost::unordered::detail::map_extractor extractor; - enum - { - is_unique = true - }; typedef typename boost::unordered::detail::pick_policy::type policy; @@ -71,10 +67,6 @@ struct multimap typedef boost::unordered::detail::table table; typedef boost::unordered::detail::map_extractor extractor; - enum - { - is_unique = false - }; typedef typename boost::unordered::detail::pick_policy::type policy; diff --git a/include/boost/unordered/detail/set.hpp b/include/boost/unordered/detail/set.hpp index edeeaa86..12949652 100644 --- a/include/boost/unordered/detail/set.hpp +++ b/include/boost/unordered/detail/set.hpp @@ -30,10 +30,6 @@ template struct set typedef boost::unordered::detail::table table; typedef boost::unordered::detail::set_extractor extractor; - enum - { - is_unique = true - }; typedef typename boost::unordered::detail::pick_policy::type policy; @@ -70,10 +66,6 @@ template struct multiset typedef boost::unordered::detail::table table; typedef boost::unordered::detail::set_extractor extractor; - enum - { - is_unique = false - }; typedef typename boost::unordered::detail::pick_policy::type policy; diff --git a/include/boost/unordered/unordered_map.hpp b/include/boost/unordered/unordered_map.hpp index b268cb19..d0e3eae6 100644 --- a/include/boost/unordered/unordered_map.hpp +++ b/include/boost/unordered/unordered_map.hpp @@ -146,7 +146,7 @@ template class unordered_map #if defined(BOOST_UNORDERED_USE_MOVE) unordered_map& operator=(BOOST_COPY_ASSIGN_REF(unordered_map) x) { - table_.assign(x.table_); + table_.assign(x.table_, true); return *this; } @@ -156,13 +156,13 @@ template class unordered_map // is_nothrow_move_assignable_v && // is_nothrow_move_assignable_v

) { - table_.move_assign(x.table_); + table_.move_assign(x.table_, true); return *this; } #else unordered_map& operator=(unordered_map const& x) { - table_.assign(x.table_); + table_.assign(x.table_, true); return *this; } @@ -173,7 +173,7 @@ template class unordered_map // is_nothrow_move_assignable_v && // is_nothrow_move_assignable_v

) { - table_.move_assign(x.table_); + table_.move_assign(x.table_, true); return *this; } #endif @@ -950,7 +950,7 @@ template class unordered_multimap #if defined(BOOST_UNORDERED_USE_MOVE) unordered_multimap& operator=(BOOST_COPY_ASSIGN_REF(unordered_multimap) x) { - table_.assign(x.table_); + table_.assign(x.table_, false); return *this; } @@ -960,13 +960,13 @@ template class unordered_multimap // is_nothrow_move_assignable_v && // is_nothrow_move_assignable_v

) { - table_.move_assign(x.table_); + table_.move_assign(x.table_, false); return *this; } #else unordered_multimap& operator=(unordered_multimap const& x) { - table_.assign(x.table_); + table_.assign(x.table_, false); return *this; } @@ -977,7 +977,7 @@ template class unordered_multimap // is_nothrow_move_assignable_v && // is_nothrow_move_assignable_v

) { - table_.move_assign(x.table_); + table_.move_assign(x.table_, false); return *this; } #endif diff --git a/include/boost/unordered/unordered_set.hpp b/include/boost/unordered/unordered_set.hpp index b5775a77..1821b3ca 100644 --- a/include/boost/unordered/unordered_set.hpp +++ b/include/boost/unordered/unordered_set.hpp @@ -144,7 +144,7 @@ template class unordered_set #if defined(BOOST_UNORDERED_USE_MOVE) unordered_set& operator=(BOOST_COPY_ASSIGN_REF(unordered_set) x) { - table_.assign(x.table_); + table_.assign(x.table_, true); return *this; } @@ -154,13 +154,13 @@ template class unordered_set // is_nothrow_move_assignable_v && // is_nothrow_move_assignable_v

) { - table_.move_assign(x.table_); + table_.move_assign(x.table_, true); return *this; } #else unordered_set& operator=(unordered_set const& x) { - table_.assign(x.table_); + table_.assign(x.table_, true); return *this; } @@ -171,7 +171,7 @@ template class unordered_set // is_nothrow_move_assignable_v && // is_nothrow_move_assignable_v

) { - table_.move_assign(x.table_); + table_.move_assign(x.table_, true); return *this; } #endif @@ -654,7 +654,7 @@ template class unordered_multiset #if defined(BOOST_UNORDERED_USE_MOVE) unordered_multiset& operator=(BOOST_COPY_ASSIGN_REF(unordered_multiset) x) { - table_.assign(x.table_); + table_.assign(x.table_, false); return *this; } @@ -664,13 +664,13 @@ template class unordered_multiset // is_nothrow_move_assignable_v && // is_nothrow_move_assignable_v

) { - table_.move_assign(x.table_); + table_.move_assign(x.table_, false); return *this; } #else unordered_multiset& operator=(unordered_multiset const& x) { - table_.assign(x.table_); + table_.assign(x.table_, false); return *this; } @@ -681,7 +681,7 @@ template class unordered_multiset // is_nothrow_move_assignable_v && // is_nothrow_move_assignable_v

) { - table_.move_assign(x.table_); + table_.move_assign(x.table_, false); return *this; } #endif From 6466ce0b518b02a490edcb569edf06799248d212 Mon Sep 17 00:00:00 2001 From: Daniel James Date: Thu, 27 Apr 2017 18:22:44 +0100 Subject: [PATCH 8/8] Make table the same for unique and equivalent keys --- include/boost/unordered/detail/map.hpp | 35 ----------------------- include/boost/unordered/detail/set.hpp | 34 ---------------------- include/boost/unordered/unordered_map.hpp | 2 +- include/boost/unordered/unordered_set.hpp | 2 +- 4 files changed, 2 insertions(+), 71 deletions(-) diff --git a/include/boost/unordered/detail/map.hpp b/include/boost/unordered/detail/map.hpp index 2547196e..4568e75f 100644 --- a/include/boost/unordered/detail/map.hpp +++ b/include/boost/unordered/detail/map.hpp @@ -45,41 +45,6 @@ template struct map insert_return_type; }; -template -struct multimap -{ - typedef boost::unordered::detail::multimap types; - - typedef std::pair value_type; - typedef H hasher; - typedef P key_equal; - typedef K const const_key_type; - - typedef typename ::boost::unordered::detail::rebind_wrap::type value_allocator; - typedef boost::unordered::detail::allocator_traits - value_allocator_traits; - - typedef boost::unordered::detail::pick_node pick; - typedef typename pick::node node; - typedef typename pick::bucket bucket; - typedef typename pick::link_pointer link_pointer; - - typedef boost::unordered::detail::table table; - typedef boost::unordered::detail::map_extractor extractor; - - typedef typename boost::unordered::detail::pick_policy::type policy; - - typedef boost::unordered::iterator_detail::iterator iterator; - typedef boost::unordered::iterator_detail::c_iterator c_iterator; - typedef boost::unordered::iterator_detail::l_iterator - l_iterator; - typedef boost::unordered::iterator_detail::cl_iterator - cl_iterator; - - typedef boost::unordered::node_handle_map node_type; -}; - template class instantiate_map { diff --git a/include/boost/unordered/detail/set.hpp b/include/boost/unordered/detail/set.hpp index 12949652..fc13fa17 100644 --- a/include/boost/unordered/detail/set.hpp +++ b/include/boost/unordered/detail/set.hpp @@ -45,40 +45,6 @@ template struct set insert_return_type; }; -template struct multiset -{ - typedef boost::unordered::detail::multiset types; - - typedef T value_type; - typedef H hasher; - typedef P key_equal; - typedef T const const_key_type; - - typedef typename ::boost::unordered::detail::rebind_wrap::type value_allocator; - typedef boost::unordered::detail::allocator_traits - value_allocator_traits; - - typedef boost::unordered::detail::pick_node pick; - typedef typename pick::node node; - typedef typename pick::bucket bucket; - typedef typename pick::link_pointer link_pointer; - - typedef boost::unordered::detail::table table; - typedef boost::unordered::detail::set_extractor extractor; - - typedef typename boost::unordered::detail::pick_policy::type policy; - - typedef boost::unordered::iterator_detail::c_iterator iterator; - typedef boost::unordered::iterator_detail::c_iterator c_iterator; - typedef boost::unordered::iterator_detail::cl_iterator - l_iterator; - typedef boost::unordered::iterator_detail::cl_iterator - cl_iterator; - - typedef boost::unordered::node_handle_set node_type; -}; - template class instantiate_set { typedef boost::unordered_set container; diff --git a/include/boost/unordered/unordered_map.hpp b/include/boost/unordered/unordered_map.hpp index d0e3eae6..6291c3dc 100644 --- a/include/boost/unordered/unordered_map.hpp +++ b/include/boost/unordered/unordered_map.hpp @@ -856,7 +856,7 @@ template class unordered_multimap typedef A allocator_type; private: - typedef boost::unordered::detail::multimap types; + typedef boost::unordered::detail::map types; typedef typename types::value_allocator_traits value_allocator_traits; typedef typename types::table table; typedef typename table::node_pointer node_pointer; diff --git a/include/boost/unordered/unordered_set.hpp b/include/boost/unordered/unordered_set.hpp index 1821b3ca..bbd07f60 100644 --- a/include/boost/unordered/unordered_set.hpp +++ b/include/boost/unordered/unordered_set.hpp @@ -560,7 +560,7 @@ template class unordered_multiset typedef A allocator_type; private: - typedef boost::unordered::detail::multiset types; + typedef boost::unordered::detail::set types; typedef typename types::value_allocator_traits value_allocator_traits; typedef typename types::table table; typedef typename table::node_pointer node_pointer;