/* 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 #define BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(F) \ static_assert(boost::callable_traits::is_invocable::value, \ "The provided Callable must be invocable with `value_type&`"); #define BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F) \ static_assert( \ boost::callable_traits::is_invocable::value, \ "The provided Callable must be invocable with `value_type const&`"); namespace boost { namespace unordered { namespace detail { 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(0) {} 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) { } /// Capacity /// size_type size() const noexcept { return table_.size(); } BOOST_ATTRIBUTE_NODISCARD bool empty() const noexcept { return size() == 0; } template std::size_t visit(key_type const& k, F f) { BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(F) return table_.visit(k, f); } template std::size_t visit(key_type const& k, F f) const { BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F) return table_.visit(k, f); } template std::size_t cvisit(key_type const& k, F f) const { BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F) return table_.visit(k, f); } template 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 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 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 std::size_t visit_all(F f) { BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(F) return table_.visit_all(f); } template std::size_t visit_all(F f) const { BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F) return table_.visit_all(f); } template 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 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 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 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 /// bool insert(value_type const& obj) { return table_.insert(obj); } bool insert(value_type&& obj) { return table_.insert(std::move(obj)); } bool insert(init_type const& obj) { return table_.insert(obj); } bool insert(init_type&& obj) { return table_.insert(std::move(obj)); } template void insert(InputIterator begin, InputIterator end) { for (auto pos = begin; pos != end; ++pos) { table_.insert(*pos); } } void insert(std::initializer_list ilist) { this->insert(ilist.begin(), ilist.end()); } template bool insert_or_assign(key_type const& k, M&& obj) { return table_.try_emplace_or_visit( k, [&](value_type& m) { m.second = std::forward(obj); }, std::forward(obj)); } template bool insert_or_assign(key_type&& k, M&& obj) { return table_.try_emplace_or_visit( std::move(k), [&](value_type& m) { m.second = std::forward(obj); }, std::forward(obj)); } template 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), [&](value_type& m) { m.second = std::forward(obj); }, std::forward(obj)); } template bool insert_or_visit(value_type const& obj, F f) { BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(F) return table_.insert_or_visit(obj, f); } template 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 bool insert_or_visit(init_type const& obj, F f) { BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(F) return table_.insert_or_visit(obj, f); } template 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 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 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 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 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 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 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 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 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 bool emplace(Args&&... args) { return table_.emplace(std::forward(args)...); } template bool emplace_or_visit(Arg&& arg, Args&&... args) { //BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(F) return table_.emplace_or_visit( std::forward(arg), std::forward(args)...); } template bool emplace_or_cvisit(Arg&& arg, Args&&... args) { //BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F) return table_.emplace_or_cvisit( std::forward(arg), std::forward(args)...); } template bool try_emplace(key_type const& k, Args&&... args) { return table_.try_emplace(k, std::forward(args)...); } template bool try_emplace(key_type&& k, Args&&... args) { return table_.try_emplace(std::move(k), std::forward(args)...); } template 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 bool try_emplace_or_visit(key_type const& k, Arg&& arg, Args&&... args) { //BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(F) return table_.try_emplace_or_visit( k, std::forward(arg), std::forward(args)...); } template bool try_emplace_or_cvisit(key_type const& k, Arg&& arg, Args&&... args) { //BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F) return table_.try_emplace_or_cvisit( k, std::forward(arg), std::forward(args)...); } template bool try_emplace_or_visit(key_type&& k, Arg&& arg, Args&&... args) { //BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(F) return table_.try_emplace_or_visit( std::move(k), std::forward(arg), std::forward(args)...); } template bool try_emplace_or_cvisit(key_type&& k, Arg&& arg, Args&&... args) { //BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F) return table_.try_emplace_or_cvisit( std::move(k), std::forward(arg), std::forward(args)...); } template bool try_emplace_or_visit(K&& k, Arg&& arg, Args&&... args) { //BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(F) return table_.try_emplace_or_visit( std::forward(k), std::forward(arg), std::forward(args)...); } template bool try_emplace_or_cvisit(K&& k, Arg&& arg, Args&&... args) { //BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F) return table_.try_emplace_or_cvisit( std::forward(k), std::forward(arg), std::forward(args)...); } size_type erase(key_type const& k) { return table_.erase(k); } template typename std::enable_if< detail::are_transparent::value, size_type>::type erase(K&& k) { return table_.erase(std::forward(k)); } template size_type erase_if(key_type const& k, F f) { return table_.erase_if(k, f); } template 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 typename std::enable_if::value, void>::type erase_if(ExecPolicy p, F f) { table_.erase_if(p, f); } #endif template 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); } }; } // namespace unordered } // namespace boost #undef BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE #undef BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE #endif // BOOST_UNORDERED_CONCURRENT_FLAT_MAP_HPP