1 #ifndef ENTT_CONTAINER_DENSE_SET_HPP
2 #define ENTT_CONTAINER_DENSE_SET_HPP
12 #include <type_traits>
15 #include "../config/config.h"
16 #include "../core/compressed_pair.hpp"
17 #include "../core/memory.hpp"
18 #include "../core/type_traits.hpp"
31 class dense_set_iterator final {
33 friend class dense_set_iterator;
36 using value_type =
typename It::value_type::second_type;
37 using pointer =
const value_type *;
38 using reference =
const value_type &;
39 using difference_type = std::ptrdiff_t;
40 using iterator_category = std::random_access_iterator_tag;
42 dense_set_iterator() ENTT_NOEXCEPT
45 dense_set_iterator(
const It iter) ENTT_NOEXCEPT
48 template<
typename Other,
typename = std::enable_if_t<!std::is_same_v<It, Other> && std::is_constructible_v<It, Other>>>
49 dense_set_iterator(
const dense_set_iterator<Other> &other) ENTT_NOEXCEPT
52 dense_set_iterator &operator++() ENTT_NOEXCEPT {
56 dense_set_iterator operator++(
int) ENTT_NOEXCEPT {
57 dense_set_iterator orig = *
this;
58 return ++(*this), orig;
61 dense_set_iterator &operator--() ENTT_NOEXCEPT {
65 dense_set_iterator operator--(
int) ENTT_NOEXCEPT {
66 dense_set_iterator orig = *
this;
67 return operator--(), orig;
70 dense_set_iterator &operator+=(
const difference_type value) ENTT_NOEXCEPT {
75 dense_set_iterator
operator+(
const difference_type value)
const ENTT_NOEXCEPT {
76 dense_set_iterator copy = *
this;
77 return (copy += value);
80 dense_set_iterator &operator-=(
const difference_type value) ENTT_NOEXCEPT {
81 return (*
this += -value);
84 dense_set_iterator operator-(
const difference_type value)
const ENTT_NOEXCEPT {
85 return (*
this + -value);
88 [[nodiscard]] reference operator[](
const difference_type value)
const ENTT_NOEXCEPT {
89 return it[value].second;
92 [[nodiscard]] pointer operator->() const ENTT_NOEXCEPT {
93 return std::addressof(it->second);
96 [[nodiscard]] reference operator*() const ENTT_NOEXCEPT {
100 template<
typename ILhs,
typename IRhs>
101 friend std::ptrdiff_t operator-(
const dense_set_iterator<ILhs> &,
const dense_set_iterator<IRhs> &) ENTT_NOEXCEPT;
103 template<
typename ILhs,
typename IRhs>
104 friend bool operator==(
const dense_set_iterator<ILhs> &,
const dense_set_iterator<IRhs> &) ENTT_NOEXCEPT;
106 template<
typename ILhs,
typename IRhs>
107 friend bool operator<(
const dense_set_iterator<ILhs> &,
const dense_set_iterator<IRhs> &) ENTT_NOEXCEPT;
113 template<
typename ILhs,
typename IRhs>
114 [[nodiscard]] std::ptrdiff_t operator-(
const dense_set_iterator<ILhs> &lhs,
const dense_set_iterator<IRhs> &rhs) ENTT_NOEXCEPT {
115 return lhs.it - rhs.it;
118 template<
typename ILhs,
typename IRhs>
119 [[nodiscard]]
bool operator==(
const dense_set_iterator<ILhs> &lhs,
const dense_set_iterator<IRhs> &rhs) ENTT_NOEXCEPT {
120 return lhs.it == rhs.it;
123 template<
typename ILhs,
typename IRhs>
124 [[nodiscard]]
bool operator!=(
const dense_set_iterator<ILhs> &lhs,
const dense_set_iterator<IRhs> &rhs) ENTT_NOEXCEPT {
125 return !(lhs == rhs);
128 template<
typename ILhs,
typename IRhs>
129 [[nodiscard]]
bool operator<(
const dense_set_iterator<ILhs> &lhs,
const dense_set_iterator<IRhs> &rhs) ENTT_NOEXCEPT {
130 return lhs.it < rhs.it;
133 template<
typename ILhs,
typename IRhs>
134 [[nodiscard]]
bool operator>(
const dense_set_iterator<ILhs> &lhs,
const dense_set_iterator<IRhs> &rhs) ENTT_NOEXCEPT {
138 template<
typename ILhs,
typename IRhs>
139 [[nodiscard]]
bool operator<=(
const dense_set_iterator<ILhs> &lhs,
const dense_set_iterator<IRhs> &rhs) ENTT_NOEXCEPT {
143 template<
typename ILhs,
typename IRhs>
144 [[nodiscard]]
bool operator>=(
const dense_set_iterator<ILhs> &lhs,
const dense_set_iterator<IRhs> &rhs) ENTT_NOEXCEPT {
148 template<
typename It>
149 class dense_set_local_iterator final {
151 friend class dense_set_local_iterator;
154 using value_type =
typename It::value_type::second_type;
155 using pointer =
const value_type *;
156 using reference =
const value_type &;
157 using difference_type = std::ptrdiff_t;
158 using iterator_category = std::forward_iterator_tag;
160 dense_set_local_iterator() ENTT_NOEXCEPT
164 dense_set_local_iterator(It iter,
const std::size_t pos) ENTT_NOEXCEPT
168 template<
typename Other,
typename = std::enable_if_t<!std::is_same_v<It, Other> && std::is_constructible_v<It, Other>>>
169 dense_set_local_iterator(
const dense_set_local_iterator<Other> &other) ENTT_NOEXCEPT
171 offset{other.offset} {}
173 dense_set_local_iterator &operator++() ENTT_NOEXCEPT {
174 return offset = it[offset].first, *
this;
177 dense_set_local_iterator operator++(
int) ENTT_NOEXCEPT {
178 dense_set_local_iterator orig = *
this;
179 return ++(*this), orig;
182 [[nodiscard]] pointer operator->() const ENTT_NOEXCEPT {
183 return std::addressof(it[offset].second);
186 [[nodiscard]] reference operator*() const ENTT_NOEXCEPT {
187 return *operator->();
190 [[nodiscard]] std::size_t index() const ENTT_NOEXCEPT {
199 template<
typename ILhs,
typename IRhs>
200 [[nodiscard]]
bool operator==(
const dense_set_local_iterator<ILhs> &lhs,
const dense_set_local_iterator<IRhs> &rhs) ENTT_NOEXCEPT {
201 return lhs.index() == rhs.index();
204 template<
typename ILhs,
typename IRhs>
205 [[nodiscard]]
bool operator!=(
const dense_set_local_iterator<ILhs> &lhs,
const dense_set_local_iterator<IRhs> &rhs) ENTT_NOEXCEPT {
206 return !(lhs == rhs);
228 template<
typename Type,
typename Hash,
typename KeyEqual,
typename Allocator>
230 static constexpr
float default_threshold = 0.875f;
231 static constexpr std::size_t minimum_capacity = 8u;
233 using node_type = std::pair<std::size_t, Type>;
234 using alloc_traits = std::allocator_traits<Allocator>;
235 static_assert(std::is_same_v<typename alloc_traits::value_type, Type>,
"Invalid value type");
236 using sparse_container_type = std::vector<std::size_t, typename alloc_traits::template rebind_alloc<std::size_t>>;
237 using packed_container_type = std::vector<node_type, typename alloc_traits::template rebind_alloc<node_type>>;
239 template<
typename Other>
240 [[nodiscard]] std::size_t value_to_bucket(
const Other &value)
const ENTT_NOEXCEPT {
244 template<
typename Other>
245 [[nodiscard]]
auto constrained_find(
const Other &value, std::size_t
bucket) {
247 if(packed.
second()(*it, value)) {
248 return begin() +
static_cast<typename iterator::difference_type
>(it.index());
255 template<
typename Other>
256 [[nodiscard]]
auto constrained_find(
const Other &value, std::size_t
bucket)
const {
258 if(packed.
second()(*it, value)) {
259 return cbegin() +
static_cast<typename iterator::difference_type
>(it.index());
266 template<
typename Other>
267 [[nodiscard]]
auto insert_or_do_nothing(Other &&value) {
268 const auto index = value_to_bucket(value);
270 if(
auto it = constrained_find(value, index); it !=
end()) {
271 return std::make_pair(it,
false);
274 packed.
first().emplace_back(sparse.
first()[index], std::forward<Other>(value));
275 sparse.
first()[index] = packed.
first().size() - 1u;
276 rehash_if_required();
278 return std::make_pair(--
end(),
true);
281 void move_and_pop(
const std::size_t pos) {
282 if(
const auto last =
size() - 1u; pos != last) {
283 packed.
first()[pos] = std::move(packed.
first().back());
285 for(; *curr != last; curr = &packed.
first()[*curr].first) {}
289 packed.
first().pop_back();
292 void rehash_if_required() {
312 using iterator = internal::dense_set_iterator<typename packed_container_type::iterator>;
314 using const_iterator = internal::dense_set_iterator<typename packed_container_type::const_iterator>;
316 using local_iterator = internal::dense_set_local_iterator<typename packed_container_type::iterator>;
318 using const_local_iterator = internal::dense_set_local_iterator<typename packed_container_type::const_iterator>;
359 : sparse{allocator, hash},
360 packed{allocator, equal},
361 threshold{default_threshold} {
374 : sparse{std::piecewise_construct, std::forward_as_tuple(other.sparse.first(), allocator), std::forward_as_tuple(other.sparse.second())},
375 packed{std::piecewise_construct, std::forward_as_tuple(other.packed.first(), allocator), std::forward_as_tuple(other.packed.second())},
376 threshold{other.threshold} {}
387 : sparse{std::piecewise_construct, std::forward_as_tuple(std::move(other.sparse.first()), allocator), std::forward_as_tuple(std::move(other.sparse.second()))},
388 packed{std::piecewise_construct, std::forward_as_tuple(std::move(other.packed.first()), allocator), std::forward_as_tuple(std::move(other.packed.second()))},
389 threshold{other.threshold} {}
408 return sparse.
first().get_allocator();
420 return packed.
first().begin();
430 return packed.
first().begin();
444 return packed.
first().end();
454 return packed.
first().end();
461 [[nodiscard]]
bool empty() const ENTT_NOEXCEPT {
462 return packed.
first().empty();
470 return packed.
first().size();
475 sparse.
first().clear();
476 packed.
first().clear();
488 return insert_or_do_nothing(value);
493 return insert_or_do_nothing(std::move(value));
502 template<
typename It>
504 for(; first != last; ++first) {
522 template<
typename... Args>
523 std::pair<iterator, bool>
emplace(Args &&...args) {
524 if constexpr(((
sizeof...(Args) == 1u) && ... && std::is_same_v<std::remove_cv_t<std::remove_reference_t<Args>>,
value_type>)) {
525 return insert_or_do_nothing(std::forward<Args>(args)...);
527 auto &node = packed.
first().emplace_back(std::piecewise_construct, std::make_tuple(packed.
first().size()), std::forward_as_tuple(std::forward<Args>(args)...));
528 const auto index = value_to_bucket(node.second);
530 if(
auto it = constrained_find(node.second, index); it !=
end()) {
531 packed.
first().pop_back();
532 return std::make_pair(it,
false);
535 std::swap(node.first, sparse.
first()[index]);
536 rehash_if_required();
538 return std::make_pair(--
end(),
true);
548 const auto diff = pos -
cbegin();
550 return begin() + diff;
560 const auto dist = first -
cbegin();
562 for(
auto from = last -
cbegin(); from != dist; --from) {
566 return (
begin() + dist);
575 for(
size_type *curr = sparse.
first().data() + value_to_bucket(value); *curr != (std::numeric_limits<size_type>::max)(); curr = &packed.
first()[*curr].first) {
576 if(packed.
second()(packed.
first()[*curr].second, value)) {
577 const auto index = *curr;
578 *curr = packed.
first()[*curr].first;
593 swap(sparse, other.sparse);
594 swap(packed, other.packed);
595 swap(threshold, other.threshold);
605 return constrained_find(value, value_to_bucket(value));
610 return constrained_find(value, value_to_bucket(value));
620 template<
typename Other>
621 [[nodiscard]] std::enable_if_t<is_transparent_v<hasher> && is_transparent_v<key_equal>, std::conditional_t<false, Other, iterator>>
623 return constrained_find(value, value_to_bucket(value));
627 template<
typename Other>
628 [[nodiscard]] std::enable_if_t<is_transparent_v<hasher> && is_transparent_v<key_equal>, std::conditional_t<false, Other, const_iterator>>
629 find(
const Other &value)
const {
630 return constrained_find(value, value_to_bucket(value));
649 template<
typename Other>
650 [[nodiscard]] std::enable_if_t<is_transparent_v<hasher> && is_transparent_v<key_equal>, std::conditional_t<false, Other, bool>>
661 return {packed.
first().begin(), sparse.
first()[index]};
679 return {packed.
first().begin(), sparse.
first()[index]};
688 return {packed.
first().begin(), (std::numeric_limits<size_type>::max)()};
706 return {packed.
first().begin(), (std::numeric_limits<size_type>::max)()};
714 return sparse.
first().size();
722 return sparse.
first().max_size();
740 return value_to_bucket(value);
764 ENTT_ASSERT(value > 0.f,
"Invalid load factor");
775 auto value = (std::max)(count, minimum_capacity);
779 sparse.
first().resize(sz);
780 std::fill(sparse.
first().begin(), sparse.
first().end(), (std::numeric_limits<size_type>::max)());
783 const auto index = value_to_bucket(packed.
first()[pos].second);
784 packed.
first()[pos].first = std::exchange(sparse.
first()[index], pos);
795 packed.
first().reserve(count);
second_type & second()
Returns the second element that a pair stores.
first_type & first()
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.
size_type size() const
Returns the number of elements in a container.
dense_set(dense_set &&)=default
Default move constructor.
const_iterator cend() const
Returns an iterator to the end.
bool empty() const
Checks whether a container is empty.
internal::dense_set_local_iterator< typename packed_container_type::const_iterator > const_local_iterator
Constant forward iterator type.
std::pair< iterator, bool > insert(value_type &&value)
Inserts an element into the container, if it does not exist.
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.
iterator erase(const_iterator first, const_iterator last)
Removes the given elements from a container.
std::pair< iterator, bool > emplace(Args &&...args)
Constructs an element in-place, if it does not exist.
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.
iterator find(const value_type &value)
Finds an element with a given value.
constexpr allocator_type get_allocator() const
Returns the associated allocator.
void swap(dense_set &other)
Exchanges the contents with those of a given container.
dense_set(const size_type bucket_count, const allocator_type &allocator)
Constructs an empty container with a given allocator and user supplied minimal number of buckets.
Type key_type
Key type of the container.
const_iterator end() const
Returns an iterator to the end.
void rehash(const size_type count)
Reserves at least the specified number of buckets and regenerates the hash table.
Type value_type
Value type of the container.
dense_set & operator=(const dense_set &)=default
Default copy assignment operator.
internal::dense_set_iterator< typename packed_container_type::iterator > iterator
Random access iterator type.
size_type bucket_count() const
Returns the number of buckets.
const_local_iterator cend([[maybe_unused]] const size_type index) const
Returns an iterator to the end of a given bucket.
local_iterator end([[maybe_unused]] const size_type index)
Returns an iterator to the end of a given bucket.
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.
dense_set(const dense_set &other, const allocator_type &allocator)
Allocator-extended copy constructor.
const_iterator find(const value_type &value) const
Finds an element with a given value.
const_iterator cbegin() const
Returns an iterator to the beginning.
hasher hash_function() const
Returns the function used to hash the elements.
internal::dense_set_local_iterator< typename packed_container_type::iterator > local_iterator
Forward iterator type.
Allocator allocator_type
Allocator type.
const_local_iterator end([[maybe_unused]] const size_type index) const
Returns an iterator to the end of a given bucket.
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.
dense_set(const dense_set &)=default
Default copy constructor.
dense_set(const size_type bucket_count, const hasher &hash, const allocator_type &allocator)
Constructs an empty container with a given allocator, hash function and user supplied minimal number ...
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.
void max_load_factor(const float value)
Sets the desired maximum average number of elements per bucket.
iterator end()
Returns an iterator to the end.
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.
dense_set(const size_type bucket_count, 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...
size_type max_bucket_count() const
Returns the maximum number of buckets.
dense_set & operator=(dense_set &&)=default
Default move assignment operator.
const_iterator begin() const
Returns an iterator to the beginning.
dense_set(const allocator_type &allocator)
Constructs an empty container with a given allocator.
dense_set()
Default constructor.
std::pair< iterator, bool > insert(const value_type &value)
Inserts an element into the container, if it does not exist.
void reserve(const size_type count)
Reserves space for at least the specified number of elements and regenerates the hash table.
iterator erase(const_iterator pos)
Removes an element from a given position.
dense_set(dense_set &&other, const allocator_type &allocator)
Allocator-extended move constructor.
void clear()
Clears the container.
void insert(It first, It last)
Inserts elements into the container, if they do not exist.
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.
bool contains(const value_type &value) const
Checks if the container contains an element with a given value.
iterator begin()
Returns an iterator to the beginning.
constexpr bool operator==(const basic_hashed_string< Char > &lhs, const basic_hashed_string< Char > &rhs)
Compares two hashed strings.
constexpr std::size_t fast_mod(const std::size_t value, const std::size_t mod)
Fast module utility function (powers of two only).
constexpr bool operator<=(const basic_hashed_string< Char > &lhs, const basic_hashed_string< Char > &rhs)
Compares two hashed strings.
constexpr std::size_t next_power_of_two(const std::size_t value)
Computes the smallest power of two greater than or equal to a value.
constexpr bool operator>=(const basic_hashed_string< Char > &lhs, const basic_hashed_string< Char > &rhs)
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)
Compares two hashed strings.
bool operator!=(const basic_any< Len, Align > &lhs, const basic_any< Len, Align > &rhs)
Checks if two wrappers differ in their content.
constexpr bool operator>(const basic_hashed_string< Char > &lhs, const basic_hashed_string< Char > &rhs)
Compares two hashed strings.