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