1#ifndef ENTT_CONTAINER_DENSE_SET_HPP
2#define ENTT_CONTAINER_DENSE_SET_HPP
14#include "../config/config.h"
15#include "../core/bit.hpp"
16#include "../core/compressed_pair.hpp"
17#include "../core/type_traits.hpp"
26class dense_set_iterator final {
28 friend class dense_set_iterator;
31 using value_type =
typename It::value_type::second_type;
32 using pointer =
const value_type *;
33 using reference =
const value_type &;
34 using difference_type = std::ptrdiff_t;
35 using iterator_category = std::random_access_iterator_tag;
37 constexpr dense_set_iterator() noexcept
40 constexpr dense_set_iterator(
const It iter) noexcept
43 template<
typename Other,
typename = std::enable_if_t<!std::is_same_v<It, Other> && std::is_constructible_v<It, Other>>>
44 constexpr dense_set_iterator(
const dense_set_iterator<Other> &other) noexcept
47 constexpr dense_set_iterator &operator++() noexcept {
51 constexpr dense_set_iterator operator++(
int)
noexcept {
52 dense_set_iterator orig = *
this;
53 return ++(*this), orig;
56 constexpr dense_set_iterator &operator--() noexcept {
60 constexpr dense_set_iterator operator--(
int)
noexcept {
61 dense_set_iterator orig = *
this;
62 return operator--(), orig;
65 constexpr dense_set_iterator &operator+=(
const difference_type value)
noexcept {
70 constexpr dense_set_iterator
operator+(
const difference_type value)
const noexcept {
71 dense_set_iterator copy = *
this;
72 return (copy += value);
75 constexpr dense_set_iterator &operator-=(
const difference_type value)
noexcept {
76 return (*
this += -value);
79 constexpr dense_set_iterator operator-(
const difference_type value)
const noexcept {
80 return (*
this + -value);
83 [[nodiscard]]
constexpr reference operator[](
const difference_type value)
const noexcept {
84 return it[value].second;
87 [[nodiscard]]
constexpr pointer operator->() const noexcept {
88 return std::addressof(
operator[](0));
91 [[nodiscard]]
constexpr reference operator*() const noexcept {
95 template<
typename Lhs,
typename Rhs>
96 friend constexpr std::ptrdiff_t operator-(
const dense_set_iterator<Lhs> &,
const dense_set_iterator<Rhs> &)
noexcept;
98 template<
typename Lhs,
typename Rhs>
99 friend constexpr bool operator==(
const dense_set_iterator<Lhs> &,
const dense_set_iterator<Rhs> &)
noexcept;
101 template<
typename Lhs,
typename Rhs>
102 friend constexpr bool operator<(
const dense_set_iterator<Lhs> &,
const dense_set_iterator<Rhs> &)
noexcept;
108template<
typename Lhs,
typename Rhs>
109[[nodiscard]]
constexpr std::ptrdiff_t operator-(
const dense_set_iterator<Lhs> &lhs,
const dense_set_iterator<Rhs> &rhs)
noexcept {
110 return lhs.it - rhs.it;
113template<
typename Lhs,
typename Rhs>
114[[nodiscard]]
constexpr bool operator==(
const dense_set_iterator<Lhs> &lhs,
const dense_set_iterator<Rhs> &rhs)
noexcept {
115 return lhs.it == rhs.it;
118template<
typename Lhs,
typename Rhs>
119[[nodiscard]]
constexpr bool operator!=(
const dense_set_iterator<Lhs> &lhs,
const dense_set_iterator<Rhs> &rhs)
noexcept {
120 return !(lhs == rhs);
123template<
typename Lhs,
typename Rhs>
124[[nodiscard]]
constexpr bool operator<(
const dense_set_iterator<Lhs> &lhs,
const dense_set_iterator<Rhs> &rhs)
noexcept {
125 return lhs.it < rhs.it;
128template<
typename Lhs,
typename Rhs>
129[[nodiscard]]
constexpr bool operator>(
const dense_set_iterator<Lhs> &lhs,
const dense_set_iterator<Rhs> &rhs)
noexcept {
133template<
typename Lhs,
typename Rhs>
134[[nodiscard]]
constexpr bool operator<=(
const dense_set_iterator<Lhs> &lhs,
const dense_set_iterator<Rhs> &rhs)
noexcept {
138template<
typename Lhs,
typename Rhs>
139[[nodiscard]]
constexpr bool operator>=(
const dense_set_iterator<Lhs> &lhs,
const dense_set_iterator<Rhs> &rhs)
noexcept {
144class dense_set_local_iterator final {
146 friend class dense_set_local_iterator;
149 using value_type =
typename It::value_type::second_type;
150 using pointer =
const value_type *;
151 using reference =
const value_type &;
152 using difference_type = std::ptrdiff_t;
153 using iterator_category = std::forward_iterator_tag;
155 constexpr dense_set_local_iterator() noexcept
159 constexpr dense_set_local_iterator(It iter,
const std::size_t pos) noexcept
163 template<
typename Other,
typename = std::enable_if_t<!std::is_same_v<It, Other> && std::is_constructible_v<It, Other>>>
164 constexpr dense_set_local_iterator(
const dense_set_local_iterator<Other> &other) noexcept
166 offset{other.offset} {}
168 constexpr dense_set_local_iterator &operator++() noexcept {
169 return offset = it[offset].first, *
this;
172 constexpr dense_set_local_iterator operator++(
int)
noexcept {
173 dense_set_local_iterator orig = *
this;
174 return ++(*this), orig;
177 [[nodiscard]]
constexpr pointer operator->() const noexcept {
178 return std::addressof(it[offset].second);
181 [[nodiscard]]
constexpr reference operator*() const noexcept {
182 return *operator->();
185 [[nodiscard]]
constexpr std::size_t index() const noexcept {
194template<
typename Lhs,
typename Rhs>
195[[nodiscard]]
constexpr bool operator==(
const dense_set_local_iterator<Lhs> &lhs,
const dense_set_local_iterator<Rhs> &rhs)
noexcept {
196 return lhs.index() == rhs.index();
199template<
typename Lhs,
typename Rhs>
200[[nodiscard]]
constexpr bool operator!=(
const dense_set_local_iterator<Lhs> &lhs,
const dense_set_local_iterator<Rhs> &rhs)
noexcept {
201 return !(lhs == rhs);
219template<
typename Type,
typename Hash,
typename KeyEqual,
typename Allocator>
221 static constexpr float default_threshold = 0.875f;
222 static constexpr std::size_t minimum_capacity = 8u;
224 using node_type = std::pair<std::size_t, Type>;
225 using alloc_traits = std::allocator_traits<Allocator>;
226 static_assert(std::is_same_v<typename alloc_traits::value_type, Type>,
"Invalid value type");
227 using sparse_container_type = std::vector<std::size_t, typename alloc_traits::template rebind_alloc<std::size_t>>;
228 using packed_container_type = std::vector<node_type, typename alloc_traits::template rebind_alloc<node_type>>;
230 template<
typename Other>
231 [[
nodiscard]] std::size_t value_to_bucket(
const Other &value)
const noexcept {
235 template<
typename Other>
239 return begin() +
static_cast<typename iterator::difference_type
>(
it.index());
246 template<
typename Other>
250 return cbegin() +
static_cast<typename iterator::difference_type
>(
it.index());
257 template<
typename Other>
259 const auto index = value_to_bucket(value);
261 if(
auto it = constrained_find(value, index);
it !=
end()) {
262 return std::make_pair(
it,
false);
265 packed.
first().emplace_back(sparse.
first()[index], std::forward<Other>(value));
266 sparse.
first()[index] = packed.
first().size() - 1u;
267 rehash_if_required();
269 return std::make_pair(--
end(),
true);
272 void move_and_pop(
const std::size_t
pos) {
273 if(
const auto last =
size() - 1u;
pos != last) {
280 packed.
first().pop_back();
283 void rehash_if_required() {
303 using iterator = internal::dense_set_iterator<typename packed_container_type::iterator>;
305 using const_iterator = internal::dense_set_iterator<typename packed_container_type::const_iterator>;
311 using local_iterator = internal::dense_set_local_iterator<typename packed_container_type::iterator>;
313 using const_local_iterator = internal::dense_set_local_iterator<typename packed_container_type::const_iterator>;
370 threshold{
other.threshold} {}
381 : sparse{std::piecewise_construct, std::forward_as_tuple(std::move(
other.sparse.first()),
allocator), std::forward_as_tuple(std::move(
other.sparse.second()))},
382 packed{std::piecewise_construct, std::forward_as_tuple(std::move(
other.packed.first()),
allocator), std::forward_as_tuple(std::move(
other.packed.second()))},
383 threshold{
other.threshold} {}
405 return sparse.
first().get_allocator();
416 return packed.
first().begin();
426 return packed.
first().begin();
435 return packed.
first().end();
445 return packed.
first().end();
456 return std::make_reverse_iterator(
cend());
466 return std::make_reverse_iterator(
end());
475 return std::make_reverse_iterator(
cbegin());
485 return std::make_reverse_iterator(
begin());
493 return packed.
first().empty();
501 return packed.
first().size();
509 return packed.
first().max_size();
514 sparse.
first().clear();
515 packed.
first().clear();
527 return insert_or_do_nothing(value);
532 return insert_or_do_nothing(std::move(value));
541 template<
typename It>
543 for(; first != last; ++first) {
561 template<
typename...
Args>
563 if constexpr(((
sizeof...(Args) == 1u) && ... && std::is_same_v<std::decay_t<Args>,
value_type>)) {
564 return insert_or_do_nothing(std::forward<Args>(
args)...);
566 auto &node = packed.
first().emplace_back(std::piecewise_construct, std::make_tuple(packed.
first().size()), std::forward_as_tuple(std::forward<Args>(
args)...));
567 const auto index = value_to_bucket(node.second);
569 if(
auto it = constrained_find(node.second, index);
it !=
end()) {
570 packed.
first().pop_back();
571 return std::make_pair(
it,
false);
574 std::swap(node.first, sparse.
first()[index]);
575 rehash_if_required();
577 return std::make_pair(--
end(),
true);
616 const auto index = *
curr;
652 template<
typename Other>
665 return constrained_find(value, value_to_bucket(value));
670 return constrained_find(value, value_to_bucket(value));
680 template<
typename Other>
683 return constrained_find(value, value_to_bucket(value));
687 template<
typename Other>
690 return constrained_find(value, value_to_bucket(value));
700 const auto it =
find(value);
706 const auto it =
find(value);
718 template<
typename Other>
721 const auto it =
find(value);
726 template<
typename Other>
729 const auto it =
find(value);
749 template<
typename Other>
761 return {packed.
first().begin(), sparse.
first()[index]};
779 return {packed.
first().begin(), sparse.
first()[index]};
788 return {packed.
first().begin(), (std::numeric_limits<size_type>::max)()};
806 return {packed.
first().begin(), (std::numeric_limits<size_type>::max)()};
814 return sparse.
first().size();
822 return sparse.
first().max_size();
840 return value_to_bucket(value);
864 ENTT_ASSERT(value > 0.f,
"Invalid load factor");
875 auto value =
cnt > minimum_capacity ?
cnt : minimum_capacity;
877 value = value >
cap ? value :
cap;
883 elem = (std::numeric_limits<size_type>::max)();
887 const auto index = value_to_bucket(packed.
first()[
pos].second);
922 float threshold{default_threshold};
constexpr second_type & second() noexcept
Returns the second element that a pair stores.
constexpr first_type & first() noexcept
Returns the first element that a pair stores.
Associative container for unique objects of a given type.
size_type bucket_size(const size_type index) const
Returns the number of elements in a given bucket.
void clear() noexcept
Clears the container.
internal::dense_set_local_iterator< typename packed_container_type::const_iterator > const_local_iterator
Constant forward iterator type.
const_reverse_iterator crbegin() const noexcept
Returns a reverse iterator to the beginning.
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.
internal::dense_set_iterator< typename packed_container_type::const_iterator > const_iterator
Constant random access iterator type.
const_iterator cend() const noexcept
Returns an iterator to the end.
iterator erase(const_iterator first, const_iterator last)
Removes the given elements from a container.
std::enable_if_t< is_transparent_v< hasher > &&is_transparent_v< key_equal >, std::conditional_t< false, Other, bool > > contains(const Other &value) const
Checks if the container contains an element that compares equivalent to a given value.
Hash hasher
Type of function to use to hash the elements.
std::size_t size_type
Unsigned integer type.
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::iterator > iterator
Random access iterator type.
std::pair< iterator, bool > emplace(Args &&...args)
Constructs an element in-place, if it does not exist.
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.
std::pair< iterator, iterator > equal_range(const value_type &value)
Returns a range containing all elements with a given value.
std::reverse_iterator< const_iterator > const_reverse_iterator
Constant reverse iterator type.
std::enable_if_t< is_transparent_v< hasher > &&is_transparent_v< key_equal >, std::conditional_t< false, Other, std::pair< iterator, iterator > > > equal_range(const Other &value)
Returns a range containing all elements that compare equivalent to a given value.
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.
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 ...
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.
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.
hasher hash_function() const
Returns the function used to hash the elements.
std::pair< iterator, bool > insert(const value_type &value)
Inserts an element into the container, if it does not exist.
internal::dense_set_local_iterator< typename packed_container_type::iterator > local_iterator
Forward iterator type.
~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.
std::reverse_iterator< iterator > reverse_iterator
Reverse iterator type.
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.
std::enable_if_t< is_transparent_v< hasher > &&is_transparent_v< key_equal >, std::conditional_t< false, Other, const_iterator > > find(const Other &value) const
Finds an element with a given value.
dense_set(const dense_set &)=default
Default copy constructor.
void swap(dense_set &other) noexcept
Exchanges the contents with those of a given container.
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 erase(const value_type &value)
Removes the element associated with a given value.
std::pair< iterator, bool > insert(value_type &&value)
Inserts an element into the container, if it does not exist.
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.
std::enable_if_t< is_transparent_v< hasher > &&is_transparent_v< key_equal >, std::conditional_t< false, Other, iterator > > find(const Other &value)
Finds an element that compares equivalent to a given value.
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.
std::enable_if_t< is_transparent_v< hasher > &&is_transparent_v< key_equal >, std::conditional_t< false, Other, size_type > > count(const Other &key) const
Returns the number of elements matching a key (either 1 or 0).
constexpr allocator_type get_allocator() const noexcept
Returns the associated allocator.
void insert(It first, It last)
Inserts elements into the container, if they do not exist.
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.
std::enable_if_t< is_transparent_v< hasher > &&is_transparent_v< key_equal >, std::conditional_t< false, Other, std::pair< const_iterator, const_iterator > > > equal_range(const Other &value) const
Returns a range containing all elements with a given value.
std::pair< const_iterator, const_iterator > equal_range(const value_type &value) const
Returns a range containing all elements with a given value.
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.
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.
constexpr Type make_obj_using_allocator(const Allocator &allocator, Args &&...args)
Uses-allocator construction utility (waiting for C++20).
constexpr std::enable_if_t< std::is_unsigned_v< Type >, Type > fast_mod(const Type value, const std::size_t mod) noexcept
Fast module utility function (powers of two only).
constexpr bool operator<=(const basic_hashed_string< Char > &lhs, const basic_hashed_string< Char > &rhs) noexcept
Compares two hashed strings.
constexpr std::enable_if_t< std::is_unsigned_v< Type >, Type > next_power_of_two(const Type value) noexcept
Computes the smallest power of two greater than or equal to a value (waiting for C++20 and std::bit_c...
constexpr bool operator<(const basic_hashed_string< Char > &lhs, const basic_hashed_string< Char > &rhs) noexcept
Compares two hashed strings.
constexpr type_list< Type..., Other... > operator+(type_list< Type... >, type_list< Other... >)
Concatenates multiple type lists.
constexpr bool operator!=(const basic_hashed_string< Char > &lhs, const basic_hashed_string< Char > &rhs) noexcept
Compares two hashed strings.
constexpr bool operator>=(const basic_hashed_string< Char > &lhs, const basic_hashed_string< Char > &rhs) noexcept
Compares two hashed strings.
constexpr bool operator>(const basic_hashed_string< Char > &lhs, const basic_hashed_string< Char > &rhs) noexcept
Compares two hashed strings.
constexpr bool operator==(const basic_hashed_string< Char > &lhs, const basic_hashed_string< Char > &rhs) noexcept
Compares two hashed strings.