1#ifndef ENTT_CONTAINER_DENSE_MAP_HPP
2#define ENTT_CONTAINER_DENSE_MAP_HPP
5#include "../config/config.h"
6#include "../core/bit.hpp"
7#include "../core/compressed_pair.hpp"
8#include "../core/iterator.hpp"
9#include "../core/memory.hpp"
10#include "../core/type_traits.hpp"
11#include "../stl/bit.hpp"
12#include "../stl/cmath.hpp"
13#include "../stl/concepts.hpp"
14#include "../stl/cstddef.hpp"
15#include "../stl/functional.hpp"
16#include "../stl/iterator.hpp"
17#include "../stl/limits.hpp"
18#include "../stl/memory.hpp"
19#include "../stl/tuple.hpp"
20#include "../stl/type_traits.hpp"
21#include "../stl/utility.hpp"
22#include "../stl/vector.hpp"
30static constexpr stl::size_t dense_map_placeholder_position = (stl::numeric_limits<stl::size_t>::max)();
32template<
typename Key,
typename Type>
33struct dense_map_node final {
34 using value_type = stl::pair<Key, Type>;
36 template<
typename... Args>
37 dense_map_node(
const stl::size_t pos, Args &&...args)
39 element{stl::forward<Args>(args)...} {}
41 template<
typename... Args>
42 dense_map_node(stl::allocator_arg_t,
const auto &allocator,
const stl::size_t pos, Args &&...args)
46 dense_map_node(stl::allocator_arg_t,
const auto &allocator,
const dense_map_node &other)
50 dense_map_node(stl::allocator_arg_t,
const auto &allocator, dense_map_node &&other)
59class dense_map_iterator final {
61 friend class dense_map_iterator;
63 static_assert(stl::is_pointer_v<It>,
"Not a pointer type");
64 using first_type =
decltype(stl::as_const(stl::declval<It>()->element.first));
65 using second_type =
decltype((stl::declval<It>()->element.second));
68 using value_type = stl::pair<first_type, second_type>;
70 using reference = value_type;
71 using difference_type = stl::ptrdiff_t;
72 using iterator_category = stl::input_iterator_tag;
73 using iterator_concept = stl::random_access_iterator_tag;
75 constexpr dense_map_iterator() noexcept
78 constexpr dense_map_iterator(
const It iter) noexcept
81 template<
typename Other>
82 requires (!stl::same_as<It, Other> && stl::constructible_from<It, Other>)
83 constexpr dense_map_iterator(
const dense_map_iterator<Other> &other) noexcept
86 constexpr dense_map_iterator &operator++()
noexcept {
90 constexpr dense_map_iterator operator++(
int)
noexcept {
91 const dense_map_iterator orig = *
this;
92 return ++(*this), orig;
95 constexpr dense_map_iterator &operator--()
noexcept {
99 constexpr dense_map_iterator operator--(
int)
noexcept {
100 const dense_map_iterator orig = *
this;
101 return operator--(), orig;
104 constexpr dense_map_iterator &operator+=(
const difference_type value)
noexcept {
109 constexpr dense_map_iterator
operator+(
const difference_type value)
const noexcept {
110 dense_map_iterator copy = *
this;
111 return (copy += value);
114 constexpr dense_map_iterator &operator-=(
const difference_type value)
noexcept {
115 return (*
this += -value);
118 constexpr dense_map_iterator operator-(
const difference_type value)
const noexcept {
119 return (*
this + -value);
122 [[nodiscard]]
constexpr reference operator[](
const difference_type value)
const noexcept {
123 return {it[value].element.first, it[value].element.second};
126 [[nodiscard]]
constexpr pointer operator->()
const noexcept {
130 [[nodiscard]]
constexpr reference operator*()
const noexcept {
131 return operator[](0);
134 template<
typename Other>
135 [[nodiscard]]
constexpr stl::ptrdiff_t operator-(
const dense_map_iterator<Other> &other)
const noexcept {
136 return it - other.it;
139 template<
typename Other>
140 [[nodiscard]]
constexpr bool operator==(
const dense_map_iterator<Other> &other)
const noexcept {
141 return it == other.it;
144 template<
typename Other>
145 [[nodiscard]]
constexpr auto operator<=>(
const dense_map_iterator<Other> &other)
const noexcept {
146 return it <=> other.it;
154class dense_map_local_iterator final {
156 friend class dense_map_local_iterator;
158 static_assert(stl::is_pointer_v<It>,
"Not a pointer type");
159 using first_type =
decltype(stl::as_const(stl::declval<It>()->element.first));
160 using second_type =
decltype((stl::declval<It>()->element.second));
163 using value_type = stl::pair<first_type, second_type>;
165 using reference = value_type;
166 using difference_type = stl::ptrdiff_t;
167 using iterator_category = stl::input_iterator_tag;
168 using iterator_concept = stl::forward_iterator_tag;
170 constexpr dense_map_local_iterator()
noexcept =
default;
172 constexpr dense_map_local_iterator(It iter,
const stl::size_t pos) noexcept
176 template<
typename Other>
177 requires (!stl::same_as<It, Other> && stl::constructible_from<It, Other>)
178 constexpr dense_map_local_iterator(
const dense_map_local_iterator<Other> &other) noexcept
180 offset{other.offset} {}
182 constexpr dense_map_local_iterator &operator++()
noexcept {
183 return (offset = it[
static_cast<difference_type
>(offset)].next), *
this;
186 constexpr dense_map_local_iterator operator++(
int)
noexcept {
187 const dense_map_local_iterator orig = *
this;
188 return ++(*this), orig;
191 [[nodiscard]]
constexpr pointer operator->()
const noexcept {
195 [[nodiscard]]
constexpr reference operator*()
const noexcept {
196 const auto idx =
static_cast<difference_type
>(offset);
197 return {it[idx].element.first, it[idx].element.second};
200 template<
typename Other>
201 [[nodiscard]]
constexpr bool operator==(
const dense_map_local_iterator<Other> &other)
const noexcept {
202 return offset == other.offset;
205 [[nodiscard]]
constexpr stl::size_t index()
const noexcept {
211 stl::size_t offset{dense_map_placeholder_position};
230template<
typename Key,
typename Type,
typename Hash,
typename KeyEqual,
typename Allocator>
232 static constexpr float default_threshold = 0.875f;
233 static constexpr stl::size_t minimum_capacity = 8u;
234 static constexpr stl::size_t placeholder_position = internal::dense_map_placeholder_position;
236 using node_type = internal::dense_map_node<Key, Type>;
237 using alloc_traits = stl::allocator_traits<Allocator>;
238 static_assert(stl::is_same_v<typename alloc_traits::value_type, stl::pair<const Key, Type>>,
"Invalid value type");
239 using sparse_container_type = stl::vector<stl::size_t, typename alloc_traits::template rebind_alloc<stl::size_t>>;
240 using packed_container_type = stl::vector<node_type, typename alloc_traits::template rebind_alloc<node_type>>;
242 [[nodiscard]] stl::size_t key_to_bucket(
const auto &key)
const noexcept {
247 [[nodiscard]]
auto constrained_find(
const auto &key,
const stl::size_t
bucket) {
248 for(
auto offset = sparse.first()[
bucket]; offset != placeholder_position; offset = packed.first()[offset].next) {
249 if(packed.second()(packed.first()[offset].element.first, key)) {
250 return begin() +
static_cast<iterator::difference_type
>(offset);
257 [[nodiscard]]
auto constrained_find(
const auto &key,
const stl::size_t
bucket)
const {
258 for(
auto offset = sparse.first()[
bucket]; offset != placeholder_position; offset = packed.first()[offset].next) {
259 if(packed.second()(packed.first()[offset].element.first, key)) {
260 return cbegin() +
static_cast<const_iterator::difference_type
>(offset);
267 template<
typename Other,
typename... Args>
268 [[nodiscard]]
auto insert_or_do_nothing(Other &&key, Args &&...args) {
269 const auto index = key_to_bucket(key);
271 if(
auto it = constrained_find(key, index); it !=
end()) {
272 return stl::make_pair(it,
false);
275 packed.first().emplace_back(sparse.first()[index], stl::piecewise_construct, stl::forward_as_tuple(stl::forward<Other>(key)), stl::forward_as_tuple(stl::forward<Args>(args)...));
276 sparse.first()[index] = packed.first().size() - 1u;
277 rehash_if_required();
279 return stl::make_pair(--
end(),
true);
282 template<
typename Other,
typename Arg>
283 [[nodiscard]]
auto insert_or_overwrite(Other &&key, Arg &&value) {
284 const auto index = key_to_bucket(key);
286 if(
auto it = constrained_find(key, index); it !=
end()) {
287 it->second = stl::forward<Arg>(value);
288 return stl::make_pair(it,
false);
291 packed.first().emplace_back(sparse.first()[index], stl::forward<Other>(key), stl::forward<Arg>(value));
292 sparse.first()[index] = packed.first().size() - 1u;
293 rehash_if_required();
295 return stl::make_pair(--
end(),
true);
298 void move_and_pop(
const stl::size_t pos) {
299 if(
const auto last =
size() - 1u; pos != last) {
300 size_type *curr = &sparse.first()[key_to_bucket(packed.first().back().element.first)];
301 packed.first()[pos] = stl::move(packed.first().back());
302 for(; *curr != last; curr = &packed.first()[*curr].next) {}
306 packed.first().pop_back();
309 void rehash_if_required() {
333 using iterator = internal::dense_map_iterator<typename packed_container_type::pointer>;
335 using const_iterator = internal::dense_map_iterator<typename packed_container_type::const_pointer>;
337 using local_iterator = internal::dense_map_local_iterator<typename packed_container_type::pointer>;
339 using const_local_iterator = internal::dense_map_local_iterator<typename packed_container_type::const_pointer>;
380 : sparse{allocator, hash},
381 packed{allocator, equal} {
394 : sparse{
stl::piecewise_construct,
stl::forward_as_tuple(other.sparse.first(), allocator),
stl::forward_as_tuple(other.sparse.second())},
395 packed{
stl::piecewise_construct,
stl::forward_as_tuple(other.packed.first(), allocator),
stl::forward_as_tuple(other.packed.second())},
396 threshold{other.threshold} {}
407 : sparse{stl::piecewise_construct, stl::forward_as_tuple(stl::move(other.sparse.first()), allocator), stl::forward_as_tuple(stl::move(other.sparse.second()))},
408 packed{stl::piecewise_construct, stl::forward_as_tuple(stl::move(other.packed.first()), allocator), stl::forward_as_tuple(stl::move(other.packed.second()))},
409 threshold{other.threshold} {}
432 swap(sparse, other.sparse);
433 swap(packed, other.packed);
434 swap(threshold, other.threshold);
442 return sparse.first().get_allocator();
453 return packed.first().data();
463 return packed.first().data();
472 return packed.first().data() + packed.first().size();
482 return packed.first().data() + packed.first().size();
489 [[nodiscard]]
bool empty() const noexcept {
490 return packed.first().empty();
498 return packed.first().size();
506 return packed.first().max_size();
511 sparse.first().clear();
512 packed.first().clear();
524 return insert_or_do_nothing(value.first, value.second);
529 return insert_or_do_nothing(stl::move(value.first), stl::move(value.second));
536 template<
typename Arg>
537 requires stl::constructible_from<value_type, Arg &&>
538 stl::pair<iterator, bool>
insert(Arg &&value) {
539 return insert_or_do_nothing(stl::forward<Arg>(value).first, stl::forward<Arg>(value).second);
547 void insert(stl::input_iterator
auto first, stl::input_iterator
auto last) {
548 for(; first != last; ++first) {
562 template<
typename Arg>
564 return insert_or_overwrite(key, stl::forward<Arg>(value));
568 template<
typename Arg>
570 return insert_or_overwrite(stl::move(key), stl::forward<Arg>(value));
586 template<
typename... Args>
587 stl::pair<iterator, bool>
emplace([[maybe_unused]] Args &&...args) {
588 if constexpr(
sizeof...(Args) == 0u) {
589 return insert_or_do_nothing(
key_type{});
590 }
else if constexpr(
sizeof...(Args) == 1u) {
591 return insert_or_do_nothing(stl::forward<Args>(args).first..., stl::forward<Args>(args).second...);
592 }
else if constexpr(
sizeof...(Args) == 2u) {
593 return insert_or_do_nothing(stl::forward<Args>(args)...);
595 auto &node = packed.first().emplace_back(packed.first().size(), stl::forward<Args>(args)...);
596 const auto index = key_to_bucket(node.element.first);
598 if(
auto it = constrained_find(node.element.first, index); it !=
end()) {
599 packed.first().pop_back();
600 return stl::make_pair(it,
false);
603 stl::swap(node.next, sparse.first()[index]);
604 rehash_if_required();
606 return stl::make_pair(--
end(),
true);
621 template<
typename... Args>
623 return insert_or_do_nothing(key, stl::forward<Args>(args)...);
627 template<
typename... Args>
629 return insert_or_do_nothing(stl::move(key), stl::forward<Args>(args)...);
638 const auto diff = pos -
cbegin();
640 return begin() + diff;
650 const auto dist = first -
cbegin();
652 for(
auto from = last -
cbegin(); from != dist; --from) {
653 erase(packed.first()[
static_cast<size_type>(from) - 1u].element.first);
656 return (
begin() + dist);
665 for(
size_type *curr = &sparse.first()[key_to_bucket(key)]; *curr != placeholder_position; curr = &packed.first()[*curr].next) {
666 if(packed.second()(packed.first()[*curr].element.first, key)) {
667 const auto index = *curr;
668 *curr = packed.first()[*curr].next;
684 ENTT_ASSERT(it !=
end(),
"Invalid key");
691 ENTT_ASSERT(it !=
cend(),
"Invalid key");
703 ENTT_ASSERT(it !=
cend(),
"Invalid key");
711 ENTT_ASSERT(it !=
end(),
"Invalid key");
721 return insert_or_do_nothing(key).first->second;
730 return insert_or_do_nothing(stl::move(key)).first->second;
759 return constrained_find(key, key_to_bucket(key));
764 return constrained_find(key, key_to_bucket(key));
776 return constrained_find(key, key_to_bucket(key));
782 return constrained_find(key, key_to_bucket(key));
792 const auto it =
find(key);
793 return {it, it + !(it ==
end())};
798 const auto it =
find(key);
799 return {it, it + !(it ==
cend())};
809 [[nodiscard]] stl::pair<iterator, iterator>
equal_range(
const auto &key)
811 const auto it =
find(key);
812 return {it, it + !(it ==
end())};
816 [[nodiscard]] stl::pair<const_iterator, const_iterator>
equal_range(
const auto &key)
const
818 const auto it =
find(key);
819 return {it, it + !(it ==
cend())};
848 return {packed.first().data(), sparse.first()[index]};
866 return {packed.first().data(), sparse.first()[index]};
901 return sparse.first().size();
909 return sparse.first().max_size();
927 return key_to_bucket(key);
951 ENTT_ASSERT(value > 0.f,
"Invalid load factor");
962 auto value = cnt > minimum_capacity ? cnt : minimum_capacity;
964 value = value > cap ? value : cap;
966 if(
const auto sz = stl::bit_ceil(value); sz !=
bucket_count()) {
967 sparse.first().resize(sz);
969 for(
auto &&elem: sparse.first()) {
970 elem = placeholder_position;
974 const auto index = key_to_bucket(packed.first()[pos].element.first);
975 packed.first()[pos].next = stl::exchange(sparse.first()[index], pos);
986 packed.first().reserve(cnt);
995 return sparse.second();
1003 return packed.second();
1009 float threshold{default_threshold};
1019template<
typename Key,
typename Value,
typename Allocator>
1020struct uses_allocator<entt::internal::dense_map_node<Key, Value>, Allocator>
1021 : entt::stl::true_type {};
dense_map(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 find(const auto &key)
Finds an element with a key that compares equivalent to a given key.
dense_map(const allocator_type &allocator)
Constructs an empty container with a given allocator.
void clear() noexcept
Clears the container.
stl::pair< iterator, bool > try_emplace(const key_type &key, Args &&...args)
Inserts in-place if the key does not exist, does nothing if the key exists.
float load_factor() const
Returns the average number of elements per bucket.
size_type erase(const key_type &key)
Removes the element associated with a given key.
bool contains(const auto &key) const
Checks if the container contains an element with a key that compares equivalent to a given value.
stl::ptrdiff_t difference_type
Signed integer type.
stl::pair< const Key, Type > value_type
Key-value type of the container.
mapped_type & operator[](key_type &&key)
Accesses or inserts a given element.
stl::pair< iterator, iterator > equal_range(const key_type &key)
Returns a range containing all elements with a given key.
const_iterator cbegin() const noexcept
Returns an iterator to the beginning.
internal::dense_map_iterator< typename packed_container_type::pointer > iterator
Input iterator type.
const_local_iterator begin(const size_type index) const
Returns an iterator to the beginning of a given bucket.
size_type size() const noexcept
Returns the number of elements in a container.
const_local_iterator end(const size_type index) const
Returns an iterator to the end of a given bucket.
dense_map & operator=(dense_map &&) noexcept=default
Default move assignment operator.
const mapped_type & at(const key_type &key) const
Accesses a given element with bounds checking.
KeyEqual key_equal
Type of function to use to compare the keys for equality.
stl::pair< const_iterator, const_iterator > equal_range(const key_type &key) const
Returns a range containing all elements with a given key.
void insert(stl::input_iterator auto first, stl::input_iterator auto last)
Inserts elements into the container, if their keys do not exist.
size_type max_size() const noexcept
Returns the maximum possible number of elements.
mapped_type & at(const key_type &key)
Accesses a given element with bounds checking.
void reserve(const size_type cnt)
Reserves space for at least the specified number of elements and regenerates the hash table.
size_type max_bucket_count() const
Returns the maximum number of buckets.
iterator erase(const_iterator first, const_iterator last)
Removes the given elements from a container.
size_type count(const key_type &key) const
Returns the number of elements matching a key (either 1 or 0).
local_iterator begin(const size_type index)
Returns an iterator to the beginning of a given bucket.
bool contains(const key_type &key) const
Checks if the container contains an element with a given key.
const_iterator cend() const noexcept
Returns an iterator to the end.
stl::pair< iterator, bool > insert_or_assign(key_type &&key, Arg &&value)
Inserts an element into the container or assigns to the current element if the key already exists.
dense_map(const size_type cnt, const allocator_type &allocator)
Constructs an empty container with a given allocator and user supplied minimal number of buckets.
size_type count(const auto &key) const
Returns the number of elements matching a key (either 1 or 0).
stl::pair< iterator, bool > insert_or_assign(const key_type &key, Arg &&value)
Inserts an element into the container or assigns to the current element if the key already exists.
float max_load_factor() const
Returns the maximum average number of elements per bucket.
mapped_type & at(const auto &key)
Accesses a given element with bounds checking.
stl::pair< iterator, bool > try_emplace(key_type &&key, Args &&...args)
Inserts in-place if the key does not exist, does nothing if the key exists.
internal::dense_map_local_iterator< typename packed_container_type::const_pointer > const_local_iterator
Constant input iterator type.
const_iterator find(const key_type &key) const
Finds an element with a given key.
void max_load_factor(const float value)
Sets the desired maximum average number of elements per bucket.
dense_map & operator=(const dense_map &)=default
Default copy assignment operator.
stl::pair< iterator, iterator > equal_range(const auto &key)
Returns a range containing all elements that compare equivalent to a given key.
mapped_type const & at(const auto &key) const
Accesses a given element with bounds checking.
iterator find(const key_type &key)
Finds an element with a given key.
const_iterator begin() const noexcept
Returns an iterator to the beginning.
Type mapped_type
Mapped type of the container.
void rehash(const size_type cnt)
Reserves at least the specified number of buckets and regenerates the hash table.
const_local_iterator cend(const size_type index) const
Returns an iterator to the end of a given bucket.
iterator erase(const_iterator pos)
Removes an element from a given position.
void swap(dense_map &other) noexcept
const_iterator find(const auto &key) const
Finds an element with a given key.
dense_map(const dense_map &other, const allocator_type &allocator)
Allocator-extended copy constructor.
size_type bucket(const key_type &key) const
Returns the bucket for a given key.
mapped_type & operator[](const key_type &key)
Accesses or inserts a given element.
constexpr allocator_type get_allocator() const noexcept
Returns the associated allocator.
stl::pair< iterator, bool > emplace(Args &&...args)
Constructs an element in-place, if the key does not exist.
const_local_iterator cbegin(const size_type index) const
Returns an iterator to the beginning of a given bucket.
stl::size_t size_type
Unsigned integer type.
dense_map(const dense_map &)=default
Default copy constructor.
stl::pair< iterator, bool > insert(const value_type &value)
Inserts an element into the container, if the key does not exist.
local_iterator end(const size_type index)
Returns an iterator to the end of a given bucket.
stl::pair< const_iterator, const_iterator > equal_range(const auto &key) const
Returns a range containing all elements with a given key.
key_equal key_eq() const
Returns the function used to compare keys for equality.
dense_map(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_map_local_iterator< typename packed_container_type::pointer > local_iterator
Input iterator type.
stl::pair< iterator, bool > insert(value_type &&value)
Inserts an element into the container, if the key does not exist.
dense_map(dense_map &&) noexcept=default
Default move constructor.
bool empty() const noexcept
Checks whether a container is empty.
Allocator allocator_type
Allocator type.
size_type bucket_size(const size_type index) const
Returns the number of elements in a given bucket.
Hash hasher
Type of function to use to hash the keys.
Key key_type
Key type of the container.
size_type bucket_count() const
Returns the number of buckets.
dense_map()
Default constructor.
~dense_map()=default
Default destructor.
stl::pair< iterator, bool > insert(Arg &&value)
Inserts an element into the container, if the key does not exist.
hasher hash_function() const
Returns the function used to hash the keys.
const_iterator end() const noexcept
Returns an iterator to the end.
iterator begin() noexcept
Returns an iterator to the beginning.
internal::dense_map_iterator< typename packed_container_type::const_pointer > const_iterator
Constant input iterator type.
iterator end() noexcept
Returns an iterator to the end.
Custom EnTT namespace for the standard template library.
constexpr Type make_obj_using_allocator(const auto &allocator, Args &&...args)
Uses-allocator construction utility (waiting for C++20).
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).