diff --git a/include/boost/unordered/detail/foa.hpp b/include/boost/unordered/detail/foa.hpp index 5731cc41..9f561ec0 100644 --- a/include/boost/unordered/detail/foa.hpp +++ b/include/boost/unordered/detail/foa.hpp @@ -11,6 +11,8 @@ #ifndef BOOST_UNORDERED_DETAIL_FOA_HPP #define BOOST_UNORDERED_DETAIL_FOA_HPP +#include + #include #include #include @@ -1498,6 +1500,14 @@ public: } } + void extract(const_iterator pos, element_type* p)noexcept + { + BOOST_ASSERT(pos!=end()); + type_policy::construct(al(),p,type_policy::move(*pos.p)); + destroy_element(pos.p); + recover_slot(pos.pc); + } + // TODO: should we accept different allocator too? template void merge(table& x) @@ -1878,7 +1888,7 @@ private: return emplace_impl(type_policy::move(*p)); } - +public: template BOOST_FORCEINLINE std::pair emplace_impl(Args&&... args) { @@ -1903,7 +1913,7 @@ private: }; } } - +private: static std::size_t capacity_for(std::size_t n) { return size_policy::size(size_index_for(n))*N-1; diff --git a/include/boost/unordered/unordered_node_map.hpp b/include/boost/unordered/unordered_node_map.hpp index bbda2300..ba0a9cee 100644 --- a/include/boost/unordered/unordered_node_map.hpp +++ b/include/boost/unordered/unordered_node_map.hpp @@ -1,4 +1,4 @@ -// Copyright (C) 2022 Christian Mazakas +// Copyright (C) 2022-2023 Christian Mazakas // 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) @@ -16,6 +16,7 @@ #include #include +#include #include #include @@ -36,6 +37,7 @@ namespace boost { template struct node_map_types { using key_type = Key; + using mapped_type = T; using raw_key_type = typename std::remove_const::type; using raw_mapped_type = typename std::remove_const::type; @@ -79,8 +81,12 @@ namespace boost { template static void construct(A&, element_type* p, element_type&& x) { + std::cout << "should be seeing this: construct(A&, element_type* p, " + "element_type&& x)" + << std::endl; p->p = x.p; x.p = nullptr; + std::cout << "p->p is now: " << p->p << std::endl; } template @@ -111,6 +117,7 @@ namespace boost { template static void destroy(A& al, element_type* p) noexcept { if (p->p) { + std::cout << "going to deallocate: " << p->p << std::endl; boost::allocator_destroy(al, p->p); boost::allocator_deallocate(al, boost::pointer_traits< @@ -119,6 +126,95 @@ namespace boost { } } }; + + template struct node_handle + { + private: + using type_policy = NodeMapTypes; + using element_type = typename type_policy::element_type; + + template + friend class boost::unordered::unordered_node_map; + + public: + using value_type = typename NodeMapTypes::value_type; + using key_type = typename NodeMapTypes::key_type; + using mapped_type = typename NodeMapTypes::mapped_type; + using allocator_type = Allocator; + + private: + alignas(element_type) unsigned char x[sizeof(element_type)]; + alignas(Allocator) unsigned char a[sizeof(Allocator)]; + bool empty_; + + element_type& element() noexcept + { + BOOST_ASSERT(!empty()); + return *reinterpret_cast(x); + } + + element_type const& element() const noexcept + { + BOOST_ASSERT(!empty()); + return *reinterpret_cast(x); + } + + Allocator& al() noexcept + { + BOOST_ASSERT(!empty()); + return *reinterpret_cast(a); + } + + Allocator const& al() const noexcept + { + BOOST_ASSERT(!empty()); + return *reinterpret_cast(a); + } + + public: + constexpr node_handle() noexcept = default; + + node_handle(node_handle&& nh) noexcept + { + new (a) Allocator(std::move(nh.al())); + type_policy::construct(al(), reinterpret_cast(x), + type_policy::move(nh.element())); + + reinterpret_cast(nh.a)->~Allocator(); + nh.empty_ = true; + } + + ~node_handle() + { + if (!empty()) { + type_policy::destroy(al(), reinterpret_cast(x)); + reinterpret_cast(a)->~Allocator(); + empty_ = true; + } + } + + key_type& key() const + { + return const_cast(type_policy::extract(element())); + } + + mapped_type& mapped() const + { + return const_cast( + type_policy::value_from(element()).second); + } + + allocator_type get_allocator() const noexcept { return al(); } + explicit operator bool() const noexcept { return !empty(); } + BOOST_ATTRIBUTE_NODISCARD bool empty() const noexcept { return empty_; } + }; + + template struct insert_return_type + { + Iterator position; + bool inserted; + NodeType node; + }; } // namespace detail template @@ -153,6 +249,9 @@ namespace boost { typename boost::allocator_const_pointer::type; using iterator = typename table_type::iterator; using const_iterator = typename table_type::const_iterator; + using node_type = detail::node_handle; + using insert_return_type = + detail::insert_return_type; unordered_node_map() : unordered_node_map(0) {} @@ -342,6 +441,22 @@ namespace boost { this->insert(ilist.begin(), ilist.end()); } + insert_return_type insert(node_type&& nh) + { + if (nh.empty()) { + return {end(), false, node_type{}}; + } + + BOOST_ASSERT(get_allocator() == nh.get_allocator()); + + auto itp = table_.emplace_impl(map_types::move(nh.element())); + if (itp.second) { + return {itp.first, true, node_type{}}; + } else { + return {itp.first, false, std::move(nh)}; + } + } + template std::pair insert_or_assign(key_type const& key, M&& obj) { @@ -498,6 +613,52 @@ namespace boost { table_.swap(rhs.table_); } + node_type extract(const_iterator pos) + { + BOOST_ASSERT(pos != end()); + node_type nh; + table_.extract( + pos, reinterpret_cast(nh.x)); + new (&nh.a) allocator_type(get_allocator()); + nh.empty_ = false; + return nh; + } + + node_type extract(key_type const& key) + { + auto pos = find(key); + node_type nh; + if (pos != end()) { + table_.extract( + pos, reinterpret_cast(nh.x)); + new (&nh.a) allocator_type(get_allocator()); + nh.empty_ = false; + } else { + nh.empty_ = true; + } + return nh; + } + + template + typename std::enable_if< + boost::unordered::detail::transparent_non_iterable::value, + node_type>::type + extract(K const& key) + { + auto pos = find(key); + node_type nh; + if (pos != end()) { + table_.extract( + pos, reinterpret_cast(nh.x)); + new (&nh.a) allocator_type(get_allocator()); + nh.empty_ = false; + } else { + nh.empty_ = true; + } + return nh; + } + template void merge( unordered_node_map& diff --git a/test/Jamfile.v2 b/test/Jamfile.v2 index 91b926b2..4f7a537f 100644 --- a/test/Jamfile.v2 +++ b/test/Jamfile.v2 @@ -142,6 +142,8 @@ build_foa erase_if ; build_foa scary_tests ; build_foa init_type_insert_tests ; build_foa max_load_tests ; +build_foa extract_tests ; +build_foa node_handle_tests ; run unordered/hash_is_avalanching_test.cpp ; diff --git a/test/unordered/extract_tests.cpp b/test/unordered/extract_tests.cpp index 14338738..1cfdadb0 100644 --- a/test/unordered/extract_tests.cpp +++ b/test/unordered/extract_tests.cpp @@ -1,14 +1,11 @@ // Copyright 2016 Daniel James. +// Copyright 2023 Christian Mazakas // 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) -// clang-format off -#include "../helpers/prefix.hpp" -#include -#include -#include "../helpers/postfix.hpp" -// clang-format on + +#include "../helpers/unordered.hpp" #include "../helpers/equivalent.hpp" #include "../helpers/helpers.hpp" @@ -113,6 +110,18 @@ namespace extract_tests { BOOST_LIGHTWEIGHT_TEST_OSTREAM << "\n"; } + using test::default_generator; + using test::generate_collisions; + +#ifdef BOOST_UNORDERED_FOA_TESTS + boost::unordered_node_map > >* + test_node_map; + + UNORDERED_TEST( + extract_tests1, ((test_node_map))((default_generator)(generate_collisions))) +#else boost::unordered_set >* test_set; boost::unordered_multiset >* test_multimap; - using test::default_generator; - using test::generate_collisions; - UNORDERED_TEST( extract_tests1, ((test_set)(test_multiset)(test_map)(test_multimap))( (default_generator)(generate_collisions))) -} +#endif +} // namespace extract_tests RUN_TESTS() diff --git a/test/unordered/node_handle_tests.cpp b/test/unordered/node_handle_tests.cpp index 89648d0f..3f48ed42 100644 --- a/test/unordered/node_handle_tests.cpp +++ b/test/unordered/node_handle_tests.cpp @@ -1,12 +1,12 @@ // Copyright 2016 Daniel James. +// Copyright 2023 Christian Mazakas // 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/unordered.hpp" #include "../helpers/prefix.hpp" -#include -#include #include "../helpers/helpers.hpp" #include "../helpers/metafunctions.hpp" @@ -17,6 +17,33 @@ #include #include +#ifdef BOOST_UNORDERED_FOA_TESTS +UNORDERED_AUTO_TEST (example1_5) { + typedef boost::unordered_node_map::insert_return_type + insert_return_type; + + boost::unordered_node_map src; + src.emplace(1, "one"); + src.emplace(2, "two"); + src.emplace(3, "buckle my shoe"); + boost::unordered_node_map dst; + dst.emplace(3, "three"); + + dst.insert(src.extract(src.find(1))); + dst.insert(src.extract(2)); + insert_return_type r = dst.insert(src.extract(3)); + + BOOST_TEST(src.empty()); + BOOST_TEST(dst.size() == 3); + BOOST_TEST(dst[1] == "one"); + BOOST_TEST(dst[2] == "two"); + BOOST_TEST(dst[3] == "three"); + BOOST_TEST(!r.inserted); + BOOST_TEST(r.position == dst.find(3)); + BOOST_TEST(r.node.mapped() == "buckle my shoe"); +} +#else + UNORDERED_AUTO_TEST (example1) { typedef boost::unordered_map::insert_return_type insert_return_type; @@ -547,4 +574,6 @@ UNORDERED_AUTO_TEST (insert_node_handle_unique_tests2) { } } +#endif + RUN_TESTS()