/* Fast open-addressing concurrent hash table. * * 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) * * See https://www.boost.org/libs/unordered for library home page. */ /* Reference: * https://github.com/joaquintides/concurrent_hashmap_api#proposed-synopsis */ #ifndef BOOST_UNORDERED_CONCURRENT_FLAT_MAP_HPP #define BOOST_UNORDERED_CONCURRENT_FLAT_MAP_HPP #include #include #include #include #include #include #include #include #include #include #define BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(F) \ static_assert(boost::unordered::detail::is_invocable::value, \ "The provided Callable must be invocable with `value_type&`"); #define BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F) \ static_assert( \ boost::unordered::detail::is_invocable::value, \ "The provided Callable must be invocable with `value_type const&`"); #define BOOST_UNORDERED_COMMA , #define BOOST_UNORDERED_LAST_ARG(Arg, Args) \ mp11::mp_back > #define BOOST_UNORDERED_STATIC_ASSERT_LAST_ARG_INVOCABLE(Arg, Args) \ BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(BOOST_UNORDERED_LAST_ARG(Arg, Args)) #define BOOST_UNORDERED_STATIC_ASSERT_LAST_ARG_CONST_INVOCABLE(Arg, Args) \ BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE( \ BOOST_UNORDERED_LAST_ARG(Arg, Args)) namespace boost { namespace unordered { namespace detail { template struct is_invocable : std::is_constructible, std::reference_wrapper::type> > { }; template struct concurrent_map_types { using key_type = Key; using raw_key_type = typename std::remove_const::type; using raw_mapped_type = typename std::remove_const::type; using init_type = std::pair; using moved_type = std::pair; using value_type = std::pair; using element_type = value_type; static value_type& value_from(element_type& x) { return x; } template static raw_key_type const& extract(std::pair const& kv) { return kv.first; } static moved_type move(init_type& x) { return {std::move(x.first), std::move(x.second)}; } static moved_type move(element_type& x) { // TODO: we probably need to launder here return {std::move(const_cast(x.first)), std::move(const_cast(x.second))}; } template static void construct(A& al, init_type* p, Args&&... args) { boost::allocator_construct(al, p, std::forward(args)...); } template static void construct(A& al, value_type* p, Args&&... args) { boost::allocator_construct(al, p, std::forward(args)...); } template static void destroy(A& al, init_type* p) noexcept { boost::allocator_destroy(al, p); } template static void destroy(A& al, value_type* p) noexcept { boost::allocator_destroy(al, p); } }; } // namespace detail template , class Pred = std::equal_to, class Allocator = std::allocator > > class concurrent_flat_map { private: using type_policy = detail::concurrent_map_types; detail::foa::concurrent_table table_; public: using key_type = Key; using mapped_type = T; using value_type = typename type_policy::value_type; using init_type = typename type_policy::init_type; using size_type = std::size_t; using difference_type = std::ptrdiff_t; using hasher = typename boost::type_identity::type; using key_equal = typename boost::type_identity::type; using allocator_type = typename boost::type_identity::type; using reference = value_type&; using const_reference = value_type const&; using pointer = typename boost::allocator_pointer::type; using const_pointer = typename boost::allocator_const_pointer::type; concurrent_flat_map() : concurrent_flat_map(detail::foa::default_bucket_count) { } explicit concurrent_flat_map(size_type n, const hasher& hf = hasher(), const key_equal& eql = key_equal(), const allocator_type& a = allocator_type()) : table_(n, hf, eql, a) { } template concurrent_flat_map(InputIterator f, InputIterator l, size_type n = detail::foa::default_bucket_count, const hasher& hf = hasher(), const key_equal& eql = key_equal(), const allocator_type& a = allocator_type()) : table_(n, hf, eql, a) { this->insert(f, l); } concurrent_flat_map(concurrent_flat_map const& rhs) : table_(rhs.table_, boost::allocator_select_on_container_copy_construction( rhs.get_allocator())) { } concurrent_flat_map(concurrent_flat_map&& rhs) : table_(std::move(rhs.table_)) { } template concurrent_flat_map(InputIterator f, InputIterator l, allocator_type a) : concurrent_flat_map(f, l, 0, hasher(), key_equal(), a) { } explicit concurrent_flat_map(allocator_type a) : table_(detail::foa::default_bucket_count, hasher(), key_equal(), a) { } concurrent_flat_map(concurrent_flat_map const& rhs, allocator_type a) : table_(rhs.table_, a) { } concurrent_flat_map(concurrent_flat_map&& rhs, allocator_type a) : table_(std::move(rhs.table_), a) { } concurrent_flat_map(std::initializer_list il, size_type n = detail::foa::default_bucket_count, const hasher& hf = hasher(), const key_equal& eql = key_equal(), const allocator_type& a = allocator_type()) : concurrent_flat_map(n, hf, eql, a) { this->insert(il.begin(), il.end()); } concurrent_flat_map(size_type n, const allocator_type& a) : concurrent_flat_map(n, hasher(), key_equal(), a) { } concurrent_flat_map( size_type n, const hasher& hf, const allocator_type& a) : concurrent_flat_map(n, hf, key_equal(), a) { } template concurrent_flat_map( InputIterator f, InputIterator l, size_type n, const allocator_type& a) : concurrent_flat_map(f, l, n, hasher(), key_equal(), a) { } template concurrent_flat_map(InputIterator f, InputIterator l, size_type n, const hasher& hf, const allocator_type& a) : concurrent_flat_map(f, l, n, hf, key_equal(), a) { } /// Capacity /// size_type size() const noexcept { return table_.size(); } BOOST_ATTRIBUTE_NODISCARD bool empty() const noexcept { return size() == 0; } template BOOST_FORCEINLINE std::size_t visit(key_type const& k, F f) { BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(F) return table_.visit(k, f); } template BOOST_FORCEINLINE std::size_t visit(key_type const& k, F f) const { BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F) return table_.visit(k, f); } template BOOST_FORCEINLINE std::size_t cvisit(key_type const& k, F f) const { BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F) return table_.visit(k, f); } template BOOST_FORCEINLINE typename std::enable_if< detail::are_transparent::value, std::size_t>::type visit(K&& k, F f) { BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(F) return table_.visit(std::forward(k), f); } template BOOST_FORCEINLINE typename std::enable_if< detail::are_transparent::value, std::size_t>::type visit(K&& k, F f) const { BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F) return table_.visit(std::forward(k), f); } template BOOST_FORCEINLINE typename std::enable_if< detail::are_transparent::value, std::size_t>::type cvisit(K&& k, F f) const { BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F) return table_.visit(std::forward(k), f); } template BOOST_FORCEINLINE std::size_t visit_all(F f) { BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(F) return table_.visit_all(f); } template BOOST_FORCEINLINE std::size_t visit_all(F f) const { BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F) return table_.visit_all(f); } template BOOST_FORCEINLINE std::size_t cvisit_all(F f) const { BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F) return table_.cvisit_all(f); } #if defined(BOOST_UNORDERED_PARALLEL_ALGORITHMS) template BOOST_FORCEINLINE typename std::enable_if::value, void>::type visit_all(ExecPolicy p, F f) { BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(F) table_.visit_all(p, f); } template BOOST_FORCEINLINE typename std::enable_if::value, void>::type visit_all(ExecPolicy p, F f) const { BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F) table_.visit_all(p, f); } template BOOST_FORCEINLINE typename std::enable_if::value, void>::type cvisit_all(ExecPolicy p, F f) const { BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F) table_.cvisit_all(p, f); } #endif /// Modifiers /// BOOST_FORCEINLINE bool insert(value_type const& obj) { return table_.insert(obj); } BOOST_FORCEINLINE bool insert(value_type&& obj) { return table_.insert(std::move(obj)); } BOOST_FORCEINLINE bool insert(init_type const& obj) { return table_.insert(obj); } BOOST_FORCEINLINE bool insert(init_type&& obj) { return table_.insert(std::move(obj)); } template BOOST_FORCEINLINE void insert(InputIterator begin, InputIterator end) { for (auto pos = begin; pos != end; ++pos) { table_.insert(*pos); } } BOOST_FORCEINLINE void insert(std::initializer_list ilist) { this->insert(ilist.begin(), ilist.end()); } template BOOST_FORCEINLINE bool insert_or_assign(key_type const& k, M&& obj) { return table_.try_emplace_or_visit(k, std::forward(obj), [&](value_type& m) { m.second = std::forward(obj); }); } template BOOST_FORCEINLINE bool insert_or_assign(key_type&& k, M&& obj) { return table_.try_emplace_or_visit(std::move(k), std::forward(obj), [&](value_type& m) { m.second = std::forward(obj); }); } template BOOST_FORCEINLINE typename std::enable_if< detail::are_transparent::value, bool>::type insert_or_assign(K&& k, M&& obj) { return table_.try_emplace_or_visit(std::forward(k), std::forward(obj), [&](value_type& m) { m.second = std::forward(obj); }); } template BOOST_FORCEINLINE bool insert_or_visit(value_type const& obj, F f) { BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(F) return table_.insert_or_visit(obj, f); } template BOOST_FORCEINLINE bool insert_or_visit(value_type&& obj, F f) { BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(F) return table_.insert_or_visit(std::move(obj), f); } template BOOST_FORCEINLINE bool insert_or_visit(init_type const& obj, F f) { BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(F) return table_.insert_or_visit(obj, f); } template BOOST_FORCEINLINE bool insert_or_visit(init_type&& obj, F f) { BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(F) return table_.insert_or_visit(std::move(obj), f); } template BOOST_FORCEINLINE void insert_or_visit( InputIterator first, InputIterator last, F f) { BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(F) for (; first != last; ++first) { table_.insert_or_visit(*first, f); } } template BOOST_FORCEINLINE void insert_or_visit( std::initializer_list ilist, F f) { BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(F) this->insert_or_visit(ilist.begin(), ilist.end(), f); } template BOOST_FORCEINLINE bool insert_or_cvisit(value_type const& obj, F f) { BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F) return table_.insert_or_cvisit(obj, f); } template BOOST_FORCEINLINE bool insert_or_cvisit(value_type&& obj, F f) { BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F) return table_.insert_or_cvisit(std::move(obj), f); } template BOOST_FORCEINLINE bool insert_or_cvisit(init_type const& obj, F f) { BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F) return table_.insert_or_cvisit(obj, f); } template BOOST_FORCEINLINE bool insert_or_cvisit(init_type&& obj, F f) { BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F) return table_.insert_or_cvisit(std::move(obj), f); } template BOOST_FORCEINLINE void insert_or_cvisit( InputIterator first, InputIterator last, F f) { BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F) for (; first != last; ++first) { table_.insert_or_cvisit(*first, f); } } template BOOST_FORCEINLINE void insert_or_cvisit( std::initializer_list ilist, F f) { BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F) this->insert_or_visit(ilist.begin(), ilist.end(), f); } template BOOST_FORCEINLINE bool emplace(Args&&... args) { return table_.emplace(std::forward(args)...); } template BOOST_FORCEINLINE bool emplace_or_visit(Arg&& arg, Args&&... args) { BOOST_UNORDERED_STATIC_ASSERT_LAST_ARG_INVOCABLE(Arg, Args...) return table_.emplace_or_visit( std::forward(arg), std::forward(args)...); } template BOOST_FORCEINLINE bool emplace_or_cvisit(Arg&& arg, Args&&... args) { BOOST_UNORDERED_STATIC_ASSERT_LAST_ARG_CONST_INVOCABLE(Arg, Args...) return table_.emplace_or_cvisit( std::forward(arg), std::forward(args)...); } template BOOST_FORCEINLINE bool try_emplace(key_type const& k, Args&&... args) { return table_.try_emplace(k, std::forward(args)...); } template BOOST_FORCEINLINE bool try_emplace(key_type&& k, Args&&... args) { return table_.try_emplace(std::move(k), std::forward(args)...); } template BOOST_FORCEINLINE typename std::enable_if< detail::are_transparent::value, bool>::type try_emplace(K&& k, Args&&... args) { return table_.try_emplace( std::forward(k), std::forward(args)...); } template BOOST_FORCEINLINE bool try_emplace_or_visit( key_type const& k, Arg&& arg, Args&&... args) { BOOST_UNORDERED_STATIC_ASSERT_LAST_ARG_INVOCABLE(Arg, Args...) return table_.try_emplace_or_visit( k, std::forward(arg), std::forward(args)...); } template BOOST_FORCEINLINE bool try_emplace_or_cvisit( key_type const& k, Arg&& arg, Args&&... args) { BOOST_UNORDERED_STATIC_ASSERT_LAST_ARG_CONST_INVOCABLE(Arg, Args...) return table_.try_emplace_or_cvisit( k, std::forward(arg), std::forward(args)...); } template BOOST_FORCEINLINE bool try_emplace_or_visit( key_type&& k, Arg&& arg, Args&&... args) { BOOST_UNORDERED_STATIC_ASSERT_LAST_ARG_INVOCABLE(Arg, Args...) return table_.try_emplace_or_visit( std::move(k), std::forward(arg), std::forward(args)...); } template BOOST_FORCEINLINE bool try_emplace_or_cvisit( key_type&& k, Arg&& arg, Args&&... args) { BOOST_UNORDERED_STATIC_ASSERT_LAST_ARG_CONST_INVOCABLE(Arg, Args...) return table_.try_emplace_or_cvisit( std::move(k), std::forward(arg), std::forward(args)...); } template BOOST_FORCEINLINE bool try_emplace_or_visit( K&& k, Arg&& arg, Args&&... args) { BOOST_UNORDERED_STATIC_ASSERT_LAST_ARG_INVOCABLE(Arg, Args...) return table_.try_emplace_or_visit(std::forward(k), std::forward(arg), std::forward(args)...); } template BOOST_FORCEINLINE bool try_emplace_or_cvisit( K&& k, Arg&& arg, Args&&... args) { BOOST_UNORDERED_STATIC_ASSERT_LAST_ARG_CONST_INVOCABLE(Arg, Args...) return table_.try_emplace_or_cvisit(std::forward(k), std::forward(arg), std::forward(args)...); } BOOST_FORCEINLINE size_type erase(key_type const& k) { return table_.erase(k); } template BOOST_FORCEINLINE typename std::enable_if< detail::are_transparent::value, size_type>::type erase(K&& k) { return table_.erase(std::forward(k)); } template BOOST_FORCEINLINE size_type erase_if(key_type const& k, F f) { return table_.erase_if(k, f); } template BOOST_FORCEINLINE typename std::enable_if< detail::are_transparent::value && !detail::is_execution_policy::value, size_type>::type erase_if(K&& k, F f) { return table_.erase_if(std::forward(k), f); } #if defined(BOOST_UNORDERED_PARALLEL_ALGORITHMS) template BOOST_FORCEINLINE typename std::enable_if::value, void>::type erase_if(ExecPolicy p, F f) { table_.erase_if(p, f); } #endif template BOOST_FORCEINLINE size_type erase_if(F f) { return table_.erase_if(f); } /// Hash Policy /// void rehash(size_type n) { table_.rehash(n); } void reserve(size_type n) { table_.reserve(n); } /// Observers /// allocator_type get_allocator() const noexcept { return table_.get_allocator(); } hasher hash_function() const { return table_.hash_function(); } key_equal key_eq() const { return table_.key_eq(); } }; } // namespace unordered } // namespace boost #undef BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE #undef BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE #undef BOOST_UNORDERED_COMMA #undef BOOST_UNORDERED_LAST_ARG #undef BOOST_UNORDERED_STATIC_ASSERT_LAST_ARG_INVOCABLE #undef BOOST_UNORDERED_STATIC_ASSERT_LAST_ARG_CONST_INVOCABLE #endif // BOOST_UNORDERED_CONCURRENT_FLAT_MAP_HPP