update boost

This commit is contained in:
2023-11-24 12:56:13 -06:00
parent cfc99971af
commit 19d727037a
9260 changed files with 849256 additions and 299957 deletions

View File

@@ -0,0 +1,849 @@
// Copyright (C) 2022 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/core/addressof.hpp>
#include <boost/core/allocator_access.hpp>
#include <boost/core/bit.hpp>
#include <boost/core/empty_value.hpp>
#include <boost/core/no_exceptions_support.hpp>
#include <boost/cstdint.hpp>
#include <boost/move/core.hpp>
#include <boost/move/utility_core.hpp>
#include <boost/swap.hpp>
#include <boost/type_traits/aligned_storage.hpp>
#include <boost/type_traits/alignment_of.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;
typename boost::aligned_storage<sizeof(value_type),
boost::alignment_of<value_type>::value>::type buf;
node() BOOST_NOEXCEPT : next(), buf() {}
value_type* value_ptr() BOOST_NOEXCEPT
{
return reinterpret_cast<value_type*>(buf.address());
}
value_type& value() BOOST_NOEXCEPT
{
return *reinterpret_cast<value_type*>(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() BOOST_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() BOOST_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 BOOST_NOEXCEPT { return dereference(); }
pointer operator->() const BOOST_NOEXCEPT
{
return boost::to_address(p);
}
grouped_bucket_iterator& operator++() BOOST_NOEXCEPT
{
increment();
return *this;
}
grouped_bucket_iterator operator++(int) BOOST_NOEXCEPT
{
grouped_bucket_iterator old = *this;
increment();
return old;
}
bool operator==(
grouped_bucket_iterator const& other) const BOOST_NOEXCEPT
{
return equal(other);
}
bool operator!=(
grouped_bucket_iterator const& other) const BOOST_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 BOOST_NOEXCEPT { return *p; }
bool equal(const grouped_bucket_iterator& x) const BOOST_NOEXCEPT
{
return p == x.p;
}
void increment() BOOST_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 <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 BOOST_NOEXCEPT { return dereference(); }
pointer operator->() const BOOST_NOEXCEPT
{
return boost::to_address(p);
}
grouped_local_bucket_iterator& operator++() BOOST_NOEXCEPT
{
increment();
return *this;
}
grouped_local_bucket_iterator operator++(int) BOOST_NOEXCEPT
{
grouped_local_bucket_iterator old = *this;
increment();
return old;
}
bool operator==(
grouped_local_bucket_iterator const& other) const BOOST_NOEXCEPT
{
return equal(other);
}
bool operator!=(
grouped_local_bucket_iterator const& other) const BOOST_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 BOOST_NOEXCEPT { return p->value(); }
bool equal(const grouped_local_bucket_iterator& x) const BOOST_NOEXCEPT
{
return p == x.p;
}
void increment() BOOST_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 BOOST_NOEXCEPT { return dereference(); }
pointer operator->() const BOOST_NOEXCEPT
{
return boost::to_address(p);
}
const_grouped_local_bucket_iterator& operator++() BOOST_NOEXCEPT
{
increment();
return *this;
}
const_grouped_local_bucket_iterator operator++(int) BOOST_NOEXCEPT
{
const_grouped_local_bucket_iterator old = *this;
increment();
return old;
}
bool operator==(
const_grouped_local_bucket_iterator const& other) const BOOST_NOEXCEPT
{
return equal(other);
}
bool operator!=(
const_grouped_local_bucket_iterator const& other) const BOOST_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 BOOST_NOEXCEPT { return p->value(); }
bool equal(
const const_grouped_local_bucket_iterator& x) const BOOST_NOEXCEPT
{
return p == x.p;
}
void increment() BOOST_NOEXCEPT { p = p->next; }
node_pointer p;
};
template <class T> struct span
{
T* begin() const BOOST_NOEXCEPT { return data; }
T* end() const BOOST_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>
{
BOOST_MOVABLE_BUT_NOT_COPYABLE(grouped_bucket_array)
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(
BOOST_RV_REF(grouped_bucket_array) other) BOOST_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=(
BOOST_RV_REF(grouped_bucket_array) other) BOOST_NOEXCEPT
{
BOOST_ASSERT(
this->get_node_allocator() == other.get_node_allocator());
if (this == boost::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;
}
void deallocate() BOOST_NOEXCEPT
{
if (buckets) {
bucket_allocator_type bucket_alloc = this->get_bucket_allocator();
boost::allocator_deallocate(
bucket_alloc, buckets, this->buckets_len());
buckets = bucket_pointer();
}
if (groups) {
group_allocator_type group_alloc = this->get_group_allocator();
boost::allocator_deallocate(
group_alloc, groups, this->groups_len());
groups = group_pointer();
}
}
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::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 BOOST_NOEXCEPT { return size_ + 1; }
size_type groups_len() const BOOST_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 BOOST_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) BOOST_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) BOOST_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) BOOST_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) BOOST_NOEXCEPT
{
node_pointer* pp = boost::addressof(itb->next);
while ((*pp) != p)
pp = boost::addressof((*pp)->next);
*pp = p->next;
if (!itb->next)
unlink_bucket(itb);
}
void extract_node_after(iterator itb, node_pointer* pp) BOOST_NOEXCEPT
{
*pp = (*pp)->next;
if (!itb->next)
unlink_bucket(itb);
}
void unlink_empty_buckets() BOOST_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

View File

@@ -0,0 +1,60 @@
/* 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
namespace boost{
namespace unordered{
namespace detail{
namespace foa{
template<class T>
struct element_type
{
using value_type=T;
value_type* 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(value_type* 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

View File

@@ -0,0 +1,73 @@
// 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 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

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

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

View File

@@ -0,0 +1,210 @@
/* 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/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 T>
union opt_storage {
BOOST_ATTRIBUTE_NO_UNIQUE_ADDRESS T t_;
opt_storage(){}
~opt_storage(){}
};
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

View File

@@ -0,0 +1,132 @@
// 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/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> 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>;
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::to_address(boost::allocator_allocate(al, 1));
BOOST_TRY
{
boost::allocator_construct(al, p->p, std::forward<Args>(args)...);
}
BOOST_CATCH(...)
{
using pointer_type = typename boost::allocator_pointer<A>::type;
using pointer_traits = boost::pointer_traits<pointer_type>;
boost::allocator_deallocate(
al, pointer_traits::pointer_to(*(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) {
using pointer_type = typename boost::allocator_pointer<A>::type;
using pointer_traits = boost::pointer_traits<pointer_type>;
destroy(al, p->p);
boost::allocator_deallocate(
al, pointer_traits::pointer_to(*(p->p)), 1);
}
}
};
} // namespace foa
} // namespace detail
} // namespace unordered
} // namespace boost
#endif // BOOST_UNORDERED_DETAIL_FOA_NODE_MAP_TYPES_HPP

View File

@@ -0,0 +1,95 @@
// 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/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> 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>;
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::to_address(boost::allocator_allocate(al, 1));
BOOST_TRY
{
boost::allocator_construct(al, p->p, std::forward<Args>(args)...);
}
BOOST_CATCH(...)
{
boost::allocator_deallocate(al,
boost::pointer_traits<typename boost::allocator_pointer<
A>::type>::pointer_to(*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, p->p);
boost::allocator_deallocate(al,
boost::pointer_traits<typename boost::allocator_pointer<
A>::type>::pointer_to(*(p->p)),
1);
}
}
};
} // namespace foa
} // namespace detail
} // namespace unordered
} // namespace boost
#endif // BOOST_UNORDERED_DETAIL_FOA_NODE_SET_TYPES_HPP

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

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

View File

@@ -0,0 +1,556 @@
/* 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/unordered/detail/foa/core.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 Group,bool Const>
class table_iterator
{
using type_policy=TypePolicy;
using table_element_type=typename type_policy::element_type;
using group_type=Group;
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()=default;
template<bool Const2,typename std::enable_if<!Const2>::type* =nullptr>
table_iterator(const table_iterator<TypePolicy,Group,Const2>& x):
pc{x.pc},p{x.p}{}
table_iterator(
const_iterator_cast_tag, const table_iterator<TypePolicy,Group,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* pg,std::size_t n,const table_element_type* p_):
pc{reinterpret_cast<unsigned char*>(const_cast<group_type*>(pg))+n},
p{const_cast<table_element_type*>(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
{
for(;;){
++p;
if(reinterpret_cast<uintptr_t>(pc)%sizeof(group_type)==N-1){
pc+=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+=n;
p+=n;
}
return;
}
pc+=sizeof(group_type);
p+=N;
}
}
inline void increment(std::false_type /* interleaved */)noexcept
{
std::size_t n0=reinterpret_cast<uintptr_t>(pc)%sizeof(group_type);
pc-=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+=n;
p-=n0;
p+=n;
}
}
unsigned char *pc=nullptr;
table_element_type *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 Group,bool Const>
class table_erase_return_type<table_iterator<TypePolicy,Group,Const>>
{
using iterator=table_iterator<TypePolicy,Group,Const>;
using const_iterator=table_iterator<TypePolicy,Group,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 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 locator=typename super::locator;
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,true>;
using iterator=typename std::conditional<
has_mutable_iterator,
table_iterator<type_policy,group_type,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()=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:
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

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

View File

@@ -1,5 +1,6 @@
// Copyright (C) 2008-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)
@@ -31,7 +32,7 @@
// 2012 = VC+11 = BOOST_MSVC 1700 Hopefully!
// I have no idea when Dinkumware added it, probably a lot
// earlier than this check.
#if BOOST_LIB_STD_DINKUMWARE >= BOOST_VERSION_NUMBER(6, 50, 0) || \
#if BOOST_LIB_STD_DINKUMWARE >= BOOST_VERSION_NUMBER(6, 10, 0) || \
BOOST_COMP_MSVC >= BOOST_VERSION_NUMBER(17, 0, 0)
#define BOOST_UNORDERED_HAVE_PIECEWISE_CONSTRUCT 1
#endif
@@ -60,4 +61,90 @@ namespace boost {
}
}
// BOOST_UNORDERED_EMPLACE_LIMIT = The maximum number of parameters in
// emplace (not including things like hints). Don't set it to a lower value, as
// that might break something.
#if !defined BOOST_UNORDERED_EMPLACE_LIMIT
#define BOOST_UNORDERED_EMPLACE_LIMIT 10
#endif
////////////////////////////////////////////////////////////////////////////////
// Configuration
//
// Unless documented elsewhere these configuration macros should be considered
// an implementation detail, I'll try not to break them, but you never know.
// Use Sun C++ workarounds
// I'm not sure which versions of the compiler require these workarounds, so
// I'm just using them of everything older than the current test compilers
// (as of May 2017).
#if !defined(BOOST_UNORDERED_SUN_WORKAROUNDS1)
#if BOOST_COMP_SUNPRO && BOOST_COMP_SUNPRO < BOOST_VERSION_NUMBER(5, 20, 0)
#define BOOST_UNORDERED_SUN_WORKAROUNDS1 1
#else
#define BOOST_UNORDERED_SUN_WORKAROUNDS1 0
#endif
#endif
// BOOST_UNORDERED_TUPLE_ARGS
//
// Maximum number of std::tuple members to support, or 0 if std::tuple
// isn't avaiable. More are supported when full C++11 is used.
// Already defined, so do nothing
#if defined(BOOST_UNORDERED_TUPLE_ARGS)
// Assume if we have C++11 tuple it's properly variadic,
// and just use a max number of 10 arguments.
#elif !defined(BOOST_NO_CXX11_HDR_TUPLE)
#define BOOST_UNORDERED_TUPLE_ARGS 10
// Visual C++ has a decent enough tuple for piecewise construction,
// so use that if available, using _VARIADIC_MAX for the maximum
// number of parameters. Note that this comes after the check
// for a full C++11 tuple.
#elif defined(BOOST_MSVC)
#if !BOOST_UNORDERED_HAVE_PIECEWISE_CONSTRUCT
#define BOOST_UNORDERED_TUPLE_ARGS 0
#elif defined(_VARIADIC_MAX)
#define BOOST_UNORDERED_TUPLE_ARGS _VARIADIC_MAX
#else
#define BOOST_UNORDERED_TUPLE_ARGS 5
#endif
// Assume that we don't have std::tuple
#else
#define BOOST_UNORDERED_TUPLE_ARGS 0
#endif
#if BOOST_UNORDERED_TUPLE_ARGS
#include <tuple>
#endif
// BOOST_UNORDERED_CXX11_CONSTRUCTION
//
// Use C++11 construction, requires variadic arguments, good construct support
// in allocator_traits and piecewise construction of std::pair
// Otherwise allocators aren't used for construction/destruction
#if BOOST_UNORDERED_HAVE_PIECEWISE_CONSTRUCT && \
!defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES) && BOOST_UNORDERED_TUPLE_ARGS
#if BOOST_COMP_SUNPRO && BOOST_LIB_STD_GNU
// Sun C++ std::pair piecewise construction doesn't seem to be exception safe.
// (At least for Sun C++ 12.5 using libstdc++).
#define BOOST_UNORDERED_CXX11_CONSTRUCTION 0
#elif BOOST_COMP_GNUC && BOOST_COMP_GNUC < BOOST_VERSION_NUMBER(4, 7, 0)
// Piecewise construction in GCC 4.6 doesn't work for uncopyable types.
#define BOOST_UNORDERED_CXX11_CONSTRUCTION 0
#elif !defined(BOOST_NO_CXX11_ALLOCATOR)
#define BOOST_UNORDERED_CXX11_CONSTRUCTION 1
#endif
#endif
#if !defined(BOOST_UNORDERED_CXX11_CONSTRUCTION)
#define BOOST_UNORDERED_CXX11_CONSTRUCTION 0
#endif
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -1,5 +1,6 @@
// 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)
@@ -25,25 +26,18 @@ namespace boost {
typedef boost::unordered::detail::allocator_traits<value_allocator>
value_allocator_traits;
typedef boost::unordered::detail::pick_node<A, value_type> pick;
typedef typename pick::node node;
typedef typename pick::bucket bucket;
typedef typename pick::link_pointer link_pointer;
typedef boost::unordered::detail::table<types> table;
typedef boost::unordered::detail::map_extractor<value_type> extractor;
typedef typename boost::unordered::detail::pick_policy<K>::type policy;
typedef typename boost::allocator_void_pointer<value_allocator>::type
void_pointer;
typedef boost::unordered::iterator_detail::iterator<node> iterator;
typedef boost::unordered::iterator_detail::c_iterator<node> c_iterator;
typedef boost::unordered::iterator_detail::l_iterator<node> l_iterator;
typedef boost::unordered::iterator_detail::cl_iterator<node>
cl_iterator;
typedef boost::unordered::node_handle_map<
node<value_type, void_pointer>, K, M, A>
node_type;
typedef boost::unordered::node_handle_map<node, K, M, A> node_type;
typedef boost::unordered::insert_return_type_map<node, K, M, A>
insert_return_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>

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

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/config.hpp>
#include <boost/static_assert.hpp>
#include <boost/type_traits/is_integral.hpp>
#include <boost/type_traits/make_unsigned.hpp>
namespace boost{
namespace unordered{
namespace detail{
template<typename To,typename From>
BOOST_CONSTEXPR To narrow_cast(From x) BOOST_NOEXCEPT
{
BOOST_STATIC_ASSERT(boost::is_integral<From>::value);
BOOST_STATIC_ASSERT(boost::is_integral<To>::value);
BOOST_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 boost::make_unsigned<To>::type>(~static_cast<To>(0))
#endif
);
}
} /* namespace detail */
} /* namespace unordered */
} /* namespace boost */
#endif

View File

@@ -0,0 +1,263 @@
// Copyright (C) 2022 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_PRIME_FMOD_HPP
#define BOOST_UNORDERED_DETAIL_PRIME_FMOD_HPP
#include <boost/cstdint.hpp>
#include <boost/preprocessor/seq/enum.hpp>
#include <boost/preprocessor/seq/for_each.hpp>
#include <boost/preprocessor/seq/size.hpp>
#include <boost/unordered/detail/narrow_cast.hpp>
#include <boost/config.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
{
// Because we compile for C++03, we don't have access to any inline
// initialization for array data members so the definitions must exist
// out-of-line. To keep the library header-only, we introduce a dummy
// template parameter which permits the definition to be included in
// multiple TUs without conflict.
//
static std::size_t sizes[];
static std::size_t const sizes_len;
static std::size_t (*positions[])(std::size_t);
#if defined(BOOST_UNORDERED_FCA_HAS_64B_SIZE_T)
static boost::uint64_t inv_sizes32[];
static std::size_t const inv_sizes32_len;
#endif /* defined(BOOST_UNORDERED_FCA_HAS_64B_SIZE_T) */
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];
}
template <std::size_t Size> static std::size_t modulo(std::size_t hash)
{
return hash % Size;
}
#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
#define BOOST_UNORDERED_PRIME_FMOD_SIZES_32BIT_INCOMPLETE \
(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)
#define BOOST_UNORDERED_PRIME_FMOD_SIZES_32BIT \
BOOST_UNORDERED_PRIME_FMOD_SIZES_32BIT_INCOMPLETE(4294967291ul)
#define BOOST_UNORDERED_PRIME_FMOD_SIZES_64BIT
#else
#define BOOST_UNORDERED_PRIME_FMOD_SIZES_32BIT \
BOOST_UNORDERED_PRIME_FMOD_SIZES_32BIT_INCOMPLETE
// The original sequence here is this:
// (6442450939ul)
// (12884901893ul)
// (25769803751ul)
// (51539607551ul)
// (103079215111ul)
// (206158430209ul)
// (412316860441ul)
// (824633720831ul)
// (1649267441651ul)
//
// but this causes problems on versions of mingw where the `long` type is 32
// bits, even for 64-bit targets. We work around this by replacing the literals
// with compile-time arithmetic, using bitshifts to reconstruct the number.
//
// clang-format off
#define BOOST_UNORDERED_PRIME_FMOD_SIZES_64BIT \
((boost::ulong_long_type(1ul) << 32) + boost::ulong_long_type(2147483643ul)) \
((boost::ulong_long_type(3ul) << 32) + boost::ulong_long_type(5ul)) \
((boost::ulong_long_type(5ul) << 32) + boost::ulong_long_type(4294967271ul)) \
((boost::ulong_long_type(11ul) << 32) + boost::ulong_long_type(4294967295ul)) \
((boost::ulong_long_type(24ul) << 32) + boost::ulong_long_type(7ul)) \
((boost::ulong_long_type(48ul) << 32) + boost::ulong_long_type(1ul)) \
((boost::ulong_long_type(96ul) << 32) + boost::ulong_long_type(25ul)) \
((boost::ulong_long_type(191ul) << 32) + boost::ulong_long_type(4294967295ul)) \
((boost::ulong_long_type(383ul) << 32) + boost::ulong_long_type(4294967283ul))
// clang-format on
#endif /* BOOST_UNORDERED_FCA_HAS_64B_SIZE_T */
#define BOOST_UNORDERED_PRIME_FMOD_SIZES \
BOOST_UNORDERED_PRIME_FMOD_SIZES_32BIT BOOST_UNORDERED_PRIME_FMOD_SIZES_64BIT
template <class T>
std::size_t prime_fmod_size<T>::sizes[] = {
BOOST_PP_SEQ_ENUM(BOOST_UNORDERED_PRIME_FMOD_SIZES)};
template <class T>
std::size_t const prime_fmod_size<T>::sizes_len = BOOST_PP_SEQ_SIZE(
BOOST_UNORDERED_PRIME_FMOD_SIZES);
// Similarly here, we have to re-express the integer initialization using
// arithmetic such that each literal can fit in a 32-bit value.
//
#if defined(BOOST_UNORDERED_FCA_HAS_64B_SIZE_T)
// clang-format off
template <class T>
boost::uint64_t prime_fmod_size<T>::inv_sizes32[] = {
(boost::ulong_long_type(330382099ul) << 32) + boost::ulong_long_type(2973438898ul) /* = 1418980313362273202 */,
(boost::ulong_long_type(148102320ul) << 32) + boost::ulong_long_type(2369637129ul) /* = 636094623231363849 */,
(boost::ulong_long_type(81037118ul) << 32) + boost::ulong_long_type(3403558990ul) /* = 348051774975651918 */,
(boost::ulong_long_type(44278013ul) << 32) + boost::ulong_long_type(1549730468ul) /* = 190172619316593316 */,
(boost::ulong_long_type(22253716ul) << 32) + boost::ulong_long_type(2403401389ul) /* = 95578984837873325 */,
(boost::ulong_long_type(11041047ul) << 32) + boost::ulong_long_type(143533612ul) /* = 47420935922132524 */,
(boost::ulong_long_type(5585133ul) << 32) + boost::ulong_long_type(106117528ul) /* = 23987963684927896 */,
(boost::ulong_long_type(2783517ul) << 32) + boost::ulong_long_type(1572687312ul) /* = 11955116055547344 */,
(boost::ulong_long_type(1394922ul) << 32) + boost::ulong_long_type(3428720239ul) /* = 5991147799191151 */,
(boost::ulong_long_type(698255ul) << 32) + boost::ulong_long_type(552319807ul) /* = 2998982941588287 */,
(boost::ulong_long_type(349496ul) << 32) + boost::ulong_long_type(3827689953ul) /* = 1501077717772769 */,
(boost::ulong_long_type(174641ul) << 32) + boost::ulong_long_type(3699438549ul) /* = 750081082979285 */,
(boost::ulong_long_type(87372ul) << 32) + boost::ulong_long_type(1912757574ul) /* = 375261795343686 */,
(boost::ulong_long_type(43684ul) << 32) + boost::ulong_long_type(3821029929ul) /* = 187625172388393 */,
(boost::ulong_long_type(21844ul) << 32) + boost::ulong_long_type(3340590800ul) /* = 93822606204624 */,
(boost::ulong_long_type(10921ul) << 32) + boost::ulong_long_type(4175852267ul) /* = 46909513691883 */,
(boost::ulong_long_type(5461ul) << 32) + boost::ulong_long_type(1401829642ul) /* = 23456218233098 */,
(boost::ulong_long_type(2730ul) << 32) + boost::ulong_long_type(2826028947ul) /* = 11728086747027 */,
(boost::ulong_long_type(1365ul) << 32) + boost::ulong_long_type(1411150351ul) /* = 5864041509391 */,
(boost::ulong_long_type(682ul) << 32) + boost::ulong_long_type(2857253105ul) /* = 2932024948977 */,
(boost::ulong_long_type(341ul) << 32) + boost::ulong_long_type(1431073224ul) /* = 1466014921160 */,
(boost::ulong_long_type(170ul) << 32) + boost::ulong_long_type(2862758116ul) /* = 733007198436 */,
(boost::ulong_long_type(85ul) << 32) + boost::ulong_long_type(1431619357ul) /* = 366503839517 */,
(boost::ulong_long_type(42ul) << 32) + boost::ulong_long_type(2863269661ul) /* = 183251896093 */,
(boost::ulong_long_type(21ul) << 32) + boost::ulong_long_type(1431647119ul) /* = 91625960335 */,
(boost::ulong_long_type(10ul) << 32) + boost::ulong_long_type(2863310962ul) /* = 45812983922 */,
(boost::ulong_long_type(5ul) << 32) + boost::ulong_long_type(1431653234ul) /* = 22906489714 */,
(boost::ulong_long_type(2ul) << 32) + boost::ulong_long_type(2863311496ul) /* = 11453246088 */,
(boost::ulong_long_type(1ul) << 32) + boost::ulong_long_type(1431655764ul) /* = 5726623060 */,
};
// clang-format on
template <class T>
std::size_t const
prime_fmod_size<T>::inv_sizes32_len = sizeof(inv_sizes32) /
sizeof(inv_sizes32[0]);
#endif /* defined(BOOST_UNORDERED_FCA_HAS_64B_SIZE_T) */
#define BOOST_UNORDERED_PRIME_FMOD_POSITIONS_ELEMENT(z, _, n) \
prime_fmod_size<T>::template modulo<n>,
template <class T>
std::size_t (*prime_fmod_size<T>::positions[])(std::size_t) = {
#if !defined(BOOST_UNORDERED_FCA_HAS_64B_SIZE_T)
BOOST_PP_SEQ_FOR_EACH(BOOST_UNORDERED_PRIME_FMOD_POSITIONS_ELEMENT, ~,
BOOST_UNORDERED_PRIME_FMOD_SIZES_32BIT)
#else
BOOST_PP_SEQ_FOR_EACH(BOOST_UNORDERED_PRIME_FMOD_POSITIONS_ELEMENT, ~,
BOOST_UNORDERED_PRIME_FMOD_SIZES_64BIT)
#endif
};
#undef BOOST_UNORDERED_PRIME_FMOD_POSITIONS_ELEMENT
#undef BOOST_UNORDERED_PRIME_FMOD_SIZES
#undef BOOST_UNORDERED_PRIME_FMOD_SIZES_64BIT
#undef BOOST_UNORDERED_PRIME_FMOD_SIZES_32BIT
#undef BOOST_UNORDERED_PRIME_FMOD_SIZES_32BIT_INCOMPLETE
} // namespace detail
} // namespace unordered
} // namespace boost
#endif // BOOST_UNORDERED_DETAIL_PRIME_FMOD_HPP

View File

@@ -0,0 +1,21 @@
#ifndef BOOST_UNORDERED_DETAIL_REQUIRES_CXX11_HPP_INCLUDED
#define BOOST_UNORDERED_DETAIL_REQUIRES_CXX11_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/config.hpp>
#include <boost/config/pragma_message.hpp>
#if defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES) || \
defined(BOOST_NO_CXX11_RVALUE_REFERENCES) || \
defined(BOOST_NO_CXX11_DECLTYPE) || \
defined(BOOST_NO_CXX11_CONSTEXPR) || \
defined(BOOST_NO_CXX11_NOEXCEPT)
BOOST_PRAGMA_MESSAGE("C++03 support is deprecated in Boost.Unordered 1.82 and will be removed in Boost.Unordered 1.84.")
#endif
#endif // #ifndef BOOST_UNORDERED_DETAIL_REQUIRES_CXX11_HPP_INCLUDED

View File

@@ -1,5 +1,6 @@
// 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)
@@ -24,24 +25,18 @@ namespace boost {
typedef boost::unordered::detail::allocator_traits<value_allocator>
value_allocator_traits;
typedef boost::unordered::detail::pick_node<A, value_type> pick;
typedef typename pick::node node;
typedef typename pick::bucket bucket;
typedef typename pick::link_pointer link_pointer;
typedef boost::unordered::detail::table<types> table;
typedef boost::unordered::detail::set_extractor<value_type> extractor;
typedef typename boost::unordered::detail::pick_policy<T>::type policy;
typedef typename boost::allocator_void_pointer<value_allocator>::type
void_pointer;
typedef boost::unordered::iterator_detail::c_iterator<node> iterator;
typedef boost::unordered::iterator_detail::c_iterator<node> c_iterator;
typedef boost::unordered::iterator_detail::cl_iterator<node> l_iterator;
typedef boost::unordered::iterator_detail::cl_iterator<node>
cl_iterator;
typedef boost::unordered::node_handle_set<
node<value_type, void_pointer>, T, A>
node_type;
typedef boost::unordered::node_handle_set<node, T, A> node_type;
typedef boost::unordered::insert_return_type_set<node, T, A>
typedef typename table::c_iterator iterator;
typedef boost::unordered::insert_return_type_set<iterator, node_type>
insert_return_type;
};

View File

@@ -0,0 +1,122 @@
// 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_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/type_traits/integral_constant.hpp>
#include <boost/type_traits/is_convertible.hpp>
#include <boost/type_traits/make_void.hpp>
#include <boost/type_traits/type_identity.hpp>
#if !defined(BOOST_NO_CXX17_DEDUCTION_GUIDES)
#include <boost/type_traits/enable_if.hpp>
#include <boost/type_traits/is_integral.hpp>
#include <boost/type_traits/remove_const.hpp>
#include <iterator>
#include <utility>
#endif
// 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 {
////////////////////////////////////////////////////////////////////////////
// Type checkers used for the transparent member functions added by C++20
// and up
template <class, class = void>
struct is_transparent : public boost::false_type
{
};
template <class T>
struct is_transparent<T,
typename boost::make_void<typename T::is_transparent>::type>
: public boost::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 &&
!boost::is_convertible<Key, iterator>::value &&
!boost::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 =
!boost::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::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 =
!boost::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

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