diff --git a/doc/ref.php b/doc/ref.php index f6e26e91..abdb9a17 100644 --- a/doc/ref.php +++ b/doc/ref.php @@ -7,6 +7,10 @@ function echo_unordered_docs( $name = 'unordered_'. ($equivalent_keys ? 'multi' : ''). ($map ? 'map' : 'set'); + // For merge.... + $node_partner = 'unordered_'. + ($equivalent_keys ? '' : 'multi'). + ($map ? 'map' : 'set'); if ($map) { @@ -1157,6 +1161,68 @@ EOL; the equality predieate and hash function are swapped using their copy constructors. + + + + + <Key, Mapped, H2, P2, Alloc>& + + <Value, H2, P2, Alloc>& + + + + + + + + <Key, Mapped, H2, P2, Alloc>&& + + <Value, H2, P2, Alloc>&& + + + + + + + + <Key, Mapped, H2, P2, Alloc>& + + <Value, H2, P2, Alloc>& + + + + + + + + <Key, Mapped, H2, P2, Alloc>&& + + <Value, H2, P2, Alloc>&& + + + +*/ ?> diff --git a/doc/ref.xml b/doc/ref.xml index bdb368e0..755721ca 100644 --- a/doc/ref.xml +++ b/doc/ref.xml @@ -1028,6 +1028,28 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) the equality predieate and hash function are swapped using their copy constructors. + + + + unordered_set<Value, H2, P2, Alloc>& + + + + + + unordered_set<Value, H2, P2, Alloc>&& + + @@ -2382,6 +2404,28 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) the equality predieate and hash function are swapped using their copy constructors. + + + + unordered_multiset<Value, H2, P2, Alloc>& + + + + + + unordered_multiset<Value, H2, P2, Alloc>&& + + @@ -3859,6 +3903,28 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) the equality predieate and hash function are swapped using their copy constructors. + + + + unordered_map<Key, Mapped, H2, P2, Alloc>& + + + + + + unordered_map<Key, Mapped, H2, P2, Alloc>&& + + @@ -5260,6 +5326,28 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) the equality predieate and hash function are swapped using their copy constructors. + + + + unordered_multimap<Key, Mapped, H2, P2, Alloc>& + + + + + + unordered_multimap<Key, Mapped, H2, P2, Alloc>&& + + diff --git a/include/boost/unordered/detail/implementation.hpp b/include/boost/unordered/detail/implementation.hpp index bbda40b1..cb6b14bc 100644 --- a/include/boost/unordered/detail/implementation.hpp +++ b/include/boost/unordered/detail/implementation.hpp @@ -3840,6 +3840,30 @@ struct table_impl : boost::unordered::detail::table return iterator(pos); } + template void merge_impl(table_impl& other) + { + if (other.size_) { + link_pointer prev = other.get_previous_start(); + + while (prev->next_) { + node_pointer n = other.next_node(prev); + const_key_type& k = this->get_key(n->value()); + std::size_t key_hash = this->hash(k); + node_pointer pos = this->find_node(key_hash, k); + + if (pos) { + prev = n; + } else { + this->reserve_for_insert(this->size_ + 1); + prev->next_ = n->next_; + --other.size_; + other.fix_bucket(other.hash_to_bucket(n->hash_), prev); + this->add_node(n, key_hash); + } + } + } + } + //////////////////////////////////////////////////////////////////////// // Insert range methods // diff --git a/include/boost/unordered/unordered_map.hpp b/include/boost/unordered/unordered_map.hpp index ab1c79e7..8ba7e83d 100644 --- a/include/boost/unordered/unordered_map.hpp +++ b/include/boost/unordered/unordered_map.hpp @@ -617,6 +617,17 @@ template class unordered_map void clear(); void swap(unordered_map&); + template + void merge(boost::unordered_map& source); +#if !defined(BOOST_NO_CXX11_RVALUE_REFERENCES) + template + void merge(boost::unordered_map&& source); +#endif + // template + // void merge(boost::unordered_multimap& source); + // template + // void merge(boost::unordered_multimap&& source); + // observers hasher hash_function() const; @@ -1055,6 +1066,17 @@ template class unordered_multimap void clear(); void swap(unordered_multimap&); + template + void merge(boost::unordered_multimap& source); +#if !defined(BOOST_NO_CXX11_RVALUE_REFERENCES) + template + void merge(boost::unordered_multimap&& source); +#endif + // template + // void merge(boost::unordered_map& source); + // template + // void merge(boost::unordered_map&& source); + // observers hasher hash_function() const; @@ -1356,6 +1378,24 @@ void unordered_map::swap(unordered_map& other) table_.swap(other.table_); } +template +template +void unordered_map::merge( + boost::unordered_map& source) +{ + table_.merge_impl(source.table_); +} + +#if !defined(BOOST_NO_CXX11_RVALUE_REFERENCES) +template +template +void unordered_map::merge( + boost::unordered_map&& source) +{ + table_.merge_impl(source.table_); +} +#endif + // observers template @@ -1756,6 +1796,28 @@ unordered_multimap::key_eq() const return table_.key_eq(); } +template +template +void unordered_multimap::merge( + boost::unordered_multimap& source) +{ + while (!source.empty()) { + insert(source.extract(source.begin())); + } +} + +#if !defined(BOOST_NO_CXX11_RVALUE_REFERENCES) +template +template +void unordered_multimap::merge( + boost::unordered_multimap&& source) +{ + while (!source.empty()) { + insert(source.extract(source.begin())); + } +} +#endif + // lookup template diff --git a/include/boost/unordered/unordered_set.hpp b/include/boost/unordered/unordered_set.hpp index 1f1efe47..9eb90898 100644 --- a/include/boost/unordered/unordered_set.hpp +++ b/include/boost/unordered/unordered_set.hpp @@ -391,6 +391,17 @@ template class unordered_set void clear(); void swap(unordered_set&); + template + void merge(boost::unordered_set& source); +#if !defined(BOOST_NO_CXX11_RVALUE_REFERENCES) + template + void merge(boost::unordered_set&& source); +#endif + // template + // void merge(boost::unordered_multiset& source); + // template + // void merge(boost::unordered_multiset&& source); + // observers hasher hash_function() const; @@ -817,6 +828,17 @@ template class unordered_multiset void clear(); void swap(unordered_multiset&); + template + void merge(boost::unordered_multiset& source); +#if !defined(BOOST_NO_CXX11_RVALUE_REFERENCES) + template + void merge(boost::unordered_multiset&& source); +#endif + // template + // void merge(boost::unordered_set& source); + // template + // void merge(boost::unordered_set&& source); + // observers hasher hash_function() const; @@ -1123,6 +1145,24 @@ unordered_set::key_eq() const return table_.key_eq(); } +template +template +void unordered_set::merge( + boost::unordered_set& source) +{ + table_.merge_impl(source.table_); +} + +#if !defined(BOOST_NO_CXX11_RVALUE_REFERENCES) +template +template +void unordered_set::merge( + boost::unordered_set&& source) +{ + table_.merge_impl(source.table_); +} +#endif + // lookup template @@ -1460,6 +1500,28 @@ unordered_multiset::key_eq() const return table_.key_eq(); } +template +template +void unordered_multiset::merge( + boost::unordered_multiset& source) +{ + while (!source.empty()) { + insert(source.extract(source.begin())); + } +} + +#if !defined(BOOST_NO_CXX11_RVALUE_REFERENCES) +template +template +void unordered_multiset::merge( + boost::unordered_multiset&& source) +{ + while (!source.empty()) { + insert(source.extract(source.begin())); + } +} +#endif + // lookup template diff --git a/test/Jamfile.v2 b/test/Jamfile.v2 index 3d7c26c8..2aad7e8d 100644 --- a/test/Jamfile.v2 +++ b/test/Jamfile.v2 @@ -49,6 +49,7 @@ test-suite unordered [ run unordered/erase_equiv_tests.cpp ] [ run unordered/extract_tests.cpp ] [ run unordered/node_handle_tests.cpp ] + [ run unordered/merge_tests.cpp ] [ compile-fail unordered/insert_node_type_fail.cpp : UNORDERED_TEST_MAP : insert_node_type_fail_map ] [ compile-fail unordered/insert_node_type_fail.cpp : UNORDERED_TEST_MULTIMAP : insert_node_type_fail_multimap ] [ compile-fail unordered/insert_node_type_fail.cpp : UNORDERED_TEST_SET : insert_node_type_fail_set ] diff --git a/test/unordered/compile_tests.hpp b/test/unordered/compile_tests.hpp index b8a6dc8a..c84b04e9 100644 --- a/test/unordered/compile_tests.hpp +++ b/test/unordered/compile_tests.hpp @@ -551,6 +551,11 @@ void unordered_test(X& x, Key& k, Hash& hf, Pred& eq) a.max_load_factor((float)2.0); a.rehash(100); + a.merge(a2); +#if !defined(BOOST_NO_CXX11_RVALUE_REFERENCES) + a.merge(rvalue_default()); +#endif + // Avoid unused variable warnings: sink(a); diff --git a/test/unordered/merge_tests.cpp b/test/unordered/merge_tests.cpp new file mode 100644 index 00000000..10337559 --- /dev/null +++ b/test/unordered/merge_tests.cpp @@ -0,0 +1,214 @@ + +// Copyright 2016 Daniel James. +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#include "../helpers/postfix.hpp" +#include "../helpers/prefix.hpp" +#include +#include + +#include "../helpers/count.hpp" +#include "../helpers/helpers.hpp" +#include "../helpers/invariants.hpp" +#include "../helpers/random_values.hpp" +#include "../helpers/test.hpp" +#include "../helpers/tracker.hpp" +#include "../objects/test.hpp" +#include + +namespace merge_tests { + +UNORDERED_AUTO_TEST(merge_set) +{ + boost::unordered_set x; + boost::unordered_set y; + + x.merge(y); + BOOST_TEST(x.empty()); + BOOST_TEST(y.empty()); + + x.insert(10); + x.merge(y); + BOOST_TEST(x.size() == 1); + BOOST_TEST(x.count(10) == 1); + BOOST_TEST(y.empty()); + + y.merge(x); + BOOST_TEST(x.empty()); + BOOST_TEST(y.size() == 1); + BOOST_TEST(y.count(10) == 1); + + x.insert(10); + x.insert(50); + y.insert(70); + y.insert(80); + x.merge(y); + BOOST_TEST_EQ(x.size(), 4u); + BOOST_TEST_EQ(y.size(), 1u); + BOOST_TEST_EQ(x.count(10), 1u); + BOOST_TEST_EQ(x.count(50), 1u); + BOOST_TEST_EQ(x.count(70), 1u); + BOOST_TEST_EQ(x.count(80), 1u); + BOOST_TEST_EQ(y.count(10), 1u); + BOOST_TEST_EQ(y.count(50), 0u); + BOOST_TEST_EQ(y.count(70), 0u); + BOOST_TEST_EQ(y.count(80), 0u); + + test::check_equivalent_keys(x); + test::check_equivalent_keys(y); +} + +UNORDERED_AUTO_TEST(merge_multiset) +{ + boost::unordered_multiset x; + boost::unordered_multiset y; + + x.merge(y); + BOOST_TEST(x.empty()); + BOOST_TEST(y.empty()); + + x.insert(10); + x.merge(y); + BOOST_TEST(x.size() == 1); + BOOST_TEST(x.count(10) == 1); + BOOST_TEST(y.empty()); + + y.merge(x); + BOOST_TEST(x.empty()); + BOOST_TEST(y.size() == 1); + BOOST_TEST(y.count(10) == 1); + + x.insert(10); + x.insert(50); + y.insert(70); + y.insert(80); + x.merge(y); + BOOST_TEST_EQ(x.size(), 5u); + BOOST_TEST_EQ(y.size(), 0u); + BOOST_TEST_EQ(x.count(10), 2u); + BOOST_TEST_EQ(x.count(50), 1u); + BOOST_TEST_EQ(x.count(70), 1u); + BOOST_TEST_EQ(x.count(80), 1u); + BOOST_TEST_EQ(y.count(10), 0u); + BOOST_TEST_EQ(y.count(50), 0u); + BOOST_TEST_EQ(y.count(70), 0u); + BOOST_TEST_EQ(y.count(80), 0u); + + test::check_equivalent_keys(x); + test::check_equivalent_keys(y); +} + +template void merge_empty_test(X*, test::random_generator generator) +{ + test::check_instances check_; + + test::random_values v(1000, generator); + X x1(v.begin(), v.end()), x2; + x1.merge(x2); + test::check_container(x1, v); + BOOST_TEST(x2.empty()); + test::check_equivalent_keys(x1); + test::check_equivalent_keys(x2); +} + +template +void merge_into_empty_test(X*, test::random_generator generator) +{ + test::check_instances check_; + + test::random_values v(1000, generator); + X x1, x2(v.begin(), v.end()); + x1.merge(x2); + test::check_container(x1, v); + BOOST_TEST(x2.empty()); + test::check_equivalent_keys(x1); + test::check_equivalent_keys(x2); +} + +template void unique_merge_test(X*, test::random_generator generator) +{ + test::check_instances check_; + + test::random_values v1(1000, generator); + test::random_values v2(1000, generator); + v1.insert(v2.begin(), boost::next(v2.begin(), 100)); + v2.insert(v1.begin(), boost::next(v1.begin(), 100)); + + X x1(v1.begin(), v1.end()), x2(v2.begin(), v2.end()); + x1.merge(x2); + + test::ordered tracker1 = test::create_ordered(x1); + test::ordered tracker2 = test::create_ordered(x2); + test::ordered tracker_tmp = test::create_ordered(x2); + tracker1.insert(v1.begin(), v1.end()); + tracker_tmp.insert(v2.begin(), v2.end()); + for (BOOST_DEDUCED_TYPENAME test::ordered::iterator it = + tracker_tmp.begin(); + it != tracker_tmp.end(); ++it) { + if (!tracker1.insert(*it).second) { + tracker2.insert(*it); + } + } + + tracker1.compare(x1); + tracker2.compare(x2); + test::check_equivalent_keys(x1); + test::check_equivalent_keys(x2); +} + +template void equiv_merge_test(X*, test::random_generator generator) +{ + test::check_instances check_; + + test::random_values v1(1000, generator); + test::random_values v2(1000, generator); + v1.insert(v2.begin(), boost::next(v2.begin(), 100)); + v2.insert(v1.begin(), boost::next(v1.begin(), 100)); + + X x1(v1.begin(), v1.end()), x2(v2.begin(), v2.end()); + x1.merge(x2); + + test::ordered tracker1 = test::create_ordered(x1); + tracker1.insert(v1.begin(), v1.end()); + tracker1.insert(v2.begin(), v2.end()); + + tracker1.compare(x1); + BOOST_TEST(x2.empty()); + test::check_equivalent_keys(x1); + test::check_equivalent_keys(x2); +} + +boost::unordered_set >* test_set_std_alloc; +boost::unordered_multimap >* test_multimap_std_alloc; + +boost::unordered_set >* test_set; +boost::unordered_multiset >* test_multiset; +boost::unordered_map >* test_map; +boost::unordered_multimap >* test_multimap; + +using test::default_generator; +using test::generate_collisions; + +UNORDERED_TEST(merge_empty_test, + ((test_set_std_alloc)(test_multimap_std_alloc)(test_set)(test_multiset)( + test_map)(test_multimap))((default_generator)(generate_collisions))) + +UNORDERED_TEST(merge_into_empty_test, + ((test_set_std_alloc)(test_multimap_std_alloc)(test_set)(test_multiset)( + test_map)(test_multimap))((default_generator)(generate_collisions))) + +UNORDERED_TEST(unique_merge_test, + ((test_set_std_alloc)(test_set)(test_map))((default_generator))) + +UNORDERED_TEST(equiv_merge_test, ((test_multimap_std_alloc)(test_multiset)( + test_multimap))((default_generator))) +} + +RUN_TESTS()