Added thirdparty: boost library

This commit is contained in:
Viacheslav Demydiuk
2024-01-06 19:55:56 +02:00
parent bf49f439e1
commit bccd1e7051
15683 changed files with 3239840 additions and 0 deletions
+860
View File
@@ -0,0 +1,860 @@
/* Fast open-addressing concurrent hashmap.
*
* Copyright 2023 Christian Mazakas.
* Copyright 2023 Joaquin M Lopez Munoz.
* 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.
*/
#ifndef BOOST_UNORDERED_CONCURRENT_FLAT_MAP_HPP
#define BOOST_UNORDERED_CONCURRENT_FLAT_MAP_HPP
#include <boost/unordered/concurrent_flat_map_fwd.hpp>
#include <boost/unordered/detail/concurrent_static_asserts.hpp>
#include <boost/unordered/detail/foa/concurrent_table.hpp>
#include <boost/unordered/detail/foa/flat_map_types.hpp>
#include <boost/unordered/detail/type_traits.hpp>
#include <boost/unordered/unordered_flat_map_fwd.hpp>
#include <boost/container_hash/hash.hpp>
#include <boost/core/allocator_access.hpp>
#include <boost/core/serialization.hpp>
#include <type_traits>
namespace boost {
namespace unordered {
template <class Key, class T, class Hash, class Pred, class Allocator>
class concurrent_flat_map
{
private:
template <class Key2, class T2, class Hash2, class Pred2,
class Allocator2>
friend class concurrent_flat_map;
template <class Key2, class T2, class Hash2, class Pred2,
class Allocator2>
friend class unordered_flat_map;
using type_policy = detail::foa::flat_map_types<Key, T>;
using table_type =
detail::foa::concurrent_table<type_policy, Hash, Pred, Allocator>;
table_type table_;
template <class K, class V, class H, class KE, class A>
bool friend operator==(concurrent_flat_map<K, V, H, KE, A> const& lhs,
concurrent_flat_map<K, V, H, KE, A> const& rhs);
template <class K, class V, class H, class KE, class A, class Predicate>
friend typename concurrent_flat_map<K, V, H, KE, A>::size_type erase_if(
concurrent_flat_map<K, V, H, KE, A>& set, Predicate pred);
template<class Archive, class K, class V, class H, class KE, class A>
friend void serialize(
Archive& ar, concurrent_flat_map<K, V, H, KE, A>& c,
unsigned int version);
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::unordered::detail::type_identity<Hash>::type;
using key_equal = typename boost::unordered::detail::type_identity<Pred>::type;
using allocator_type = typename boost::unordered::detail::type_identity<Allocator>::type;
using reference = value_type&;
using const_reference = value_type const&;
using pointer = typename boost::allocator_pointer<allocator_type>::type;
using const_pointer =
typename boost::allocator_const_pointer<allocator_type>::type;
static constexpr size_type bulk_visit_size = table_type::bulk_visit_size;
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 <class InputIterator>
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 <class InputIterator>
concurrent_flat_map(
InputIterator f, InputIterator l, allocator_type const& a)
: concurrent_flat_map(f, l, 0, hasher(), key_equal(), a)
{
}
explicit concurrent_flat_map(allocator_type const& a)
: table_(detail::foa::default_bucket_count, hasher(), key_equal(), a)
{
}
concurrent_flat_map(
concurrent_flat_map const& rhs, allocator_type const& a)
: table_(rhs.table_, a)
{
}
concurrent_flat_map(concurrent_flat_map&& rhs, allocator_type const& a)
: table_(std::move(rhs.table_), a)
{
}
concurrent_flat_map(std::initializer_list<value_type> 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 <typename InputIterator>
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 <typename InputIterator>
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)
{
}
concurrent_flat_map(
std::initializer_list<value_type> il, const allocator_type& a)
: concurrent_flat_map(
il, detail::foa::default_bucket_count, hasher(), key_equal(), a)
{
}
concurrent_flat_map(std::initializer_list<value_type> il, size_type n,
const allocator_type& a)
: concurrent_flat_map(il, n, hasher(), key_equal(), a)
{
}
concurrent_flat_map(std::initializer_list<value_type> il, size_type n,
const hasher& hf, const allocator_type& a)
: concurrent_flat_map(il, n, hf, key_equal(), a)
{
}
concurrent_flat_map(
unordered_flat_map<Key, T, Hash, Pred, Allocator>&& other)
: table_(std::move(other.table_))
{
}
~concurrent_flat_map() = default;
concurrent_flat_map& operator=(concurrent_flat_map const& rhs)
{
table_ = rhs.table_;
return *this;
}
concurrent_flat_map& operator=(concurrent_flat_map&& rhs) noexcept(
noexcept(std::declval<table_type&>() = std::declval<table_type&&>()))
{
table_ = std::move(rhs.table_);
return *this;
}
concurrent_flat_map& operator=(std::initializer_list<value_type> ilist)
{
table_ = ilist;
return *this;
}
/// Capacity
///
size_type size() const noexcept { return table_.size(); }
size_type max_size() const noexcept { return table_.max_size(); }
BOOST_ATTRIBUTE_NODISCARD bool empty() const noexcept
{
return size() == 0;
}
template <class F>
BOOST_FORCEINLINE size_type visit(key_type const& k, F f)
{
BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(F)
return table_.visit(k, f);
}
template <class F>
BOOST_FORCEINLINE size_type visit(key_type const& k, F f) const
{
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
return table_.visit(k, f);
}
template <class F>
BOOST_FORCEINLINE size_type cvisit(key_type const& k, F f) const
{
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
return table_.visit(k, f);
}
template <class K, class F>
BOOST_FORCEINLINE typename std::enable_if<
detail::are_transparent<K, hasher, key_equal>::value, size_type>::type
visit(K&& k, F f)
{
BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(F)
return table_.visit(std::forward<K>(k), f);
}
template <class K, class F>
BOOST_FORCEINLINE typename std::enable_if<
detail::are_transparent<K, hasher, key_equal>::value, size_type>::type
visit(K&& k, F f) const
{
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
return table_.visit(std::forward<K>(k), f);
}
template <class K, class F>
BOOST_FORCEINLINE typename std::enable_if<
detail::are_transparent<K, hasher, key_equal>::value, size_type>::type
cvisit(K&& k, F f) const
{
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
return table_.visit(std::forward<K>(k), f);
}
template<class FwdIterator, class F>
BOOST_FORCEINLINE
size_t visit(FwdIterator first, FwdIterator last, F f)
{
BOOST_UNORDERED_STATIC_ASSERT_BULK_VISIT_ITERATOR(FwdIterator)
BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(F)
return table_.visit(first, last, f);
}
template<class FwdIterator, class F>
BOOST_FORCEINLINE
size_t visit(FwdIterator first, FwdIterator last, F f) const
{
BOOST_UNORDERED_STATIC_ASSERT_BULK_VISIT_ITERATOR(FwdIterator)
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
return table_.visit(first, last, f);
}
template<class FwdIterator, class F>
BOOST_FORCEINLINE
size_t cvisit(FwdIterator first, FwdIterator last, F f) const
{
BOOST_UNORDERED_STATIC_ASSERT_BULK_VISIT_ITERATOR(FwdIterator)
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
return table_.visit(first, last, f);
}
template <class F> size_type visit_all(F f)
{
BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(F)
return table_.visit_all(f);
}
template <class F> size_type visit_all(F f) const
{
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
return table_.visit_all(f);
}
template <class F> size_type cvisit_all(F f) const
{
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
return table_.cvisit_all(f);
}
#if defined(BOOST_UNORDERED_PARALLEL_ALGORITHMS)
template <class ExecPolicy, class F>
typename std::enable_if<detail::is_execution_policy<ExecPolicy>::value,
void>::type
visit_all(ExecPolicy&& p, F f)
{
BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(F)
BOOST_UNORDERED_STATIC_ASSERT_EXEC_POLICY(ExecPolicy)
table_.visit_all(p, f);
}
template <class ExecPolicy, class F>
typename std::enable_if<detail::is_execution_policy<ExecPolicy>::value,
void>::type
visit_all(ExecPolicy&& p, F f) const
{
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
BOOST_UNORDERED_STATIC_ASSERT_EXEC_POLICY(ExecPolicy)
table_.visit_all(p, f);
}
template <class ExecPolicy, class F>
typename std::enable_if<detail::is_execution_policy<ExecPolicy>::value,
void>::type
cvisit_all(ExecPolicy&& p, F f) const
{
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
BOOST_UNORDERED_STATIC_ASSERT_EXEC_POLICY(ExecPolicy)
table_.cvisit_all(p, f);
}
#endif
template <class F> bool visit_while(F f)
{
BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(F)
return table_.visit_while(f);
}
template <class F> bool visit_while(F f) const
{
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
return table_.visit_while(f);
}
template <class F> bool cvisit_while(F f) const
{
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
return table_.cvisit_while(f);
}
#if defined(BOOST_UNORDERED_PARALLEL_ALGORITHMS)
template <class ExecPolicy, class F>
typename std::enable_if<detail::is_execution_policy<ExecPolicy>::value,
bool>::type
visit_while(ExecPolicy&& p, F f)
{
BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(F)
BOOST_UNORDERED_STATIC_ASSERT_EXEC_POLICY(ExecPolicy)
return table_.visit_while(p, f);
}
template <class ExecPolicy, class F>
typename std::enable_if<detail::is_execution_policy<ExecPolicy>::value,
bool>::type
visit_while(ExecPolicy&& p, F f) const
{
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
BOOST_UNORDERED_STATIC_ASSERT_EXEC_POLICY(ExecPolicy)
return table_.visit_while(p, f);
}
template <class ExecPolicy, class F>
typename std::enable_if<detail::is_execution_policy<ExecPolicy>::value,
bool>::type
cvisit_while(ExecPolicy&& p, F f) const
{
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
BOOST_UNORDERED_STATIC_ASSERT_EXEC_POLICY(ExecPolicy)
return table_.cvisit_while(p, f);
}
#endif
/// Modifiers
///
template <class Ty>
BOOST_FORCEINLINE auto insert(Ty&& value)
-> decltype(table_.insert(std::forward<Ty>(value)))
{
return table_.insert(std::forward<Ty>(value));
}
BOOST_FORCEINLINE bool insert(init_type&& obj)
{
return table_.insert(std::move(obj));
}
template <class InputIterator>
void insert(InputIterator begin, InputIterator end)
{
for (auto pos = begin; pos != end; ++pos) {
table_.emplace(*pos);
}
}
void insert(std::initializer_list<value_type> ilist)
{
this->insert(ilist.begin(), ilist.end());
}
template <class M>
BOOST_FORCEINLINE bool insert_or_assign(key_type const& k, M&& obj)
{
return table_.try_emplace_or_visit(k, std::forward<M>(obj),
[&](value_type& m) { m.second = std::forward<M>(obj); });
}
template <class M>
BOOST_FORCEINLINE bool insert_or_assign(key_type&& k, M&& obj)
{
return table_.try_emplace_or_visit(std::move(k), std::forward<M>(obj),
[&](value_type& m) { m.second = std::forward<M>(obj); });
}
template <class K, class M>
BOOST_FORCEINLINE typename std::enable_if<
detail::are_transparent<K, hasher, key_equal>::value, bool>::type
insert_or_assign(K&& k, M&& obj)
{
return table_.try_emplace_or_visit(std::forward<K>(k),
std::forward<M>(obj),
[&](value_type& m) { m.second = std::forward<M>(obj); });
}
template <class Ty, class F>
BOOST_FORCEINLINE auto insert_or_visit(Ty&& value, F f)
-> decltype(table_.insert_or_visit(std::forward<Ty>(value), f))
{
BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(F)
return table_.insert_or_visit(std::forward<Ty>(value), f);
}
template <class F>
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 <class InputIterator, class F>
void insert_or_visit(InputIterator first, InputIterator last, F f)
{
BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(F)
for (; first != last; ++first) {
table_.emplace_or_visit(*first, f);
}
}
template <class F>
void insert_or_visit(std::initializer_list<value_type> ilist, F f)
{
BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(F)
this->insert_or_visit(ilist.begin(), ilist.end(), f);
}
template <class Ty, class F>
BOOST_FORCEINLINE auto insert_or_cvisit(Ty&& value, F f)
-> decltype(table_.insert_or_cvisit(std::forward<Ty>(value), f))
{
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
return table_.insert_or_cvisit(std::forward<Ty>(value), f);
}
template <class F>
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 <class InputIterator, class F>
void insert_or_cvisit(InputIterator first, InputIterator last, F f)
{
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
for (; first != last; ++first) {
table_.emplace_or_cvisit(*first, f);
}
}
template <class F>
void insert_or_cvisit(std::initializer_list<value_type> ilist, F f)
{
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
this->insert_or_cvisit(ilist.begin(), ilist.end(), f);
}
template <class... Args> BOOST_FORCEINLINE bool emplace(Args&&... args)
{
return table_.emplace(std::forward<Args>(args)...);
}
template <class Arg, class... Args>
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>(arg), std::forward<Args>(args)...);
}
template <class Arg, class... Args>
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>(arg), std::forward<Args>(args)...);
}
template <class... Args>
BOOST_FORCEINLINE bool try_emplace(key_type const& k, Args&&... args)
{
return table_.try_emplace(k, std::forward<Args>(args)...);
}
template <class... Args>
BOOST_FORCEINLINE bool try_emplace(key_type&& k, Args&&... args)
{
return table_.try_emplace(std::move(k), std::forward<Args>(args)...);
}
template <class K, class... Args>
BOOST_FORCEINLINE typename std::enable_if<
detail::are_transparent<K, hasher, key_equal>::value, bool>::type
try_emplace(K&& k, Args&&... args)
{
return table_.try_emplace(
std::forward<K>(k), std::forward<Args>(args)...);
}
template <class Arg, class... Args>
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>(arg), std::forward<Args>(args)...);
}
template <class Arg, class... Args>
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>(arg), std::forward<Args>(args)...);
}
template <class Arg, class... Args>
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>(arg), std::forward<Args>(args)...);
}
template <class Arg, class... Args>
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>(arg), std::forward<Args>(args)...);
}
template <class K, class Arg, class... Args>
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>(k),
std::forward<Arg>(arg), std::forward<Args>(args)...);
}
template <class K, class Arg, class... Args>
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>(k),
std::forward<Arg>(arg), std::forward<Args>(args)...);
}
BOOST_FORCEINLINE size_type erase(key_type const& k)
{
return table_.erase(k);
}
template <class K>
BOOST_FORCEINLINE typename std::enable_if<
detail::are_transparent<K, hasher, key_equal>::value, size_type>::type
erase(K&& k)
{
return table_.erase(std::forward<K>(k));
}
template <class F>
BOOST_FORCEINLINE size_type erase_if(key_type const& k, F f)
{
return table_.erase_if(k, f);
}
template <class K, class F>
BOOST_FORCEINLINE typename std::enable_if<
detail::are_transparent<K, hasher, key_equal>::value &&
!detail::is_execution_policy<K>::value,
size_type>::type
erase_if(K&& k, F f)
{
return table_.erase_if(std::forward<K>(k), f);
}
#if defined(BOOST_UNORDERED_PARALLEL_ALGORITHMS)
template <class ExecPolicy, class F>
typename std::enable_if<detail::is_execution_policy<ExecPolicy>::value,
void>::type
erase_if(ExecPolicy&& p, F f)
{
BOOST_UNORDERED_STATIC_ASSERT_EXEC_POLICY(ExecPolicy)
table_.erase_if(p, f);
}
#endif
template <class F> size_type erase_if(F f) { return table_.erase_if(f); }
void swap(concurrent_flat_map& other) noexcept(
boost::allocator_is_always_equal<Allocator>::type::value ||
boost::allocator_propagate_on_container_swap<Allocator>::type::value)
{
return table_.swap(other.table_);
}
void clear() noexcept { table_.clear(); }
template <typename H2, typename P2>
size_type merge(concurrent_flat_map<Key, T, H2, P2, Allocator>& x)
{
BOOST_ASSERT(get_allocator() == x.get_allocator());
return table_.merge(x.table_);
}
template <typename H2, typename P2>
size_type merge(concurrent_flat_map<Key, T, H2, P2, Allocator>&& x)
{
return merge(x);
}
BOOST_FORCEINLINE size_type count(key_type const& k) const
{
return table_.count(k);
}
template <class K>
BOOST_FORCEINLINE typename std::enable_if<
detail::are_transparent<K, hasher, key_equal>::value, size_type>::type
count(K const& k)
{
return table_.count(k);
}
BOOST_FORCEINLINE bool contains(key_type const& k) const
{
return table_.contains(k);
}
template <class K>
BOOST_FORCEINLINE typename std::enable_if<
detail::are_transparent<K, hasher, key_equal>::value, bool>::type
contains(K const& k) const
{
return table_.contains(k);
}
/// Hash Policy
///
size_type bucket_count() const noexcept { return table_.capacity(); }
float load_factor() const noexcept { return table_.load_factor(); }
float max_load_factor() const noexcept
{
return table_.max_load_factor();
}
void max_load_factor(float) {}
size_type max_load() const noexcept { return table_.max_load(); }
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(); }
};
template <class Key, class T, class Hash, class KeyEqual, class Allocator>
bool operator==(
concurrent_flat_map<Key, T, Hash, KeyEqual, Allocator> const& lhs,
concurrent_flat_map<Key, T, Hash, KeyEqual, Allocator> const& rhs)
{
return lhs.table_ == rhs.table_;
}
template <class Key, class T, class Hash, class KeyEqual, class Allocator>
bool operator!=(
concurrent_flat_map<Key, T, Hash, KeyEqual, Allocator> const& lhs,
concurrent_flat_map<Key, T, Hash, KeyEqual, Allocator> const& rhs)
{
return !(lhs == rhs);
}
template <class Key, class T, class Hash, class Pred, class Alloc>
void swap(concurrent_flat_map<Key, T, Hash, Pred, Alloc>& x,
concurrent_flat_map<Key, T, Hash, Pred, Alloc>& y)
noexcept(noexcept(x.swap(y)))
{
x.swap(y);
}
template <class K, class T, class H, class P, class A, class Predicate>
typename concurrent_flat_map<K, T, H, P, A>::size_type erase_if(
concurrent_flat_map<K, T, H, P, A>& c, Predicate pred)
{
return c.table_.erase_if(pred);
}
template<class Archive, class K, class V, class H, class KE, class A>
void serialize(
Archive& ar, concurrent_flat_map<K, V, H, KE, A>& c, unsigned int)
{
ar & core::make_nvp("table",c.table_);
}
#if BOOST_UNORDERED_TEMPLATE_DEDUCTION_GUIDES
template <class InputIterator,
class Hash =
boost::hash<boost::unordered::detail::iter_key_t<InputIterator> >,
class Pred =
std::equal_to<boost::unordered::detail::iter_key_t<InputIterator> >,
class Allocator = std::allocator<
boost::unordered::detail::iter_to_alloc_t<InputIterator> >,
class = std::enable_if_t<detail::is_input_iterator_v<InputIterator> >,
class = std::enable_if_t<detail::is_hash_v<Hash> >,
class = std::enable_if_t<detail::is_pred_v<Pred> >,
class = std::enable_if_t<detail::is_allocator_v<Allocator> > >
concurrent_flat_map(InputIterator, InputIterator,
std::size_t = boost::unordered::detail::foa::default_bucket_count,
Hash = Hash(), Pred = Pred(), Allocator = Allocator())
-> concurrent_flat_map<
boost::unordered::detail::iter_key_t<InputIterator>,
boost::unordered::detail::iter_val_t<InputIterator>, Hash, Pred,
Allocator>;
template <class Key, class T,
class Hash = boost::hash<std::remove_const_t<Key> >,
class Pred = std::equal_to<std::remove_const_t<Key> >,
class Allocator = std::allocator<std::pair<const Key, T> >,
class = std::enable_if_t<detail::is_hash_v<Hash> >,
class = std::enable_if_t<detail::is_pred_v<Pred> >,
class = std::enable_if_t<detail::is_allocator_v<Allocator> > >
concurrent_flat_map(std::initializer_list<std::pair<Key, T> >,
std::size_t = boost::unordered::detail::foa::default_bucket_count,
Hash = Hash(), Pred = Pred(), Allocator = Allocator())
-> concurrent_flat_map<std::remove_const_t<Key>, T, Hash, Pred,
Allocator>;
template <class InputIterator, class Allocator,
class = std::enable_if_t<detail::is_input_iterator_v<InputIterator> >,
class = std::enable_if_t<detail::is_allocator_v<Allocator> > >
concurrent_flat_map(InputIterator, InputIterator, std::size_t, Allocator)
-> concurrent_flat_map<
boost::unordered::detail::iter_key_t<InputIterator>,
boost::unordered::detail::iter_val_t<InputIterator>,
boost::hash<boost::unordered::detail::iter_key_t<InputIterator> >,
std::equal_to<boost::unordered::detail::iter_key_t<InputIterator> >,
Allocator>;
template <class InputIterator, class Allocator,
class = std::enable_if_t<detail::is_input_iterator_v<InputIterator> >,
class = std::enable_if_t<detail::is_allocator_v<Allocator> > >
concurrent_flat_map(InputIterator, InputIterator, Allocator)
-> concurrent_flat_map<
boost::unordered::detail::iter_key_t<InputIterator>,
boost::unordered::detail::iter_val_t<InputIterator>,
boost::hash<boost::unordered::detail::iter_key_t<InputIterator> >,
std::equal_to<boost::unordered::detail::iter_key_t<InputIterator> >,
Allocator>;
template <class InputIterator, class Hash, class Allocator,
class = std::enable_if_t<detail::is_hash_v<Hash> >,
class = std::enable_if_t<detail::is_input_iterator_v<InputIterator> >,
class = std::enable_if_t<detail::is_allocator_v<Allocator> > >
concurrent_flat_map(
InputIterator, InputIterator, std::size_t, Hash, Allocator)
-> concurrent_flat_map<
boost::unordered::detail::iter_key_t<InputIterator>,
boost::unordered::detail::iter_val_t<InputIterator>, Hash,
std::equal_to<boost::unordered::detail::iter_key_t<InputIterator> >,
Allocator>;
template <class Key, class T, class Allocator,
class = std::enable_if_t<detail::is_allocator_v<Allocator> > >
concurrent_flat_map(std::initializer_list<std::pair<Key, T> >, std::size_t,
Allocator) -> concurrent_flat_map<std::remove_const_t<Key>, T,
boost::hash<std::remove_const_t<Key> >,
std::equal_to<std::remove_const_t<Key> >, Allocator>;
template <class Key, class T, class Allocator,
class = std::enable_if_t<detail::is_allocator_v<Allocator> > >
concurrent_flat_map(std::initializer_list<std::pair<Key, T> >, Allocator)
-> concurrent_flat_map<std::remove_const_t<Key>, T,
boost::hash<std::remove_const_t<Key> >,
std::equal_to<std::remove_const_t<Key> >, Allocator>;
template <class Key, class T, class Hash, class Allocator,
class = std::enable_if_t<detail::is_hash_v<Hash> >,
class = std::enable_if_t<detail::is_allocator_v<Allocator> > >
concurrent_flat_map(std::initializer_list<std::pair<Key, T> >, std::size_t,
Hash, Allocator) -> concurrent_flat_map<std::remove_const_t<Key>, T,
Hash, std::equal_to<std::remove_const_t<Key> >, Allocator>;
#endif
} // namespace unordered
} // namespace boost
#endif // BOOST_UNORDERED_CONCURRENT_FLAT_MAP_HPP
+51
View File
@@ -0,0 +1,51 @@
/* Fast open-addressing concurrent hashmap.
*
* 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.
*/
#ifndef BOOST_UNORDERED_CONCURRENT_FLAT_MAP_FWD_HPP
#define BOOST_UNORDERED_CONCURRENT_FLAT_MAP_FWD_HPP
#include <boost/container_hash/hash_fwd.hpp>
#include <functional>
#include <memory>
namespace boost {
namespace unordered {
template <class Key, class T, class Hash = boost::hash<Key>,
class Pred = std::equal_to<Key>,
class Allocator = std::allocator<std::pair<Key const, T> > >
class concurrent_flat_map;
template <class Key, class T, class Hash, class KeyEqual, class Allocator>
bool operator==(
concurrent_flat_map<Key, T, Hash, KeyEqual, Allocator> const& lhs,
concurrent_flat_map<Key, T, Hash, KeyEqual, Allocator> const& rhs);
template <class Key, class T, class Hash, class KeyEqual, class Allocator>
bool operator!=(
concurrent_flat_map<Key, T, Hash, KeyEqual, Allocator> const& lhs,
concurrent_flat_map<Key, T, Hash, KeyEqual, Allocator> const& rhs);
template <class Key, class T, class Hash, class Pred, class Alloc>
void swap(concurrent_flat_map<Key, T, Hash, Pred, Alloc>& x,
concurrent_flat_map<Key, T, Hash, Pred, Alloc>& y)
noexcept(noexcept(x.swap(y)));
template <class K, class T, class H, class P, class A, class Predicate>
typename concurrent_flat_map<K, T, H, P, A>::size_type erase_if(
concurrent_flat_map<K, T, H, P, A>& c, Predicate pred);
} // namespace unordered
using boost::unordered::concurrent_flat_map;
} // namespace boost
#endif // BOOST_UNORDERED_CONCURRENT_FLAT_MAP_HPP
+716
View File
@@ -0,0 +1,716 @@
/* Fast open-addressing concurrent hashset.
*
* Copyright 2023 Christian Mazakas.
* Copyright 2023 Joaquin M Lopez Munoz.
* 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.
*/
#ifndef BOOST_UNORDERED_CONCURRENT_FLAT_SET_HPP
#define BOOST_UNORDERED_CONCURRENT_FLAT_SET_HPP
#include <boost/unordered/concurrent_flat_set_fwd.hpp>
#include <boost/unordered/detail/concurrent_static_asserts.hpp>
#include <boost/unordered/detail/foa/concurrent_table.hpp>
#include <boost/unordered/detail/foa/flat_set_types.hpp>
#include <boost/unordered/detail/type_traits.hpp>
#include <boost/unordered/unordered_flat_set_fwd.hpp>
#include <boost/container_hash/hash.hpp>
#include <boost/core/allocator_access.hpp>
#include <boost/core/serialization.hpp>
#include <utility>
namespace boost {
namespace unordered {
template <class Key, class Hash, class Pred, class Allocator>
class concurrent_flat_set
{
private:
template <class Key2, class Hash2, class Pred2, class Allocator2>
friend class concurrent_flat_set;
template <class Key2, class Hash2, class Pred2, class Allocator2>
friend class unordered_flat_set;
using type_policy = detail::foa::flat_set_types<Key>;
using table_type =
detail::foa::concurrent_table<type_policy, Hash, Pred, Allocator>;
table_type table_;
template <class K, class H, class KE, class A>
bool friend operator==(concurrent_flat_set<K, H, KE, A> const& lhs,
concurrent_flat_set<K, H, KE, A> const& rhs);
template <class K, class H, class KE, class A, class Predicate>
friend typename concurrent_flat_set<K, H, KE, A>::size_type erase_if(
concurrent_flat_set<K, H, KE, A>& set, Predicate pred);
template<class Archive, class K, class H, class KE, class A>
friend void serialize(
Archive& ar, concurrent_flat_set<K, H, KE, A>& c,
unsigned int version);
public:
using key_type = Key;
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::unordered::detail::type_identity<Hash>::type;
using key_equal = typename boost::unordered::detail::type_identity<Pred>::type;
using allocator_type = typename boost::unordered::detail::type_identity<Allocator>::type;
using reference = value_type&;
using const_reference = value_type const&;
using pointer = typename boost::allocator_pointer<allocator_type>::type;
using const_pointer =
typename boost::allocator_const_pointer<allocator_type>::type;
static constexpr size_type bulk_visit_size = table_type::bulk_visit_size;
concurrent_flat_set()
: concurrent_flat_set(detail::foa::default_bucket_count)
{
}
explicit concurrent_flat_set(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 <class InputIterator>
concurrent_flat_set(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_set(concurrent_flat_set const& rhs)
: table_(rhs.table_,
boost::allocator_select_on_container_copy_construction(
rhs.get_allocator()))
{
}
concurrent_flat_set(concurrent_flat_set&& rhs)
: table_(std::move(rhs.table_))
{
}
template <class InputIterator>
concurrent_flat_set(
InputIterator f, InputIterator l, allocator_type const& a)
: concurrent_flat_set(f, l, 0, hasher(), key_equal(), a)
{
}
explicit concurrent_flat_set(allocator_type const& a)
: table_(detail::foa::default_bucket_count, hasher(), key_equal(), a)
{
}
concurrent_flat_set(
concurrent_flat_set const& rhs, allocator_type const& a)
: table_(rhs.table_, a)
{
}
concurrent_flat_set(concurrent_flat_set&& rhs, allocator_type const& a)
: table_(std::move(rhs.table_), a)
{
}
concurrent_flat_set(std::initializer_list<value_type> 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_set(n, hf, eql, a)
{
this->insert(il.begin(), il.end());
}
concurrent_flat_set(size_type n, const allocator_type& a)
: concurrent_flat_set(n, hasher(), key_equal(), a)
{
}
concurrent_flat_set(
size_type n, const hasher& hf, const allocator_type& a)
: concurrent_flat_set(n, hf, key_equal(), a)
{
}
template <typename InputIterator>
concurrent_flat_set(
InputIterator f, InputIterator l, size_type n, const allocator_type& a)
: concurrent_flat_set(f, l, n, hasher(), key_equal(), a)
{
}
template <typename InputIterator>
concurrent_flat_set(InputIterator f, InputIterator l, size_type n,
const hasher& hf, const allocator_type& a)
: concurrent_flat_set(f, l, n, hf, key_equal(), a)
{
}
concurrent_flat_set(
std::initializer_list<value_type> il, const allocator_type& a)
: concurrent_flat_set(
il, detail::foa::default_bucket_count, hasher(), key_equal(), a)
{
}
concurrent_flat_set(std::initializer_list<value_type> il, size_type n,
const allocator_type& a)
: concurrent_flat_set(il, n, hasher(), key_equal(), a)
{
}
concurrent_flat_set(std::initializer_list<value_type> il, size_type n,
const hasher& hf, const allocator_type& a)
: concurrent_flat_set(il, n, hf, key_equal(), a)
{
}
concurrent_flat_set(
unordered_flat_set<Key, Hash, Pred, Allocator>&& other)
: table_(std::move(other.table_))
{
}
~concurrent_flat_set() = default;
concurrent_flat_set& operator=(concurrent_flat_set const& rhs)
{
table_ = rhs.table_;
return *this;
}
concurrent_flat_set& operator=(concurrent_flat_set&& rhs)
noexcept(boost::allocator_is_always_equal<Allocator>::type::value ||
boost::allocator_propagate_on_container_move_assignment<
Allocator>::type::value)
{
table_ = std::move(rhs.table_);
return *this;
}
concurrent_flat_set& operator=(std::initializer_list<value_type> ilist)
{
table_ = ilist;
return *this;
}
/// Capacity
///
size_type size() const noexcept { return table_.size(); }
size_type max_size() const noexcept { return table_.max_size(); }
BOOST_ATTRIBUTE_NODISCARD bool empty() const noexcept
{
return size() == 0;
}
template <class F>
BOOST_FORCEINLINE size_type visit(key_type const& k, F f) const
{
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
return table_.visit(k, f);
}
template <class F>
BOOST_FORCEINLINE size_type cvisit(key_type const& k, F f) const
{
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
return table_.visit(k, f);
}
template <class K, class F>
BOOST_FORCEINLINE typename std::enable_if<
detail::are_transparent<K, hasher, key_equal>::value, size_type>::type
visit(K&& k, F f) const
{
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
return table_.visit(std::forward<K>(k), f);
}
template <class K, class F>
BOOST_FORCEINLINE typename std::enable_if<
detail::are_transparent<K, hasher, key_equal>::value, size_type>::type
cvisit(K&& k, F f) const
{
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
return table_.visit(std::forward<K>(k), f);
}
template<class FwdIterator, class F>
BOOST_FORCEINLINE
size_t visit(FwdIterator first, FwdIterator last, F f) const
{
BOOST_UNORDERED_STATIC_ASSERT_BULK_VISIT_ITERATOR(FwdIterator)
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
return table_.visit(first, last, f);
}
template<class FwdIterator, class F>
BOOST_FORCEINLINE
size_t cvisit(FwdIterator first, FwdIterator last, F f) const
{
BOOST_UNORDERED_STATIC_ASSERT_BULK_VISIT_ITERATOR(FwdIterator)
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
return table_.visit(first, last, f);
}
template <class F> size_type visit_all(F f) const
{
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
return table_.visit_all(f);
}
template <class F> size_type cvisit_all(F f) const
{
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
return table_.cvisit_all(f);
}
#if defined(BOOST_UNORDERED_PARALLEL_ALGORITHMS)
template <class ExecPolicy, class F>
typename std::enable_if<detail::is_execution_policy<ExecPolicy>::value,
void>::type
visit_all(ExecPolicy&& p, F f) const
{
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
BOOST_UNORDERED_STATIC_ASSERT_EXEC_POLICY(ExecPolicy)
table_.visit_all(p, f);
}
template <class ExecPolicy, class F>
typename std::enable_if<detail::is_execution_policy<ExecPolicy>::value,
void>::type
cvisit_all(ExecPolicy&& p, F f) const
{
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
BOOST_UNORDERED_STATIC_ASSERT_EXEC_POLICY(ExecPolicy)
table_.cvisit_all(p, f);
}
#endif
template <class F> bool visit_while(F f) const
{
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
return table_.visit_while(f);
}
template <class F> bool cvisit_while(F f) const
{
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
return table_.cvisit_while(f);
}
#if defined(BOOST_UNORDERED_PARALLEL_ALGORITHMS)
template <class ExecPolicy, class F>
typename std::enable_if<detail::is_execution_policy<ExecPolicy>::value,
bool>::type
visit_while(ExecPolicy&& p, F f) const
{
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
BOOST_UNORDERED_STATIC_ASSERT_EXEC_POLICY(ExecPolicy)
return table_.visit_while(p, f);
}
template <class ExecPolicy, class F>
typename std::enable_if<detail::is_execution_policy<ExecPolicy>::value,
bool>::type
cvisit_while(ExecPolicy&& p, F f) const
{
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
BOOST_UNORDERED_STATIC_ASSERT_EXEC_POLICY(ExecPolicy)
return table_.cvisit_while(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));
}
template <class K>
BOOST_FORCEINLINE typename std::enable_if<
detail::are_transparent<K, hasher, key_equal>::value,
bool >::type
insert(K&& k)
{
return table_.try_emplace(std::forward<K>(k));
}
template <class InputIterator>
void insert(InputIterator begin, InputIterator end)
{
for (auto pos = begin; pos != end; ++pos) {
table_.emplace(*pos);
}
}
void insert(std::initializer_list<value_type> ilist)
{
this->insert(ilist.begin(), ilist.end());
}
template <class F>
BOOST_FORCEINLINE bool insert_or_visit(value_type const& obj, F f)
{
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
return table_.insert_or_cvisit(obj, f);
}
template <class F>
BOOST_FORCEINLINE bool insert_or_visit(value_type&& obj, F f)
{
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
return table_.insert_or_cvisit(std::move(obj), f);
}
template <class K, class F>
BOOST_FORCEINLINE typename std::enable_if<
detail::are_transparent<K, hasher, key_equal>::value,
bool >::type
insert_or_visit(K&& k, F f)
{
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
return table_.try_emplace_or_cvisit(std::forward<K>(k), f);
}
template <class InputIterator, class F>
void insert_or_visit(InputIterator first, InputIterator last, F f)
{
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
for (; first != last; ++first) {
table_.emplace_or_cvisit(*first, f);
}
}
template <class F>
void insert_or_visit(std::initializer_list<value_type> ilist, F f)
{
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
this->insert_or_cvisit(ilist.begin(), ilist.end(), f);
}
template <class F>
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 <class F>
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 <class K, class F>
BOOST_FORCEINLINE typename std::enable_if<
detail::are_transparent<K, hasher, key_equal>::value,
bool >::type
insert_or_cvisit(K&& k, F f)
{
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
return table_.try_emplace_or_cvisit(std::forward<K>(k), f);
}
template <class InputIterator, class F>
void insert_or_cvisit(InputIterator first, InputIterator last, F f)
{
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
for (; first != last; ++first) {
table_.emplace_or_cvisit(*first, f);
}
}
template <class F>
void insert_or_cvisit(std::initializer_list<value_type> ilist, F f)
{
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
this->insert_or_cvisit(ilist.begin(), ilist.end(), f);
}
template <class... Args> BOOST_FORCEINLINE bool emplace(Args&&... args)
{
return table_.emplace(std::forward<Args>(args)...);
}
template <class Arg, class... Args>
BOOST_FORCEINLINE bool emplace_or_visit(Arg&& arg, Args&&... args)
{
BOOST_UNORDERED_STATIC_ASSERT_LAST_ARG_CONST_INVOCABLE(Arg, Args...)
return table_.emplace_or_cvisit(
std::forward<Arg>(arg), std::forward<Args>(args)...);
}
template <class Arg, class... Args>
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>(arg), std::forward<Args>(args)...);
}
BOOST_FORCEINLINE size_type erase(key_type const& k)
{
return table_.erase(k);
}
template <class K>
BOOST_FORCEINLINE typename std::enable_if<
detail::are_transparent<K, hasher, key_equal>::value, size_type>::type
erase(K&& k)
{
return table_.erase(std::forward<K>(k));
}
template <class F>
BOOST_FORCEINLINE size_type erase_if(key_type const& k, F f)
{
return table_.erase_if(k, f);
}
template <class K, class F>
BOOST_FORCEINLINE typename std::enable_if<
detail::are_transparent<K, hasher, key_equal>::value &&
!detail::is_execution_policy<K>::value,
size_type>::type
erase_if(K&& k, F f)
{
return table_.erase_if(std::forward<K>(k), f);
}
#if defined(BOOST_UNORDERED_PARALLEL_ALGORITHMS)
template <class ExecPolicy, class F>
typename std::enable_if<detail::is_execution_policy<ExecPolicy>::value,
void>::type
erase_if(ExecPolicy&& p, F f)
{
BOOST_UNORDERED_STATIC_ASSERT_EXEC_POLICY(ExecPolicy)
table_.erase_if(p, f);
}
#endif
template <class F> size_type erase_if(F f) { return table_.erase_if(f); }
void swap(concurrent_flat_set& other) noexcept(
boost::allocator_is_always_equal<Allocator>::type::value ||
boost::allocator_propagate_on_container_swap<Allocator>::type::value)
{
return table_.swap(other.table_);
}
void clear() noexcept { table_.clear(); }
template <typename H2, typename P2>
size_type merge(concurrent_flat_set<Key, H2, P2, Allocator>& x)
{
BOOST_ASSERT(get_allocator() == x.get_allocator());
return table_.merge(x.table_);
}
template <typename H2, typename P2>
size_type merge(concurrent_flat_set<Key, H2, P2, Allocator>&& x)
{
return merge(x);
}
BOOST_FORCEINLINE size_type count(key_type const& k) const
{
return table_.count(k);
}
template <class K>
BOOST_FORCEINLINE typename std::enable_if<
detail::are_transparent<K, hasher, key_equal>::value, size_type>::type
count(K const& k)
{
return table_.count(k);
}
BOOST_FORCEINLINE bool contains(key_type const& k) const
{
return table_.contains(k);
}
template <class K>
BOOST_FORCEINLINE typename std::enable_if<
detail::are_transparent<K, hasher, key_equal>::value, bool>::type
contains(K const& k) const
{
return table_.contains(k);
}
/// Hash Policy
///
size_type bucket_count() const noexcept { return table_.capacity(); }
float load_factor() const noexcept { return table_.load_factor(); }
float max_load_factor() const noexcept
{
return table_.max_load_factor();
}
void max_load_factor(float) {}
size_type max_load() const noexcept { return table_.max_load(); }
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(); }
};
template <class Key, class Hash, class KeyEqual, class Allocator>
bool operator==(
concurrent_flat_set<Key, Hash, KeyEqual, Allocator> const& lhs,
concurrent_flat_set<Key, Hash, KeyEqual, Allocator> const& rhs)
{
return lhs.table_ == rhs.table_;
}
template <class Key, class Hash, class KeyEqual, class Allocator>
bool operator!=(
concurrent_flat_set<Key, Hash, KeyEqual, Allocator> const& lhs,
concurrent_flat_set<Key, Hash, KeyEqual, Allocator> const& rhs)
{
return !(lhs == rhs);
}
template <class Key, class Hash, class Pred, class Alloc>
void swap(concurrent_flat_set<Key, Hash, Pred, Alloc>& x,
concurrent_flat_set<Key, Hash, Pred, Alloc>& y)
noexcept(noexcept(x.swap(y)))
{
x.swap(y);
}
template <class K, class H, class P, class A, class Predicate>
typename concurrent_flat_set<K, H, P, A>::size_type erase_if(
concurrent_flat_set<K, H, P, A>& c, Predicate pred)
{
return c.table_.erase_if(pred);
}
template<class Archive, class K, class H, class KE, class A>
void serialize(
Archive& ar, concurrent_flat_set<K, H, KE, A>& c, unsigned int)
{
ar & core::make_nvp("table",c.table_);
}
#if BOOST_UNORDERED_TEMPLATE_DEDUCTION_GUIDES
template <class InputIterator,
class Hash =
boost::hash<typename std::iterator_traits<InputIterator>::value_type>,
class Pred =
std::equal_to<typename std::iterator_traits<InputIterator>::value_type>,
class Allocator = std::allocator<
typename std::iterator_traits<InputIterator>::value_type>,
class = std::enable_if_t<detail::is_input_iterator_v<InputIterator> >,
class = std::enable_if_t<detail::is_hash_v<Hash> >,
class = std::enable_if_t<detail::is_pred_v<Pred> >,
class = std::enable_if_t<detail::is_allocator_v<Allocator> > >
concurrent_flat_set(InputIterator, InputIterator,
std::size_t = boost::unordered::detail::foa::default_bucket_count,
Hash = Hash(), Pred = Pred(), Allocator = Allocator())
-> concurrent_flat_set<
typename std::iterator_traits<InputIterator>::value_type, Hash, Pred,
Allocator>;
template <class T, class Hash = boost::hash<T>,
class Pred = std::equal_to<T>, class Allocator = std::allocator<T>,
class = std::enable_if_t<detail::is_hash_v<Hash> >,
class = std::enable_if_t<detail::is_pred_v<Pred> >,
class = std::enable_if_t<detail::is_allocator_v<Allocator> > >
concurrent_flat_set(std::initializer_list<T>,
std::size_t = boost::unordered::detail::foa::default_bucket_count,
Hash = Hash(), Pred = Pred(), Allocator = Allocator())
-> concurrent_flat_set< T, Hash, Pred, Allocator>;
template <class InputIterator, class Allocator,
class = std::enable_if_t<detail::is_input_iterator_v<InputIterator> >,
class = std::enable_if_t<detail::is_allocator_v<Allocator> > >
concurrent_flat_set(InputIterator, InputIterator, std::size_t, Allocator)
-> concurrent_flat_set<
typename std::iterator_traits<InputIterator>::value_type,
boost::hash<typename std::iterator_traits<InputIterator>::value_type>,
std::equal_to<typename std::iterator_traits<InputIterator>::value_type>,
Allocator>;
template <class InputIterator, class Allocator,
class = std::enable_if_t<detail::is_input_iterator_v<InputIterator> >,
class = std::enable_if_t<detail::is_allocator_v<Allocator> > >
concurrent_flat_set(InputIterator, InputIterator, Allocator)
-> concurrent_flat_set<
typename std::iterator_traits<InputIterator>::value_type,
boost::hash<typename std::iterator_traits<InputIterator>::value_type>,
std::equal_to<typename std::iterator_traits<InputIterator>::value_type>,
Allocator>;
template <class InputIterator, class Hash, class Allocator,
class = std::enable_if_t<detail::is_hash_v<Hash> >,
class = std::enable_if_t<detail::is_input_iterator_v<InputIterator> >,
class = std::enable_if_t<detail::is_allocator_v<Allocator> > >
concurrent_flat_set(
InputIterator, InputIterator, std::size_t, Hash, Allocator)
-> concurrent_flat_set<
typename std::iterator_traits<InputIterator>::value_type, Hash,
std::equal_to<typename std::iterator_traits<InputIterator>::value_type>,
Allocator>;
template <class T, class Allocator,
class = std::enable_if_t<detail::is_allocator_v<Allocator> > >
concurrent_flat_set(std::initializer_list<T>, std::size_t, Allocator)
-> concurrent_flat_set<T, boost::hash<T>,std::equal_to<T>, Allocator>;
template <class T, class Allocator,
class = std::enable_if_t<detail::is_allocator_v<Allocator> > >
concurrent_flat_set(std::initializer_list<T >, Allocator)
-> concurrent_flat_set<T, boost::hash<T>, std::equal_to<T>, Allocator>;
template <class T, class Hash, class Allocator,
class = std::enable_if_t<detail::is_hash_v<Hash> >,
class = std::enable_if_t<detail::is_allocator_v<Allocator> > >
concurrent_flat_set(std::initializer_list<T >, std::size_t,Hash, Allocator)
-> concurrent_flat_set<T, Hash, std::equal_to<T>, Allocator>;
#endif
} // namespace unordered
} // namespace boost
#endif // BOOST_UNORDERED_CONCURRENT_FLAT_SET_HPP
+52
View File
@@ -0,0 +1,52 @@
/* Fast open-addressing concurrent hashset.
*
* Copyright 2023 Christian Mazakas.
* Copyright 2023 Joaquin M Lopez Munoz.
* 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.
*/
#ifndef BOOST_UNORDERED_CONCURRENT_FLAT_SET_FWD_HPP
#define BOOST_UNORDERED_CONCURRENT_FLAT_SET_FWD_HPP
#include <boost/container_hash/hash_fwd.hpp>
#include <functional>
#include <memory>
namespace boost {
namespace unordered {
template <class Key, class Hash = boost::hash<Key>,
class Pred = std::equal_to<Key>,
class Allocator = std::allocator<Key> >
class concurrent_flat_set;
template <class Key, class Hash, class KeyEqual, class Allocator>
bool operator==(
concurrent_flat_set<Key, Hash, KeyEqual, Allocator> const& lhs,
concurrent_flat_set<Key, Hash, KeyEqual, Allocator> const& rhs);
template <class Key, class Hash, class KeyEqual, class Allocator>
bool operator!=(
concurrent_flat_set<Key, Hash, KeyEqual, Allocator> const& lhs,
concurrent_flat_set<Key, Hash, KeyEqual, Allocator> const& rhs);
template <class Key, class Hash, class Pred, class Alloc>
void swap(concurrent_flat_set<Key, Hash, Pred, Alloc>& x,
concurrent_flat_set<Key, Hash, Pred, Alloc>& y)
noexcept(noexcept(x.swap(y)));
template <class K, class H, class P, class A, class Predicate>
typename concurrent_flat_set<K, H, P, A>::size_type erase_if(
concurrent_flat_set<K, H, P, A>& c, Predicate pred);
} // namespace unordered
using boost::unordered::concurrent_flat_set;
} // namespace boost
#endif // BOOST_UNORDERED_CONCURRENT_FLAT_SET_FWD_HPP
+71
View File
@@ -0,0 +1,71 @@
/* Copyright 2023 Joaquin M Lopez Munoz.
* 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.
*/
#ifndef BOOST_UNORDERED_DETAIL_ARCHIVE_CONSTRUCTED_HPP
#define BOOST_UNORDERED_DETAIL_ARCHIVE_CONSTRUCTED_HPP
#include <boost/unordered/detail/opt_storage.hpp>
#include <boost/config.hpp>
#include <boost/core/no_exceptions_support.hpp>
#include <boost/core/noncopyable.hpp>
#include <boost/core/serialization.hpp>
namespace boost{
namespace unordered{
namespace detail{
/* constructs a stack-based object from a serialization archive */
template<typename T>
struct archive_constructed:private noncopyable
{
template<class Archive>
archive_constructed(const char* name,Archive& ar,unsigned int version)
{
core::load_construct_data_adl(ar,std::addressof(get()),version);
BOOST_TRY{
ar>>core::make_nvp(name,get());
}
BOOST_CATCH(...){
get().~T();
BOOST_RETHROW;
}
BOOST_CATCH_END
}
~archive_constructed()
{
get().~T();
}
#if defined(BOOST_GCC)&&(BOOST_GCC>=4*10000+6*100)
#define BOOST_UNORDERED_IGNORE_WSTRICT_ALIASING
#endif
#if defined(BOOST_UNORDERED_IGNORE_WSTRICT_ALIASING)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wstrict-aliasing"
#endif
T& get(){return *space.address();}
#if defined(BOOST_UNORDERED_IGNORE_WSTRICT_ALIASING)
#pragma GCC diagnostic pop
#undef BOOST_UNORDERED_IGNORE_WSTRICT_ALIASING
#endif
private:
opt_storage<T> space;
};
} /* namespace detail */
} /* namespace unordered */
} /* namespace boost */
#endif
+27
View File
@@ -0,0 +1,27 @@
/* Copyright 2023 Joaquin M Lopez Munoz.
* 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.
*/
#ifndef BOOST_UNORDERED_DETAIL_BAD_ARCHIVE_EXCEPTION_HPP
#define BOOST_UNORDERED_DETAIL_BAD_ARCHIVE_EXCEPTION_HPP
#include <stdexcept>
namespace boost{
namespace unordered{
namespace detail{
struct bad_archive_exception:std::runtime_error
{
bad_archive_exception():std::runtime_error("Invalid or corrupted archive"){}
};
} /* namespace detail */
} /* namespace unordered */
} /* namespace boost */
#endif
+105
View File
@@ -0,0 +1,105 @@
/* Copyright 2023 Christian Mazakas.
* Copyright 2023 Joaquin M Lopez Munoz.
* 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.
*/
#ifndef BOOST_UNORDERED_DETAIL_CONCURRENT_STATIC_ASSERTS_HPP
#define BOOST_UNORDERED_DETAIL_CONCURRENT_STATIC_ASSERTS_HPP
#include <boost/config.hpp>
#include <boost/mp11/algorithm.hpp>
#include <boost/mp11/list.hpp>
#include <functional>
#include <iterator>
#include <type_traits>
#define BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(F) \
static_assert(boost::unordered::detail::is_invocable<F, value_type&>::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<F, value_type const&>::value, \
"The provided Callable must be invocable with value_type const&");
#if BOOST_CXX_VERSION >= 202002L
#define BOOST_UNORDERED_STATIC_ASSERT_EXEC_POLICY(P) \
static_assert(!std::is_base_of<std::execution::parallel_unsequenced_policy, \
ExecPolicy>::value, \
"ExecPolicy must be sequenced."); \
static_assert( \
!std::is_base_of<std::execution::unsequenced_policy, ExecPolicy>::value, \
"ExecPolicy must be sequenced.");
#else
#define BOOST_UNORDERED_STATIC_ASSERT_EXEC_POLICY(P) \
static_assert(!std::is_base_of<std::execution::parallel_unsequenced_policy, \
ExecPolicy>::value, \
"ExecPolicy must be sequenced.");
#endif
#define BOOST_UNORDERED_DETAIL_COMMA ,
#define BOOST_UNORDERED_DETAIL_LAST_ARG(Arg, Args) \
mp11::mp_back<mp11::mp_list<Arg BOOST_UNORDERED_DETAIL_COMMA Args> >
#define BOOST_UNORDERED_STATIC_ASSERT_LAST_ARG_INVOCABLE(Arg, Args) \
BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE( \
BOOST_UNORDERED_DETAIL_LAST_ARG(Arg, Args))
#define BOOST_UNORDERED_STATIC_ASSERT_LAST_ARG_CONST_INVOCABLE(Arg, Args) \
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE( \
BOOST_UNORDERED_DETAIL_LAST_ARG(Arg, Args))
namespace boost {
namespace unordered {
namespace detail {
template <class F, class... Args>
struct is_invocable
: std::is_constructible<std::function<void(Args...)>,
std::reference_wrapper<typename std::remove_reference<F>::type> >
{
};
} // namespace detail
} // namespace unordered
} // namespace boost
#if defined(BOOST_NO_CXX20_HDR_CONCEPTS)
#define BOOST_UNORDERED_STATIC_ASSERT_FWD_ITERATOR(Iterator) \
static_assert( \
std::is_base_of< \
std::forward_iterator_tag, \
typename std::iterator_traits<Iterator>::iterator_category>::value, \
"The provided iterator must be at least forward");
#else
#define BOOST_UNORDERED_STATIC_ASSERT_FWD_ITERATOR(Iterator) \
static_assert(std::forward_iterator<Iterator>, \
"The provided iterator must be at least forward");
#endif
#define BOOST_UNORDERED_STATIC_ASSERT_KEY_COMPATIBLE_ITERATOR(Iterator) \
static_assert( \
std::is_same< \
typename std::iterator_traits<Iterator>::value_type, \
key_type>::value || \
detail::are_transparent< \
typename std::iterator_traits<Iterator>::value_type, \
hasher, key_equal>::value, \
"The provided iterator must dereference to a compatible key value");
#define BOOST_UNORDERED_STATIC_ASSERT_BULK_VISIT_ITERATOR(Iterator) \
BOOST_UNORDERED_STATIC_ASSERT_FWD_ITERATOR(Iterator) \
BOOST_UNORDERED_STATIC_ASSERT_KEY_COMPATIBLE_ITERATOR(Iterator)
#endif // BOOST_UNORDERED_DETAIL_CONCURRENT_STATIC_ASSERTS_HPP
+878
View File
@@ -0,0 +1,878 @@
// Copyright (C) 2022-2023 Joaquin M Lopez Munoz.
// Copyright (C) 2022 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)
#ifndef BOOST_UNORDERED_DETAIL_FCA_HPP
#define BOOST_UNORDERED_DETAIL_FCA_HPP
/*
The general structure of the fast closed addressing implementation is that we
use straight-forward separate chaining (i.e. each bucket contains its own linked
list) and then improve iteration time by adding an array of "bucket groups".
A bucket group is a constant-width view into a subsection of the buckets array,
containing a bitmask that indicates which one of the buckets in the subsection
contains a list of nodes. This allows the code to test N buckets for occupancy
in a single operation. Additional speed can be found by inter-linking occupied
bucket groups with one another in a doubly-linked list. To this end, large
swathes of the bucket groups array no longer need to be iterated and have their
bitmasks examined for occupancy.
A bucket group iterator contains a pointer to a bucket group along with a
pointer into the buckets array. The iterator's bucket pointer is guaranteed to
point to a bucket within the bucket group's view of the array. To advance the
iterator, we need to determine if we need to skip to the next bucket group or
simply move to the next occupied bucket as denoted by the bitmask.
To accomplish this, we perform something roughly equivalent to this:
```
bucket_iterator itb = ...
bucket_pointer p = itb.p
bucket_group_pointer pbg = itb.pbg
offset = p - pbg->buckets
// because we wish to see if the _next_ bit in the mask is occupied, we'll
// generate a testing mask from the current offset + 1
//
testing_mask = reset_first_bits(offset + 1)
n = ctz(pbg->bitmask & testing_mask)
if (n < N) {
p = pbg->buckets + n
} else {
pbg = pbg->next
p = pbg->buckets + ctz(pbg->bitmask)
}
```
`reset_first_bits` yields an unsigned integral with the first n bits set to 0
and then by counting the number of trailing zeroes when AND'd against the bucket
group's bitmask, we can derive the offset into the buckets array. When the
calculated offset is equal to N, we know we've reached the end of a bucket group
and we can advance to the next one.
This is a rough explanation for how iterator incrementation should work for a
fixed width size of N as 3 for the bucket groups
```
N = 3
p = buckets
pbg->bitmask = 0b101
pbg->buckets = buckets
offset = p - pbg->buckets // => 0
testing_mask = reset_first_bits(offset + 1) // reset_first_bits(1) => 0b110
x = bitmask & testing_mask // => 0b101 & 0b110 => 0b100
ctz(x) // ctz(0b100) => 2
// 2 < 3
=> p = pbg->buckets + 2
// increment again...
offset = p - pbg->buckets // => 2
testing_mask = reset_first_bits(offset + 1) // reset_first_bits(3) => 0b000
bitmask & testing_mask // 0b101 & 0b000 => 0b000
ctz(0b000) => 3
// 3 < 3 is false now
pbg = pbg->next
initial_offset = ctz(pbg->bitmask)
p = pbg->buckets + initial_offset
```
For `size_` number of buckets, there are `1 + (size_ / N)` bucket groups where
`N` is the width of a bucket group, determined at compile-time.
We allocate space for `size_ + 1` buckets, using the last one as a dummy bucket
which is kept permanently empty so it can act as a sentinel value in the
implementation of `iterator end();`. We set the last bucket group to act as a
sentinel.
```
num_groups = size_ / N + 1
groups = allocate(num_groups)
pbg = groups + (num_groups - 1)
// not guaranteed to point to exactly N buckets
pbg->buckets = buckets + N * (size_ / N)
// this marks the true end of the bucket array
buckets pbg->bitmask = set_bit(size_ % N)
// links in on itself
pbg->next = pbg->prev = pbg
```
To this end, we can devise a safe iteration scheme while also creating a useful
sentinel to use as the end iterator.
Otherwise, usage of the data structure is relatively straight-forward compared
to normal separate chaining implementations.
*/
#include <boost/unordered/detail/prime_fmod.hpp>
#include <boost/unordered/detail/serialize_tracked_address.hpp>
#include <boost/unordered/detail/opt_storage.hpp>
#include <boost/assert.hpp>
#include <boost/core/allocator_access.hpp>
#include <boost/core/bit.hpp>
#include <boost/core/empty_value.hpp>
#include <boost/core/invoke_swap.hpp>
#include <boost/core/no_exceptions_support.hpp>
#include <boost/core/serialization.hpp>
#include <boost/cstdint.hpp>
#include <boost/config.hpp>
#include <iterator>
namespace boost {
namespace unordered {
namespace detail {
template <class ValueType, class VoidPtr> struct node
{
typedef ValueType value_type;
typedef typename boost::pointer_traits<VoidPtr>::template rebind_to<
node>::type node_pointer;
node_pointer next;
opt_storage<value_type> buf;
node() noexcept : next(), buf() {}
value_type* value_ptr() noexcept
{
return buf.address();
}
value_type& value() noexcept
{
return *buf.address();
}
};
template <class Node, class VoidPtr> struct bucket
{
typedef typename boost::pointer_traits<VoidPtr>::template rebind_to<
Node>::type node_pointer;
typedef typename boost::pointer_traits<VoidPtr>::template rebind_to<
bucket>::type bucket_pointer;
node_pointer next;
bucket() noexcept : next() {}
};
template <class Bucket> struct bucket_group
{
typedef typename Bucket::bucket_pointer bucket_pointer;
typedef
typename boost::pointer_traits<bucket_pointer>::template rebind_to<
bucket_group>::type bucket_group_pointer;
BOOST_STATIC_CONSTANT(std::size_t, N = sizeof(std::size_t) * CHAR_BIT);
bucket_pointer buckets;
std::size_t bitmask;
bucket_group_pointer next, prev;
bucket_group() noexcept : buckets(), bitmask(0), next(), prev() {}
~bucket_group() {}
};
inline std::size_t set_bit(std::size_t n) { return std::size_t(1) << n; }
inline std::size_t reset_bit(std::size_t n)
{
return ~(std::size_t(1) << n);
}
inline std::size_t reset_first_bits(std::size_t n) // n>0
{
return ~(~(std::size_t(0)) >> (sizeof(std::size_t) * 8 - n));
}
template <class Bucket> struct grouped_bucket_iterator
{
public:
typedef typename Bucket::bucket_pointer bucket_pointer;
typedef
typename boost::pointer_traits<bucket_pointer>::template rebind_to<
bucket_group<Bucket> >::type bucket_group_pointer;
typedef Bucket value_type;
typedef typename boost::pointer_traits<bucket_pointer>::difference_type
difference_type;
typedef Bucket& reference;
typedef Bucket* pointer;
typedef std::forward_iterator_tag iterator_category;
private:
bucket_pointer p;
bucket_group_pointer pbg;
public:
grouped_bucket_iterator() : p(), pbg() {}
reference operator*() const noexcept { return dereference(); }
pointer operator->() const noexcept { return boost::to_address(p); }
grouped_bucket_iterator& operator++() noexcept
{
increment();
return *this;
}
grouped_bucket_iterator operator++(int) noexcept
{
grouped_bucket_iterator old = *this;
increment();
return old;
}
bool operator==(grouped_bucket_iterator const& other) const noexcept
{
return equal(other);
}
bool operator!=(grouped_bucket_iterator const& other) const noexcept
{
return !equal(other);
}
private:
template <typename, typename, typename>
friend class grouped_bucket_array;
BOOST_STATIC_CONSTANT(std::size_t, N = bucket_group<Bucket>::N);
grouped_bucket_iterator(bucket_pointer p_, bucket_group_pointer pbg_)
: p(p_), pbg(pbg_)
{
}
Bucket& dereference() const noexcept { return *p; }
bool equal(const grouped_bucket_iterator& x) const noexcept
{
return p == x.p;
}
void increment() noexcept
{
std::size_t const offset = static_cast<std::size_t>(p - pbg->buckets);
std::size_t n = std::size_t(boost::core::countr_zero(
pbg->bitmask & reset_first_bits(offset + 1)));
if (n < N) {
p = pbg->buckets + static_cast<difference_type>(n);
} else {
pbg = pbg->next;
std::ptrdiff_t x = boost::core::countr_zero(pbg->bitmask);
p = pbg->buckets + x;
}
}
template <typename Archive>
friend void serialization_track(
Archive& ar, grouped_bucket_iterator const& x)
{
// requires: not at end() position
track_address(ar, x.p);
track_address(ar, x.pbg);
}
friend class boost::serialization::access;
template <typename Archive> void serialize(Archive& ar, unsigned int)
{
// requires: not at end() position
serialize_tracked_address(ar, p);
serialize_tracked_address(ar, pbg);
}
};
template <class Node> struct const_grouped_local_bucket_iterator;
template <class Node> struct grouped_local_bucket_iterator
{
typedef typename Node::node_pointer node_pointer;
public:
typedef typename Node::value_type value_type;
typedef value_type element_type;
typedef value_type* pointer;
typedef value_type& reference;
typedef std::ptrdiff_t difference_type;
typedef std::forward_iterator_tag iterator_category;
grouped_local_bucket_iterator() : p() {}
reference operator*() const noexcept { return dereference(); }
pointer operator->() const noexcept
{
return std::addressof(dereference());
}
grouped_local_bucket_iterator& operator++() noexcept
{
increment();
return *this;
}
grouped_local_bucket_iterator operator++(int) noexcept
{
grouped_local_bucket_iterator old = *this;
increment();
return old;
}
bool operator==(
grouped_local_bucket_iterator const& other) const noexcept
{
return equal(other);
}
bool operator!=(
grouped_local_bucket_iterator const& other) const noexcept
{
return !equal(other);
}
private:
template <typename, typename, typename>
friend class grouped_bucket_array;
template <class> friend struct const_grouped_local_bucket_iterator;
grouped_local_bucket_iterator(node_pointer p_) : p(p_) {}
value_type& dereference() const noexcept { return p->value(); }
bool equal(const grouped_local_bucket_iterator& x) const noexcept
{
return p == x.p;
}
void increment() noexcept { p = p->next; }
node_pointer p;
};
template <class Node> struct const_grouped_local_bucket_iterator
{
typedef typename Node::node_pointer node_pointer;
public:
typedef typename Node::value_type const value_type;
typedef value_type const element_type;
typedef value_type const* pointer;
typedef value_type const& reference;
typedef std::ptrdiff_t difference_type;
typedef std::forward_iterator_tag iterator_category;
const_grouped_local_bucket_iterator() : p() {}
const_grouped_local_bucket_iterator(
grouped_local_bucket_iterator<Node> it)
: p(it.p)
{
}
reference operator*() const noexcept { return dereference(); }
pointer operator->() const noexcept
{
return std::addressof(dereference());
}
const_grouped_local_bucket_iterator& operator++() noexcept
{
increment();
return *this;
}
const_grouped_local_bucket_iterator operator++(int) noexcept
{
const_grouped_local_bucket_iterator old = *this;
increment();
return old;
}
bool operator==(
const_grouped_local_bucket_iterator const& other) const noexcept
{
return equal(other);
}
bool operator!=(
const_grouped_local_bucket_iterator const& other) const noexcept
{
return !equal(other);
}
private:
template <typename, typename, typename>
friend class grouped_bucket_array;
const_grouped_local_bucket_iterator(node_pointer p_) : p(p_) {}
value_type& dereference() const noexcept { return p->value(); }
bool equal(const const_grouped_local_bucket_iterator& x) const noexcept
{
return p == x.p;
}
void increment() noexcept { p = p->next; }
node_pointer p;
};
template <class T> struct span
{
T* begin() const noexcept { return data; }
T* end() const noexcept { return data + size; }
T* data;
std::size_t size;
span(T* data_, std::size_t size_) : data(data_), size(size_) {}
};
template <class Bucket, class Allocator, class SizePolicy>
class grouped_bucket_array
: boost::empty_value<typename boost::allocator_rebind<Allocator,
node<typename boost::allocator_value_type<Allocator>::type,
typename boost::allocator_void_pointer<Allocator>::type> >::
type>
{
typedef typename boost::allocator_value_type<Allocator>::type
allocator_value_type;
typedef
typename boost::allocator_void_pointer<Allocator>::type void_pointer;
typedef typename boost::allocator_difference_type<Allocator>::type
difference_type;
public:
typedef typename boost::allocator_rebind<Allocator,
node<allocator_value_type, void_pointer> >::type node_allocator_type;
typedef node<allocator_value_type, void_pointer> node_type;
typedef typename boost::allocator_pointer<node_allocator_type>::type
node_pointer;
typedef SizePolicy size_policy;
private:
typedef typename boost::allocator_rebind<Allocator, Bucket>::type
bucket_allocator_type;
typedef typename boost::allocator_pointer<bucket_allocator_type>::type
bucket_pointer;
typedef boost::pointer_traits<bucket_pointer> bucket_pointer_traits;
typedef bucket_group<Bucket> group;
typedef typename boost::allocator_rebind<Allocator, group>::type
group_allocator_type;
typedef typename boost::allocator_pointer<group_allocator_type>::type
group_pointer;
typedef typename boost::pointer_traits<group_pointer>
group_pointer_traits;
public:
typedef Bucket value_type;
typedef Bucket bucket_type;
typedef std::size_t size_type;
typedef Allocator allocator_type;
typedef grouped_bucket_iterator<Bucket> iterator;
typedef grouped_local_bucket_iterator<node_type> local_iterator;
typedef const_grouped_local_bucket_iterator<node_type>
const_local_iterator;
private:
std::size_t size_index_, size_;
bucket_pointer buckets;
group_pointer groups;
public:
static std::size_t bucket_count_for(std::size_t num_buckets)
{
if (num_buckets == 0) {
return 0;
}
return size_policy::size(size_policy::size_index(num_buckets));
}
grouped_bucket_array()
: empty_value<node_allocator_type>(
empty_init_t(), node_allocator_type()),
size_index_(0), size_(0), buckets(), groups()
{
}
grouped_bucket_array(size_type n, const Allocator& al)
: empty_value<node_allocator_type>(empty_init_t(), al),
size_index_(0), size_(0), buckets(), groups()
{
if (n == 0) {
return;
}
size_index_ = size_policy::size_index(n);
size_ = size_policy::size(size_index_);
bucket_allocator_type bucket_alloc = this->get_bucket_allocator();
group_allocator_type group_alloc = this->get_group_allocator();
size_type const num_buckets = buckets_len();
size_type const num_groups = groups_len();
buckets = boost::allocator_allocate(bucket_alloc, num_buckets);
BOOST_TRY
{
groups = boost::allocator_allocate(group_alloc, num_groups);
bucket_type* pb = boost::to_address(buckets);
for (size_type i = 0; i < num_buckets; ++i) {
new (pb + i) bucket_type();
}
group* pg = boost::to_address(groups);
for (size_type i = 0; i < num_groups; ++i) {
new (pg + i) group();
}
}
BOOST_CATCH(...)
{
boost::allocator_deallocate(bucket_alloc, buckets, num_buckets);
BOOST_RETHROW
}
BOOST_CATCH_END
size_type const N = group::N;
group_pointer pbg =
groups + static_cast<difference_type>(num_groups - 1);
pbg->buckets =
buckets + static_cast<difference_type>(N * (size_ / N));
pbg->bitmask = set_bit(size_ % N);
pbg->next = pbg->prev = pbg;
}
~grouped_bucket_array() { this->deallocate(); }
grouped_bucket_array(grouped_bucket_array const&) = delete;
grouped_bucket_array& operator=(grouped_bucket_array const&) = delete;
grouped_bucket_array(grouped_bucket_array&& other) noexcept
: empty_value<node_allocator_type>(
empty_init_t(), other.get_node_allocator()),
size_index_(other.size_index_),
size_(other.size_),
buckets(other.buckets),
groups(other.groups)
{
other.size_ = 0;
other.size_index_ = 0;
other.buckets = bucket_pointer();
other.groups = group_pointer();
}
grouped_bucket_array& operator=(grouped_bucket_array&& other) noexcept
{
BOOST_ASSERT(
this->get_node_allocator() == other.get_node_allocator());
if (this == std::addressof(other)) {
return *this;
}
this->deallocate();
size_index_ = other.size_index_;
size_ = other.size_;
buckets = other.buckets;
groups = other.groups;
other.size_index_ = 0;
other.size_ = 0;
other.buckets = bucket_pointer();
other.groups = group_pointer();
return *this;
}
#if defined(BOOST_MSVC)
#pragma warning(push)
#pragma warning(disable : 4100) // unreferenced formal parameter (dtor calls)
#endif
void deallocate() noexcept
{
if (buckets) {
size_type const num_buckets = buckets_len();
bucket_type* pb = boost::to_address(buckets);
(void)pb; // VS complains when dtor is trivial
for (size_type i = 0; i < num_buckets; ++i) {
(pb + i)->~bucket_type();
}
bucket_allocator_type bucket_alloc = this->get_bucket_allocator();
boost::allocator_deallocate(bucket_alloc, buckets, num_buckets);
buckets = bucket_pointer();
}
if (groups) {
size_type const num_groups = groups_len();
group* pg = boost::to_address(groups);
(void)pg; // VS complains when dtor is trivial
for (size_type i = 0; i < num_groups; ++i) {
(pg + i)->~group();
}
group_allocator_type group_alloc = this->get_group_allocator();
boost::allocator_deallocate(group_alloc, groups, num_groups);
groups = group_pointer();
}
}
#if defined(BOOST_MSVC)
#pragma warning(pop)
#endif
void swap(grouped_bucket_array& other)
{
std::swap(size_index_, other.size_index_);
std::swap(size_, other.size_);
std::swap(buckets, other.buckets);
std::swap(groups, other.groups);
bool b = boost::allocator_propagate_on_container_swap<
allocator_type>::type::value;
if (b) {
boost::core::invoke_swap(
get_node_allocator(), other.get_node_allocator());
}
}
node_allocator_type const& get_node_allocator() const
{
return empty_value<node_allocator_type>::get();
}
node_allocator_type& get_node_allocator()
{
return empty_value<node_allocator_type>::get();
}
bucket_allocator_type get_bucket_allocator() const
{
return this->get_node_allocator();
}
group_allocator_type get_group_allocator() const
{
return this->get_node_allocator();
}
size_type buckets_len() const noexcept { return size_ + 1; }
size_type groups_len() const noexcept { return size_ / group::N + 1; }
void reset_allocator(Allocator const& allocator_)
{
this->get_node_allocator() = node_allocator_type(allocator_);
}
size_type bucket_count() const { return size_; }
iterator begin() const { return size_ == 0 ? end() : ++at(size_); }
iterator end() const
{
// micro optimization: no need to return the bucket group
// as end() is not incrementable
iterator pbg;
pbg.p =
buckets + static_cast<difference_type>(this->buckets_len() - 1);
return pbg;
}
local_iterator begin(size_type n) const
{
if (size_ == 0) {
return this->end(n);
}
return local_iterator(
(buckets + static_cast<difference_type>(n))->next);
}
local_iterator end(size_type) const { return local_iterator(); }
size_type capacity() const noexcept { return size_; }
iterator at(size_type n) const
{
if (size_ > 0) {
std::size_t const N = group::N;
iterator pbg(buckets + static_cast<difference_type>(n),
groups + static_cast<difference_type>(n / N));
return pbg;
} else {
return this->end();
}
}
span<Bucket> raw()
{
BOOST_ASSERT(size_ == 0 || size_ < this->buckets_len());
return span<Bucket>(boost::to_address(buckets), size_);
}
size_type position(std::size_t hash) const
{
return size_policy::position(hash, size_index_);
}
void clear()
{
this->deallocate();
size_index_ = 0;
size_ = 0;
}
void append_bucket_group(iterator itb) noexcept
{
std::size_t const N = group::N;
bool const is_empty_bucket = (!itb->next);
if (is_empty_bucket) {
bucket_pointer pb = itb.p;
group_pointer pbg = itb.pbg;
std::size_t n =
static_cast<std::size_t>(boost::to_address(pb) - &buckets[0]);
bool const is_empty_group = (!pbg->bitmask);
if (is_empty_group) {
size_type const num_groups = this->groups_len();
group_pointer last_group =
groups + static_cast<difference_type>(num_groups - 1);
pbg->buckets =
buckets + static_cast<difference_type>(N * (n / N));
pbg->next = last_group->next;
pbg->next->prev = pbg;
pbg->prev = last_group;
pbg->prev->next = pbg;
}
pbg->bitmask |= set_bit(n % N);
}
}
void insert_node(iterator itb, node_pointer p) noexcept
{
this->append_bucket_group(itb);
p->next = itb->next;
itb->next = p;
}
void insert_node_hint(
iterator itb, node_pointer p, node_pointer hint) noexcept
{
this->append_bucket_group(itb);
if (hint) {
p->next = hint->next;
hint->next = p;
} else {
p->next = itb->next;
itb->next = p;
}
}
void extract_node(iterator itb, node_pointer p) noexcept
{
node_pointer* pp = std::addressof(itb->next);
while ((*pp) != p)
pp = std::addressof((*pp)->next);
*pp = p->next;
if (!itb->next)
unlink_bucket(itb);
}
void extract_node_after(iterator itb, node_pointer* pp) noexcept
{
*pp = (*pp)->next;
if (!itb->next)
unlink_bucket(itb);
}
void unlink_empty_buckets() noexcept
{
std::size_t const N = group::N;
group_pointer pbg = groups,
last = groups + static_cast<difference_type>(
this->groups_len() - 1);
for (; pbg != last; ++pbg) {
if (!pbg->buckets) {
continue;
}
for (std::size_t n = 0; n < N; ++n) {
bucket_pointer bs = pbg->buckets;
bucket_type& b = bs[static_cast<std::ptrdiff_t>(n)];
if (!b.next)
pbg->bitmask &= reset_bit(n);
}
if (!pbg->bitmask && pbg->next)
unlink_group(pbg);
}
// do not check end bucket
for (std::size_t n = 0; n < size_ % N; ++n) {
if (!pbg->buckets[static_cast<std::ptrdiff_t>(n)].next)
pbg->bitmask &= reset_bit(n);
}
}
void unlink_bucket(iterator itb)
{
typename iterator::bucket_pointer p = itb.p;
typename iterator::bucket_group_pointer pbg = itb.pbg;
if (!(pbg->bitmask &=
reset_bit(static_cast<std::size_t>(p - pbg->buckets))))
unlink_group(pbg);
}
private:
void unlink_group(group_pointer pbg)
{
pbg->next->prev = pbg->prev;
pbg->prev->next = pbg->next;
pbg->prev = pbg->next = group_pointer();
}
};
} // namespace detail
} // namespace unordered
} // namespace boost
#endif // BOOST_UNORDERED_DETAIL_FCA_HPP
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+64
View File
@@ -0,0 +1,64 @@
/* 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.
*/
#ifndef BOOST_UNORDERED_DETAIL_FOA_ELEMENT_TYPE_HPP
#define BOOST_UNORDERED_DETAIL_FOA_ELEMENT_TYPE_HPP
#include <boost/core/pointer_traits.hpp>
namespace boost{
namespace unordered{
namespace detail{
namespace foa{
template<class T,class VoidPtr>
struct element_type
{
using value_type=T;
using pointer=typename boost::pointer_traits<VoidPtr>::template rebind<T>;
pointer p;
/*
* we use a deleted copy constructor here so the type is no longer
* trivially copy-constructible which inhibits our memcpy
* optimizations when copying the tables
*/
element_type()=default;
element_type(pointer p_):p(p_){}
element_type(element_type const&)=delete;
element_type(element_type&& rhs)noexcept
{
p = rhs.p;
rhs.p = nullptr;
}
element_type& operator=(element_type const&)=delete;
element_type& operator=(element_type&& rhs)noexcept
{
if (this!=&rhs){
p=rhs.p;
rhs.p=nullptr;
}
return *this;
}
void swap(element_type& rhs)noexcept
{
auto tmp=p;
p=rhs.p;
rhs.p=tmp;
}
};
}
}
}
}
#endif // BOOST_UNORDERED_DETAIL_FOA_ELEMENT_TYPE_HPP
+74
View File
@@ -0,0 +1,74 @@
// Copyright (C) 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)
#ifndef BOOST_UNORDERED_DETAIL_FOA_FLAT_MAP_TYPES_HPP
#define BOOST_UNORDERED_DETAIL_FOA_FLAT_MAP_TYPES_HPP
#include <boost/core/allocator_access.hpp>
namespace boost {
namespace unordered {
namespace detail {
namespace foa {
template <class Key, class T> struct flat_map_types
{
using key_type = Key;
using mapped_type = T;
using raw_key_type = typename std::remove_const<Key>::type;
using raw_mapped_type = typename std::remove_const<T>::type;
using init_type = std::pair<raw_key_type, raw_mapped_type>;
using moved_type = std::pair<raw_key_type&&, raw_mapped_type&&>;
using value_type = std::pair<Key const, T>;
using element_type = value_type;
static value_type& value_from(element_type& x) { return x; }
template <class K, class V>
static raw_key_type const& extract(std::pair<K, V> 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<raw_key_type&>(x.first)),
std::move(const_cast<raw_mapped_type&>(x.second))};
}
template <class A, class... Args>
static void construct(A& al, init_type* p, Args&&... args)
{
boost::allocator_construct(al, p, std::forward<Args>(args)...);
}
template <class A, class... Args>
static void construct(A& al, value_type* p, Args&&... args)
{
boost::allocator_construct(al, p, std::forward<Args>(args)...);
}
template <class A> static void destroy(A& al, init_type* p) noexcept
{
boost::allocator_destroy(al, p);
}
template <class A> static void destroy(A& al, value_type* p) noexcept
{
boost::allocator_destroy(al, p);
}
};
} // namespace foa
} // namespace detail
} // namespace unordered
} // namespace boost
#endif // BOOST_UNORDERED_DETAIL_FOA_FLAT_MAP_TYPES_HPP
+44
View File
@@ -0,0 +1,44 @@
// Copyright (C) 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)
#ifndef BOOST_UNORDERED_DETAIL_FOA_FLAT_SET_TYPES_HPP
#define BOOST_UNORDERED_DETAIL_FOA_FLAT_SET_TYPES_HPP
#include <boost/core/allocator_access.hpp>
namespace boost {
namespace unordered {
namespace detail {
namespace foa {
template <class Key> struct flat_set_types
{
using key_type = Key;
using init_type = Key;
using value_type = Key;
static Key const& extract(value_type const& key) { return key; }
using element_type = value_type;
static Key& value_from(element_type& x) { return x; }
static element_type&& move(element_type& x) { return std::move(x); }
template <class A, class... Args>
static void construct(A& al, value_type* p, Args&&... args)
{
boost::allocator_construct(al, p, std::forward<Args>(args)...);
}
template <class A> static void destroy(A& al, value_type* p) noexcept
{
boost::allocator_destroy(al, p);
}
};
} // namespace foa
} // namespace detail
} // namespace unordered
} // namespace boost
#endif // BOOST_UNORDERED_DETAIL_FOA_FLAT_SET_TYPES_HPP
+35
View File
@@ -0,0 +1,35 @@
/* Copyright 2023 Joaquin M Lopez Munoz.
* 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.
*/
#include <boost/config.hpp>
#if defined(BOOST_GCC)
#if !defined(BOOST_UNORDERED_DETAIL_RESTORE_WSHADOW)
/* GCC's -Wshadow triggers at scenarios like this:
*
* struct foo{};
* template<typename Base>
* struct derived:Base
* {
* void f(){int foo;}
* };
*
* derived<foo>x;
* x.f(); // declaration of "foo" in derived::f shadows base type "foo"
*
* This makes shadowing warnings unavoidable in general when a class template
* derives from user-provided classes, as is the case with foa::table_core
* deriving from empty_value.
*/
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wshadow"
#else
#pragma GCC diagnostic pop
#endif
#endif
+204
View File
@@ -0,0 +1,204 @@
/* 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.
*/
#ifndef BOOST_UNORDERED_DETAIL_FOA_NODE_HANDLE_HPP
#define BOOST_UNORDERED_DETAIL_FOA_NODE_HANDLE_HPP
#include <boost/unordered/detail/opt_storage.hpp>
#include <boost/config.hpp>
#include <boost/core/allocator_access.hpp>
namespace boost{
namespace unordered{
namespace detail{
namespace foa{
template <class Iterator,class NodeType>
struct insert_return_type
{
Iterator position;
bool inserted;
NodeType node;
};
template <class TypePolicy,class Allocator>
struct node_handle_base
{
protected:
using type_policy=TypePolicy;
using element_type=typename type_policy::element_type;
public:
using allocator_type = Allocator;
private:
using node_value_type=typename type_policy::value_type;
element_type p_;
BOOST_ATTRIBUTE_NO_UNIQUE_ADDRESS opt_storage<Allocator> a_;
protected:
node_value_type& data()noexcept
{
return *(p_.p);
}
node_value_type const& data()const noexcept
{
return *(p_.p);
}
element_type& element()noexcept
{
BOOST_ASSERT(!empty());
return p_;
}
element_type const& element()const noexcept
{
BOOST_ASSERT(!empty());
return p_;
}
Allocator& al()noexcept
{
BOOST_ASSERT(!empty());
return a_.t_;
}
Allocator const& al()const noexcept
{
BOOST_ASSERT(!empty());
return a_.t_;
}
void emplace(element_type&& x,Allocator a)
{
BOOST_ASSERT(empty());
auto* p=x.p;
p_.p=p;
new(&a_.t_)Allocator(a);
x.p=nullptr;
}
void reset()
{
a_.t_.~Allocator();
p_.p=nullptr;
}
public:
constexpr node_handle_base()noexcept:p_{nullptr}{}
node_handle_base(node_handle_base&& nh) noexcept
{
p_.p = nullptr;
if (!nh.empty()){
emplace(std::move(nh.p_),nh.al());
nh.reset();
}
}
node_handle_base& operator=(node_handle_base&& nh)noexcept
{
if(this!=&nh){
if(empty()){
if(nh.empty()){ /* empty(), nh.empty() */
/* nothing to do */
}else{ /* empty(), !nh.empty() */
emplace(std::move(nh.p_),std::move(nh.al()));
nh.reset();
}
}else{
if(nh.empty()){ /* !empty(), nh.empty() */
type_policy::destroy(al(),&p_);
reset();
}else{ /* !empty(), !nh.empty() */
bool const pocma=
boost::allocator_propagate_on_container_move_assignment<
Allocator>::type::value;
BOOST_ASSERT(pocma||al()==nh.al());
type_policy::destroy(al(),&p_);
if(pocma){
al()=std::move(nh.al());
}
p_=std::move(nh.p_);
nh.reset();
}
}
}else{
if(empty()){ /* empty(), nh.empty() */
/* nothing to do */
}else{ /* !empty(), !nh.empty() */
type_policy::destroy(al(),&p_);
reset();
}
}
return *this;
}
~node_handle_base()
{
if(!empty()){
type_policy::destroy(al(),&p_);
reset();
}
}
allocator_type get_allocator()const noexcept{return al();}
explicit operator bool()const noexcept{ return !empty();}
BOOST_ATTRIBUTE_NODISCARD bool empty()const noexcept{return p_.p==nullptr;}
void swap(node_handle_base& nh) noexcept(
boost::allocator_is_always_equal<Allocator>::type::value||
boost::allocator_propagate_on_container_swap<Allocator>::type::value)
{
if(this!=&nh){
if(empty()){
if(nh.empty()) {
/* nothing to do here */
} else {
emplace(std::move(nh.p_), nh.al());
nh.reset();
}
}else{
if(nh.empty()){
nh.emplace(std::move(p_),al());
reset();
}else{
bool const pocs=
boost::allocator_propagate_on_container_swap<
Allocator>::type::value;
BOOST_ASSERT(pocs || al()==nh.al());
using std::swap;
p_.swap(nh.p_);
if(pocs)swap(al(),nh.al());
}
}
}
}
friend
void swap(node_handle_base& lhs,node_handle_base& rhs)
noexcept(noexcept(lhs.swap(rhs)))
{
return lhs.swap(rhs);
}
};
}
}
}
}
#endif // BOOST_UNORDERED_DETAIL_FOA_NODE_HANDLE_HPP
+127
View File
@@ -0,0 +1,127 @@
// Copyright (C) 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)
#ifndef BOOST_UNORDERED_DETAIL_FOA_NODE_MAP_TYPES_HPP
#define BOOST_UNORDERED_DETAIL_FOA_NODE_MAP_TYPES_HPP
#include <boost/unordered/detail/foa/element_type.hpp>
#include <boost/core/allocator_access.hpp>
#include <boost/core/no_exceptions_support.hpp>
#include <boost/core/pointer_traits.hpp>
namespace boost {
namespace unordered {
namespace detail {
namespace foa {
template <class Key, class T, class VoidPtr> struct node_map_types
{
using key_type = Key;
using mapped_type = T;
using raw_key_type = typename std::remove_const<Key>::type;
using raw_mapped_type = typename std::remove_const<T>::type;
using init_type = std::pair<raw_key_type, raw_mapped_type>;
using value_type = std::pair<Key const, T>;
using moved_type = std::pair<raw_key_type&&, raw_mapped_type&&>;
using element_type = foa::element_type<value_type, VoidPtr>;
static value_type& value_from(element_type const& x)
{
return *(x.p);
}
template <class K, class V>
static raw_key_type const& extract(std::pair<K, V> const& kv)
{
return kv.first;
}
static raw_key_type const& extract(element_type const& kv)
{
return kv.p->first;
}
static element_type&& move(element_type& x) { return std::move(x); }
static moved_type move(init_type& x)
{
return {std::move(x.first), std::move(x.second)};
}
static moved_type move(value_type& x)
{
return {std::move(const_cast<raw_key_type&>(x.first)),
std::move(const_cast<raw_mapped_type&>(x.second))};
}
template <class A>
static void construct(A&, element_type* p, element_type&& x) noexcept
{
p->p = x.p;
x.p = nullptr;
}
template <class A>
static void construct(
A& al, element_type* p, element_type const& copy)
{
construct(al, p, *copy.p);
}
template <class A, class... Args>
static void construct(A& al, init_type* p, Args&&... args)
{
boost::allocator_construct(al, p, std::forward<Args>(args)...);
}
template <class A, class... Args>
static void construct(A& al, value_type* p, Args&&... args)
{
boost::allocator_construct(al, p, std::forward<Args>(args)...);
}
template <class A, class... Args>
static void construct(A& al, element_type* p, Args&&... args)
{
p->p = boost::allocator_allocate(al, 1);
BOOST_TRY
{
boost::allocator_construct(
al, boost::to_address(p->p), std::forward<Args>(args)...);
}
BOOST_CATCH(...)
{
boost::allocator_deallocate(al, p->p, 1);
BOOST_RETHROW
}
BOOST_CATCH_END
}
template <class A> static void destroy(A& al, value_type* p) noexcept
{
boost::allocator_destroy(al, p);
}
template <class A> static void destroy(A& al, init_type* p) noexcept
{
boost::allocator_destroy(al, p);
}
template <class A>
static void destroy(A& al, element_type* p) noexcept
{
if (p->p) {
destroy(al, boost::to_address(p->p));
boost::allocator_deallocate(al, p->p, 1);
}
}
};
} // namespace foa
} // namespace detail
} // namespace unordered
} // namespace boost
#endif // BOOST_UNORDERED_DETAIL_FOA_NODE_MAP_TYPES_HPP
+92
View File
@@ -0,0 +1,92 @@
// Copyright (C) 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)
#ifndef BOOST_UNORDERED_DETAIL_FOA_NODE_SET_TYPES_HPP
#define BOOST_UNORDERED_DETAIL_FOA_NODE_SET_TYPES_HPP
#include <boost/unordered/detail/foa/element_type.hpp>
#include <boost/core/allocator_access.hpp>
#include <boost/core/no_exceptions_support.hpp>
#include <boost/core/pointer_traits.hpp>
namespace boost {
namespace unordered {
namespace detail {
namespace foa {
template <class Key, class VoidPtr> struct node_set_types
{
using key_type = Key;
using init_type = Key;
using value_type = Key;
static Key const& extract(value_type const& key) { return key; }
using element_type = foa::element_type<value_type, VoidPtr>;
static value_type& value_from(element_type const& x) { return *x.p; }
static Key const& extract(element_type const& k) { return *k.p; }
static element_type&& move(element_type& x) { return std::move(x); }
static value_type&& move(value_type& x) { return std::move(x); }
template <class A>
static void construct(
A& al, element_type* p, element_type const& copy)
{
construct(al, p, *copy.p);
}
template <typename Allocator>
static void construct(
Allocator&, element_type* p, element_type&& x) noexcept
{
p->p = x.p;
x.p = nullptr;
}
template <class A, class... Args>
static void construct(A& al, value_type* p, Args&&... args)
{
boost::allocator_construct(al, p, std::forward<Args>(args)...);
}
template <class A, class... Args>
static void construct(A& al, element_type* p, Args&&... args)
{
p->p = boost::allocator_allocate(al, 1);
BOOST_TRY
{
boost::allocator_construct(
al, boost::to_address(p->p), std::forward<Args>(args)...);
}
BOOST_CATCH(...)
{
boost::allocator_deallocate(al, p->p, 1);
BOOST_RETHROW
}
BOOST_CATCH_END
}
template <class A> static void destroy(A& al, value_type* p) noexcept
{
boost::allocator_destroy(al, p);
}
template <class A>
static void destroy(A& al, element_type* p) noexcept
{
if (p->p) {
destroy(al, boost::to_address(p->p));
boost::allocator_deallocate(al, p->p, 1);
}
}
};
} // namespace foa
} // namespace detail
} // namespace unordered
} // namespace boost
#endif // BOOST_UNORDERED_DETAIL_FOA_NODE_SET_TYPES_HPP
+138
View File
@@ -0,0 +1,138 @@
/* Copyright 2023 Joaquin M Lopez Munoz.
* 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.
*/
#ifndef BOOST_UNORDERED_DETAIL_FOA_REENTRANCY_CHECK_HPP
#define BOOST_UNORDERED_DETAIL_FOA_REENTRANCY_CHECK_HPP
#include <boost/assert.hpp>
#include <utility>
#if !defined(BOOST_UNORDERED_DISABLE_REENTRANCY_CHECK)&& \
!defined(BOOST_ASSERT_IS_VOID)
#define BOOST_UNORDERED_REENTRANCY_CHECK
#endif
namespace boost{
namespace unordered{
namespace detail{
namespace foa{
#if defined(BOOST_UNORDERED_REENTRANCY_CHECK)
class entry_trace
{
public:
entry_trace(const void* px_):px{px_}
{
if(px){
BOOST_ASSERT_MSG(!find(px),"reentrancy not allowed");
header()=this;
}
}
/* not used but VS in pre-C++17 mode needs to see it for RVO */
entry_trace(const entry_trace&);
~entry_trace(){clear();}
void clear()
{
if(px){
header()=next;
px=nullptr;
}
}
private:
static entry_trace*& header()
{
thread_local entry_trace *pe=nullptr;
return pe;
}
static bool find(const void* px)
{
for(auto pe=header();pe;pe=pe->next){
if(pe->px==px)return true;
}
return false;
}
const void *px;
entry_trace *next=header();
};
template<typename LockGuard>
struct reentrancy_checked
{
template<typename... Args>
reentrancy_checked(const void* px,Args&&... args):
tr{px},lck{std::forward<Args>(args)...}{}
void unlock()
{
lck.unlock();
tr.clear();
}
entry_trace tr;
LockGuard lck;
};
template<typename LockGuard>
struct reentrancy_bichecked
{
template<typename... Args>
reentrancy_bichecked(const void* px,const void* py,Args&&... args):
tr1{px},tr2{py!=px?py:nullptr},lck{std::forward<Args>(args)...}{}
void unlock()
{
lck.unlock();
tr2.clear();
tr1.clear();
}
entry_trace tr1,tr2;
LockGuard lck;
};
#else
template<typename LockGuard>
struct reentrancy_checked
{
template<typename... Args>
reentrancy_checked(const void*,Args&&... args):
lck{std::forward<Args>(args)...}{}
void unlock(){lck.unlock();}
LockGuard lck;
};
template<typename LockGuard>
struct reentrancy_bichecked
{
template<typename... Args>
reentrancy_bichecked(const void*,const void*,Args&&... args):
lck{std::forward<Args>(args)...}{}
void unlock(){lck.unlock();}
LockGuard lck;
};
#endif
} /* namespace foa */
} /* namespace detail */
} /* namespace unordered */
} /* namespace boost */
#endif
+11
View File
@@ -0,0 +1,11 @@
/* Copyright 2023 Joaquin M Lopez Munoz.
* 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.
*/
#define BOOST_UNORDERED_DETAIL_RESTORE_WSHADOW
#include <boost/unordered/detail/foa/ignore_wshadow.hpp>
#undef BOOST_UNORDERED_DETAIL_RESTORE_WSHADOW
+179
View File
@@ -0,0 +1,179 @@
#ifndef BOOST_UNORDERED_DETAIL_FOA_RW_SPINLOCK_HPP_INCLUDED
#define BOOST_UNORDERED_DETAIL_FOA_RW_SPINLOCK_HPP_INCLUDED
// Copyright 2023 Peter Dimov
// Distributed under the Boost Software License, Version 1.0.
// https://www.boost.org/LICENSE_1_0.txt
#include <boost/core/yield_primitives.hpp>
#include <atomic>
#include <cstdint>
namespace boost{
namespace unordered{
namespace detail{
namespace foa{
class rw_spinlock
{
private:
// bit 31: locked exclusive
// bit 30: writer pending
// bit 29..0: reader lock count
static constexpr std::uint32_t locked_exclusive_mask = 1u << 31; // 0x8000'0000
static constexpr std::uint32_t writer_pending_mask = 1u << 30; // 0x4000'0000
static constexpr std::uint32_t reader_lock_count_mask = writer_pending_mask - 1; // 0x3FFF'FFFF
std::atomic<std::uint32_t> state_ = {};
private:
// Effects: Provides a hint to the implementation that the current thread
// has been unable to make progress for k+1 iterations.
static void yield( unsigned k ) noexcept
{
unsigned const sleep_every = 1024; // see below
k %= sleep_every;
if( k < 5 )
{
// Intel recommendation from the Optimization Reference Manual
// Exponentially increase number of PAUSE instructions each
// iteration until reaching a maximum which is approximately
// one timeslice long (2^4 == 16 in our case)
unsigned const pause_count = 1u << k;
for( unsigned i = 0; i < pause_count; ++i )
{
boost::core::sp_thread_pause();
}
}
else if( k < sleep_every - 1 )
{
// Once the maximum number of PAUSE instructions is reached,
// we switch to yielding the timeslice immediately
boost::core::sp_thread_yield();
}
else
{
// After `sleep_every` iterations of no progress, we sleep,
// to avoid a deadlock if a lower priority thread has the lock
boost::core::sp_thread_sleep();
}
}
public:
bool try_lock_shared() noexcept
{
std::uint32_t st = state_.load( std::memory_order_relaxed );
if( st >= reader_lock_count_mask )
{
// either bit 31 set, bit 30 set, or reader count is max
return false;
}
std::uint32_t newst = st + 1;
return state_.compare_exchange_strong( st, newst, std::memory_order_acquire, std::memory_order_relaxed );
}
void lock_shared() noexcept
{
for( unsigned k = 0; ; ++k )
{
std::uint32_t st = state_.load( std::memory_order_relaxed );
if( st < reader_lock_count_mask )
{
std::uint32_t newst = st + 1;
if( state_.compare_exchange_weak( st, newst, std::memory_order_acquire, std::memory_order_relaxed ) ) return;
}
yield( k );
}
}
void unlock_shared() noexcept
{
// pre: locked shared, not locked exclusive
state_.fetch_sub( 1, std::memory_order_release );
// if the writer pending bit is set, there's a writer waiting
// let it acquire the lock; it will clear the bit on unlock
}
bool try_lock() noexcept
{
std::uint32_t st = state_.load( std::memory_order_relaxed );
if( st & locked_exclusive_mask )
{
// locked exclusive
return false;
}
if( st & reader_lock_count_mask )
{
// locked shared
return false;
}
std::uint32_t newst = locked_exclusive_mask;
return state_.compare_exchange_strong( st, newst, std::memory_order_acquire, std::memory_order_relaxed );
}
void lock() noexcept
{
for( unsigned k = 0; ; ++k )
{
std::uint32_t st = state_.load( std::memory_order_relaxed );
if( st & locked_exclusive_mask )
{
// locked exclusive, spin
}
else if( ( st & reader_lock_count_mask ) == 0 )
{
// not locked exclusive, not locked shared, try to lock
std::uint32_t newst = locked_exclusive_mask;
if( state_.compare_exchange_weak( st, newst, std::memory_order_acquire, std::memory_order_relaxed ) ) return;
}
else if( st & writer_pending_mask )
{
// writer pending bit already set, nothing to do
}
else
{
// locked shared, set writer pending bit
std::uint32_t newst = st | writer_pending_mask;
state_.compare_exchange_weak( st, newst, std::memory_order_relaxed, std::memory_order_relaxed );
}
yield( k );
}
}
void unlock() noexcept
{
// pre: locked exclusive, not locked shared
state_.store( 0, std::memory_order_release );
}
};
} /* namespace foa */
} /* namespace detail */
} /* namespace unordered */
} /* namespace boost */
#endif // BOOST_UNORDERED_DETAIL_FOA_RW_SPINLOCK_HPP_INCLUDED
+626
View File
@@ -0,0 +1,626 @@
/* Fast open-addressing hash table.
*
* Copyright 2022-2023 Joaquin M Lopez Munoz.
* 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.
*/
#ifndef BOOST_UNORDERED_DETAIL_FOA_TABLE_HPP
#define BOOST_UNORDERED_DETAIL_FOA_TABLE_HPP
#include <boost/assert.hpp>
#include <boost/config.hpp>
#include <boost/config/workaround.hpp>
#include <boost/core/serialization.hpp>
#include <boost/unordered/detail/foa/core.hpp>
#include <boost/unordered/detail/serialize_tracked_address.hpp>
#include <cstddef>
#include <iterator>
#include <memory>
#include <type_traits>
#include <utility>
namespace boost{
namespace unordered{
namespace detail{
namespace foa{
/* use plain integrals for group metadata storage */
template<typename Integral>
struct plain_integral
{
operator Integral()const{return n;}
void operator=(Integral m){n=m;}
#if BOOST_WORKAROUND(BOOST_GCC,>=50000 && BOOST_GCC<60000)
void operator|=(Integral m){n=static_cast<Integral>(n|m);}
void operator&=(Integral m){n=static_cast<Integral>(n&m);}
#else
void operator|=(Integral m){n|=m;}
void operator&=(Integral m){n&=m;}
#endif
Integral n;
};
struct plain_size_control
{
std::size_t ml;
std::size_t size;
};
template<typename,typename,typename,typename>
class table;
/* table_iterator keeps two pointers:
*
* - A pointer p to the element slot.
* - A pointer pc to the n-th byte of the associated group metadata, where n
* is the position of the element in the group.
*
* A simpler solution would have been to keep a pointer p to the element, a
* pointer pg to the group, and the position n, but that would increase
* sizeof(table_iterator) by 4/8 bytes. In order to make this compact
* representation feasible, it is required that group objects are aligned
* to their size, so that we can recover pg and n as
*
* - n = pc%sizeof(group)
* - pg = pc-n
*
* (for explanatory purposes pg and pc are treated above as if they were memory
* addresses rather than pointers).
*
* p = nullptr is conventionally used to mark end() iterators.
*/
/* internal conversion from const_iterator to iterator */
struct const_iterator_cast_tag{};
template<typename TypePolicy,typename GroupPtr,bool Const>
class table_iterator
{
using group_pointer_traits=boost::pointer_traits<GroupPtr>;
using type_policy=TypePolicy;
using table_element_type=typename type_policy::element_type;
using group_type=typename group_pointer_traits::element_type;
using table_element_pointer=
typename group_pointer_traits::template rebind<table_element_type>;
using char_pointer=
typename group_pointer_traits::template rebind<unsigned char>;
static constexpr auto N=group_type::N;
static constexpr auto regular_layout=group_type::regular_layout;
public:
using difference_type=std::ptrdiff_t;
using value_type=typename type_policy::value_type;
using pointer=
typename std::conditional<Const,value_type const*,value_type*>::type;
using reference=
typename std::conditional<Const,value_type const&,value_type&>::type;
using iterator_category=std::forward_iterator_tag;
using element_type=
typename std::conditional<Const,value_type const,value_type>::type;
table_iterator():pc_{nullptr},p_{nullptr}{};
template<bool Const2,typename std::enable_if<!Const2>::type* =nullptr>
table_iterator(const table_iterator<TypePolicy,GroupPtr,Const2>& x):
pc_{x.pc_},p_{x.p_}{}
table_iterator(
const_iterator_cast_tag, const table_iterator<TypePolicy,GroupPtr,true>& x):
pc_{x.pc_},p_{x.p_}{}
inline reference operator*()const noexcept
{return type_policy::value_from(*p());}
inline pointer operator->()const noexcept
{return std::addressof(type_policy::value_from(*p()));}
inline table_iterator& operator++()noexcept{increment();return *this;}
inline table_iterator operator++(int)noexcept
{auto x=*this;increment();return x;}
friend inline bool operator==(
const table_iterator& x,const table_iterator& y)
{return x.p()==y.p();}
friend inline bool operator!=(
const table_iterator& x,const table_iterator& y)
{return !(x==y);}
private:
template<typename,typename,bool> friend class table_iterator;
template<typename> friend class table_erase_return_type;
template<typename,typename,typename,typename> friend class table;
table_iterator(group_type* pg,std::size_t n,const table_element_type* ptet):
pc_{to_pointer<char_pointer>(
reinterpret_cast<unsigned char*>(const_cast<group_type*>(pg))+n)},
p_{to_pointer<table_element_pointer>(const_cast<table_element_type*>(ptet))}
{}
unsigned char* pc()const noexcept{return boost::to_address(pc_);}
table_element_type* p()const noexcept{return boost::to_address(p_);}
inline void increment()noexcept
{
BOOST_ASSERT(p()!=nullptr);
increment(std::integral_constant<bool,regular_layout>{});
}
inline void increment(std::true_type /* regular layout */)noexcept
{
using diff_type=
typename boost::pointer_traits<char_pointer>::difference_type;
for(;;){
++p_;
if(reinterpret_cast<uintptr_t>(pc())%sizeof(group_type)==N-1){
pc_+=static_cast<diff_type>(sizeof(group_type)-(N-1));
break;
}
++pc_;
if(!group_type::is_occupied(pc()))continue;
if(BOOST_UNLIKELY(group_type::is_sentinel(pc())))p_=nullptr;
return;
}
for(;;){
int mask=reinterpret_cast<group_type*>(pc())->match_occupied();
if(mask!=0){
auto n=unchecked_countr_zero(mask);
if(BOOST_UNLIKELY(reinterpret_cast<group_type*>(pc())->is_sentinel(n))){
p_=nullptr;
}
else{
pc_+=static_cast<diff_type>(n);
p_+=static_cast<diff_type>(n);
}
return;
}
pc_+=static_cast<diff_type>(sizeof(group_type));
p_+=static_cast<diff_type>(N);
}
}
inline void increment(std::false_type /* interleaved */)noexcept
{
using diff_type=
typename boost::pointer_traits<char_pointer>::difference_type;
std::size_t n0=reinterpret_cast<uintptr_t>(pc())%sizeof(group_type);
pc_-=static_cast<diff_type>(n0);
int mask=(
reinterpret_cast<group_type*>(pc())->match_occupied()>>(n0+1))<<(n0+1);
if(!mask){
do{
pc_+=sizeof(group_type);
p_+=N;
}
while((mask=reinterpret_cast<group_type*>(pc())->match_occupied())==0);
}
auto n=unchecked_countr_zero(mask);
if(BOOST_UNLIKELY(reinterpret_cast<group_type*>(pc())->is_sentinel(n))){
p_=nullptr;
}
else{
pc_+=static_cast<diff_type>(n);
p_-=static_cast<diff_type>(n0);
p_+=static_cast<diff_type>(n);
}
}
template<typename Archive>
friend void serialization_track(Archive& ar,const table_iterator& x)
{
if(x.p()){
track_address(ar,x.pc_);
track_address(ar,x.p_);
}
}
friend class boost::serialization::access;
template<typename Archive>
void serialize(Archive& ar,unsigned int)
{
if(!p())pc_=nullptr;
serialize_tracked_address(ar,pc_);
serialize_tracked_address(ar,p_);
}
char_pointer pc_=nullptr;
table_element_pointer p_=nullptr;
};
/* Returned by table::erase([const_]iterator) to avoid iterator increment
* if discarded.
*/
template<typename Iterator>
class table_erase_return_type;
template<typename TypePolicy,typename GroupPtr,bool Const>
class table_erase_return_type<table_iterator<TypePolicy,GroupPtr,Const>>
{
using iterator=table_iterator<TypePolicy,GroupPtr,Const>;
using const_iterator=table_iterator<TypePolicy,GroupPtr,true>;
public:
/* can't delete it because VS in pre-C++17 mode needs to see it for RVO */
table_erase_return_type(const table_erase_return_type&);
operator iterator()const noexcept
{
auto it=pos;
it.increment(); /* valid even if *it was erased */
return iterator(const_iterator_cast_tag{},it);
}
template<
bool dependent_value=false,
typename std::enable_if<!Const||dependent_value>::type* =nullptr
>
operator const_iterator()const noexcept{return this->operator iterator();}
private:
template<typename,typename,typename,typename> friend class table;
table_erase_return_type(const_iterator pos_):pos{pos_}{}
table_erase_return_type& operator=(const table_erase_return_type&)=delete;
const_iterator pos;
};
/* foa::table interface departs in a number of ways from that of C++ unordered
* associative containers because it's not for end-user consumption
* (boost::unordered_(flat|node)_(map|set) wrappers complete it as
* appropriate).
*
* The table supports two main modes of operation: flat and node-based. In the
* flat case, buckets directly store elements. For node-based, buckets store
* pointers to individually heap-allocated elements.
*
* For both flat and node-based:
*
* - begin() is not O(1).
* - No bucket API.
* - Load factor is fixed and can't be set by the user.
*
* For flat only:
*
* - value_type must be moveable.
* - Pointer stability is not kept under rehashing.
* - No extract API.
*
* try_emplace, erase and find support heterogeneous lookup by default,
* that is, without checking for any ::is_transparent typedefs --the
* checking is done by boost::unordered_(flat|node)_(map|set).
*/
template<typename,typename,typename,typename>
class concurrent_table; /* concurrent/non-concurrent interop */
template <typename TypePolicy,typename Hash,typename Pred,typename Allocator>
using table_core_impl=
table_core<TypePolicy,group15<plain_integral>,table_arrays,
plain_size_control,Hash,Pred,Allocator>;
#include <boost/unordered/detail/foa/ignore_wshadow.hpp>
#if defined(BOOST_MSVC)
#pragma warning(push)
#pragma warning(disable:4714) /* marked as __forceinline not inlined */
#endif
template<typename TypePolicy,typename Hash,typename Pred,typename Allocator>
class table:table_core_impl<TypePolicy,Hash,Pred,Allocator>
{
using super=table_core_impl<TypePolicy,Hash,Pred,Allocator>;
using type_policy=typename super::type_policy;
using group_type=typename super::group_type;
using super::N;
using prober=typename super::prober;
using arrays_type=typename super::arrays_type;
using size_ctrl_type=typename super::size_ctrl_type;
using locator=typename super::locator;
using compatible_concurrent_table=
concurrent_table<TypePolicy,Hash,Pred,Allocator>;
using group_type_pointer=typename boost::pointer_traits<
typename boost::allocator_pointer<Allocator>::type
>::template rebind<group_type>;
friend compatible_concurrent_table;
public:
using key_type=typename super::key_type;
using init_type=typename super::init_type;
using value_type=typename super::value_type;
using element_type=typename super::element_type;
private:
static constexpr bool has_mutable_iterator=
!std::is_same<key_type,value_type>::value;
public:
using hasher=typename super::hasher;
using key_equal=typename super::key_equal;
using allocator_type=typename super::allocator_type;
using pointer=typename super::pointer;
using const_pointer=typename super::const_pointer;
using reference=typename super::reference;
using const_reference=typename super::const_reference;
using size_type=typename super::size_type;
using difference_type=typename super::difference_type;
using const_iterator=table_iterator<type_policy,group_type_pointer,true>;
using iterator=typename std::conditional<
has_mutable_iterator,
table_iterator<type_policy,group_type_pointer,false>,
const_iterator>::type;
using erase_return_type=table_erase_return_type<iterator>;
table(
std::size_t n=default_bucket_count,const Hash& h_=Hash(),
const Pred& pred_=Pred(),const Allocator& al_=Allocator()):
super{n,h_,pred_,al_}
{}
table(const table& x)=default;
table(table&& x)=default;
table(const table& x,const Allocator& al_):super{x,al_}{}
table(table&& x,const Allocator& al_):super{std::move(x),al_}{}
table(compatible_concurrent_table&& x):
table(std::move(x),x.exclusive_access()){}
~table()=default;
table& operator=(const table& x)=default;
table& operator=(table&& x)=default;
using super::get_allocator;
iterator begin()noexcept
{
iterator it{this->arrays.groups(),0,this->arrays.elements()};
if(this->arrays.elements()&&
!(this->arrays.groups()[0].match_occupied()&0x1))++it;
return it;
}
const_iterator begin()const noexcept
{return const_cast<table*>(this)->begin();}
iterator end()noexcept{return {};}
const_iterator end()const noexcept{return const_cast<table*>(this)->end();}
const_iterator cbegin()const noexcept{return begin();}
const_iterator cend()const noexcept{return end();}
using super::empty;
using super::size;
using super::max_size;
template<typename... Args>
BOOST_FORCEINLINE std::pair<iterator,bool> emplace(Args&&... args)
{
auto x=alloc_make_insert_type<type_policy>(
this->al(),std::forward<Args>(args)...);
return emplace_impl(type_policy::move(x.value()));
}
template<typename Key,typename... Args>
BOOST_FORCEINLINE std::pair<iterator,bool> try_emplace(
Key&& x,Args&&... args)
{
return emplace_impl(
try_emplace_args_t{},std::forward<Key>(x),std::forward<Args>(args)...);
}
BOOST_FORCEINLINE std::pair<iterator,bool>
insert(const init_type& x){return emplace_impl(x);}
BOOST_FORCEINLINE std::pair<iterator,bool>
insert(init_type&& x){return emplace_impl(std::move(x));}
/* template<typename=void> tilts call ambiguities in favor of init_type */
template<typename=void>
BOOST_FORCEINLINE std::pair<iterator,bool>
insert(const value_type& x){return emplace_impl(x);}
template<typename=void>
BOOST_FORCEINLINE std::pair<iterator,bool>
insert(value_type&& x){return emplace_impl(std::move(x));}
template<typename T=element_type>
BOOST_FORCEINLINE
typename std::enable_if<
!std::is_same<T,value_type>::value,
std::pair<iterator,bool>
>::type
insert(element_type&& x){return emplace_impl(std::move(x));}
template<
bool dependent_value=false,
typename std::enable_if<
has_mutable_iterator||dependent_value>::type* =nullptr
>
erase_return_type erase(iterator pos)noexcept
{return erase(const_iterator(pos));}
BOOST_FORCEINLINE
erase_return_type erase(const_iterator pos)noexcept
{
super::erase(pos.pc(),pos.p());
return {pos};
}
template<typename Key>
BOOST_FORCEINLINE
auto erase(Key&& x) -> typename std::enable_if<
!std::is_convertible<Key,iterator>::value&&
!std::is_convertible<Key,const_iterator>::value, std::size_t>::type
{
auto it=find(x);
if(it!=end()){
erase(it);
return 1;
}
else return 0;
}
void swap(table& x)
noexcept(noexcept(std::declval<super&>().swap(std::declval<super&>())))
{
super::swap(x);
}
using super::clear;
element_type extract(const_iterator pos)
{
BOOST_ASSERT(pos!=end());
erase_on_exit e{*this,pos};
(void)e;
return std::move(*pos.p());
}
// TODO: should we accept different allocator too?
template<typename Hash2,typename Pred2>
void merge(table<TypePolicy,Hash2,Pred2,Allocator>& x)
{
x.for_all_elements([&,this](group_type* pg,unsigned int n,element_type* p){
erase_on_exit e{x,{pg,n,p}};
if(!emplace_impl(type_policy::move(*p)).second)e.rollback();
});
}
template<typename Hash2,typename Pred2>
void merge(table<TypePolicy,Hash2,Pred2,Allocator>&& x){merge(x);}
using super::hash_function;
using super::key_eq;
template<typename Key>
BOOST_FORCEINLINE iterator find(const Key& x)
{
return make_iterator(super::find(x));
}
template<typename Key>
BOOST_FORCEINLINE const_iterator find(const Key& x)const
{
return const_cast<table*>(this)->find(x);
}
using super::capacity;
using super::load_factor;
using super::max_load_factor;
using super::max_load;
using super::rehash;
using super::reserve;
template<typename Predicate>
friend std::size_t erase_if(table& x,Predicate& pr)
{
using value_reference=typename std::conditional<
std::is_same<key_type,value_type>::value,
const_reference,
reference
>::type;
std::size_t s=x.size();
x.for_all_elements(
[&](group_type* pg,unsigned int n,element_type* p){
if(pr(const_cast<value_reference>(type_policy::value_from(*p)))){
x.super::erase(pg,n,p);
}
});
return std::size_t(s-x.size());
}
friend bool operator==(const table& x,const table& y)
{
return static_cast<const super&>(x)==static_cast<const super&>(y);
}
friend bool operator!=(const table& x,const table& y){return !(x==y);}
private:
template<typename ArraysType>
table(compatible_concurrent_table&& x,arrays_holder<ArraysType,Allocator>&& ah):
super{
std::move(x.h()),std::move(x.pred()),std::move(x.al()),
[&x]{return arrays_type{
x.arrays.groups_size_index,x.arrays.groups_size_mask,
to_pointer<group_type_pointer>(
reinterpret_cast<group_type*>(x.arrays.groups())),
x.arrays.elements_};},
size_ctrl_type{x.size_ctrl.ml,x.size_ctrl.size}}
{
compatible_concurrent_table::arrays_type::delete_group_access(x.al(),x.arrays);
x.arrays=ah.release();
x.size_ctrl.ml=x.initial_max_load();
x.size_ctrl.size=0;
}
template<typename ExclusiveLockGuard>
table(compatible_concurrent_table&& x,ExclusiveLockGuard):
table(std::move(x),x.make_empty_arrays())
{}
struct erase_on_exit
{
erase_on_exit(table& x_,const_iterator it_):x(x_),it(it_){}
~erase_on_exit(){if(!rollback_)x.erase(it);}
void rollback(){rollback_=true;}
table& x;
const_iterator it;
bool rollback_=false;
};
static inline iterator make_iterator(const locator& l)noexcept
{
return {l.pg,l.n,l.p};
}
template<typename... Args>
BOOST_FORCEINLINE std::pair<iterator,bool> emplace_impl(Args&&... args)
{
const auto &k=this->key_from(std::forward<Args>(args)...);
auto hash=this->hash_for(k);
auto pos0=this->position_for(hash);
auto loc=super::find(k,pos0,hash);
if(loc){
return {make_iterator(loc),false};
}
if(BOOST_LIKELY(this->size_ctrl.size<this->size_ctrl.ml)){
return {
make_iterator(
this->unchecked_emplace_at(pos0,hash,std::forward<Args>(args)...)),
true
};
}
else{
return {
make_iterator(
this->unchecked_emplace_with_rehash(
hash,std::forward<Args>(args)...)),
true
};
}
}
};
#if defined(BOOST_MSVC)
#pragma warning(pop) /* C4714 */
#endif
#include <boost/unordered/detail/foa/restore_wshadow.hpp>
} /* namespace foa */
} /* namespace detail */
} /* namespace unordered */
} /* namespace boost */
#endif
+52
View File
@@ -0,0 +1,52 @@
/* Copyright 2023 Joaquin M Lopez Munoz.
* 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.
*/
#ifndef BOOST_UNORDERED_DETAIL_FOA_TUPLE_ROTATE_RIGHT_HPP
#define BOOST_UNORDERED_DETAIL_FOA_TUPLE_ROTATE_RIGHT_HPP
#include <boost/mp11/algorithm.hpp>
#include <boost/mp11/integer_sequence.hpp>
#include <tuple>
#include <utility>
namespace boost{
namespace unordered{
namespace detail{
namespace foa{
template<typename Tuple>
using tuple_rotate_right_return_type=mp11::mp_rotate_right_c<
typename std::remove_cv<typename std::remove_reference<Tuple>::type>::type,
1
>;
template<std::size_t... Is,typename Tuple>
tuple_rotate_right_return_type<Tuple>
tuple_rotate_right_aux(mp11::index_sequence<Is...>,Tuple&& x)
{
return tuple_rotate_right_return_type<Tuple>{
std::get<(Is+sizeof...(Is)-1)%sizeof...(Is)>(std::forward<Tuple>(x))...};
}
template<typename Tuple>
tuple_rotate_right_return_type<Tuple> tuple_rotate_right(Tuple&& x)
{
using RawTuple=typename std::remove_cv<
typename std::remove_reference<Tuple>::type>::type;
return tuple_rotate_right_aux(
mp11::make_index_sequence<std::tuple_size<RawTuple>::value>{},
std::forward<Tuple>(x));
}
} /* namespace foa */
} /* namespace detail */
} /* namespace unordered */
} /* namespace boost */
#endif
File diff suppressed because it is too large Load Diff
+61
View File
@@ -0,0 +1,61 @@
// Copyright (C) 2005-2016 Daniel James
// Copyright (C) 2022 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 <boost/unordered/detail/implementation.hpp>
#include <boost/unordered/unordered_map_fwd.hpp>
namespace boost {
namespace unordered {
namespace detail {
template <typename A, typename K, typename M, typename H, typename P>
struct map
{
typedef boost::unordered::detail::map<A, K, M, H, P> types;
typedef std::pair<K const, M> value_type;
typedef H hasher;
typedef P key_equal;
typedef K const const_key_type;
typedef
typename ::boost::unordered::detail::rebind_wrap<A, value_type>::type
value_allocator;
typedef boost::unordered::detail::allocator_traits<value_allocator>
value_allocator_traits;
typedef boost::unordered::detail::table<types> table;
typedef boost::unordered::detail::map_extractor<value_type> extractor;
typedef typename boost::allocator_void_pointer<value_allocator>::type
void_pointer;
typedef boost::unordered::node_handle_map<
node<value_type, void_pointer>, K, M, A>
node_type;
typedef typename table::iterator iterator;
typedef boost::unordered::insert_return_type_map<iterator, node_type> insert_return_type;
};
template <typename K, typename M, typename H, typename P, typename A>
class instantiate_map
{
typedef boost::unordered_map<K, M, H, P, A> container;
container x;
typename container::node_type node_type;
typename container::insert_return_type insert_return_type;
};
template <typename K, typename M, typename H, typename P, typename A>
class instantiate_multimap
{
typedef boost::unordered_multimap<K, M, H, P, A> container;
container x;
typename container::node_type node_type;
};
}
}
}
+129
View File
@@ -0,0 +1,129 @@
#ifndef BOOST_UNORDERED_DETAIL_MULX_HPP
#define BOOST_UNORDERED_DETAIL_MULX_HPP
// Copyright 2022 Peter Dimov.
// Copyright 2022 Joaquin M Lopez Munoz.
// Distributed under the Boost Software License, Version 1.0.
// https://www.boost.org/LICENSE_1_0.txt)
#include <boost/cstdint.hpp>
#include <climits>
#include <cstddef>
#if defined(_MSC_VER) && !defined(__clang__)
# include <intrin.h>
#endif
namespace boost {
namespace unordered {
namespace detail {
// Bit mixer based on the mulx primitive
#if defined(_MSC_VER) && defined(_M_X64) && !defined(__clang__)
__forceinline boost::uint64_t mulx64( boost::uint64_t x, boost::uint64_t y )
{
boost::uint64_t r2;
boost::uint64_t r = _umul128( x, y, &r2 );
return r ^ r2;
}
#elif defined(_MSC_VER) && defined(_M_ARM64) && !defined(__clang__)
__forceinline boost::uint64_t mulx64( boost::uint64_t x, boost::uint64_t y )
{
boost::uint64_t r = x * y;
boost::uint64_t r2 = __umulh( x, y );
return r ^ r2;
}
#elif defined(__SIZEOF_INT128__)
inline boost::uint64_t mulx64( boost::uint64_t x, boost::uint64_t y )
{
__uint128_t r = (__uint128_t)x * y;
return (boost::uint64_t)r ^ (boost::uint64_t)( r >> 64 );
}
#else
inline boost::uint64_t mulx64( boost::uint64_t x, boost::uint64_t y )
{
boost::uint64_t x1 = (boost::uint32_t)x;
boost::uint64_t x2 = x >> 32;
boost::uint64_t y1 = (boost::uint32_t)y;
boost::uint64_t y2 = y >> 32;
boost::uint64_t r3 = x2 * y2;
boost::uint64_t r2a = x1 * y2;
r3 += r2a >> 32;
boost::uint64_t r2b = x2 * y1;
r3 += r2b >> 32;
boost::uint64_t r1 = x1 * y1;
boost::uint64_t r2 = (r1 >> 32) + (boost::uint32_t)r2a + (boost::uint32_t)r2b;
r1 = (r2 << 32) + (boost::uint32_t)r1;
r3 += r2 >> 32;
return r1 ^ r3;
}
#endif
inline boost::uint32_t mulx32( boost::uint32_t x, boost::uint32_t y )
{
boost::uint64_t r = (boost::uint64_t)x * y;
#if defined(__MSVC_RUNTIME_CHECKS)
return (boost::uint32_t)(r & UINT32_MAX) ^ (boost::uint32_t)(r >> 32);
#else
return (boost::uint32_t)r ^ (boost::uint32_t)(r >> 32);
#endif
}
#if defined(SIZE_MAX)
#if ((((SIZE_MAX >> 16) >> 16) >> 16) >> 15) != 0
#define BOOST_UNORDERED_64B_ARCHITECTURE /* >64 bits assumed as 64 bits */
#endif
#elif defined(UINTPTR_MAX) /* used as proxy for std::size_t */
#if ((((UINTPTR_MAX >> 16) >> 16) >> 16) >> 15) != 0
#define BOOST_UNORDERED_64B_ARCHITECTURE
#endif
#endif
inline std::size_t mulx( std::size_t x ) noexcept
{
#if defined(BOOST_UNORDERED_64B_ARCHITECTURE)
// multiplier is phi
return (std::size_t)mulx64( (boost::uint64_t)x, 0x9E3779B97F4A7C15ull );
#else /* 32 bits assumed */
// multiplier from https://arxiv.org/abs/2001.05304
return mulx32( x, 0xE817FB2Du );
#endif
}
#ifdef BOOST_UNORDERED_64B_ARCHITECTURE
#undef BOOST_UNORDERED_64B_ARCHITECTURE
#endif
} // namespace detail
} // namespace unordered
} // namespace boost
#endif // #ifndef BOOST_UNORDERED_DETAIL_MULX_HPP
+44
View File
@@ -0,0 +1,44 @@
/* Copyright 2022 Joaquin M Lopez Munoz.
* 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.
*/
#ifndef BOOST_UNORDERED_DETAIL_NARROW_CAST_HPP
#define BOOST_UNORDERED_DETAIL_NARROW_CAST_HPP
#include <boost/unordered/detail/static_assert.hpp>
#include <boost/config.hpp>
#include <type_traits>
namespace boost{
namespace unordered{
namespace detail{
template<typename To,typename From>
constexpr To narrow_cast(From x) noexcept
{
BOOST_UNORDERED_STATIC_ASSERT(std::is_integral<From>::value);
BOOST_UNORDERED_STATIC_ASSERT(std::is_integral<To>::value);
BOOST_UNORDERED_STATIC_ASSERT(sizeof(From)>=sizeof(To));
return static_cast<To>(
x
#if defined(__MSVC_RUNTIME_CHECKS)
/* Avoids VS's "Run-Time Check Failure #1 - A cast to a smaller data type
* has caused a loss of data."
*/
&static_cast<typename std::make_unsigned<To>::type>(~static_cast<To>(0))
#endif
);
}
} /* namespace detail */
} /* namespace unordered */
} /* namespace boost */
#endif
+30
View File
@@ -0,0 +1,30 @@
// Copyright (C) 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)
#ifndef BOOST_UNORDERED_DETAIL_OPT_STORAGE_HPP
#define BOOST_UNORDERED_DETAIL_OPT_STORAGE_HPP
#include <boost/config.hpp>
#include <memory>
namespace boost {
namespace unordered {
namespace detail {
template <class T> union opt_storage
{
BOOST_ATTRIBUTE_NO_UNIQUE_ADDRESS T t_;
opt_storage() {}
~opt_storage() {}
T* address() noexcept { return std::addressof(t_); }
T const* address() const noexcept { return std::addressof(t_); }
};
} // namespace detail
} // namespace unordered
} // namespace boost
#endif // BOOST_UNORDERED_DETAIL_OPT_STORAGE_HPP
+214
View File
@@ -0,0 +1,214 @@
// Copyright (C) 2022 Joaquin M Lopez Munoz.
// 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)
#ifndef BOOST_UNORDERED_DETAIL_PRIME_FMOD_HPP
#define BOOST_UNORDERED_DETAIL_PRIME_FMOD_HPP
#include <boost/unordered/detail/narrow_cast.hpp>
#include <boost/config.hpp>
#include <boost/cstdint.hpp>
#include <climits>
#include <cstddef>
#if defined(SIZE_MAX)
#if ((((SIZE_MAX >> 16) >> 16) >> 16) >> 15) != 0
#define BOOST_UNORDERED_FCA_HAS_64B_SIZE_T
#endif
#elif defined(UINTPTR_MAX) /* used as proxy for std::size_t */
#if ((((UINTPTR_MAX >> 16) >> 16) >> 16) >> 15) != 0
#define BOOST_UNORDERED_FCA_HAS_64B_SIZE_T
#endif
#endif
#if defined(BOOST_UNORDERED_FCA_HAS_64B_SIZE_T) && defined(_MSC_VER)
#include <intrin.h>
#endif
namespace boost {
namespace unordered {
namespace detail {
template <class = void> struct prime_fmod_size
{
constexpr static std::size_t const sizes[] = {13ul, 29ul, 53ul, 97ul,
193ul, 389ul, 769ul, 1543ul, 3079ul, 6151ul, 12289ul, 24593ul,
49157ul, 98317ul, 196613ul, 393241ul, 786433ul, 1572869ul, 3145739ul,
6291469ul, 12582917ul, 25165843ul, 50331653ul, 100663319ul,
201326611ul, 402653189ul, 805306457ul, 1610612741ul, 3221225473ul,
#if !defined(BOOST_UNORDERED_FCA_HAS_64B_SIZE_T)
4294967291ul
#else
6442450939ull, 12884901893ull, 25769803751ull, 51539607551ull,
103079215111ull, 206158430209ull, 412316860441ull, 824633720831ull,
1649267441651ull
#endif
};
constexpr static std::size_t const sizes_len =
sizeof(sizes) / sizeof(sizes[0]);
#if defined(BOOST_UNORDERED_FCA_HAS_64B_SIZE_T)
constexpr static boost::uint64_t const inv_sizes32[] = {
1418980313362273202ull, 636094623231363849ull, 348051774975651918ull,
190172619316593316ull, 95578984837873325ull, 47420935922132524ull,
23987963684927896ull, 11955116055547344ull, 5991147799191151ull,
2998982941588287ull, 1501077717772769ull, 750081082979285ull,
375261795343686ull, 187625172388393ull, 93822606204624ull,
46909513691883ull, 23456218233098ull, 11728086747027ull,
5864041509391ull, 2932024948977ull, 1466014921160ull, 733007198436ull,
366503839517ull, 183251896093ull, 91625960335ull, 45812983922ull,
22906489714ull, 11453246088ull, 5726623060ull};
constexpr static std::size_t const inv_sizes32_len =
sizeof(inv_sizes32) / sizeof(inv_sizes32[0]);
#endif /* defined(BOOST_UNORDERED_FCA_HAS_64B_SIZE_T) */
template <std::size_t SizeIndex, std::size_t Size = sizes[SizeIndex]>
static std::size_t position(std::size_t hash)
{
return hash % Size;
}
constexpr static std::size_t (*positions[])(std::size_t) = {
#if !defined(BOOST_UNORDERED_FCA_HAS_64B_SIZE_T)
position<0, sizes[0]>,
position<1, sizes[1]>,
position<2, sizes[2]>,
position<3, sizes[3]>,
position<4, sizes[4]>,
position<5, sizes[5]>,
position<6, sizes[6]>,
position<7, sizes[7]>,
position<8, sizes[8]>,
position<9, sizes[9]>,
position<10, sizes[10]>,
position<11, sizes[11]>,
position<12, sizes[12]>,
position<13, sizes[13]>,
position<14, sizes[14]>,
position<15, sizes[15]>,
position<16, sizes[16]>,
position<17, sizes[17]>,
position<18, sizes[18]>,
position<19, sizes[19]>,
position<20, sizes[20]>,
position<21, sizes[21]>,
position<22, sizes[22]>,
position<23, sizes[23]>,
position<24, sizes[24]>,
position<25, sizes[25]>,
position<26, sizes[26]>,
position<27, sizes[27]>,
position<28, sizes[28]>,
position<29, sizes[29]>,
#else
position<29, sizes[29]>,
position<30, sizes[30]>,
position<31, sizes[31]>,
position<32, sizes[32]>,
position<33, sizes[33]>,
position<34, sizes[34]>,
position<35, sizes[35]>,
position<36, sizes[36]>,
position<37, sizes[37]>,
#endif
};
static inline std::size_t size_index(std::size_t n)
{
std::size_t i = 0;
for (; i < (sizes_len - 1); ++i) {
if (sizes[i] >= n) {
break;
}
}
return i;
}
static inline std::size_t size(std::size_t size_index)
{
return sizes[size_index];
}
#if defined(BOOST_UNORDERED_FCA_HAS_64B_SIZE_T)
// We emulate the techniques taken from:
// Faster Remainder by Direct Computation: Applications to Compilers and
// Software Libraries
// https://arxiv.org/abs/1902.01961
//
// In essence, use fancy math to directly calculate the remainder (aka
// modulo) exploiting how compilers transform division
//
static inline boost::uint64_t get_remainder(
boost::uint64_t fractional, boost::uint32_t d)
{
#if defined(_MSC_VER)
// use MSVC intrinsics when available to avoid promotion to 128 bits
return __umulh(fractional, d);
#elif defined(BOOST_HAS_INT128)
return static_cast<boost::uint64_t>(
((boost::uint128_type)fractional * d) >> 64);
#else
// portable implementation in the absence of boost::uint128_type on 64
// bits, which happens at least in GCC 4.5 and prior
boost::uint64_t r1 = (fractional & UINT32_MAX) * d;
boost::uint64_t r2 = (fractional >> 32) * d;
r2 += r1 >> 32;
return r2 >> 32;
#endif /* defined(_MSC_VER) */
}
static inline boost::uint32_t fast_modulo(
boost::uint32_t a, boost::uint64_t M, boost::uint32_t d)
{
boost::uint64_t fractional = M * a;
return (boost::uint32_t)(get_remainder(fractional, d));
}
#endif /* defined(BOOST_UNORDERED_FCA_HAS_64B_SIZE_T) */
static inline std::size_t position(
std::size_t hash, std::size_t size_index)
{
#if defined(BOOST_UNORDERED_FCA_HAS_64B_SIZE_T)
std::size_t sizes_under_32bit = inv_sizes32_len;
if (BOOST_LIKELY(size_index < sizes_under_32bit)) {
return fast_modulo(narrow_cast<boost::uint32_t>(hash) +
narrow_cast<boost::uint32_t>(hash >> 32),
inv_sizes32[size_index], boost::uint32_t(sizes[size_index]));
} else {
return positions[size_index - sizes_under_32bit](hash);
}
#else
return positions[size_index](hash);
#endif /* defined(BOOST_UNORDERED_FCA_HAS_64B_SIZE_T) */
}
}; // prime_fmod_size
#if defined(BOOST_NO_CXX17_INLINE_VARIABLES)
// https://en.cppreference.com/w/cpp/language/static#Constant_static_members
// If a const non-inline (since C++17) static data member or a constexpr
// static data member (since C++11)(until C++17) is odr-used, a definition
// at namespace scope is still required, but it cannot have an
// initializer.
template <class T> constexpr std::size_t prime_fmod_size<T>::sizes[];
#if defined(BOOST_UNORDERED_FCA_HAS_64B_SIZE_T)
template <class T>
constexpr boost::uint64_t prime_fmod_size<T>::inv_sizes32[];
#endif
template <class T>
constexpr std::size_t (*prime_fmod_size<T>::positions[])(std::size_t);
#endif
} // namespace detail
} // namespace unordered
} // namespace boost
#endif // BOOST_UNORDERED_DETAIL_PRIME_FMOD_HPP
+74
View File
@@ -0,0 +1,74 @@
/* Copyright 2023 Joaquin M Lopez Munoz.
* 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.
*/
#ifndef BOOST_UNORDERED_DETAIL_SERIALIZATION_VERSION_HPP
#define BOOST_UNORDERED_DETAIL_SERIALIZATION_VERSION_HPP
#include <boost/config.hpp>
#include <boost/core/serialization.hpp>
namespace boost{
namespace unordered{
namespace detail{
/* boost::serialization::load_construct_adl(ar,t,version) requires user code
* to pass the serialization version for t, when this information is really
* stored in the archive. serialization_version<T> circumvents this design
* error by acting as a regular serializable type with the same serialization
* version as T; loading/saving serialization_version<T> does nothing with
* the archive data itself but captures the stored serialization version
* at load() time.
*/
template<typename T>
struct serialization_version
{
serialization_version():
value(boost::serialization::version<serialization_version>::value){}
serialization_version& operator=(unsigned int x){value=x;return *this;};
operator unsigned int()const{return value;}
private:
friend class boost::serialization::access;
template<class Archive>
void serialize(Archive& ar,unsigned int version)
{
core::split_member(ar,*this,version);
}
template<class Archive>
void save(Archive&,unsigned int)const{}
template<class Archive>
void load(Archive&,unsigned int version)
{
this->value=version;
}
unsigned int value;
};
} /* namespace detail */
} /* namespace unordered */
namespace serialization{
template<typename T>
struct version<boost::unordered::detail::serialization_version<T> >
{
BOOST_STATIC_CONSTANT(int,value=version<T>::value);
};
} /* namespace serialization */
} /* namespace boost */
#endif
+204
View File
@@ -0,0 +1,204 @@
/* Copyright 2023 Joaquin M Lopez Munoz.
* 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.
*/
#ifndef BOOST_UNORDERED_DETAIL_SERIALIZE_CONTAINER_HPP
#define BOOST_UNORDERED_DETAIL_SERIALIZE_CONTAINER_HPP
#include <boost/core/serialization.hpp>
#include <boost/throw_exception.hpp>
#include <boost/unordered/detail/archive_constructed.hpp>
#include <boost/unordered/detail/bad_archive_exception.hpp>
#include <boost/unordered/detail/serialization_version.hpp>
#include <cstddef>
namespace boost{
namespace unordered{
namespace detail{
/* serialize_container(ar,x,v) serializes any of the unordered associative
* containers in Boost.Unordered. Iterator serialization is also supported
* through the following protocol:
* - At saving time, for each iterator it in [x.begin(),x.end()),
* serialization_track(ar,it) is ADL-called to instruct the archive to
* track the positions internally pointed to by the iterator via
* track_address().
* - At loading time, these addresses are mapped to those of the equivalent
* reconstructed positions using again serialization_track(ar,it).
* - Serializing an iterator reduces to serializing pointers to previously
* tracked addresses via serialize_address().
*/
template<typename Iterator>
std::pair<Iterator,bool> adapt_insert_return_type(Iterator it)
{
return std::pair<Iterator,bool>(it,true);
}
template<typename Iterator>
std::pair<Iterator,bool> adapt_insert_return_type(std::pair<Iterator,bool> p)
{
return p;
}
template<typename Set,bool IsSaving> struct load_or_save_unordered_set;
template<typename Set> struct load_or_save_unordered_set<Set,true> /* save */
{
template<typename Archive>
void operator()(Archive& ar,const Set& x,unsigned int)const
{
typedef typename Set::value_type value_type;
typedef typename Set::const_iterator const_iterator;
const std::size_t s=x.size();
const serialization_version<value_type> value_version;
ar<<core::make_nvp("count",s);
ar<<core::make_nvp("value_version",value_version);
for(const_iterator first=x.begin(),last=x.end();first!=last;++first){
core::save_construct_data_adl(ar,std::addressof(*first),value_version);
ar<<core::make_nvp("item",*first);
serialization_track(ar,first);
}
}
};
template<typename Set> struct load_or_save_unordered_set<Set,false> /* load */
{
template<typename Archive>
void operator()(Archive& ar,Set& x,unsigned int)const
{
typedef typename Set::value_type value_type;
typedef typename Set::iterator iterator;
std::size_t s;
serialization_version<value_type> value_version;
ar>>core::make_nvp("count",s);
ar>>core::make_nvp("value_version",value_version);
x.clear();
x.reserve(s); /* critical so that iterator tracking is stable */
for(std::size_t n=0;n<s;++n){
archive_constructed<value_type> value("item",ar,value_version);
std::pair<iterator,bool> p=adapt_insert_return_type(
x.insert(std::move(value.get())));
if(!p.second)throw_exception(bad_archive_exception());
ar.reset_object_address(
std::addressof(*p.first),std::addressof(value.get()));
serialization_track(ar,p.first);
}
}
};
template<typename Map,bool IsSaving> struct load_or_save_unordered_map;
template<typename Map> struct load_or_save_unordered_map<Map,true> /* save */
{
template<typename Archive>
void operator()(Archive& ar,const Map& x,unsigned int)const
{
typedef typename std::remove_const<
typename Map::key_type>::type key_type;
typedef typename std::remove_const<
typename Map::mapped_type>::type mapped_type;
typedef typename Map::const_iterator const_iterator;
const std::size_t s=x.size();
const serialization_version<key_type> key_version;
const serialization_version<mapped_type> mapped_version;
ar<<core::make_nvp("count",s);
ar<<core::make_nvp("key_version",key_version);
ar<<core::make_nvp("mapped_version",mapped_version);
for(const_iterator first=x.begin(),last=x.end();first!=last;++first){
/* To remain lib-independent from Boost.Serialization and not rely on
* the user having included the serialization code for std::pair
* (boost/serialization/utility.hpp), we serialize the key and the
* mapped value separately.
*/
core::save_construct_data_adl(
ar,std::addressof(first->first),key_version);
ar<<core::make_nvp("key",first->first);
core::save_construct_data_adl(
ar,std::addressof(first->second),mapped_version);
ar<<core::make_nvp("mapped",first->second);
serialization_track(ar,first);
}
}
};
template<typename Map> struct load_or_save_unordered_map<Map,false> /* load */
{
template<typename Archive>
void operator()(Archive& ar,Map& x,unsigned int)const
{
typedef typename std::remove_const<
typename Map::key_type>::type key_type;
typedef typename std::remove_const<
typename Map::mapped_type>::type mapped_type;
typedef typename Map::iterator iterator;
std::size_t s;
serialization_version<key_type> key_version;
serialization_version<mapped_type> mapped_version;
ar>>core::make_nvp("count",s);
ar>>core::make_nvp("key_version",key_version);
ar>>core::make_nvp("mapped_version",mapped_version);
x.clear();
x.reserve(s); /* critical so that iterator tracking is stable */
for(std::size_t n=0;n<s;++n){
archive_constructed<key_type> key("key",ar,key_version);
archive_constructed<mapped_type> mapped("mapped",ar,mapped_version);
std::pair<iterator,bool> p=adapt_insert_return_type(
x.emplace(std::move(key.get()),std::move(mapped.get())));
if(!p.second)throw_exception(bad_archive_exception());
ar.reset_object_address(
std::addressof(p.first->first),std::addressof(key.get()));
ar.reset_object_address(
std::addressof(p.first->second),std::addressof(mapped.get()));
serialization_track(ar,p.first);
}
}
};
template<typename Container,bool IsSet,bool IsSaving>
struct load_or_save_container;
template<typename Set,bool IsSaving>
struct load_or_save_container<Set,true,IsSaving>:
load_or_save_unordered_set<Set,IsSaving>{};
template<typename Map,bool IsSaving>
struct load_or_save_container<Map,false,IsSaving>:
load_or_save_unordered_map<Map,IsSaving>{};
template<typename Archive,typename Container>
void serialize_container(Archive& ar,Container& x,unsigned int version)
{
load_or_save_container<
Container,
std::is_same<
typename Container::key_type,typename Container::value_type>::value,
Archive::is_saving::value>()(ar,x,version);
}
} /* namespace detail */
} /* namespace unordered */
} /* namespace boost */
#endif
+156
View File
@@ -0,0 +1,156 @@
/* Copyright 2023 Joaquin M Lopez Munoz.
* 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.
*/
#ifndef BOOST_UNORDERED_DETAIL_SERIALIZE_FCA_CONTAINER_HPP
#define BOOST_UNORDERED_DETAIL_SERIALIZE_FCA_CONTAINER_HPP
#include <boost/unordered/detail/serialize_container.hpp>
#if defined(BOOST_UNORDERED_ENABLE_SERIALIZATION_COMPATIBILITY_V0)
#define BOOST_UNORDERED_BLOCK_BOOSTDEP_HEADER \
<boost/serialization/archive_input_unordered_map.hpp>
#include BOOST_UNORDERED_BLOCK_BOOSTDEP_HEADER
#undef BOOST_UNORDERED_BLOCK_BOOSTDEP_HEADER
#define BOOST_UNORDERED_BLOCK_BOOSTDEP_HEADER \
<boost/serialization/archive_input_unordered_set.hpp>
#include BOOST_UNORDERED_BLOCK_BOOSTDEP_HEADER
#undef BOOST_UNORDERED_BLOCK_BOOSTDEP_HEADER
#define BOOST_UNORDERED_BLOCK_BOOSTDEP_HEADER \
<boost/serialization/unordered_collections_load_imp.hpp>
#include BOOST_UNORDERED_BLOCK_BOOSTDEP_HEADER
#undef BOOST_UNORDERED_BLOCK_BOOSTDEP_HEADER
#define BOOST_UNORDERED_BLOCK_BOOSTDEP_HEADER \
<boost/serialization/utility.hpp>
#include BOOST_UNORDERED_BLOCK_BOOSTDEP_HEADER
#undef BOOST_UNORDERED_BLOCK_BOOSTDEP_HEADER
#include <boost/unordered/unordered_map_fwd.hpp>
#include <boost/unordered/unordered_set_fwd.hpp>
#else
#include <boost/throw_exception.hpp>
#include <stdexcept>
#endif
namespace boost{
namespace unordered{
namespace detail{
/* Support for boost::unordered_[multi](map|set) loading from legacy archives.
* Until Boost 1.84, serialization of these containers was provided from
* Boost.Serialization via boost/serialization/boost_unordered_(map|set).hpp,
* from that release on support is native in Boost.Unordered. To enable legacy
* archive loading, BOOST_UNORDERED_ENABLE_SERIALIZATION_COMPATIBILITY_V0
* must be defined (it implies header dependency from Boost.Serialization).
*/
#if defined(BOOST_UNORDERED_ENABLE_SERIALIZATION_COMPATIBILITY_V0)
template<typename Archive,typename Container>
struct archive_input;
template<
typename Archive,typename K,typename T,typename H,typename P,typename A
>
struct archive_input<Archive,boost::unordered_map<K,T,H,P,A> >:
boost::serialization::stl::archive_input_unordered_map<
Archive,
boost::unordered_map<K,T,H,P,A>
>
{};
template<
typename Archive,typename K,typename T,typename H,typename P,typename A
>
struct archive_input<Archive,boost::unordered_multimap<K,T,H,P,A> >:
boost::serialization::stl::archive_input_unordered_multimap<
Archive,
boost::unordered_multimap<K,T,H,P,A>
>
{};
template<
typename Archive,typename K,typename H,typename P,typename A
>
struct archive_input<Archive,boost::unordered_set<K,H,P,A> >:
boost::serialization::stl::archive_input_unordered_set<
Archive,
boost::unordered_set<K,H,P,A>
>
{};
template<
typename Archive,typename K,typename H,typename P,typename A
>
struct archive_input<Archive,boost::unordered_multiset<K,H,P,A> >:
boost::serialization::stl::archive_input_unordered_multiset<
Archive,
boost::unordered_multiset<K,H,P,A>
>
{};
#else
struct legacy_archive_exception:std::runtime_error
{
legacy_archive_exception():std::runtime_error(
"Legacy archive detected, define "
"BOOST_UNORDERED_ENABLE_SERIALIZATION_COMPATIBILITY_V0 to load"){}
};
#endif
template<typename Container,bool IsSaving>
struct load_or_save_fca_container;
template<typename Container>
struct load_or_save_fca_container<Container,true> /* save */
{
template<typename Archive>
void operator()(Archive& ar,Container& x,unsigned int version)const
{
serialize_container(ar,x,version);
}
};
template<typename Container>
struct load_or_save_fca_container<Container,false> /* load */
{
template<typename Archive>
void operator()(Archive& ar,Container& x,unsigned int version)const
{
if(version==0){
#if defined(BOOST_UNORDERED_ENABLE_SERIALIZATION_COMPATIBILITY_V0)
boost::serialization::stl::load_unordered_collection<
Archive,Container,archive_input<Archive,Container>
>(ar,x);
#else
throw_exception(legacy_archive_exception());
#endif
}
else{
serialize_container(ar,x,version);
}
}
};
template<typename Archive,typename Container>
void serialize_fca_container(Archive& ar,Container& x,unsigned int version)
{
load_or_save_fca_container<Container,Archive::is_saving::value>()(
ar,x,version);
}
} /* namespace detail */
} /* namespace unordered */
} /* namespace boost */
#endif
+104
View File
@@ -0,0 +1,104 @@
/* Copyright 2023 Joaquin M Lopez Munoz.
* 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.
*/
#ifndef BOOST_UNORDERED_DETAIL_SERIALIZE_TRACKED_ADDRESS_HPP
#define BOOST_UNORDERED_DETAIL_SERIALIZE_TRACKED_ADDRESS_HPP
#include <boost/unordered/detail/bad_archive_exception.hpp>
#include <boost/core/pointer_traits.hpp>
#include <boost/core/serialization.hpp>
#include <boost/throw_exception.hpp>
#include <type_traits>
namespace boost{
namespace unordered{
namespace detail{
/* Tracked address serialization to support iterator serialization as described
* in serialize_container.hpp. The underlying technique is to reinterpret_cast
* T pointers to serialization_tracker<T> pointers, which, when dereferenced
* and serialized, do not emit any serialization payload to the
* archive, but activate object tracking on the relevant addresses for later
* use with serialize_tracked_address().
*/
template<typename T>
struct serialization_tracker
{
/* An attempt to construct a serialization_tracker means a stray address
* in the archive, that is, one without a previously tracked address.
*/
serialization_tracker(){throw_exception(bad_archive_exception());}
template<typename Archive>
void serialize(Archive&,unsigned int){} /* no data emitted */
};
template<typename Archive,typename Ptr>
void track_address(Archive& ar,Ptr p)
{
typedef typename boost::pointer_traits<Ptr> ptr_traits;
typedef typename std::remove_const<
typename ptr_traits::element_type>::type element_type;
if(p){
ar&core::make_nvp(
"address",
*reinterpret_cast<serialization_tracker<element_type>*>(
const_cast<element_type*>(
boost::to_address(p))));
}
}
template<typename Archive,typename Ptr>
void serialize_tracked_address(Archive& ar,Ptr& p,std::true_type /* save */)
{
typedef typename boost::pointer_traits<Ptr> ptr_traits;
typedef typename std::remove_const<
typename ptr_traits::element_type>::type element_type;
typedef serialization_tracker<element_type> tracker;
tracker* pt=
const_cast<tracker*>(
reinterpret_cast<const tracker*>(
const_cast<const element_type*>(
boost::to_address(p))));
ar<<core::make_nvp("pointer",pt);
}
template<typename Archive,typename Ptr>
void serialize_tracked_address(Archive& ar,Ptr& p,std::false_type /* load */)
{
typedef typename boost::pointer_traits<Ptr> ptr_traits;
typedef typename std::remove_const<
typename ptr_traits::element_type>::type element_type;
typedef serialization_tracker<element_type> tracker;
tracker* pt;
ar>>core::make_nvp("pointer",pt);
element_type* pn=const_cast<element_type*>(
reinterpret_cast<const element_type*>(
const_cast<const tracker*>(pt)));
p=pn?ptr_traits::pointer_to(*pn):0;
}
template<typename Archive,typename Ptr>
void serialize_tracked_address(Archive& ar,Ptr& p)
{
serialize_tracked_address(
ar,p,
std::integral_constant<bool,Archive::is_saving::value>());
}
} /* namespace detail */
} /* namespace unordered */
} /* namespace boost */
#endif
+61
View File
@@ -0,0 +1,61 @@
// Copyright (C) 2005-2016 Daniel James
// Copyright (C) 2022 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 <boost/unordered/detail/implementation.hpp>
#include <boost/unordered/unordered_set_fwd.hpp>
namespace boost {
namespace unordered {
namespace detail {
template <typename A, typename T, typename H, typename P> struct set
{
typedef boost::unordered::detail::set<A, T, H, P> types;
typedef T value_type;
typedef H hasher;
typedef P key_equal;
typedef T const const_key_type;
typedef
typename ::boost::unordered::detail::rebind_wrap<A, value_type>::type
value_allocator;
typedef boost::unordered::detail::allocator_traits<value_allocator>
value_allocator_traits;
typedef boost::unordered::detail::table<types> table;
typedef boost::unordered::detail::set_extractor<value_type> extractor;
typedef typename boost::allocator_void_pointer<value_allocator>::type
void_pointer;
typedef boost::unordered::node_handle_set<
node<value_type, void_pointer>, T, A>
node_type;
typedef typename table::c_iterator iterator;
typedef boost::unordered::insert_return_type_set<iterator, node_type>
insert_return_type;
};
template <typename T, typename H, typename P, typename A>
class instantiate_set
{
typedef boost::unordered_set<T, H, P, A> container;
container x;
typename container::node_type node_type;
typename container::insert_return_type insert_return_type;
};
template <typename T, typename H, typename P, typename A>
class instantiate_multiset
{
typedef boost::unordered_multiset<T, H, P, A> container;
container x;
typename container::node_type node_type;
};
}
}
}
+16
View File
@@ -0,0 +1,16 @@
// 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)
#ifndef BOOST_UNORDERED_DETAIL_STATIC_ASSERT_HPP
#define BOOST_UNORDERED_DETAIL_STATIC_ASSERT_HPP
#include <boost/config.hpp>
#if defined(BOOST_HAS_PRAGMA_ONCE)
#pragma once
#endif
#define BOOST_UNORDERED_STATIC_ASSERT(...) \
static_assert(__VA_ARGS__, #__VA_ARGS__)
#endif // BOOST_UNORDERED_DETAIL_STATIC_ASSERT_HPP
+194
View File
@@ -0,0 +1,194 @@
// 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)
#ifndef BOOST_UNORDERED_DETAIL_TYPE_TRAITS_HPP
#define BOOST_UNORDERED_DETAIL_TYPE_TRAITS_HPP
#include <boost/config.hpp>
#if defined(BOOST_HAS_PRAGMA_ONCE)
#pragma once
#endif
#include <boost/config/workaround.hpp>
#if !defined(BOOST_NO_CXX17_DEDUCTION_GUIDES)
#include <iterator>
#endif
#include <type_traits>
#include <utility>
// BOOST_UNORDERED_TEMPLATE_DEDUCTION_GUIDES
#if !defined(BOOST_UNORDERED_TEMPLATE_DEDUCTION_GUIDES)
#if !defined(BOOST_NO_CXX17_DEDUCTION_GUIDES)
#define BOOST_UNORDERED_TEMPLATE_DEDUCTION_GUIDES 1
#endif
#endif
#if !defined(BOOST_UNORDERED_TEMPLATE_DEDUCTION_GUIDES)
#define BOOST_UNORDERED_TEMPLATE_DEDUCTION_GUIDES 0
#endif
namespace boost {
namespace unordered {
namespace detail {
template <class T> struct type_identity
{
using type = T;
};
template <typename... Ts> struct make_void
{
typedef void type;
};
template <typename... Ts> using void_t = typename make_void<Ts...>::type;
#if BOOST_WORKAROUND(BOOST_LIBSTDCXX_VERSION, < 50000)
/* std::is_trivially_default_constructible not provided */
template <class T>
struct is_trivially_default_constructible
: public std::integral_constant<bool,
std::is_default_constructible<T>::value &&
std::has_trivial_default_constructor<T>::value>
{
};
#else
using std::is_trivially_default_constructible;
#endif
#if BOOST_WORKAROUND(BOOST_LIBSTDCXX_VERSION, < 50000)
/* std::is_trivially_copy_constructible not provided */
template <class T>
struct is_trivially_copy_constructible
: public std::integral_constant<bool,
std::is_copy_constructible<T>::value &&
std::has_trivial_copy_constructor<T>::value>
{
};
#else
using std::is_trivially_copy_constructible;
#endif
#if BOOST_WORKAROUND(BOOST_LIBSTDCXX_VERSION, < 50000)
/* std::is_trivially_copy_assignable not provided */
template <class T>
struct is_trivially_copy_assignable
: public std::integral_constant<bool,
std::is_copy_assignable<T>::value &&
std::has_trivial_copy_assign<T>::value>
{
};
#else
using std::is_trivially_copy_assignable;
#endif
namespace type_traits_detail {
using std::swap;
template <class T, class = void> struct is_nothrow_swappable_helper
{
constexpr static bool const value = false;
};
template <class T>
struct is_nothrow_swappable_helper<T,
void_t<decltype(swap(std::declval<T&>(), std::declval<T&>()))> >
{
constexpr static bool const value =
noexcept(swap(std::declval<T&>(), std::declval<T&>()));
};
} // namespace type_traits_detail
template <class T>
struct is_nothrow_swappable
: public std::integral_constant<bool,
type_traits_detail::is_nothrow_swappable_helper<T>::value>
{
};
////////////////////////////////////////////////////////////////////////////
// Type checkers used for the transparent member functions added by C++20
// and up
template <class, class = void>
struct is_transparent : public std::false_type
{
};
template <class T>
struct is_transparent<T,
boost::unordered::detail::void_t<typename T::is_transparent> >
: public std::true_type
{
};
template <class, class Hash, class KeyEqual> struct are_transparent
{
static bool const value =
is_transparent<Hash>::value && is_transparent<KeyEqual>::value;
};
template <class Key, class UnorderedMap> struct transparent_non_iterable
{
typedef typename UnorderedMap::hasher hash;
typedef typename UnorderedMap::key_equal key_equal;
typedef typename UnorderedMap::iterator iterator;
typedef typename UnorderedMap::const_iterator const_iterator;
static bool const value =
are_transparent<Key, hash, key_equal>::value &&
!std::is_convertible<Key, iterator>::value &&
!std::is_convertible<Key, const_iterator>::value;
};
#if BOOST_UNORDERED_TEMPLATE_DEDUCTION_GUIDES
// https://eel.is/c++draft/container.requirements#container.alloc.reqmts-34
// https://eel.is/c++draft/container.requirements#unord.req.general-243
template <class InputIterator>
constexpr bool const is_input_iterator_v =
!std::is_integral<InputIterator>::value;
template <class A, class = void> struct is_allocator
{
constexpr static bool const value = false;
};
template <class A>
struct is_allocator<A,
boost::unordered::detail::void_t<typename A::value_type,
decltype(std::declval<A&>().allocate(std::size_t{}))> >
{
constexpr static bool const value = true;
};
template <class A>
constexpr bool const is_allocator_v = is_allocator<A>::value;
template <class H>
constexpr bool const is_hash_v =
!std::is_integral<H>::value && !is_allocator_v<H>;
template <class P> constexpr bool const is_pred_v = !is_allocator_v<P>;
template <typename T>
using iter_key_t =
typename std::iterator_traits<T>::value_type::first_type;
template <typename T>
using iter_val_t =
typename std::iterator_traits<T>::value_type::second_type;
template <typename T>
using iter_to_alloc_t =
typename std::pair<iter_key_t<T> const, iter_val_t<T> >;
#endif
} // namespace detail
} // namespace unordered
} // namespace boost
#endif // BOOST_UNORDERED_DETAIL_TYPE_TRAITS_HPP
+75
View File
@@ -0,0 +1,75 @@
/* 32b/64b xmx mix function.
*
* Copyright 2022 Peter Dimov.
* Copyright 2022 Joaquin M Lopez Munoz.
* 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.
*/
#ifndef BOOST_UNORDERED_DETAIL_XMX_HPP
#define BOOST_UNORDERED_DETAIL_XMX_HPP
#include <boost/cstdint.hpp>
#include <climits>
#include <cstddef>
namespace boost{
namespace unordered{
namespace detail{
/* Bit mixer for improvement of statistical properties of hash functions.
* The implementation is different on 64bit and 32bit architectures:
*
* - 64bit: same as xmx function in
* http://jonkagstrom.com/bit-mixer-construction/index.html
* - 32bit: generated by Hash Function Prospector
* (https://github.com/skeeto/hash-prospector) and selected as the
* best overall performer in benchmarks of Boost.Unordered flat containers.
* Score assigned by Hash Prospector: 333.7934929677524
*/
#if defined(SIZE_MAX)
#if ((((SIZE_MAX >> 16) >> 16) >> 16) >> 15) != 0
#define BOOST_UNORDERED_64B_ARCHITECTURE /* >64 bits assumed as 64 bits */
#endif
#elif defined(UINTPTR_MAX) /* used as proxy for std::size_t */
#if ((((UINTPTR_MAX >> 16) >> 16) >> 16) >> 15) != 0
#define BOOST_UNORDERED_64B_ARCHITECTURE
#endif
#endif
static inline std::size_t xmx(std::size_t x)noexcept
{
#if defined(BOOST_UNORDERED_64B_ARCHITECTURE)
boost::uint64_t z=(boost::uint64_t)x;
z^=z>>23;
z*=0xff51afd7ed558ccdull;
z^=z>>23;
return (std::size_t)z;
#else /* 32 bits assumed */
x^=x>>18;
x*=0x56b5aaadu;
x^=x>>16;
return x;
#endif
}
#ifdef BOOST_UNORDERED_64B_ARCHITECTURE
#undef BOOST_UNORDERED_64B_ARCHITECTURE
#endif
} /* namespace detail */
} /* namespace unordered */
} /* namespace boost */
#endif
+44
View File
@@ -0,0 +1,44 @@
/* Hash function characterization.
*
* Copyright 2022 Joaquin M Lopez Munoz.
* 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.
*/
#ifndef BOOST_UNORDERED_HASH_TRAITS_HPP
#define BOOST_UNORDERED_HASH_TRAITS_HPP
#include <boost/unordered/detail/type_traits.hpp>
namespace boost{
namespace unordered{
namespace detail{
template<typename Hash,typename=void>
struct hash_is_avalanching_impl: std::false_type{};
template<typename Hash>
struct hash_is_avalanching_impl<Hash,
boost::unordered::detail::void_t<typename Hash::is_avalanching> >:
std::true_type{};
} /* namespace detail */
/* Each trait can be partially specialized by users for concrete hash functions
* when actual characterization differs from default.
*/
/* hash_is_avalanching<Hash>::value is true when the type Hash::is_avalanching
* is present, false otherwise.
*/
template<typename Hash>
struct hash_is_avalanching: detail::hash_is_avalanching_impl<Hash>::type{};
} /* namespace unordered */
} /* namespace boost */
#endif
+800
View File
@@ -0,0 +1,800 @@
// 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)
#ifndef BOOST_UNORDERED_UNORDERED_FLAT_MAP_HPP_INCLUDED
#define BOOST_UNORDERED_UNORDERED_FLAT_MAP_HPP_INCLUDED
#include <boost/config.hpp>
#if defined(BOOST_HAS_PRAGMA_ONCE)
#pragma once
#endif
#include <boost/unordered/concurrent_flat_map_fwd.hpp>
#include <boost/unordered/detail/foa/flat_map_types.hpp>
#include <boost/unordered/detail/foa/table.hpp>
#include <boost/unordered/detail/serialize_container.hpp>
#include <boost/unordered/detail/type_traits.hpp>
#include <boost/unordered/unordered_flat_map_fwd.hpp>
#include <boost/core/allocator_access.hpp>
#include <boost/container_hash/hash.hpp>
#include <boost/throw_exception.hpp>
#include <initializer_list>
#include <iterator>
#include <stdexcept>
#include <type_traits>
#include <utility>
namespace boost {
namespace unordered {
#if defined(BOOST_MSVC)
#pragma warning(push)
#pragma warning(disable : 4714) /* marked as __forceinline not inlined */
#endif
template <class Key, class T, class Hash, class KeyEqual, class Allocator>
class unordered_flat_map
{
template <class Key2, class T2, class Hash2, class Pred2,
class Allocator2>
friend class concurrent_flat_map;
using map_types = detail::foa::flat_map_types<Key, T>;
using table_type = detail::foa::table<map_types, Hash, KeyEqual,
typename boost::allocator_rebind<Allocator,
typename map_types::value_type>::type>;
table_type table_;
template <class K, class V, class H, class KE, class A>
bool friend operator==(unordered_flat_map<K, V, H, KE, A> const& lhs,
unordered_flat_map<K, V, H, KE, A> const& rhs);
template <class K, class V, class H, class KE, class A, class Pred>
typename unordered_flat_map<K, V, H, KE, A>::size_type friend erase_if(
unordered_flat_map<K, V, H, KE, A>& set, Pred pred);
public:
using key_type = Key;
using mapped_type = T;
using value_type = typename map_types::value_type;
using init_type = typename map_types::init_type;
using size_type = std::size_t;
using difference_type = std::ptrdiff_t;
using hasher = typename boost::unordered::detail::type_identity<Hash>::type;
using key_equal = typename boost::unordered::detail::type_identity<KeyEqual>::type;
using allocator_type = typename boost::unordered::detail::type_identity<Allocator>::type;
using reference = value_type&;
using const_reference = value_type const&;
using pointer = typename boost::allocator_pointer<allocator_type>::type;
using const_pointer =
typename boost::allocator_const_pointer<allocator_type>::type;
using iterator = typename table_type::iterator;
using const_iterator = typename table_type::const_iterator;
unordered_flat_map() : unordered_flat_map(0) {}
explicit unordered_flat_map(size_type n, hasher const& h = hasher(),
key_equal const& pred = key_equal(),
allocator_type const& a = allocator_type())
: table_(n, h, pred, a)
{
}
unordered_flat_map(size_type n, allocator_type const& a)
: unordered_flat_map(n, hasher(), key_equal(), a)
{
}
unordered_flat_map(size_type n, hasher const& h, allocator_type const& a)
: unordered_flat_map(n, h, key_equal(), a)
{
}
template <class InputIterator>
unordered_flat_map(
InputIterator f, InputIterator l, allocator_type const& a)
: unordered_flat_map(f, l, size_type(0), hasher(), key_equal(), a)
{
}
explicit unordered_flat_map(allocator_type const& a)
: unordered_flat_map(0, a)
{
}
template <class Iterator>
unordered_flat_map(Iterator first, Iterator last, size_type n = 0,
hasher const& h = hasher(), key_equal const& pred = key_equal(),
allocator_type const& a = allocator_type())
: unordered_flat_map(n, h, pred, a)
{
this->insert(first, last);
}
template <class Iterator>
unordered_flat_map(
Iterator first, Iterator last, size_type n, allocator_type const& a)
: unordered_flat_map(first, last, n, hasher(), key_equal(), a)
{
}
template <class Iterator>
unordered_flat_map(Iterator first, Iterator last, size_type n,
hasher const& h, allocator_type const& a)
: unordered_flat_map(first, last, n, h, key_equal(), a)
{
}
unordered_flat_map(unordered_flat_map const& other) : table_(other.table_)
{
}
unordered_flat_map(
unordered_flat_map const& other, allocator_type const& a)
: table_(other.table_, a)
{
}
unordered_flat_map(unordered_flat_map&& other)
noexcept(std::is_nothrow_move_constructible<table_type>::value)
: table_(std::move(other.table_))
{
}
unordered_flat_map(unordered_flat_map&& other, allocator_type const& al)
: table_(std::move(other.table_), al)
{
}
unordered_flat_map(std::initializer_list<value_type> ilist,
size_type n = 0, hasher const& h = hasher(),
key_equal const& pred = key_equal(),
allocator_type const& a = allocator_type())
: unordered_flat_map(ilist.begin(), ilist.end(), n, h, pred, a)
{
}
unordered_flat_map(
std::initializer_list<value_type> il, allocator_type const& a)
: unordered_flat_map(il, size_type(0), hasher(), key_equal(), a)
{
}
unordered_flat_map(std::initializer_list<value_type> init, size_type n,
allocator_type const& a)
: unordered_flat_map(init, n, hasher(), key_equal(), a)
{
}
unordered_flat_map(std::initializer_list<value_type> init, size_type n,
hasher const& h, allocator_type const& a)
: unordered_flat_map(init, n, h, key_equal(), a)
{
}
unordered_flat_map(
concurrent_flat_map<Key, T, Hash, KeyEqual, Allocator>&& other)
: table_(std::move(other.table_))
{
}
~unordered_flat_map() = default;
unordered_flat_map& operator=(unordered_flat_map const& other)
{
table_ = other.table_;
return *this;
}
unordered_flat_map& operator=(unordered_flat_map&& other) noexcept(
noexcept(std::declval<table_type&>() = std::declval<table_type&&>()))
{
table_ = std::move(other.table_);
return *this;
}
allocator_type get_allocator() const noexcept
{
return table_.get_allocator();
}
/// Iterators
///
iterator begin() noexcept { return table_.begin(); }
const_iterator begin() const noexcept { return table_.begin(); }
const_iterator cbegin() const noexcept { return table_.cbegin(); }
iterator end() noexcept { return table_.end(); }
const_iterator end() const noexcept { return table_.end(); }
const_iterator cend() const noexcept { return table_.cend(); }
/// Capacity
///
BOOST_ATTRIBUTE_NODISCARD bool empty() const noexcept
{
return table_.empty();
}
size_type size() const noexcept { return table_.size(); }
size_type max_size() const noexcept { return table_.max_size(); }
/// Modifiers
///
void clear() noexcept { table_.clear(); }
template <class Ty>
BOOST_FORCEINLINE auto insert(Ty&& value)
-> decltype(table_.insert(std::forward<Ty>(value)))
{
return table_.insert(std::forward<Ty>(value));
}
BOOST_FORCEINLINE std::pair<iterator, bool> insert(init_type&& value)
{
return table_.insert(std::move(value));
}
template <class Ty>
BOOST_FORCEINLINE auto insert(const_iterator, Ty&& value)
-> decltype(table_.insert(std::forward<Ty>(value)).first)
{
return table_.insert(std::forward<Ty>(value)).first;
}
BOOST_FORCEINLINE iterator insert(const_iterator, init_type&& value)
{
return table_.insert(std::move(value)).first;
}
template <class InputIterator>
BOOST_FORCEINLINE void insert(InputIterator first, InputIterator last)
{
for (auto pos = first; pos != last; ++pos) {
table_.emplace(*pos);
}
}
void insert(std::initializer_list<value_type> ilist)
{
this->insert(ilist.begin(), ilist.end());
}
template <class M>
std::pair<iterator, bool> insert_or_assign(key_type const& key, M&& obj)
{
auto ibp = table_.try_emplace(key, std::forward<M>(obj));
if (ibp.second) {
return ibp;
}
ibp.first->second = std::forward<M>(obj);
return ibp;
}
template <class M>
std::pair<iterator, bool> insert_or_assign(key_type&& key, M&& obj)
{
auto ibp = table_.try_emplace(std::move(key), std::forward<M>(obj));
if (ibp.second) {
return ibp;
}
ibp.first->second = std::forward<M>(obj);
return ibp;
}
template <class K, class M>
typename std::enable_if<
boost::unordered::detail::are_transparent<K, hasher, key_equal>::value,
std::pair<iterator, bool> >::type
insert_or_assign(K&& k, M&& obj)
{
auto ibp = table_.try_emplace(std::forward<K>(k), std::forward<M>(obj));
if (ibp.second) {
return ibp;
}
ibp.first->second = std::forward<M>(obj);
return ibp;
}
template <class M>
iterator insert_or_assign(const_iterator, key_type const& key, M&& obj)
{
return this->insert_or_assign(key, std::forward<M>(obj)).first;
}
template <class M>
iterator insert_or_assign(const_iterator, key_type&& key, M&& obj)
{
return this->insert_or_assign(std::move(key), std::forward<M>(obj))
.first;
}
template <class K, class M>
typename std::enable_if<
boost::unordered::detail::are_transparent<K, hasher, key_equal>::value,
iterator>::type
insert_or_assign(const_iterator, K&& k, M&& obj)
{
return this->insert_or_assign(std::forward<K>(k), std::forward<M>(obj))
.first;
}
template <class... Args>
BOOST_FORCEINLINE std::pair<iterator, bool> emplace(Args&&... args)
{
return table_.emplace(std::forward<Args>(args)...);
}
template <class... Args>
BOOST_FORCEINLINE iterator emplace_hint(const_iterator, Args&&... args)
{
return table_.emplace(std::forward<Args>(args)...).first;
}
template <class... Args>
BOOST_FORCEINLINE std::pair<iterator, bool> try_emplace(
key_type const& key, Args&&... args)
{
return table_.try_emplace(key, std::forward<Args>(args)...);
}
template <class... Args>
BOOST_FORCEINLINE std::pair<iterator, bool> try_emplace(
key_type&& key, Args&&... args)
{
return table_.try_emplace(std::move(key), std::forward<Args>(args)...);
}
template <class K, class... Args>
BOOST_FORCEINLINE typename std::enable_if<
boost::unordered::detail::transparent_non_iterable<K,
unordered_flat_map>::value,
std::pair<iterator, bool> >::type
try_emplace(K&& key, Args&&... args)
{
return table_.try_emplace(
std::forward<K>(key), std::forward<Args>(args)...);
}
template <class... Args>
BOOST_FORCEINLINE iterator try_emplace(
const_iterator, key_type const& key, Args&&... args)
{
return table_.try_emplace(key, std::forward<Args>(args)...).first;
}
template <class... Args>
BOOST_FORCEINLINE iterator try_emplace(
const_iterator, key_type&& key, Args&&... args)
{
return table_.try_emplace(std::move(key), std::forward<Args>(args)...)
.first;
}
template <class K, class... Args>
BOOST_FORCEINLINE typename std::enable_if<
boost::unordered::detail::transparent_non_iterable<K,
unordered_flat_map>::value,
iterator>::type
try_emplace(const_iterator, K&& key, Args&&... args)
{
return table_
.try_emplace(std::forward<K>(key), std::forward<Args>(args)...)
.first;
}
BOOST_FORCEINLINE typename table_type::erase_return_type erase(
iterator pos)
{
return table_.erase(pos);
}
BOOST_FORCEINLINE typename table_type::erase_return_type erase(
const_iterator pos)
{
return table_.erase(pos);
}
iterator erase(const_iterator first, const_iterator last)
{
while (first != last) {
this->erase(first++);
}
return iterator{detail::foa::const_iterator_cast_tag{}, last};
}
BOOST_FORCEINLINE size_type erase(key_type const& key)
{
return table_.erase(key);
}
template <class K>
BOOST_FORCEINLINE typename std::enable_if<
detail::transparent_non_iterable<K, unordered_flat_map>::value,
size_type>::type
erase(K const& key)
{
return table_.erase(key);
}
void swap(unordered_flat_map& rhs) noexcept(
noexcept(std::declval<table_type&>().swap(std::declval<table_type&>())))
{
table_.swap(rhs.table_);
}
template <class H2, class P2>
void merge(
unordered_flat_map<key_type, mapped_type, H2, P2, allocator_type>&
source)
{
table_.merge(source.table_);
}
template <class H2, class P2>
void merge(
unordered_flat_map<key_type, mapped_type, H2, P2, allocator_type>&&
source)
{
table_.merge(std::move(source.table_));
}
/// Lookup
///
mapped_type& at(key_type const& key)
{
auto pos = table_.find(key);
if (pos != table_.end()) {
return pos->second;
}
// TODO: someday refactor this to conditionally serialize the key and
// include it in the error message
//
boost::throw_exception(
std::out_of_range("key was not found in unordered_flat_map"));
}
mapped_type const& at(key_type const& key) const
{
auto pos = table_.find(key);
if (pos != table_.end()) {
return pos->second;
}
boost::throw_exception(
std::out_of_range("key was not found in unordered_flat_map"));
}
template <class K>
typename std::enable_if<
boost::unordered::detail::are_transparent<K, hasher, key_equal>::value,
mapped_type&>::type
at(K&& key)
{
auto pos = table_.find(std::forward<K>(key));
if (pos != table_.end()) {
return pos->second;
}
boost::throw_exception(
std::out_of_range("key was not found in unordered_flat_map"));
}
template <class K>
typename std::enable_if<
boost::unordered::detail::are_transparent<K, hasher, key_equal>::value,
mapped_type const&>::type
at(K&& key) const
{
auto pos = table_.find(std::forward<K>(key));
if (pos != table_.end()) {
return pos->second;
}
boost::throw_exception(
std::out_of_range("key was not found in unordered_flat_map"));
}
BOOST_FORCEINLINE mapped_type& operator[](key_type const& key)
{
return table_.try_emplace(key).first->second;
}
BOOST_FORCEINLINE mapped_type& operator[](key_type&& key)
{
return table_.try_emplace(std::move(key)).first->second;
}
template <class K>
typename std::enable_if<
boost::unordered::detail::are_transparent<K, hasher, key_equal>::value,
mapped_type&>::type
operator[](K&& key)
{
return table_.try_emplace(std::forward<K>(key)).first->second;
}
BOOST_FORCEINLINE size_type count(key_type const& key) const
{
auto pos = table_.find(key);
return pos != table_.end() ? 1 : 0;
}
template <class K>
BOOST_FORCEINLINE typename std::enable_if<
detail::are_transparent<K, hasher, key_equal>::value, size_type>::type
count(K const& key) const
{
auto pos = table_.find(key);
return pos != table_.end() ? 1 : 0;
}
BOOST_FORCEINLINE iterator find(key_type const& key)
{
return table_.find(key);
}
BOOST_FORCEINLINE const_iterator find(key_type const& key) const
{
return table_.find(key);
}
template <class K>
BOOST_FORCEINLINE typename std::enable_if<
boost::unordered::detail::are_transparent<K, hasher, key_equal>::value,
iterator>::type
find(K const& key)
{
return table_.find(key);
}
template <class K>
BOOST_FORCEINLINE typename std::enable_if<
boost::unordered::detail::are_transparent<K, hasher, key_equal>::value,
const_iterator>::type
find(K const& key) const
{
return table_.find(key);
}
BOOST_FORCEINLINE bool contains(key_type const& key) const
{
return this->find(key) != this->end();
}
template <class K>
BOOST_FORCEINLINE typename std::enable_if<
boost::unordered::detail::are_transparent<K, hasher, key_equal>::value,
bool>::type
contains(K const& key) const
{
return this->find(key) != this->end();
}
std::pair<iterator, iterator> equal_range(key_type const& key)
{
auto pos = table_.find(key);
if (pos == table_.end()) {
return {pos, pos};
}
auto next = pos;
++next;
return {pos, next};
}
std::pair<const_iterator, const_iterator> equal_range(
key_type const& key) const
{
auto pos = table_.find(key);
if (pos == table_.end()) {
return {pos, pos};
}
auto next = pos;
++next;
return {pos, next};
}
template <class K>
typename std::enable_if<
detail::are_transparent<K, hasher, key_equal>::value,
std::pair<iterator, iterator> >::type
equal_range(K const& key)
{
auto pos = table_.find(key);
if (pos == table_.end()) {
return {pos, pos};
}
auto next = pos;
++next;
return {pos, next};
}
template <class K>
typename std::enable_if<
detail::are_transparent<K, hasher, key_equal>::value,
std::pair<const_iterator, const_iterator> >::type
equal_range(K const& key) const
{
auto pos = table_.find(key);
if (pos == table_.end()) {
return {pos, pos};
}
auto next = pos;
++next;
return {pos, next};
}
/// Hash Policy
///
size_type bucket_count() const noexcept { return table_.capacity(); }
float load_factor() const noexcept { return table_.load_factor(); }
float max_load_factor() const noexcept
{
return table_.max_load_factor();
}
void max_load_factor(float) {}
size_type max_load() const noexcept { return table_.max_load(); }
void rehash(size_type n) { table_.rehash(n); }
void reserve(size_type n) { table_.reserve(n); }
/// Observers
///
hasher hash_function() const { return table_.hash_function(); }
key_equal key_eq() const { return table_.key_eq(); }
};
template <class Key, class T, class Hash, class KeyEqual, class Allocator>
bool operator==(
unordered_flat_map<Key, T, Hash, KeyEqual, Allocator> const& lhs,
unordered_flat_map<Key, T, Hash, KeyEqual, Allocator> const& rhs)
{
return lhs.table_ == rhs.table_;
}
template <class Key, class T, class Hash, class KeyEqual, class Allocator>
bool operator!=(
unordered_flat_map<Key, T, Hash, KeyEqual, Allocator> const& lhs,
unordered_flat_map<Key, T, Hash, KeyEqual, Allocator> const& rhs)
{
return !(lhs == rhs);
}
template <class Key, class T, class Hash, class KeyEqual, class Allocator>
void swap(unordered_flat_map<Key, T, Hash, KeyEqual, Allocator>& lhs,
unordered_flat_map<Key, T, Hash, KeyEqual, Allocator>& rhs)
noexcept(noexcept(lhs.swap(rhs)))
{
lhs.swap(rhs);
}
template <class Key, class T, class Hash, class KeyEqual, class Allocator,
class Pred>
typename unordered_flat_map<Key, T, Hash, KeyEqual, Allocator>::size_type
erase_if(
unordered_flat_map<Key, T, Hash, KeyEqual, Allocator>& map, Pred pred)
{
return erase_if(map.table_, pred);
}
template <class Archive, class Key, class T, class Hash, class KeyEqual,
class Allocator>
void serialize(Archive& ar,
unordered_flat_map<Key, T, Hash, KeyEqual, Allocator>& map,
unsigned int version)
{
detail::serialize_container(ar, map, version);
}
#if defined(BOOST_MSVC)
#pragma warning(pop) /* C4714 */
#endif
#if BOOST_UNORDERED_TEMPLATE_DEDUCTION_GUIDES
template <class InputIterator,
class Hash =
boost::hash<boost::unordered::detail::iter_key_t<InputIterator> >,
class Pred =
std::equal_to<boost::unordered::detail::iter_key_t<InputIterator> >,
class Allocator = std::allocator<
boost::unordered::detail::iter_to_alloc_t<InputIterator> >,
class = std::enable_if_t<detail::is_input_iterator_v<InputIterator> >,
class = std::enable_if_t<detail::is_hash_v<Hash> >,
class = std::enable_if_t<detail::is_pred_v<Pred> >,
class = std::enable_if_t<detail::is_allocator_v<Allocator> > >
unordered_flat_map(InputIterator, InputIterator,
std::size_t = boost::unordered::detail::foa::default_bucket_count,
Hash = Hash(), Pred = Pred(), Allocator = Allocator())
-> unordered_flat_map<boost::unordered::detail::iter_key_t<InputIterator>,
boost::unordered::detail::iter_val_t<InputIterator>, Hash, Pred,
Allocator>;
template <class Key, class T,
class Hash = boost::hash<std::remove_const_t<Key> >,
class Pred = std::equal_to<std::remove_const_t<Key> >,
class Allocator = std::allocator<std::pair<const Key, T> >,
class = std::enable_if_t<detail::is_hash_v<Hash> >,
class = std::enable_if_t<detail::is_pred_v<Pred> >,
class = std::enable_if_t<detail::is_allocator_v<Allocator> > >
unordered_flat_map(std::initializer_list<std::pair<Key, T> >,
std::size_t = boost::unordered::detail::foa::default_bucket_count,
Hash = Hash(), Pred = Pred(), Allocator = Allocator())
-> unordered_flat_map<std::remove_const_t<Key>, T, Hash, Pred,
Allocator>;
template <class InputIterator, class Allocator,
class = std::enable_if_t<detail::is_input_iterator_v<InputIterator> >,
class = std::enable_if_t<detail::is_allocator_v<Allocator> > >
unordered_flat_map(InputIterator, InputIterator, std::size_t, Allocator)
-> unordered_flat_map<boost::unordered::detail::iter_key_t<InputIterator>,
boost::unordered::detail::iter_val_t<InputIterator>,
boost::hash<boost::unordered::detail::iter_key_t<InputIterator> >,
std::equal_to<boost::unordered::detail::iter_key_t<InputIterator> >,
Allocator>;
template <class InputIterator, class Allocator,
class = std::enable_if_t<detail::is_input_iterator_v<InputIterator> >,
class = std::enable_if_t<detail::is_allocator_v<Allocator> > >
unordered_flat_map(InputIterator, InputIterator, Allocator)
-> unordered_flat_map<boost::unordered::detail::iter_key_t<InputIterator>,
boost::unordered::detail::iter_val_t<InputIterator>,
boost::hash<boost::unordered::detail::iter_key_t<InputIterator> >,
std::equal_to<boost::unordered::detail::iter_key_t<InputIterator> >,
Allocator>;
template <class InputIterator, class Hash, class Allocator,
class = std::enable_if_t<detail::is_hash_v<Hash> >,
class = std::enable_if_t<detail::is_input_iterator_v<InputIterator> >,
class = std::enable_if_t<detail::is_allocator_v<Allocator> > >
unordered_flat_map(
InputIterator, InputIterator, std::size_t, Hash, Allocator)
-> unordered_flat_map<boost::unordered::detail::iter_key_t<InputIterator>,
boost::unordered::detail::iter_val_t<InputIterator>, Hash,
std::equal_to<boost::unordered::detail::iter_key_t<InputIterator> >,
Allocator>;
template <class Key, class T, class Allocator,
class = std::enable_if_t<detail::is_allocator_v<Allocator> > >
unordered_flat_map(std::initializer_list<std::pair<Key, T> >, std::size_t,
Allocator) -> unordered_flat_map<std::remove_const_t<Key>, T,
boost::hash<std::remove_const_t<Key> >,
std::equal_to<std::remove_const_t<Key> >, Allocator>;
template <class Key, class T, class Allocator,
class = std::enable_if_t<detail::is_allocator_v<Allocator> > >
unordered_flat_map(std::initializer_list<std::pair<Key, T> >, Allocator)
-> unordered_flat_map<std::remove_const_t<Key>, T,
boost::hash<std::remove_const_t<Key> >,
std::equal_to<std::remove_const_t<Key> >, Allocator>;
template <class Key, class T, class Hash, class Allocator,
class = std::enable_if_t<detail::is_hash_v<Hash> >,
class = std::enable_if_t<detail::is_allocator_v<Allocator> > >
unordered_flat_map(std::initializer_list<std::pair<Key, T> >, std::size_t,
Hash, Allocator) -> unordered_flat_map<std::remove_const_t<Key>, T,
Hash, std::equal_to<std::remove_const_t<Key> >, Allocator>;
#endif
} // namespace unordered
} // namespace boost
#endif
+44
View File
@@ -0,0 +1,44 @@
// Copyright (C) 2022 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)
#ifndef BOOST_UNORDERED_FLAT_MAP_FWD_HPP_INCLUDED
#define BOOST_UNORDERED_FLAT_MAP_FWD_HPP_INCLUDED
#include <boost/config.hpp>
#if defined(BOOST_HAS_PRAGMA_ONCE)
#pragma once
#endif
#include <boost/container_hash/hash_fwd.hpp>
#include <functional>
#include <memory>
namespace boost {
namespace unordered {
template <class Key, class T, class Hash = boost::hash<Key>,
class KeyEqual = std::equal_to<Key>,
class Allocator = std::allocator<std::pair<const Key, T> > >
class unordered_flat_map;
template <class Key, class T, class Hash, class KeyEqual, class Allocator>
bool operator==(
unordered_flat_map<Key, T, Hash, KeyEqual, Allocator> const& lhs,
unordered_flat_map<Key, T, Hash, KeyEqual, Allocator> const& rhs);
template <class Key, class T, class Hash, class KeyEqual, class Allocator>
bool operator!=(
unordered_flat_map<Key, T, Hash, KeyEqual, Allocator> const& lhs,
unordered_flat_map<Key, T, Hash, KeyEqual, Allocator> const& rhs);
template <class Key, class T, class Hash, class KeyEqual, class Allocator>
void swap(unordered_flat_map<Key, T, Hash, KeyEqual, Allocator>& lhs,
unordered_flat_map<Key, T, Hash, KeyEqual, Allocator>& rhs)
noexcept(noexcept(lhs.swap(rhs)));
} // namespace unordered
using boost::unordered::unordered_flat_map;
} // namespace boost
#endif
+610
View File
@@ -0,0 +1,610 @@
// 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)
#ifndef BOOST_UNORDERED_UNORDERED_FLAT_SET_HPP_INCLUDED
#define BOOST_UNORDERED_UNORDERED_FLAT_SET_HPP_INCLUDED
#include <boost/config.hpp>
#if defined(BOOST_HAS_PRAGMA_ONCE)
#pragma once
#endif
#include <boost/unordered/concurrent_flat_set_fwd.hpp>
#include <boost/unordered/detail/foa/flat_set_types.hpp>
#include <boost/unordered/detail/foa/table.hpp>
#include <boost/unordered/detail/serialize_container.hpp>
#include <boost/unordered/detail/type_traits.hpp>
#include <boost/unordered/unordered_flat_set_fwd.hpp>
#include <boost/core/allocator_access.hpp>
#include <boost/container_hash/hash.hpp>
#include <initializer_list>
#include <iterator>
#include <type_traits>
#include <utility>
namespace boost {
namespace unordered {
#if defined(BOOST_MSVC)
#pragma warning(push)
#pragma warning(disable : 4714) /* marked as __forceinline not inlined */
#endif
template <class Key, class Hash, class KeyEqual, class Allocator>
class unordered_flat_set
{
template <class Key2, class Hash2, class KeyEqual2, class Allocator2>
friend class concurrent_flat_set;
using set_types = detail::foa::flat_set_types<Key>;
using table_type = detail::foa::table<set_types, Hash, KeyEqual,
typename boost::allocator_rebind<Allocator,
typename set_types::value_type>::type>;
table_type table_;
template <class K, class H, class KE, class A>
bool friend operator==(unordered_flat_set<K, H, KE, A> const& lhs,
unordered_flat_set<K, H, KE, A> const& rhs);
template <class K, class H, class KE, class A, class Pred>
typename unordered_flat_set<K, H, KE, A>::size_type friend erase_if(
unordered_flat_set<K, H, KE, A>& set, Pred pred);
public:
using key_type = Key;
using value_type = typename set_types::value_type;
using init_type = typename set_types::init_type;
using size_type = std::size_t;
using difference_type = std::ptrdiff_t;
using hasher = Hash;
using key_equal = KeyEqual;
using allocator_type = Allocator;
using reference = value_type&;
using const_reference = value_type const&;
using pointer = typename boost::allocator_pointer<allocator_type>::type;
using const_pointer =
typename boost::allocator_const_pointer<allocator_type>::type;
using iterator = typename table_type::iterator;
using const_iterator = typename table_type::const_iterator;
unordered_flat_set() : unordered_flat_set(0) {}
explicit unordered_flat_set(size_type n, hasher const& h = hasher(),
key_equal const& pred = key_equal(),
allocator_type const& a = allocator_type())
: table_(n, h, pred, a)
{
}
unordered_flat_set(size_type n, allocator_type const& a)
: unordered_flat_set(n, hasher(), key_equal(), a)
{
}
unordered_flat_set(size_type n, hasher const& h, allocator_type const& a)
: unordered_flat_set(n, h, key_equal(), a)
{
}
template <class InputIterator>
unordered_flat_set(
InputIterator f, InputIterator l, allocator_type const& a)
: unordered_flat_set(f, l, size_type(0), hasher(), key_equal(), a)
{
}
explicit unordered_flat_set(allocator_type const& a)
: unordered_flat_set(0, a)
{
}
template <class Iterator>
unordered_flat_set(Iterator first, Iterator last, size_type n = 0,
hasher const& h = hasher(), key_equal const& pred = key_equal(),
allocator_type const& a = allocator_type())
: unordered_flat_set(n, h, pred, a)
{
this->insert(first, last);
}
template <class InputIt>
unordered_flat_set(
InputIt first, InputIt last, size_type n, allocator_type const& a)
: unordered_flat_set(first, last, n, hasher(), key_equal(), a)
{
}
template <class Iterator>
unordered_flat_set(Iterator first, Iterator last, size_type n,
hasher const& h, allocator_type const& a)
: unordered_flat_set(first, last, n, h, key_equal(), a)
{
}
unordered_flat_set(unordered_flat_set const& other) : table_(other.table_)
{
}
unordered_flat_set(
unordered_flat_set const& other, allocator_type const& a)
: table_(other.table_, a)
{
}
unordered_flat_set(unordered_flat_set&& other)
noexcept(std::is_nothrow_move_constructible<table_type>::value)
: table_(std::move(other.table_))
{
}
unordered_flat_set(unordered_flat_set&& other, allocator_type const& al)
: table_(std::move(other.table_), al)
{
}
unordered_flat_set(std::initializer_list<value_type> ilist,
size_type n = 0, hasher const& h = hasher(),
key_equal const& pred = key_equal(),
allocator_type const& a = allocator_type())
: unordered_flat_set(ilist.begin(), ilist.end(), n, h, pred, a)
{
}
unordered_flat_set(
std::initializer_list<value_type> il, allocator_type const& a)
: unordered_flat_set(il, size_type(0), hasher(), key_equal(), a)
{
}
unordered_flat_set(std::initializer_list<value_type> init, size_type n,
allocator_type const& a)
: unordered_flat_set(init, n, hasher(), key_equal(), a)
{
}
unordered_flat_set(std::initializer_list<value_type> init, size_type n,
hasher const& h, allocator_type const& a)
: unordered_flat_set(init, n, h, key_equal(), a)
{
}
unordered_flat_set(
concurrent_flat_set<Key, Hash, KeyEqual, Allocator>&& other)
: table_(std::move(other.table_))
{
}
~unordered_flat_set() = default;
unordered_flat_set& operator=(unordered_flat_set const& other)
{
table_ = other.table_;
return *this;
}
unordered_flat_set& operator=(unordered_flat_set&& other) noexcept(
noexcept(std::declval<table_type&>() = std::declval<table_type&&>()))
{
table_ = std::move(other.table_);
return *this;
}
allocator_type get_allocator() const noexcept
{
return table_.get_allocator();
}
/// Iterators
///
iterator begin() noexcept { return table_.begin(); }
const_iterator begin() const noexcept { return table_.begin(); }
const_iterator cbegin() const noexcept { return table_.cbegin(); }
iterator end() noexcept { return table_.end(); }
const_iterator end() const noexcept { return table_.end(); }
const_iterator cend() const noexcept { return table_.cend(); }
/// Capacity
///
BOOST_ATTRIBUTE_NODISCARD bool empty() const noexcept
{
return table_.empty();
}
size_type size() const noexcept { return table_.size(); }
size_type max_size() const noexcept { return table_.max_size(); }
/// Modifiers
///
void clear() noexcept { table_.clear(); }
BOOST_FORCEINLINE std::pair<iterator, bool> insert(
value_type const& value)
{
return table_.insert(value);
}
BOOST_FORCEINLINE std::pair<iterator, bool> insert(value_type&& value)
{
return table_.insert(std::move(value));
}
template <class K>
BOOST_FORCEINLINE typename std::enable_if<
detail::transparent_non_iterable<K, unordered_flat_set>::value,
std::pair<iterator, bool> >::type
insert(K&& k)
{
return table_.try_emplace(std::forward<K>(k));
}
BOOST_FORCEINLINE iterator insert(const_iterator, value_type const& value)
{
return table_.insert(value).first;
}
BOOST_FORCEINLINE iterator insert(const_iterator, value_type&& value)
{
return table_.insert(std::move(value)).first;
}
template <class K>
BOOST_FORCEINLINE typename std::enable_if<
detail::transparent_non_iterable<K, unordered_flat_set>::value,
iterator>::type
insert(const_iterator, K&& k)
{
return table_.try_emplace(std::forward<K>(k)).first;
}
template <class InputIterator>
void insert(InputIterator first, InputIterator last)
{
for (auto pos = first; pos != last; ++pos) {
table_.emplace(*pos);
}
}
void insert(std::initializer_list<value_type> ilist)
{
this->insert(ilist.begin(), ilist.end());
}
template <class... Args>
BOOST_FORCEINLINE std::pair<iterator, bool> emplace(Args&&... args)
{
return table_.emplace(std::forward<Args>(args)...);
}
template <class... Args>
BOOST_FORCEINLINE iterator emplace_hint(const_iterator, Args&&... args)
{
return table_.emplace(std::forward<Args>(args)...).first;
}
BOOST_FORCEINLINE typename table_type::erase_return_type erase(
const_iterator pos)
{
return table_.erase(pos);
}
iterator erase(const_iterator first, const_iterator last)
{
while (first != last) {
this->erase(first++);
}
return iterator{detail::foa::const_iterator_cast_tag{}, last};
}
BOOST_FORCEINLINE size_type erase(key_type const& key)
{
return table_.erase(key);
}
template <class K>
BOOST_FORCEINLINE typename std::enable_if<
detail::transparent_non_iterable<K, unordered_flat_set>::value,
size_type>::type
erase(K const& key)
{
return table_.erase(key);
}
void swap(unordered_flat_set& rhs) noexcept(
noexcept(std::declval<table_type&>().swap(std::declval<table_type&>())))
{
table_.swap(rhs.table_);
}
template <class H2, class P2>
void merge(unordered_flat_set<key_type, H2, P2, allocator_type>& source)
{
table_.merge(source.table_);
}
template <class H2, class P2>
void merge(unordered_flat_set<key_type, H2, P2, allocator_type>&& source)
{
table_.merge(std::move(source.table_));
}
/// Lookup
///
BOOST_FORCEINLINE size_type count(key_type const& key) const
{
auto pos = table_.find(key);
return pos != table_.end() ? 1 : 0;
}
template <class K>
BOOST_FORCEINLINE typename std::enable_if<
detail::are_transparent<K, hasher, key_equal>::value, size_type>::type
count(K const& key) const
{
auto pos = table_.find(key);
return pos != table_.end() ? 1 : 0;
}
BOOST_FORCEINLINE iterator find(key_type const& key)
{
return table_.find(key);
}
BOOST_FORCEINLINE const_iterator find(key_type const& key) const
{
return table_.find(key);
}
template <class K>
BOOST_FORCEINLINE typename std::enable_if<
boost::unordered::detail::are_transparent<K, hasher, key_equal>::value,
iterator>::type
find(K const& key)
{
return table_.find(key);
}
template <class K>
BOOST_FORCEINLINE typename std::enable_if<
boost::unordered::detail::are_transparent<K, hasher, key_equal>::value,
const_iterator>::type
find(K const& key) const
{
return table_.find(key);
}
BOOST_FORCEINLINE bool contains(key_type const& key) const
{
return this->find(key) != this->end();
}
template <class K>
BOOST_FORCEINLINE typename std::enable_if<
boost::unordered::detail::are_transparent<K, hasher, key_equal>::value,
bool>::type
contains(K const& key) const
{
return this->find(key) != this->end();
}
std::pair<iterator, iterator> equal_range(key_type const& key)
{
auto pos = table_.find(key);
if (pos == table_.end()) {
return {pos, pos};
}
auto next = pos;
++next;
return {pos, next};
}
std::pair<const_iterator, const_iterator> equal_range(
key_type const& key) const
{
auto pos = table_.find(key);
if (pos == table_.end()) {
return {pos, pos};
}
auto next = pos;
++next;
return {pos, next};
}
template <class K>
typename std::enable_if<
detail::are_transparent<K, hasher, key_equal>::value,
std::pair<iterator, iterator> >::type
equal_range(K const& key)
{
auto pos = table_.find(key);
if (pos == table_.end()) {
return {pos, pos};
}
auto next = pos;
++next;
return {pos, next};
}
template <class K>
typename std::enable_if<
detail::are_transparent<K, hasher, key_equal>::value,
std::pair<const_iterator, const_iterator> >::type
equal_range(K const& key) const
{
auto pos = table_.find(key);
if (pos == table_.end()) {
return {pos, pos};
}
auto next = pos;
++next;
return {pos, next};
}
/// Hash Policy
///
size_type bucket_count() const noexcept { return table_.capacity(); }
float load_factor() const noexcept { return table_.load_factor(); }
float max_load_factor() const noexcept
{
return table_.max_load_factor();
}
void max_load_factor(float) {}
size_type max_load() const noexcept { return table_.max_load(); }
void rehash(size_type n) { table_.rehash(n); }
void reserve(size_type n) { table_.reserve(n); }
/// Observers
///
hasher hash_function() const { return table_.hash_function(); }
key_equal key_eq() const { return table_.key_eq(); }
};
template <class Key, class Hash, class KeyEqual, class Allocator>
bool operator==(
unordered_flat_set<Key, Hash, KeyEqual, Allocator> const& lhs,
unordered_flat_set<Key, Hash, KeyEqual, Allocator> const& rhs)
{
return lhs.table_ == rhs.table_;
}
template <class Key, class Hash, class KeyEqual, class Allocator>
bool operator!=(
unordered_flat_set<Key, Hash, KeyEqual, Allocator> const& lhs,
unordered_flat_set<Key, Hash, KeyEqual, Allocator> const& rhs)
{
return !(lhs == rhs);
}
template <class Key, class Hash, class KeyEqual, class Allocator>
void swap(unordered_flat_set<Key, Hash, KeyEqual, Allocator>& lhs,
unordered_flat_set<Key, Hash, KeyEqual, Allocator>& rhs)
noexcept(noexcept(lhs.swap(rhs)))
{
lhs.swap(rhs);
}
template <class Key, class Hash, class KeyEqual, class Allocator,
class Pred>
typename unordered_flat_set<Key, Hash, KeyEqual, Allocator>::size_type
erase_if(unordered_flat_set<Key, Hash, KeyEqual, Allocator>& set, Pred pred)
{
return erase_if(set.table_, pred);
}
template <class Archive, class Key, class Hash, class KeyEqual,
class Allocator>
void serialize(Archive& ar,
unordered_flat_set<Key, Hash, KeyEqual, Allocator>& set,
unsigned int version)
{
detail::serialize_container(ar, set, version);
}
#if defined(BOOST_MSVC)
#pragma warning(pop) /* C4714 */
#endif
#if BOOST_UNORDERED_TEMPLATE_DEDUCTION_GUIDES
template <class InputIterator,
class Hash =
boost::hash<typename std::iterator_traits<InputIterator>::value_type>,
class Pred =
std::equal_to<typename std::iterator_traits<InputIterator>::value_type>,
class Allocator = std::allocator<
typename std::iterator_traits<InputIterator>::value_type>,
class = std::enable_if_t<detail::is_input_iterator_v<InputIterator> >,
class = std::enable_if_t<detail::is_hash_v<Hash> >,
class = std::enable_if_t<detail::is_pred_v<Pred> >,
class = std::enable_if_t<detail::is_allocator_v<Allocator> > >
unordered_flat_set(InputIterator, InputIterator,
std::size_t = boost::unordered::detail::foa::default_bucket_count,
Hash = Hash(), Pred = Pred(), Allocator = Allocator())
-> unordered_flat_set<
typename std::iterator_traits<InputIterator>::value_type, Hash, Pred,
Allocator>;
template <class T, class Hash = boost::hash<T>,
class Pred = std::equal_to<T>, class Allocator = std::allocator<T>,
class = std::enable_if_t<detail::is_hash_v<Hash> >,
class = std::enable_if_t<detail::is_pred_v<Pred> >,
class = std::enable_if_t<detail::is_allocator_v<Allocator> > >
unordered_flat_set(std::initializer_list<T>,
std::size_t = boost::unordered::detail::foa::default_bucket_count,
Hash = Hash(), Pred = Pred(), Allocator = Allocator())
-> unordered_flat_set<T, Hash, Pred, Allocator>;
template <class InputIterator, class Allocator,
class = std::enable_if_t<detail::is_input_iterator_v<InputIterator> >,
class = std::enable_if_t<detail::is_allocator_v<Allocator> > >
unordered_flat_set(InputIterator, InputIterator, std::size_t, Allocator)
-> unordered_flat_set<
typename std::iterator_traits<InputIterator>::value_type,
boost::hash<typename std::iterator_traits<InputIterator>::value_type>,
std::equal_to<typename std::iterator_traits<InputIterator>::value_type>,
Allocator>;
template <class InputIterator, class Hash, class Allocator,
class = std::enable_if_t<detail::is_hash_v<Hash> >,
class = std::enable_if_t<detail::is_input_iterator_v<InputIterator> >,
class = std::enable_if_t<detail::is_allocator_v<Allocator> > >
unordered_flat_set(
InputIterator, InputIterator, std::size_t, Hash, Allocator)
-> unordered_flat_set<
typename std::iterator_traits<InputIterator>::value_type, Hash,
std::equal_to<typename std::iterator_traits<InputIterator>::value_type>,
Allocator>;
template <class T, class Allocator,
class = std::enable_if_t<detail::is_allocator_v<Allocator> > >
unordered_flat_set(std::initializer_list<T>, std::size_t, Allocator)
-> unordered_flat_set<T, boost::hash<T>, std::equal_to<T>, Allocator>;
template <class T, class Hash, class Allocator,
class = std::enable_if_t<detail::is_hash_v<Hash> >,
class = std::enable_if_t<detail::is_allocator_v<Allocator> > >
unordered_flat_set(std::initializer_list<T>, std::size_t, Hash, Allocator)
-> unordered_flat_set<T, Hash, std::equal_to<T>, Allocator>;
template <class InputIterator, class Allocator,
class = std::enable_if_t<detail::is_input_iterator_v<InputIterator> >,
class = std::enable_if_t<detail::is_allocator_v<Allocator> > >
unordered_flat_set(InputIterator, InputIterator, Allocator)
-> unordered_flat_set<
typename std::iterator_traits<InputIterator>::value_type,
boost::hash<typename std::iterator_traits<InputIterator>::value_type>,
std::equal_to<typename std::iterator_traits<InputIterator>::value_type>,
Allocator>;
template <class T, class Allocator,
class = std::enable_if_t<detail::is_allocator_v<Allocator> > >
unordered_flat_set(std::initializer_list<T>, Allocator)
-> unordered_flat_set<T, boost::hash<T>, std::equal_to<T>, Allocator>;
#endif
} // namespace unordered
} // namespace boost
#endif
+44
View File
@@ -0,0 +1,44 @@
// Copyright (C) 2022 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)
#ifndef BOOST_UNORDERED_FLAT_SET_FWD_HPP_INCLUDED
#define BOOST_UNORDERED_FLAT_SET_FWD_HPP_INCLUDED
#include <boost/config.hpp>
#if defined(BOOST_HAS_PRAGMA_ONCE)
#pragma once
#endif
#include <boost/container_hash/hash_fwd.hpp>
#include <functional>
#include <memory>
namespace boost {
namespace unordered {
template <class Key, class Hash = boost::hash<Key>,
class KeyEqual = std::equal_to<Key>,
class Allocator = std::allocator<Key> >
class unordered_flat_set;
template <class Key, class Hash, class KeyEqual, class Allocator>
bool operator==(
unordered_flat_set<Key, Hash, KeyEqual, Allocator> const& lhs,
unordered_flat_set<Key, Hash, KeyEqual, Allocator> const& rhs);
template <class Key, class Hash, class KeyEqual, class Allocator>
bool operator!=(
unordered_flat_set<Key, Hash, KeyEqual, Allocator> const& lhs,
unordered_flat_set<Key, Hash, KeyEqual, Allocator> const& rhs);
template <class Key, class Hash, class KeyEqual, class Allocator>
void swap(unordered_flat_set<Key, Hash, KeyEqual, Allocator>& lhs,
unordered_flat_set<Key, Hash, KeyEqual, Allocator>& rhs)
noexcept(noexcept(lhs.swap(rhs)));
} // namespace unordered
using boost::unordered::unordered_flat_set;
} // namespace boost
#endif
File diff suppressed because it is too large Load Diff
+67
View File
@@ -0,0 +1,67 @@
// Copyright (C) 2008-2011 Daniel James.
// 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)
#ifndef BOOST_UNORDERED_MAP_FWD_HPP_INCLUDED
#define BOOST_UNORDERED_MAP_FWD_HPP_INCLUDED
#include <boost/config.hpp>
#if defined(BOOST_HAS_PRAGMA_ONCE)
#pragma once
#endif
#include <boost/container_hash/hash_fwd.hpp>
#include <functional>
#include <memory>
namespace boost {
namespace unordered {
template <class K, class T, class H = boost::hash<K>,
class P = std::equal_to<K>,
class A = std::allocator<std::pair<const K, T> > >
class unordered_map;
template <class K, class T, class H, class P, class A>
inline bool operator==(
unordered_map<K, T, H, P, A> const&, unordered_map<K, T, H, P, A> const&);
template <class K, class T, class H, class P, class A>
inline bool operator!=(
unordered_map<K, T, H, P, A> const&, unordered_map<K, T, H, P, A> const&);
template <class K, class T, class H, class P, class A>
inline void swap(unordered_map<K, T, H, P, A>& m1,
unordered_map<K, T, H, P, A>& m2) noexcept(noexcept(m1.swap(m2)));
template <class K, class T, class H, class P, class A, class Predicate>
typename unordered_map<K, T, H, P, A>::size_type erase_if(
unordered_map<K, T, H, P, A>& c, Predicate pred);
template <class K, class T, class H = boost::hash<K>,
class P = std::equal_to<K>,
class A = std::allocator<std::pair<const K, T> > >
class unordered_multimap;
template <class K, class T, class H, class P, class A>
inline bool operator==(unordered_multimap<K, T, H, P, A> const&,
unordered_multimap<K, T, H, P, A> const&);
template <class K, class T, class H, class P, class A>
inline bool operator!=(unordered_multimap<K, T, H, P, A> const&,
unordered_multimap<K, T, H, P, A> const&);
template <class K, class T, class H, class P, class A>
inline void swap(unordered_multimap<K, T, H, P, A>& m1,
unordered_multimap<K, T, H, P, A>& m2) noexcept(noexcept(m1.swap(m2)));
template <class K, class T, class H, class P, class A, class Predicate>
typename unordered_multimap<K, T, H, P, A>::size_type erase_if(
unordered_multimap<K, T, H, P, A>& c, Predicate pred);
template <class N, class K, class T, class A> class node_handle_map;
template <class Iter, class NodeType> struct insert_return_type_map;
} // namespace unordered
using boost::unordered::unordered_map;
using boost::unordered::unordered_multimap;
} // namespace boost
#endif
+895
View File
@@ -0,0 +1,895 @@
// 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)
#ifndef BOOST_UNORDERED_UNORDERED_NODE_MAP_HPP_INCLUDED
#define BOOST_UNORDERED_UNORDERED_NODE_MAP_HPP_INCLUDED
#include <boost/config.hpp>
#if defined(BOOST_HAS_PRAGMA_ONCE)
#pragma once
#endif
#include <boost/unordered/detail/foa/element_type.hpp>
#include <boost/unordered/detail/foa/node_handle.hpp>
#include <boost/unordered/detail/foa/node_map_types.hpp>
#include <boost/unordered/detail/foa/table.hpp>
#include <boost/unordered/detail/serialize_container.hpp>
#include <boost/unordered/detail/type_traits.hpp>
#include <boost/unordered/unordered_node_map_fwd.hpp>
#include <boost/core/allocator_access.hpp>
#include <boost/container_hash/hash.hpp>
#include <boost/throw_exception.hpp>
#include <initializer_list>
#include <iterator>
#include <stdexcept>
#include <type_traits>
#include <utility>
namespace boost {
namespace unordered {
#if defined(BOOST_MSVC)
#pragma warning(push)
#pragma warning(disable : 4714) /* marked as __forceinline not inlined */
#endif
namespace detail {
template <class TypePolicy, class Allocator>
struct node_map_handle
: public detail::foa::node_handle_base<TypePolicy, Allocator>
{
private:
using base_type = detail::foa::node_handle_base<TypePolicy, Allocator>;
using typename base_type::type_policy;
template <class Key, class T, class Hash, class Pred, class Alloc>
friend class boost::unordered::unordered_node_map;
public:
using key_type = typename TypePolicy::key_type;
using mapped_type = typename TypePolicy::mapped_type;
constexpr node_map_handle() noexcept = default;
node_map_handle(node_map_handle&& nh) noexcept = default;
node_map_handle& operator=(node_map_handle&&) noexcept = default;
key_type& key() const
{
BOOST_ASSERT(!this->empty());
return const_cast<key_type&>(this->data().first);
}
mapped_type& mapped() const
{
BOOST_ASSERT(!this->empty());
return const_cast<mapped_type&>(this->data().second);
}
};
} // namespace detail
template <class Key, class T, class Hash, class KeyEqual, class Allocator>
class unordered_node_map
{
using map_types = detail::foa::node_map_types<Key, T,
typename boost::allocator_void_pointer<Allocator>::type>;
using table_type = detail::foa::table<map_types, Hash, KeyEqual,
typename boost::allocator_rebind<Allocator,
std::pair<Key const, T> >::type>;
table_type table_;
template <class K, class V, class H, class KE, class A>
bool friend operator==(unordered_node_map<K, V, H, KE, A> const& lhs,
unordered_node_map<K, V, H, KE, A> const& rhs);
template <class K, class V, class H, class KE, class A, class Pred>
typename unordered_node_map<K, V, H, KE, A>::size_type friend erase_if(
unordered_node_map<K, V, H, KE, A>& set, Pred pred);
public:
using key_type = Key;
using mapped_type = T;
using value_type = typename map_types::value_type;
using init_type = typename map_types::init_type;
using size_type = std::size_t;
using difference_type = std::ptrdiff_t;
using hasher = typename boost::unordered::detail::type_identity<Hash>::type;
using key_equal = typename boost::unordered::detail::type_identity<KeyEqual>::type;
using allocator_type = typename boost::unordered::detail::type_identity<Allocator>::type;
using reference = value_type&;
using const_reference = value_type const&;
using pointer = typename boost::allocator_pointer<allocator_type>::type;
using const_pointer =
typename boost::allocator_const_pointer<allocator_type>::type;
using iterator = typename table_type::iterator;
using const_iterator = typename table_type::const_iterator;
using node_type = detail::node_map_handle<map_types,
typename boost::allocator_rebind<Allocator,
typename map_types::value_type>::type>;
using insert_return_type =
detail::foa::insert_return_type<iterator, node_type>;
unordered_node_map() : unordered_node_map(0) {}
explicit unordered_node_map(size_type n, hasher const& h = hasher(),
key_equal const& pred = key_equal(),
allocator_type const& a = allocator_type())
: table_(n, h, pred, a)
{
}
unordered_node_map(size_type n, allocator_type const& a)
: unordered_node_map(n, hasher(), key_equal(), a)
{
}
unordered_node_map(size_type n, hasher const& h, allocator_type const& a)
: unordered_node_map(n, h, key_equal(), a)
{
}
template <class InputIterator>
unordered_node_map(
InputIterator f, InputIterator l, allocator_type const& a)
: unordered_node_map(f, l, size_type(0), hasher(), key_equal(), a)
{
}
explicit unordered_node_map(allocator_type const& a)
: unordered_node_map(0, a)
{
}
template <class Iterator>
unordered_node_map(Iterator first, Iterator last, size_type n = 0,
hasher const& h = hasher(), key_equal const& pred = key_equal(),
allocator_type const& a = allocator_type())
: unordered_node_map(n, h, pred, a)
{
this->insert(first, last);
}
template <class Iterator>
unordered_node_map(
Iterator first, Iterator last, size_type n, allocator_type const& a)
: unordered_node_map(first, last, n, hasher(), key_equal(), a)
{
}
template <class Iterator>
unordered_node_map(Iterator first, Iterator last, size_type n,
hasher const& h, allocator_type const& a)
: unordered_node_map(first, last, n, h, key_equal(), a)
{
}
unordered_node_map(unordered_node_map const& other) : table_(other.table_)
{
}
unordered_node_map(
unordered_node_map const& other, allocator_type const& a)
: table_(other.table_, a)
{
}
unordered_node_map(unordered_node_map&& other)
noexcept(std::is_nothrow_move_constructible<table_type>::value)
: table_(std::move(other.table_))
{
}
unordered_node_map(unordered_node_map&& other, allocator_type const& al)
: table_(std::move(other.table_), al)
{
}
unordered_node_map(std::initializer_list<value_type> ilist,
size_type n = 0, hasher const& h = hasher(),
key_equal const& pred = key_equal(),
allocator_type const& a = allocator_type())
: unordered_node_map(ilist.begin(), ilist.end(), n, h, pred, a)
{
}
unordered_node_map(
std::initializer_list<value_type> il, allocator_type const& a)
: unordered_node_map(il, size_type(0), hasher(), key_equal(), a)
{
}
unordered_node_map(std::initializer_list<value_type> init, size_type n,
allocator_type const& a)
: unordered_node_map(init, n, hasher(), key_equal(), a)
{
}
unordered_node_map(std::initializer_list<value_type> init, size_type n,
hasher const& h, allocator_type const& a)
: unordered_node_map(init, n, h, key_equal(), a)
{
}
~unordered_node_map() = default;
unordered_node_map& operator=(unordered_node_map const& other)
{
table_ = other.table_;
return *this;
}
unordered_node_map& operator=(unordered_node_map&& other) noexcept(
noexcept(std::declval<table_type&>() = std::declval<table_type&&>()))
{
table_ = std::move(other.table_);
return *this;
}
allocator_type get_allocator() const noexcept
{
return table_.get_allocator();
}
/// Iterators
///
iterator begin() noexcept { return table_.begin(); }
const_iterator begin() const noexcept { return table_.begin(); }
const_iterator cbegin() const noexcept { return table_.cbegin(); }
iterator end() noexcept { return table_.end(); }
const_iterator end() const noexcept { return table_.end(); }
const_iterator cend() const noexcept { return table_.cend(); }
/// Capacity
///
BOOST_ATTRIBUTE_NODISCARD bool empty() const noexcept
{
return table_.empty();
}
size_type size() const noexcept { return table_.size(); }
size_type max_size() const noexcept { return table_.max_size(); }
/// Modifiers
///
void clear() noexcept { table_.clear(); }
template <class Ty>
BOOST_FORCEINLINE auto insert(Ty&& value)
-> decltype(table_.insert(std::forward<Ty>(value)))
{
return table_.insert(std::forward<Ty>(value));
}
BOOST_FORCEINLINE std::pair<iterator, bool> insert(init_type&& value)
{
return table_.insert(std::move(value));
}
template <class Ty>
BOOST_FORCEINLINE auto insert(const_iterator, Ty&& value)
-> decltype(table_.insert(std::forward<Ty>(value)).first)
{
return table_.insert(std::forward<Ty>(value)).first;
}
BOOST_FORCEINLINE iterator insert(const_iterator, init_type&& value)
{
return table_.insert(std::move(value)).first;
}
template <class InputIterator>
BOOST_FORCEINLINE void insert(InputIterator first, InputIterator last)
{
for (auto pos = first; pos != last; ++pos) {
table_.emplace(*pos);
}
}
void insert(std::initializer_list<value_type> ilist)
{
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_.insert(std::move(nh.element()));
if (itp.second) {
nh.reset();
return {itp.first, true, node_type{}};
} else {
return {itp.first, false, std::move(nh)};
}
}
iterator insert(const_iterator, node_type&& nh)
{
if (nh.empty()) {
return end();
}
BOOST_ASSERT(get_allocator() == nh.get_allocator());
auto itp = table_.insert(std::move(nh.element()));
if (itp.second) {
nh.reset();
return itp.first;
} else {
return itp.first;
}
}
template <class M>
std::pair<iterator, bool> insert_or_assign(key_type const& key, M&& obj)
{
auto ibp = table_.try_emplace(key, std::forward<M>(obj));
if (ibp.second) {
return ibp;
}
ibp.first->second = std::forward<M>(obj);
return ibp;
}
template <class M>
std::pair<iterator, bool> insert_or_assign(key_type&& key, M&& obj)
{
auto ibp = table_.try_emplace(std::move(key), std::forward<M>(obj));
if (ibp.second) {
return ibp;
}
ibp.first->second = std::forward<M>(obj);
return ibp;
}
template <class K, class M>
typename std::enable_if<
boost::unordered::detail::are_transparent<K, hasher, key_equal>::value,
std::pair<iterator, bool> >::type
insert_or_assign(K&& k, M&& obj)
{
auto ibp = table_.try_emplace(std::forward<K>(k), std::forward<M>(obj));
if (ibp.second) {
return ibp;
}
ibp.first->second = std::forward<M>(obj);
return ibp;
}
template <class M>
iterator insert_or_assign(const_iterator, key_type const& key, M&& obj)
{
return this->insert_or_assign(key, std::forward<M>(obj)).first;
}
template <class M>
iterator insert_or_assign(const_iterator, key_type&& key, M&& obj)
{
return this->insert_or_assign(std::move(key), std::forward<M>(obj))
.first;
}
template <class K, class M>
typename std::enable_if<
boost::unordered::detail::are_transparent<K, hasher, key_equal>::value,
iterator>::type
insert_or_assign(const_iterator, K&& k, M&& obj)
{
return this->insert_or_assign(std::forward<K>(k), std::forward<M>(obj))
.first;
}
template <class... Args>
BOOST_FORCEINLINE std::pair<iterator, bool> emplace(Args&&... args)
{
return table_.emplace(std::forward<Args>(args)...);
}
template <class... Args>
BOOST_FORCEINLINE iterator emplace_hint(const_iterator, Args&&... args)
{
return table_.emplace(std::forward<Args>(args)...).first;
}
template <class... Args>
BOOST_FORCEINLINE std::pair<iterator, bool> try_emplace(
key_type const& key, Args&&... args)
{
return table_.try_emplace(key, std::forward<Args>(args)...);
}
template <class... Args>
BOOST_FORCEINLINE std::pair<iterator, bool> try_emplace(
key_type&& key, Args&&... args)
{
return table_.try_emplace(std::move(key), std::forward<Args>(args)...);
}
template <class K, class... Args>
BOOST_FORCEINLINE typename std::enable_if<
boost::unordered::detail::transparent_non_iterable<K,
unordered_node_map>::value,
std::pair<iterator, bool> >::type
try_emplace(K&& key, Args&&... args)
{
return table_.try_emplace(
std::forward<K>(key), std::forward<Args>(args)...);
}
template <class... Args>
BOOST_FORCEINLINE iterator try_emplace(
const_iterator, key_type const& key, Args&&... args)
{
return table_.try_emplace(key, std::forward<Args>(args)...).first;
}
template <class... Args>
BOOST_FORCEINLINE iterator try_emplace(
const_iterator, key_type&& key, Args&&... args)
{
return table_.try_emplace(std::move(key), std::forward<Args>(args)...)
.first;
}
template <class K, class... Args>
BOOST_FORCEINLINE typename std::enable_if<
boost::unordered::detail::transparent_non_iterable<K,
unordered_node_map>::value,
iterator>::type
try_emplace(const_iterator, K&& key, Args&&... args)
{
return table_
.try_emplace(std::forward<K>(key), std::forward<Args>(args)...)
.first;
}
BOOST_FORCEINLINE typename table_type::erase_return_type erase(
iterator pos)
{
return table_.erase(pos);
}
BOOST_FORCEINLINE typename table_type::erase_return_type erase(
const_iterator pos)
{
return table_.erase(pos);
}
iterator erase(const_iterator first, const_iterator last)
{
while (first != last) {
this->erase(first++);
}
return iterator{detail::foa::const_iterator_cast_tag{}, last};
}
BOOST_FORCEINLINE size_type erase(key_type const& key)
{
return table_.erase(key);
}
template <class K>
BOOST_FORCEINLINE typename std::enable_if<
detail::transparent_non_iterable<K, unordered_node_map>::value,
size_type>::type
erase(K const& key)
{
return table_.erase(key);
}
void swap(unordered_node_map& rhs) noexcept(
noexcept(std::declval<table_type&>().swap(std::declval<table_type&>())))
{
table_.swap(rhs.table_);
}
node_type extract(const_iterator pos)
{
BOOST_ASSERT(pos != end());
node_type nh;
auto elem = table_.extract(pos);
nh.emplace(std::move(elem), get_allocator());
return nh;
}
node_type extract(key_type const& key)
{
auto pos = find(key);
return pos != end() ? extract(pos) : node_type();
}
template <class K>
typename std::enable_if<
boost::unordered::detail::transparent_non_iterable<K,
unordered_node_map>::value,
node_type>::type
extract(K const& key)
{
auto pos = find(key);
return pos != end() ? extract(pos) : node_type();
}
template <class H2, class P2>
void merge(
unordered_node_map<key_type, mapped_type, H2, P2, allocator_type>&
source)
{
BOOST_ASSERT(get_allocator() == source.get_allocator());
table_.merge(source.table_);
}
template <class H2, class P2>
void merge(
unordered_node_map<key_type, mapped_type, H2, P2, allocator_type>&&
source)
{
BOOST_ASSERT(get_allocator() == source.get_allocator());
table_.merge(std::move(source.table_));
}
/// Lookup
///
mapped_type& at(key_type const& key)
{
auto pos = table_.find(key);
if (pos != table_.end()) {
return pos->second;
}
// TODO: someday refactor this to conditionally serialize the key and
// include it in the error message
//
boost::throw_exception(
std::out_of_range("key was not found in unordered_node_map"));
}
mapped_type const& at(key_type const& key) const
{
auto pos = table_.find(key);
if (pos != table_.end()) {
return pos->second;
}
boost::throw_exception(
std::out_of_range("key was not found in unordered_node_map"));
}
template <class K>
typename std::enable_if<
boost::unordered::detail::are_transparent<K, hasher, key_equal>::value,
mapped_type&>::type
at(K&& key)
{
auto pos = table_.find(std::forward<K>(key));
if (pos != table_.end()) {
return pos->second;
}
boost::throw_exception(
std::out_of_range("key was not found in unordered_node_map"));
}
template <class K>
typename std::enable_if<
boost::unordered::detail::are_transparent<K, hasher, key_equal>::value,
mapped_type const&>::type
at(K&& key) const
{
auto pos = table_.find(std::forward<K>(key));
if (pos != table_.end()) {
return pos->second;
}
boost::throw_exception(
std::out_of_range("key was not found in unordered_node_map"));
}
BOOST_FORCEINLINE mapped_type& operator[](key_type const& key)
{
return table_.try_emplace(key).first->second;
}
BOOST_FORCEINLINE mapped_type& operator[](key_type&& key)
{
return table_.try_emplace(std::move(key)).first->second;
}
template <class K>
typename std::enable_if<
boost::unordered::detail::are_transparent<K, hasher, key_equal>::value,
mapped_type&>::type
operator[](K&& key)
{
return table_.try_emplace(std::forward<K>(key)).first->second;
}
BOOST_FORCEINLINE size_type count(key_type const& key) const
{
auto pos = table_.find(key);
return pos != table_.end() ? 1 : 0;
}
template <class K>
BOOST_FORCEINLINE typename std::enable_if<
detail::are_transparent<K, hasher, key_equal>::value, size_type>::type
count(K const& key) const
{
auto pos = table_.find(key);
return pos != table_.end() ? 1 : 0;
}
BOOST_FORCEINLINE iterator find(key_type const& key)
{
return table_.find(key);
}
BOOST_FORCEINLINE const_iterator find(key_type const& key) const
{
return table_.find(key);
}
template <class K>
BOOST_FORCEINLINE typename std::enable_if<
boost::unordered::detail::are_transparent<K, hasher, key_equal>::value,
iterator>::type
find(K const& key)
{
return table_.find(key);
}
template <class K>
BOOST_FORCEINLINE typename std::enable_if<
boost::unordered::detail::are_transparent<K, hasher, key_equal>::value,
const_iterator>::type
find(K const& key) const
{
return table_.find(key);
}
BOOST_FORCEINLINE bool contains(key_type const& key) const
{
return this->find(key) != this->end();
}
template <class K>
BOOST_FORCEINLINE typename std::enable_if<
boost::unordered::detail::are_transparent<K, hasher, key_equal>::value,
bool>::type
contains(K const& key) const
{
return this->find(key) != this->end();
}
std::pair<iterator, iterator> equal_range(key_type const& key)
{
auto pos = table_.find(key);
if (pos == table_.end()) {
return {pos, pos};
}
auto next = pos;
++next;
return {pos, next};
}
std::pair<const_iterator, const_iterator> equal_range(
key_type const& key) const
{
auto pos = table_.find(key);
if (pos == table_.end()) {
return {pos, pos};
}
auto next = pos;
++next;
return {pos, next};
}
template <class K>
typename std::enable_if<
detail::are_transparent<K, hasher, key_equal>::value,
std::pair<iterator, iterator> >::type
equal_range(K const& key)
{
auto pos = table_.find(key);
if (pos == table_.end()) {
return {pos, pos};
}
auto next = pos;
++next;
return {pos, next};
}
template <class K>
typename std::enable_if<
detail::are_transparent<K, hasher, key_equal>::value,
std::pair<const_iterator, const_iterator> >::type
equal_range(K const& key) const
{
auto pos = table_.find(key);
if (pos == table_.end()) {
return {pos, pos};
}
auto next = pos;
++next;
return {pos, next};
}
/// Hash Policy
///
size_type bucket_count() const noexcept { return table_.capacity(); }
float load_factor() const noexcept { return table_.load_factor(); }
float max_load_factor() const noexcept
{
return table_.max_load_factor();
}
void max_load_factor(float) {}
size_type max_load() const noexcept { return table_.max_load(); }
void rehash(size_type n) { table_.rehash(n); }
void reserve(size_type n) { table_.reserve(n); }
/// Observers
///
hasher hash_function() const { return table_.hash_function(); }
key_equal key_eq() const { return table_.key_eq(); }
};
template <class Key, class T, class Hash, class KeyEqual, class Allocator>
bool operator==(
unordered_node_map<Key, T, Hash, KeyEqual, Allocator> const& lhs,
unordered_node_map<Key, T, Hash, KeyEqual, Allocator> const& rhs)
{
return lhs.table_ == rhs.table_;
}
template <class Key, class T, class Hash, class KeyEqual, class Allocator>
bool operator!=(
unordered_node_map<Key, T, Hash, KeyEqual, Allocator> const& lhs,
unordered_node_map<Key, T, Hash, KeyEqual, Allocator> const& rhs)
{
return !(lhs == rhs);
}
template <class Key, class T, class Hash, class KeyEqual, class Allocator>
void swap(unordered_node_map<Key, T, Hash, KeyEqual, Allocator>& lhs,
unordered_node_map<Key, T, Hash, KeyEqual, Allocator>& rhs)
noexcept(noexcept(lhs.swap(rhs)))
{
lhs.swap(rhs);
}
template <class Key, class T, class Hash, class KeyEqual, class Allocator,
class Pred>
typename unordered_node_map<Key, T, Hash, KeyEqual, Allocator>::size_type
erase_if(
unordered_node_map<Key, T, Hash, KeyEqual, Allocator>& map, Pred pred)
{
return erase_if(map.table_, pred);
}
template <class Archive, class Key, class T, class Hash, class KeyEqual,
class Allocator>
void serialize(Archive& ar,
unordered_node_map<Key, T, Hash, KeyEqual, Allocator>& map,
unsigned int version)
{
detail::serialize_container(ar, map, version);
}
#if defined(BOOST_MSVC)
#pragma warning(pop) /* C4714 */
#endif
#if BOOST_UNORDERED_TEMPLATE_DEDUCTION_GUIDES
template <class InputIterator,
class Hash =
boost::hash<boost::unordered::detail::iter_key_t<InputIterator> >,
class Pred =
std::equal_to<boost::unordered::detail::iter_key_t<InputIterator> >,
class Allocator = std::allocator<
boost::unordered::detail::iter_to_alloc_t<InputIterator> >,
class = std::enable_if_t<detail::is_input_iterator_v<InputIterator> >,
class = std::enable_if_t<detail::is_hash_v<Hash> >,
class = std::enable_if_t<detail::is_pred_v<Pred> >,
class = std::enable_if_t<detail::is_allocator_v<Allocator> > >
unordered_node_map(InputIterator, InputIterator,
std::size_t = boost::unordered::detail::foa::default_bucket_count,
Hash = Hash(), Pred = Pred(), Allocator = Allocator())
-> unordered_node_map<boost::unordered::detail::iter_key_t<InputIterator>,
boost::unordered::detail::iter_val_t<InputIterator>, Hash, Pred,
Allocator>;
template <class Key, class T,
class Hash = boost::hash<std::remove_const_t<Key> >,
class Pred = std::equal_to<std::remove_const_t<Key> >,
class Allocator = std::allocator<std::pair<const Key, T> >,
class = std::enable_if_t<detail::is_hash_v<Hash> >,
class = std::enable_if_t<detail::is_pred_v<Pred> >,
class = std::enable_if_t<detail::is_allocator_v<Allocator> > >
unordered_node_map(std::initializer_list<std::pair<Key, T> >,
std::size_t = boost::unordered::detail::foa::default_bucket_count,
Hash = Hash(), Pred = Pred(), Allocator = Allocator())
-> unordered_node_map<std::remove_const_t<Key>, T, Hash, Pred,
Allocator>;
template <class InputIterator, class Allocator,
class = std::enable_if_t<detail::is_input_iterator_v<InputIterator> >,
class = std::enable_if_t<detail::is_allocator_v<Allocator> > >
unordered_node_map(InputIterator, InputIterator, std::size_t, Allocator)
-> unordered_node_map<boost::unordered::detail::iter_key_t<InputIterator>,
boost::unordered::detail::iter_val_t<InputIterator>,
boost::hash<boost::unordered::detail::iter_key_t<InputIterator> >,
std::equal_to<boost::unordered::detail::iter_key_t<InputIterator> >,
Allocator>;
template <class InputIterator, class Allocator,
class = std::enable_if_t<detail::is_input_iterator_v<InputIterator> >,
class = std::enable_if_t<detail::is_allocator_v<Allocator> > >
unordered_node_map(InputIterator, InputIterator, Allocator)
-> unordered_node_map<boost::unordered::detail::iter_key_t<InputIterator>,
boost::unordered::detail::iter_val_t<InputIterator>,
boost::hash<boost::unordered::detail::iter_key_t<InputIterator> >,
std::equal_to<boost::unordered::detail::iter_key_t<InputIterator> >,
Allocator>;
template <class InputIterator, class Hash, class Allocator,
class = std::enable_if_t<detail::is_hash_v<Hash> >,
class = std::enable_if_t<detail::is_input_iterator_v<InputIterator> >,
class = std::enable_if_t<detail::is_allocator_v<Allocator> > >
unordered_node_map(
InputIterator, InputIterator, std::size_t, Hash, Allocator)
-> unordered_node_map<boost::unordered::detail::iter_key_t<InputIterator>,
boost::unordered::detail::iter_val_t<InputIterator>, Hash,
std::equal_to<boost::unordered::detail::iter_key_t<InputIterator> >,
Allocator>;
template <class Key, class T, class Allocator,
class = std::enable_if_t<detail::is_allocator_v<Allocator> > >
unordered_node_map(std::initializer_list<std::pair<Key, T> >, std::size_t,
Allocator) -> unordered_node_map<std::remove_const_t<Key>, T,
boost::hash<std::remove_const_t<Key> >,
std::equal_to<std::remove_const_t<Key> >, Allocator>;
template <class Key, class T, class Allocator,
class = std::enable_if_t<detail::is_allocator_v<Allocator> > >
unordered_node_map(std::initializer_list<std::pair<Key, T> >, Allocator)
-> unordered_node_map<std::remove_const_t<Key>, T,
boost::hash<std::remove_const_t<Key> >,
std::equal_to<std::remove_const_t<Key> >, Allocator>;
template <class Key, class T, class Hash, class Allocator,
class = std::enable_if_t<detail::is_hash_v<Hash> >,
class = std::enable_if_t<detail::is_allocator_v<Allocator> > >
unordered_node_map(std::initializer_list<std::pair<Key, T> >, std::size_t,
Hash, Allocator) -> unordered_node_map<std::remove_const_t<Key>, T,
Hash, std::equal_to<std::remove_const_t<Key> >, Allocator>;
#endif
} // namespace unordered
} // namespace boost
#endif
+44
View File
@@ -0,0 +1,44 @@
// Copyright (C) 2022 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)
#ifndef BOOST_UNORDERED_NODE_MAP_FWD_HPP_INCLUDED
#define BOOST_UNORDERED_NODE_MAP_FWD_HPP_INCLUDED
#include <boost/config.hpp>
#if defined(BOOST_HAS_PRAGMA_ONCE)
#pragma once
#endif
#include <boost/container_hash/hash_fwd.hpp>
#include <functional>
#include <memory>
namespace boost {
namespace unordered {
template <class Key, class T, class Hash = boost::hash<Key>,
class KeyEqual = std::equal_to<Key>,
class Allocator = std::allocator<std::pair<const Key, T> > >
class unordered_node_map;
template <class Key, class T, class Hash, class KeyEqual, class Allocator>
bool operator==(
unordered_node_map<Key, T, Hash, KeyEqual, Allocator> const& lhs,
unordered_node_map<Key, T, Hash, KeyEqual, Allocator> const& rhs);
template <class Key, class T, class Hash, class KeyEqual, class Allocator>
bool operator!=(
unordered_node_map<Key, T, Hash, KeyEqual, Allocator> const& lhs,
unordered_node_map<Key, T, Hash, KeyEqual, Allocator> const& rhs);
template <class Key, class T, class Hash, class KeyEqual, class Allocator>
void swap(unordered_node_map<Key, T, Hash, KeyEqual, Allocator>& lhs,
unordered_node_map<Key, T, Hash, KeyEqual, Allocator>& rhs)
noexcept(noexcept(lhs.swap(rhs)));
} // namespace unordered
using boost::unordered::unordered_node_map;
} // namespace boost
#endif
+699
View File
@@ -0,0 +1,699 @@
// 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)
#ifndef BOOST_UNORDERED_UNORDERED_NODE_SET_HPP_INCLUDED
#define BOOST_UNORDERED_UNORDERED_NODE_SET_HPP_INCLUDED
#include <boost/config.hpp>
#if defined(BOOST_HAS_PRAGMA_ONCE)
#pragma once
#endif
#include <boost/unordered/detail/foa/element_type.hpp>
#include <boost/unordered/detail/foa/node_handle.hpp>
#include <boost/unordered/detail/foa/node_set_types.hpp>
#include <boost/unordered/detail/foa/table.hpp>
#include <boost/unordered/detail/serialize_container.hpp>
#include <boost/unordered/detail/type_traits.hpp>
#include <boost/unordered/unordered_node_set_fwd.hpp>
#include <boost/core/allocator_access.hpp>
#include <boost/container_hash/hash.hpp>
#include <boost/throw_exception.hpp>
#include <initializer_list>
#include <iterator>
#include <type_traits>
#include <utility>
namespace boost {
namespace unordered {
#if defined(BOOST_MSVC)
#pragma warning(push)
#pragma warning(disable : 4714) /* marked as __forceinline not inlined */
#endif
namespace detail {
template <class TypePolicy, class Allocator>
struct node_set_handle
: public detail::foa::node_handle_base<TypePolicy, Allocator>
{
private:
using base_type = detail::foa::node_handle_base<TypePolicy, Allocator>;
using typename base_type::type_policy;
template <class Key, class Hash, class Pred, class Alloc>
friend class boost::unordered::unordered_node_set;
public:
using value_type = typename TypePolicy::value_type;
constexpr node_set_handle() noexcept = default;
node_set_handle(node_set_handle&& nh) noexcept = default;
node_set_handle& operator=(node_set_handle&&) noexcept = default;
value_type& value() const
{
BOOST_ASSERT(!this->empty());
return const_cast<value_type&>(this->data());
}
};
} // namespace detail
template <class Key, class Hash, class KeyEqual, class Allocator>
class unordered_node_set
{
using set_types = detail::foa::node_set_types<Key,
typename boost::allocator_void_pointer<Allocator>::type>;
using table_type = detail::foa::table<set_types, Hash, KeyEqual,
typename boost::allocator_rebind<Allocator,
typename set_types::value_type>::type>;
table_type table_;
template <class K, class H, class KE, class A>
bool friend operator==(unordered_node_set<K, H, KE, A> const& lhs,
unordered_node_set<K, H, KE, A> const& rhs);
template <class K, class H, class KE, class A, class Pred>
typename unordered_node_set<K, H, KE, A>::size_type friend erase_if(
unordered_node_set<K, H, KE, A>& set, Pred pred);
public:
using key_type = Key;
using value_type = typename set_types::value_type;
using init_type = typename set_types::init_type;
using size_type = std::size_t;
using difference_type = std::ptrdiff_t;
using hasher = Hash;
using key_equal = KeyEqual;
using allocator_type = Allocator;
using reference = value_type&;
using const_reference = value_type const&;
using pointer = typename boost::allocator_pointer<allocator_type>::type;
using const_pointer =
typename boost::allocator_const_pointer<allocator_type>::type;
using iterator = typename table_type::iterator;
using const_iterator = typename table_type::const_iterator;
using node_type = detail::node_set_handle<set_types,
typename boost::allocator_rebind<Allocator,
typename set_types::value_type>::type>;
using insert_return_type =
detail::foa::insert_return_type<iterator, node_type>;
unordered_node_set() : unordered_node_set(0) {}
explicit unordered_node_set(size_type n, hasher const& h = hasher(),
key_equal const& pred = key_equal(),
allocator_type const& a = allocator_type())
: table_(n, h, pred, a)
{
}
unordered_node_set(size_type n, allocator_type const& a)
: unordered_node_set(n, hasher(), key_equal(), a)
{
}
unordered_node_set(size_type n, hasher const& h, allocator_type const& a)
: unordered_node_set(n, h, key_equal(), a)
{
}
template <class InputIterator>
unordered_node_set(
InputIterator f, InputIterator l, allocator_type const& a)
: unordered_node_set(f, l, size_type(0), hasher(), key_equal(), a)
{
}
explicit unordered_node_set(allocator_type const& a)
: unordered_node_set(0, a)
{
}
template <class Iterator>
unordered_node_set(Iterator first, Iterator last, size_type n = 0,
hasher const& h = hasher(), key_equal const& pred = key_equal(),
allocator_type const& a = allocator_type())
: unordered_node_set(n, h, pred, a)
{
this->insert(first, last);
}
template <class InputIt>
unordered_node_set(
InputIt first, InputIt last, size_type n, allocator_type const& a)
: unordered_node_set(first, last, n, hasher(), key_equal(), a)
{
}
template <class Iterator>
unordered_node_set(Iterator first, Iterator last, size_type n,
hasher const& h, allocator_type const& a)
: unordered_node_set(first, last, n, h, key_equal(), a)
{
}
unordered_node_set(unordered_node_set const& other) : table_(other.table_)
{
}
unordered_node_set(
unordered_node_set const& other, allocator_type const& a)
: table_(other.table_, a)
{
}
unordered_node_set(unordered_node_set&& other)
noexcept(std::is_nothrow_move_constructible<table_type>::value)
: table_(std::move(other.table_))
{
}
unordered_node_set(unordered_node_set&& other, allocator_type const& al)
: table_(std::move(other.table_), al)
{
}
unordered_node_set(std::initializer_list<value_type> ilist,
size_type n = 0, hasher const& h = hasher(),
key_equal const& pred = key_equal(),
allocator_type const& a = allocator_type())
: unordered_node_set(ilist.begin(), ilist.end(), n, h, pred, a)
{
}
unordered_node_set(
std::initializer_list<value_type> il, allocator_type const& a)
: unordered_node_set(il, size_type(0), hasher(), key_equal(), a)
{
}
unordered_node_set(std::initializer_list<value_type> init, size_type n,
allocator_type const& a)
: unordered_node_set(init, n, hasher(), key_equal(), a)
{
}
unordered_node_set(std::initializer_list<value_type> init, size_type n,
hasher const& h, allocator_type const& a)
: unordered_node_set(init, n, h, key_equal(), a)
{
}
~unordered_node_set() = default;
unordered_node_set& operator=(unordered_node_set const& other)
{
table_ = other.table_;
return *this;
}
unordered_node_set& operator=(unordered_node_set&& other) noexcept(
noexcept(std::declval<table_type&>() = std::declval<table_type&&>()))
{
table_ = std::move(other.table_);
return *this;
}
allocator_type get_allocator() const noexcept
{
return table_.get_allocator();
}
/// Iterators
///
iterator begin() noexcept { return table_.begin(); }
const_iterator begin() const noexcept { return table_.begin(); }
const_iterator cbegin() const noexcept { return table_.cbegin(); }
iterator end() noexcept { return table_.end(); }
const_iterator end() const noexcept { return table_.end(); }
const_iterator cend() const noexcept { return table_.cend(); }
/// Capacity
///
BOOST_ATTRIBUTE_NODISCARD bool empty() const noexcept
{
return table_.empty();
}
size_type size() const noexcept { return table_.size(); }
size_type max_size() const noexcept { return table_.max_size(); }
/// Modifiers
///
void clear() noexcept { table_.clear(); }
BOOST_FORCEINLINE std::pair<iterator, bool> insert(
value_type const& value)
{
return table_.insert(value);
}
BOOST_FORCEINLINE std::pair<iterator, bool> insert(value_type&& value)
{
return table_.insert(std::move(value));
}
template <class K>
BOOST_FORCEINLINE typename std::enable_if<
detail::transparent_non_iterable<K, unordered_node_set>::value,
std::pair<iterator, bool> >::type
insert(K&& k)
{
return table_.try_emplace(std::forward<K>(k));
}
BOOST_FORCEINLINE iterator insert(const_iterator, value_type const& value)
{
return table_.insert(value).first;
}
BOOST_FORCEINLINE iterator insert(const_iterator, value_type&& value)
{
return table_.insert(std::move(value)).first;
}
template <class K>
BOOST_FORCEINLINE typename std::enable_if<
detail::transparent_non_iterable<K, unordered_node_set>::value,
iterator>::type
insert(const_iterator, K&& k)
{
return table_.try_emplace(std::forward<K>(k)).first;
}
template <class InputIterator>
void insert(InputIterator first, InputIterator last)
{
for (auto pos = first; pos != last; ++pos) {
table_.emplace(*pos);
}
}
void insert(std::initializer_list<value_type> ilist)
{
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_.insert(std::move(nh.element()));
if (itp.second) {
nh.reset();
return {itp.first, true, node_type{}};
} else {
return {itp.first, false, std::move(nh)};
}
}
iterator insert(const_iterator, node_type&& nh)
{
if (nh.empty()) {
return end();
}
BOOST_ASSERT(get_allocator() == nh.get_allocator());
auto itp = table_.insert(std::move(nh.element()));
if (itp.second) {
nh.reset();
return itp.first;
} else {
return itp.first;
}
}
template <class... Args>
BOOST_FORCEINLINE std::pair<iterator, bool> emplace(Args&&... args)
{
return table_.emplace(std::forward<Args>(args)...);
}
template <class... Args>
BOOST_FORCEINLINE iterator emplace_hint(const_iterator, Args&&... args)
{
return table_.emplace(std::forward<Args>(args)...).first;
}
BOOST_FORCEINLINE typename table_type::erase_return_type erase(
const_iterator pos)
{
return table_.erase(pos);
}
iterator erase(const_iterator first, const_iterator last)
{
while (first != last) {
this->erase(first++);
}
return iterator{detail::foa::const_iterator_cast_tag{}, last};
}
BOOST_FORCEINLINE size_type erase(key_type const& key)
{
return table_.erase(key);
}
template <class K>
BOOST_FORCEINLINE typename std::enable_if<
detail::transparent_non_iterable<K, unordered_node_set>::value,
size_type>::type
erase(K const& key)
{
return table_.erase(key);
}
void swap(unordered_node_set& rhs) noexcept(
noexcept(std::declval<table_type&>().swap(std::declval<table_type&>())))
{
table_.swap(rhs.table_);
}
node_type extract(const_iterator pos)
{
BOOST_ASSERT(pos != end());
node_type nh;
auto elem = table_.extract(pos);
nh.emplace(std::move(elem), get_allocator());
return nh;
}
node_type extract(key_type const& key)
{
auto pos = find(key);
return pos != end() ? extract(pos) : node_type();
}
template <class K>
typename std::enable_if<
boost::unordered::detail::transparent_non_iterable<K,
unordered_node_set>::value,
node_type>::type
extract(K const& key)
{
auto pos = find(key);
return pos != end() ? extract(pos) : node_type();
}
template <class H2, class P2>
void merge(unordered_node_set<key_type, H2, P2, allocator_type>& source)
{
BOOST_ASSERT(get_allocator() == source.get_allocator());
table_.merge(source.table_);
}
template <class H2, class P2>
void merge(unordered_node_set<key_type, H2, P2, allocator_type>&& source)
{
BOOST_ASSERT(get_allocator() == source.get_allocator());
table_.merge(std::move(source.table_));
}
/// Lookup
///
BOOST_FORCEINLINE size_type count(key_type const& key) const
{
auto pos = table_.find(key);
return pos != table_.end() ? 1 : 0;
}
template <class K>
BOOST_FORCEINLINE typename std::enable_if<
detail::are_transparent<K, hasher, key_equal>::value, size_type>::type
count(K const& key) const
{
auto pos = table_.find(key);
return pos != table_.end() ? 1 : 0;
}
BOOST_FORCEINLINE iterator find(key_type const& key)
{
return table_.find(key);
}
BOOST_FORCEINLINE const_iterator find(key_type const& key) const
{
return table_.find(key);
}
template <class K>
BOOST_FORCEINLINE typename std::enable_if<
boost::unordered::detail::are_transparent<K, hasher, key_equal>::value,
iterator>::type
find(K const& key)
{
return table_.find(key);
}
template <class K>
BOOST_FORCEINLINE typename std::enable_if<
boost::unordered::detail::are_transparent<K, hasher, key_equal>::value,
const_iterator>::type
find(K const& key) const
{
return table_.find(key);
}
BOOST_FORCEINLINE bool contains(key_type const& key) const
{
return this->find(key) != this->end();
}
template <class K>
BOOST_FORCEINLINE typename std::enable_if<
boost::unordered::detail::are_transparent<K, hasher, key_equal>::value,
bool>::type
contains(K const& key) const
{
return this->find(key) != this->end();
}
std::pair<iterator, iterator> equal_range(key_type const& key)
{
auto pos = table_.find(key);
if (pos == table_.end()) {
return {pos, pos};
}
auto next = pos;
++next;
return {pos, next};
}
std::pair<const_iterator, const_iterator> equal_range(
key_type const& key) const
{
auto pos = table_.find(key);
if (pos == table_.end()) {
return {pos, pos};
}
auto next = pos;
++next;
return {pos, next};
}
template <class K>
typename std::enable_if<
detail::are_transparent<K, hasher, key_equal>::value,
std::pair<iterator, iterator> >::type
equal_range(K const& key)
{
auto pos = table_.find(key);
if (pos == table_.end()) {
return {pos, pos};
}
auto next = pos;
++next;
return {pos, next};
}
template <class K>
typename std::enable_if<
detail::are_transparent<K, hasher, key_equal>::value,
std::pair<const_iterator, const_iterator> >::type
equal_range(K const& key) const
{
auto pos = table_.find(key);
if (pos == table_.end()) {
return {pos, pos};
}
auto next = pos;
++next;
return {pos, next};
}
/// Hash Policy
///
size_type bucket_count() const noexcept { return table_.capacity(); }
float load_factor() const noexcept { return table_.load_factor(); }
float max_load_factor() const noexcept
{
return table_.max_load_factor();
}
void max_load_factor(float) {}
size_type max_load() const noexcept { return table_.max_load(); }
void rehash(size_type n) { table_.rehash(n); }
void reserve(size_type n) { table_.reserve(n); }
/// Observers
///
hasher hash_function() const { return table_.hash_function(); }
key_equal key_eq() const { return table_.key_eq(); }
};
template <class Key, class Hash, class KeyEqual, class Allocator>
bool operator==(
unordered_node_set<Key, Hash, KeyEqual, Allocator> const& lhs,
unordered_node_set<Key, Hash, KeyEqual, Allocator> const& rhs)
{
return lhs.table_ == rhs.table_;
}
template <class Key, class Hash, class KeyEqual, class Allocator>
bool operator!=(
unordered_node_set<Key, Hash, KeyEqual, Allocator> const& lhs,
unordered_node_set<Key, Hash, KeyEqual, Allocator> const& rhs)
{
return !(lhs == rhs);
}
template <class Key, class Hash, class KeyEqual, class Allocator>
void swap(unordered_node_set<Key, Hash, KeyEqual, Allocator>& lhs,
unordered_node_set<Key, Hash, KeyEqual, Allocator>& rhs)
noexcept(noexcept(lhs.swap(rhs)))
{
lhs.swap(rhs);
}
template <class Key, class Hash, class KeyEqual, class Allocator,
class Pred>
typename unordered_node_set<Key, Hash, KeyEqual, Allocator>::size_type
erase_if(unordered_node_set<Key, Hash, KeyEqual, Allocator>& set, Pred pred)
{
return erase_if(set.table_, pred);
}
template <class Archive, class Key, class Hash, class KeyEqual,
class Allocator>
void serialize(Archive& ar,
unordered_node_set<Key, Hash, KeyEqual, Allocator>& set,
unsigned int version)
{
detail::serialize_container(ar, set, version);
}
#if defined(BOOST_MSVC)
#pragma warning(pop) /* C4714 */
#endif
#if BOOST_UNORDERED_TEMPLATE_DEDUCTION_GUIDES
template <class InputIterator,
class Hash =
boost::hash<typename std::iterator_traits<InputIterator>::value_type>,
class Pred =
std::equal_to<typename std::iterator_traits<InputIterator>::value_type>,
class Allocator = std::allocator<
typename std::iterator_traits<InputIterator>::value_type>,
class = std::enable_if_t<detail::is_input_iterator_v<InputIterator> >,
class = std::enable_if_t<detail::is_hash_v<Hash> >,
class = std::enable_if_t<detail::is_pred_v<Pred> >,
class = std::enable_if_t<detail::is_allocator_v<Allocator> > >
unordered_node_set(InputIterator, InputIterator,
std::size_t = boost::unordered::detail::foa::default_bucket_count,
Hash = Hash(), Pred = Pred(), Allocator = Allocator())
-> unordered_node_set<
typename std::iterator_traits<InputIterator>::value_type, Hash, Pred,
Allocator>;
template <class T, class Hash = boost::hash<T>,
class Pred = std::equal_to<T>, class Allocator = std::allocator<T>,
class = std::enable_if_t<detail::is_hash_v<Hash> >,
class = std::enable_if_t<detail::is_pred_v<Pred> >,
class = std::enable_if_t<detail::is_allocator_v<Allocator> > >
unordered_node_set(std::initializer_list<T>,
std::size_t = boost::unordered::detail::foa::default_bucket_count,
Hash = Hash(), Pred = Pred(), Allocator = Allocator())
-> unordered_node_set<T, Hash, Pred, Allocator>;
template <class InputIterator, class Allocator,
class = std::enable_if_t<detail::is_input_iterator_v<InputIterator> >,
class = std::enable_if_t<detail::is_allocator_v<Allocator> > >
unordered_node_set(InputIterator, InputIterator, std::size_t, Allocator)
-> unordered_node_set<
typename std::iterator_traits<InputIterator>::value_type,
boost::hash<typename std::iterator_traits<InputIterator>::value_type>,
std::equal_to<typename std::iterator_traits<InputIterator>::value_type>,
Allocator>;
template <class InputIterator, class Hash, class Allocator,
class = std::enable_if_t<detail::is_hash_v<Hash> >,
class = std::enable_if_t<detail::is_input_iterator_v<InputIterator> >,
class = std::enable_if_t<detail::is_allocator_v<Allocator> > >
unordered_node_set(
InputIterator, InputIterator, std::size_t, Hash, Allocator)
-> unordered_node_set<
typename std::iterator_traits<InputIterator>::value_type, Hash,
std::equal_to<typename std::iterator_traits<InputIterator>::value_type>,
Allocator>;
template <class T, class Allocator,
class = std::enable_if_t<detail::is_allocator_v<Allocator> > >
unordered_node_set(std::initializer_list<T>, std::size_t, Allocator)
-> unordered_node_set<T, boost::hash<T>, std::equal_to<T>, Allocator>;
template <class T, class Hash, class Allocator,
class = std::enable_if_t<detail::is_hash_v<Hash> >,
class = std::enable_if_t<detail::is_allocator_v<Allocator> > >
unordered_node_set(std::initializer_list<T>, std::size_t, Hash, Allocator)
-> unordered_node_set<T, Hash, std::equal_to<T>, Allocator>;
template <class InputIterator, class Allocator,
class = std::enable_if_t<detail::is_input_iterator_v<InputIterator> >,
class = std::enable_if_t<detail::is_allocator_v<Allocator> > >
unordered_node_set(InputIterator, InputIterator, Allocator)
-> unordered_node_set<
typename std::iterator_traits<InputIterator>::value_type,
boost::hash<typename std::iterator_traits<InputIterator>::value_type>,
std::equal_to<typename std::iterator_traits<InputIterator>::value_type>,
Allocator>;
template <class T, class Allocator,
class = std::enable_if_t<detail::is_allocator_v<Allocator> > >
unordered_node_set(std::initializer_list<T>, Allocator)
-> unordered_node_set<T, boost::hash<T>, std::equal_to<T>, Allocator>;
#endif
} // namespace unordered
} // namespace boost
#endif
+44
View File
@@ -0,0 +1,44 @@
// Copyright (C) 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)
#ifndef BOOST_UNORDERED_NODE_SET_FWD_HPP_INCLUDED
#define BOOST_UNORDERED_NODE_SET_FWD_HPP_INCLUDED
#include <boost/config.hpp>
#if defined(BOOST_HAS_PRAGMA_ONCE)
#pragma once
#endif
#include <boost/container_hash/hash_fwd.hpp>
#include <functional>
#include <memory>
namespace boost {
namespace unordered {
template <class Key, class Hash = boost::hash<Key>,
class KeyEqual = std::equal_to<Key>,
class Allocator = std::allocator<Key> >
class unordered_node_set;
template <class Key, class Hash, class KeyEqual, class Allocator>
bool operator==(
unordered_node_set<Key, Hash, KeyEqual, Allocator> const& lhs,
unordered_node_set<Key, Hash, KeyEqual, Allocator> const& rhs);
template <class Key, class Hash, class KeyEqual, class Allocator>
bool operator!=(
unordered_node_set<Key, Hash, KeyEqual, Allocator> const& lhs,
unordered_node_set<Key, Hash, KeyEqual, Allocator> const& rhs);
template <class Key, class Hash, class KeyEqual, class Allocator>
void swap(unordered_node_set<Key, Hash, KeyEqual, Allocator>& lhs,
unordered_node_set<Key, Hash, KeyEqual, Allocator>& rhs)
noexcept(noexcept(lhs.swap(rhs)));
} // namespace unordered
using boost::unordered::unordered_node_set;
} // namespace boost
#endif
File diff suppressed because it is too large Load Diff
+65
View File
@@ -0,0 +1,65 @@
// Copyright (C) 2008-2011 Daniel James.
// Copyright (C) 2022 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)
#ifndef BOOST_UNORDERED_SET_FWD_HPP_INCLUDED
#define BOOST_UNORDERED_SET_FWD_HPP_INCLUDED
#include <boost/config.hpp>
#if defined(BOOST_HAS_PRAGMA_ONCE)
#pragma once
#endif
#include <boost/container_hash/hash_fwd.hpp>
#include <functional>
#include <memory>
namespace boost {
namespace unordered {
template <class T, class H = boost::hash<T>, class P = std::equal_to<T>,
class A = std::allocator<T> >
class unordered_set;
template <class T, class H, class P, class A>
inline bool operator==(
unordered_set<T, H, P, A> const&, unordered_set<T, H, P, A> const&);
template <class T, class H, class P, class A>
inline bool operator!=(
unordered_set<T, H, P, A> const&, unordered_set<T, H, P, A> const&);
template <class T, class H, class P, class A>
inline void swap(unordered_set<T, H, P, A>& m1,
unordered_set<T, H, P, A>& m2) noexcept(noexcept(m1.swap(m2)));
template <class K, class H, class P, class A, class Predicate>
typename unordered_set<K, H, P, A>::size_type erase_if(
unordered_set<K, H, P, A>& c, Predicate pred);
template <class T, class H = boost::hash<T>, class P = std::equal_to<T>,
class A = std::allocator<T> >
class unordered_multiset;
template <class T, class H, class P, class A>
inline bool operator==(unordered_multiset<T, H, P, A> const&,
unordered_multiset<T, H, P, A> const&);
template <class T, class H, class P, class A>
inline bool operator!=(unordered_multiset<T, H, P, A> const&,
unordered_multiset<T, H, P, A> const&);
template <class T, class H, class P, class A>
inline void swap(unordered_multiset<T, H, P, A>& m1,
unordered_multiset<T, H, P, A>& m2) noexcept(noexcept(m1.swap(m2)));
template <class K, class H, class P, class A, class Predicate>
typename unordered_multiset<K, H, P, A>::size_type erase_if(
unordered_multiset<K, H, P, A>& c, Predicate pred);
template <class N, class T, class A> class node_handle_set;
template <class Iter, class NodeType> struct insert_return_type_set;
} // namespace unordered
using boost::unordered::unordered_multiset;
using boost::unordered::unordered_set;
} // namespace boost
#endif