1#ifndef ENTT_CONTAINER_DENSE_SET_HPP
2#define ENTT_CONTAINER_DENSE_SET_HPP
5#include "../config/config.h"
6#include "../core/bit.hpp"
7#include "../core/compressed_pair.hpp"
8#include "../core/type_traits.hpp"
9#include "../stl/bit.hpp"
10#include "../stl/cmath.hpp"
11#include "../stl/concepts.hpp"
12#include "../stl/cstddef.hpp"
13#include "../stl/functional.hpp"
14#include "../stl/iterator.hpp"
15#include "../stl/limits.hpp"
16#include "../stl/memory.hpp"
17#include "../stl/tuple.hpp"
18#include "../stl/type_traits.hpp"
19#include "../stl/utility.hpp"
20#include "../stl/vector.hpp"
28static constexpr stl::size_t dense_set_placeholder_position = (stl::numeric_limits<stl::size_t>::max)();
31class dense_set_iterator final {
33 friend class dense_set_iterator;
35 static_assert(stl::is_pointer_v<It>,
"Not a pointer type");
38 using value_type = stl::remove_const_t<stl::remove_pointer_t<It>>::second_type;
39 using pointer =
const value_type *;
40 using reference =
const value_type &;
41 using difference_type = stl::ptrdiff_t;
42 using iterator_category = stl::random_access_iterator_tag;
44 constexpr dense_set_iterator() noexcept
47 constexpr dense_set_iterator(
const It iter) noexcept
50 template<
typename Other>
51 requires (!stl::same_as<It, Other> && stl::constructible_from<It, Other>)
52 constexpr dense_set_iterator(
const dense_set_iterator<Other> &other) noexcept
55 constexpr dense_set_iterator &operator++() noexcept {
59 constexpr dense_set_iterator operator++(
int)
noexcept {
60 const dense_set_iterator orig = *
this;
61 return ++(*this), orig;
64 constexpr dense_set_iterator &operator--() noexcept {
68 constexpr dense_set_iterator operator--(
int)
noexcept {
69 const dense_set_iterator orig = *
this;
70 return operator--(), orig;
73 constexpr dense_set_iterator &operator+=(
const difference_type value)
noexcept {
78 constexpr dense_set_iterator
operator+(
const difference_type value)
const noexcept {
79 dense_set_iterator copy = *
this;
80 return (copy += value);
83 constexpr dense_set_iterator &operator-=(
const difference_type value)
noexcept {
84 return (*
this += -value);
87 constexpr dense_set_iterator operator-(
const difference_type value)
const noexcept {
88 return (*
this + -value);
91 [[nodiscard]]
constexpr reference operator[](
const difference_type value)
const noexcept {
92 return it[value].second;
95 [[nodiscard]]
constexpr pointer operator->() const noexcept {
96 return stl::addressof(
operator[](0));
99 [[nodiscard]]
constexpr reference operator*() const noexcept {
100 return operator[](0);
103 template<
typename Other>
104 [[nodiscard]]
constexpr stl::ptrdiff_t operator-(
const dense_set_iterator<Other> &other)
const noexcept {
105 return it - other.it;
108 template<
typename Other>
109 [[nodiscard]]
constexpr bool operator==(
const dense_set_iterator<Other> &other)
const noexcept {
110 return it == other.it;
113 template<
typename Other>
114 [[nodiscard]]
constexpr auto operator<=>(
const dense_set_iterator<Other> &other)
const noexcept {
115 return it <=> other.it;
123class dense_set_local_iterator final {
125 friend class dense_set_local_iterator;
127 static_assert(stl::is_pointer_v<It>,
"Not a pointer type");
130 using value_type = stl::remove_const_t<stl::remove_pointer_t<It>>::second_type;
131 using pointer =
const value_type *;
132 using reference =
const value_type &;
133 using difference_type = stl::ptrdiff_t;
134 using iterator_category = stl::forward_iterator_tag;
136 constexpr dense_set_local_iterator() noexcept = default;
138 constexpr dense_set_local_iterator(It iter, const stl::
size_t pos) noexcept
142 template<
typename Other>
143 requires (!stl::same_as<It, Other> && stl::constructible_from<It, Other>)
144 constexpr dense_set_local_iterator(
const dense_set_local_iterator<Other> &other) noexcept
146 offset{other.offset} {}
148 constexpr dense_set_local_iterator &operator++() noexcept {
149 return offset = it[
static_cast<difference_type
>(offset)].first, *
this;
152 constexpr dense_set_local_iterator operator++(
int)
noexcept {
153 const dense_set_local_iterator orig = *
this;
154 return ++(*this), orig;
157 [[nodiscard]]
constexpr pointer operator->() const noexcept {
158 return stl::addressof(it[
static_cast<difference_type
>(offset)].second);
161 [[nodiscard]]
constexpr reference operator*() const noexcept {
162 return *operator->();
165 template<
typename Other>
166 [[nodiscard]]
constexpr bool operator==(
const dense_set_local_iterator<Other> &other)
const noexcept {
167 return offset == other.offset;
170 [[nodiscard]]
constexpr stl::size_t index() const noexcept {
176 stl::size_t offset{dense_set_placeholder_position};
194template<
typename Type,
typename Hash,
typename KeyEqual,
typename Allocator>
196 static constexpr float default_threshold = 0.875f;
197 static constexpr stl::size_t minimum_capacity = 8u;
198 static constexpr stl::size_t placeholder_position = internal::dense_set_placeholder_position;
200 using node_type = stl::pair<stl::size_t, Type>;
201 using alloc_traits = stl::allocator_traits<Allocator>;
202 static_assert(stl::is_same_v<typename alloc_traits::value_type, Type>,
"Invalid value type");
203 using sparse_container_type = stl::vector<stl::size_t, typename alloc_traits::template rebind_alloc<stl::size_t>>;
204 using packed_container_type = stl::vector<node_type, typename alloc_traits::template rebind_alloc<node_type>>;
206 [[nodiscard]] stl::size_t value_to_bucket(
const auto &value)
const noexcept {
210 [[nodiscard]]
auto constrained_find(
const auto &value,
const stl::size_t
bucket) {
211 for(
auto offset = sparse.first()[
bucket]; offset != placeholder_position; offset = packed.first()[offset].first) {
212 if(packed.second()(packed.first()[offset].second, value)) {
213 return begin() +
static_cast<iterator::difference_type
>(offset);
220 [[nodiscard]]
auto constrained_find(
const auto &value,
const stl::size_t
bucket)
const {
221 for(
auto offset = sparse.first()[
bucket]; offset != placeholder_position; offset = packed.first()[offset].first) {
222 if(packed.second()(packed.first()[offset].second, value)) {
223 return cbegin() +
static_cast<const_iterator::difference_type
>(offset);
230 template<
typename Other>
231 [[nodiscard]]
auto insert_or_do_nothing(Other &&value) {
232 const auto index = value_to_bucket(value);
234 if(
auto it = constrained_find(value, index); it !=
end()) {
235 return stl::make_pair(it,
false);
238 packed.first().emplace_back(sparse.first()[index], stl::forward<Other>(value));
239 sparse.first()[index] = packed.first().size() - 1u;
240 rehash_if_required();
242 return stl::make_pair(--
end(),
true);
245 void move_and_pop(
const stl::size_t pos) {
246 if(
const auto last =
size() - 1u; pos != last) {
247 size_type *curr = &sparse.first()[value_to_bucket(packed.first().back().second)];
248 packed.first()[pos] = stl::move(packed.first().back());
249 for(; *curr != last; curr = &packed.first()[*curr].first) {}
253 packed.first().pop_back();
256 void rehash_if_required() {
278 using iterator = internal::dense_set_iterator<typename packed_container_type::pointer>;
280 using const_iterator = internal::dense_set_iterator<typename packed_container_type::const_pointer>;
286 using local_iterator = internal::dense_set_local_iterator<typename packed_container_type::pointer>;
288 using const_local_iterator = internal::dense_set_local_iterator<typename packed_container_type::const_pointer>;
329 : sparse{allocator, hash},
330 packed{allocator, equal} {
343 : sparse{
stl::piecewise_construct,
stl::forward_as_tuple(other.sparse.first(), allocator),
stl::forward_as_tuple(other.sparse.second())},
344 packed{
stl::piecewise_construct,
stl::forward_as_tuple(other.packed.first(), allocator),
stl::forward_as_tuple(other.packed.second())},
345 threshold{other.threshold} {}
356 : sparse{stl::piecewise_construct, stl::forward_as_tuple(stl::move(other.sparse.first()), allocator), stl::forward_as_tuple(stl::move(other.sparse.second()))},
357 packed{stl::piecewise_construct, stl::forward_as_tuple(stl::move(other.packed.first()), allocator), stl::forward_as_tuple(stl::move(other.packed.second()))},
358 threshold{other.threshold} {}
381 swap(sparse, other.sparse);
382 swap(packed, other.packed);
383 swap(threshold, other.threshold);
391 return sparse.first().get_allocator();
402 return packed.first().data();
412 return packed.first().data();
421 return packed.first().data() + packed.first().size();
431 return packed.first().data() + packed.first().size();
442 return stl::make_reverse_iterator(
cend());
452 return stl::make_reverse_iterator(
end());
461 return stl::make_reverse_iterator(
cbegin());
471 return stl::make_reverse_iterator(
begin());
478 [[nodiscard]]
bool empty() const noexcept {
479 return packed.first().empty();
487 return packed.first().size();
495 return packed.first().max_size();
500 sparse.first().clear();
501 packed.first().clear();
513 return insert_or_do_nothing(value);
518 return insert_or_do_nothing(stl::move(value));
526 void insert(stl::input_iterator
auto first, stl::input_iterator
auto last) {
527 for(; first != last; ++first) {
545 template<
typename... Args>
546 stl::pair<iterator, bool>
emplace(Args &&...args) {
547 if constexpr(((
sizeof...(Args) == 1u) && ... && stl::is_same_v<stl::decay_t<Args>,
value_type>)) {
548 return insert_or_do_nothing(stl::forward<Args>(args)...);
550 auto &node = packed.first().emplace_back(stl::piecewise_construct, stl::make_tuple(packed.first().size()), stl::forward_as_tuple(stl::forward<Args>(args)...));
551 const auto index = value_to_bucket(node.second);
553 if(
auto it = constrained_find(node.second, index); it !=
end()) {
554 packed.first().pop_back();
555 return stl::make_pair(it,
false);
558 stl::swap(node.first, sparse.first()[index]);
559 rehash_if_required();
561 return stl::make_pair(--
end(),
true);
571 const auto diff = pos -
cbegin();
573 return begin() + diff;
583 const auto dist = first -
cbegin();
585 for(
auto from = last -
cbegin(); from != dist; --from) {
589 return (
begin() + dist);
598 for(
size_type *curr = &sparse.first()[value_to_bucket(value)]; *curr != placeholder_position; curr = &packed.first()[*curr].first) {
599 if(packed.second()(packed.first()[*curr].second, value)) {
600 const auto index = *curr;
601 *curr = packed.first()[*curr].first;
636 return constrained_find(value, value_to_bucket(value));
641 return constrained_find(value, value_to_bucket(value));
652 return constrained_find(value, value_to_bucket(value));
658 return constrained_find(value, value_to_bucket(value));
668 const auto it =
find(value);
669 return {it, it + !(it ==
end())};
674 const auto it =
find(value);
675 return {it, it + !(it ==
cend())};
685 [[nodiscard]] stl::pair<iterator, iterator>
equal_range(
const auto &value)
687 const auto it =
find(value);
688 return {it, it + !(it ==
end())};
692 [[nodiscard]] stl::pair<const_iterator, const_iterator>
equal_range(
const auto &value)
const
694 const auto it =
find(value);
695 return {it, it + !(it ==
cend())};
713 [[nodiscard]]
bool contains(
const auto &value)
const
724 return {packed.first().data(), sparse.first()[index]};
742 return {packed.first().data(), sparse.first()[index]};
777 return sparse.first().size();
785 return sparse.first().max_size();
803 return value_to_bucket(value);
827 ENTT_ASSERT(value > 0.f,
"Invalid load factor");
838 auto value = cnt > minimum_capacity ? cnt : minimum_capacity;
840 value = value > cap ? value : cap;
842 if(
const auto sz = stl::bit_ceil(value); sz !=
bucket_count()) {
843 sparse.first().resize(sz);
845 for(
auto &&elem: sparse.first()) {
846 elem = placeholder_position;
850 const auto index = value_to_bucket(packed.first()[pos].second);
851 packed.first()[pos].first = stl::exchange(sparse.first()[index], pos);
862 packed.first().reserve(cnt);
871 return sparse.second();
879 return packed.second();
885 float threshold{default_threshold};
size_type bucket_size(const size_type index) const
Returns the number of elements in a given bucket.
void clear() noexcept
Clears the container.
stl::pair< const_iterator, const_iterator > equal_range(const auto &value) const
Returns a range containing all elements with a given value.
const_reverse_iterator crbegin() const noexcept
Returns a reverse iterator to the beginning.
const_iterator find(const auto &value) const
Finds an element with a given value.
stl::ptrdiff_t difference_type
Signed integer type.
const_iterator begin() const noexcept
Returns an iterator to the beginning.
key_equal key_eq() const
Returns the function used to compare elements for equality.
KeyEqual key_equal
Type of function to use to compare the elements for equality.
const_iterator cend() const noexcept
Returns an iterator to the end.
internal::dense_set_iterator< typename packed_container_type::pointer > iterator
Random access iterator type.
iterator erase(const_iterator first, const_iterator last)
Removes the given elements from a container.
Hash hasher
Type of function to use to hash the elements.
size_type bucket(const value_type &value) const
Returns the bucket for a given element.
reverse_iterator rbegin() noexcept
Returns a reverse iterator to the beginning.
iterator find(const value_type &value)
Finds an element with a given value.
Type key_type
Key type of the container.
Type value_type
Value type of the container.
internal::dense_set_iterator< typename packed_container_type::const_pointer > const_iterator
Constant random access iterator type.
dense_set & operator=(dense_set &&) noexcept=default
Default move assignment operator.
size_type bucket_count() const
Returns the number of buckets.
size_type max_size() const noexcept
Returns the maximum possible number of elements.
size_type size() const noexcept
Returns the number of elements in a container.
void insert(stl::input_iterator auto first, stl::input_iterator auto last)
Inserts elements into the container, if they do not exist.
local_iterator end(const size_type index)
Returns an iterator to the end of a given bucket.
dense_set(const dense_set &other, const allocator_type &allocator)
Allocator-extended copy constructor.
bool empty() const noexcept
Checks whether a container is empty.
stl::reverse_iterator< const_iterator > const_reverse_iterator
Constant reverse iterator type.
dense_set(const size_type cnt, const hasher &hash, const allocator_type &allocator)
Constructs an empty container with a given allocator, hash function and user supplied minimal number ...
internal::dense_set_local_iterator< typename packed_container_type::pointer > local_iterator
Forward iterator type.
const_iterator find(const value_type &value) const
Finds an element with a given value.
const_iterator cbegin() const noexcept
Returns an iterator to the beginning.
const_reverse_iterator rend() const noexcept
Returns a reverse iterator to the end.
stl::size_t size_type
Unsigned integer type.
dense_set(const size_type cnt, const allocator_type &allocator)
Constructs an empty container with a given allocator and user supplied minimal number of buckets.
iterator find(const auto &value)
Finds an element that compares equivalent to a given value.
hasher hash_function() const
Returns the function used to hash the elements.
stl::pair< iterator, bool > insert(value_type &&value)
Inserts an element into the container, if it does not exist.
~dense_set()=default
Default destructor.
void reserve(const size_type cnt)
Reserves space for at least the specified number of elements and regenerates the hash table.
const_local_iterator cend(const size_type index) const
Returns an iterator to the end of a given bucket.
Allocator allocator_type
Allocator type.
size_type count(const value_type &key) const
Returns the number of elements matching a value (either 1 or 0).
iterator end() noexcept
Returns an iterator to the end.
internal::dense_set_local_iterator< typename packed_container_type::const_pointer > const_local_iterator
Constant forward iterator type.
stl::pair< iterator, bool > insert(const value_type &value)
Inserts an element into the container, if it does not exist.
dense_set(const dense_set &)=default
Default copy constructor.
stl::pair< iterator, bool > emplace(Args &&...args)
Constructs an element in-place, if it does not exist.
void swap(dense_set &other) noexcept
bool contains(const auto &value) const
Checks if the container contains an element that compares equivalent to a given value.
dense_set(dense_set &&) noexcept=default
Default move constructor.
const_local_iterator end(const size_type index) const
Returns an iterator to the end of a given bucket.
void max_load_factor(const float value)
Sets the desired maximum average number of elements per bucket.
float load_factor() const
Returns the average number of elements per bucket.
size_type count(const auto &key) const
Returns the number of elements matching a key (either 1 or 0).
size_type erase(const value_type &value)
Removes the element associated with a given value.
stl::reverse_iterator< iterator > reverse_iterator
Reverse iterator type.
size_type max_bucket_count() const
Returns the maximum number of buckets.
dense_set(const allocator_type &allocator)
Constructs an empty container with a given allocator.
const_reverse_iterator rbegin() const noexcept
Returns a reverse iterator to the beginning.
dense_set()
Default constructor.
const_iterator end() const noexcept
Returns an iterator to the end.
dense_set & operator=(const dense_set &)=default
Default copy assignment operator.
dense_set(const size_type cnt, const hasher &hash=hasher{}, const key_equal &equal=key_equal{}, const allocator_type &allocator=allocator_type{})
Constructs an empty container with a given allocator, hash function, compare function and user suppli...
iterator erase(const_iterator pos)
Removes an element from a given position.
void rehash(const size_type cnt)
Reserves at least the specified number of buckets and regenerates the hash table.
const_reverse_iterator crend() const noexcept
Returns a reverse iterator to the end.
constexpr allocator_type get_allocator() const noexcept
Returns the associated allocator.
stl::pair< const_iterator, const_iterator > equal_range(const value_type &value) const
Returns a range containing all elements with a given value.
stl::pair< iterator, iterator > equal_range(const auto &value)
Returns a range containing all elements that compare equivalent to a given value.
reverse_iterator rend() noexcept
Returns a reverse iterator to the end.
float max_load_factor() const
Returns the maximum average number of elements per bucket.
const_local_iterator begin(const size_type index) const
Returns an iterator to the beginning of a given bucket.
local_iterator begin(const size_type index)
Returns an iterator to the beginning of a given bucket.
const_local_iterator cbegin(const size_type index) const
Returns an iterator to the beginning of a given bucket.
stl::pair< iterator, iterator > equal_range(const value_type &value)
Returns a range containing all elements with a given value.
bool contains(const value_type &value) const
Checks if the container contains an element with a given value.
iterator begin() noexcept
Returns an iterator to the beginning.
Custom EnTT namespace for the standard template library.
constexpr bool is_transparent_v
Helper variable template.
constexpr type_list< Type..., Other... > operator+(type_list< Type... >, type_list< Other... >)
Concatenates multiple type lists.
constexpr Type fast_mod(const Type value, const stl::size_t mod) noexcept
Fast module utility function (powers of two only).