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
+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