EnTT 3.13.0
Loading...
Searching...
No Matches
dense_map.hpp
1#ifndef ENTT_CONTAINER_DENSE_MAP_HPP
2#define ENTT_CONTAINER_DENSE_MAP_HPP
3
4#include <cmath>
5#include <cstddef>
6#include <functional>
7#include <iterator>
8#include <limits>
9#include <memory>
10#include <tuple>
11#include <type_traits>
12#include <utility>
13#include <vector>
14#include "../config/config.h"
15#include "../core/compressed_pair.hpp"
16#include "../core/iterator.hpp"
17#include "../core/memory.hpp"
18#include "../core/type_traits.hpp"
19#include "fwd.hpp"
20
21namespace entt {
22
24namespace internal {
25
26template<typename Key, typename Type>
27struct dense_map_node final {
28 using value_type = std::pair<Key, Type>;
29
30 template<typename... Args>
31 dense_map_node(const std::size_t pos, Args &&...args)
32 : next{pos},
33 element{std::forward<Args>(args)...} {}
34
35 template<typename Allocator, typename... Args>
36 dense_map_node(std::allocator_arg_t, const Allocator &allocator, const std::size_t pos, Args &&...args)
37 : next{pos},
38 element{entt::make_obj_using_allocator<value_type>(allocator, std::forward<Args>(args)...)} {}
39
40 template<typename Allocator>
41 dense_map_node(std::allocator_arg_t, const Allocator &allocator, const dense_map_node &other)
42 : next{other.next},
43 element{entt::make_obj_using_allocator<value_type>(allocator, other.element)} {}
44
45 template<typename Allocator>
46 dense_map_node(std::allocator_arg_t, const Allocator &allocator, dense_map_node &&other)
47 : next{other.next},
48 element{entt::make_obj_using_allocator<value_type>(allocator, std::move(other.element))} {}
49
50 std::size_t next;
51 value_type element;
52};
53
54template<typename It>
55class dense_map_iterator final {
56 template<typename>
57 friend class dense_map_iterator;
58
59 using first_type = decltype(std::as_const(std::declval<It>()->element.first));
60 using second_type = decltype((std::declval<It>()->element.second));
61
62public:
63 using value_type = std::pair<first_type, second_type>;
65 using reference = value_type;
66 using difference_type = std::ptrdiff_t;
67 using iterator_category = std::input_iterator_tag;
68 using iterator_concept = std::random_access_iterator_tag;
69
70 constexpr dense_map_iterator() noexcept
71 : it{} {}
72
73 constexpr dense_map_iterator(const It iter) noexcept
74 : it{iter} {}
75
76 template<typename Other, typename = std::enable_if_t<!std::is_same_v<It, Other> && std::is_constructible_v<It, Other>>>
77 constexpr dense_map_iterator(const dense_map_iterator<Other> &other) noexcept
78 : it{other.it} {}
79
80 constexpr dense_map_iterator &operator++() noexcept {
81 return ++it, *this;
82 }
83
84 constexpr dense_map_iterator operator++(int) noexcept {
85 dense_map_iterator orig = *this;
86 return ++(*this), orig;
87 }
88
89 constexpr dense_map_iterator &operator--() noexcept {
90 return --it, *this;
91 }
92
93 constexpr dense_map_iterator operator--(int) noexcept {
94 dense_map_iterator orig = *this;
95 return operator--(), orig;
96 }
97
98 constexpr dense_map_iterator &operator+=(const difference_type value) noexcept {
99 it += value;
100 return *this;
101 }
102
103 constexpr dense_map_iterator operator+(const difference_type value) const noexcept {
104 dense_map_iterator copy = *this;
105 return (copy += value);
106 }
107
108 constexpr dense_map_iterator &operator-=(const difference_type value) noexcept {
109 return (*this += -value);
110 }
111
112 constexpr dense_map_iterator operator-(const difference_type value) const noexcept {
113 return (*this + -value);
114 }
115
116 [[nodiscard]] constexpr reference operator[](const difference_type value) const noexcept {
117 return {it[value].element.first, it[value].element.second};
118 }
119
120 [[nodiscard]] constexpr pointer operator->() const noexcept {
121 return operator*();
122 }
123
124 [[nodiscard]] constexpr reference operator*() const noexcept {
125 return {it->element.first, it->element.second};
126 }
127
128 template<typename Lhs, typename Rhs>
129 friend constexpr std::ptrdiff_t operator-(const dense_map_iterator<Lhs> &, const dense_map_iterator<Rhs> &) noexcept;
130
131 template<typename Lhs, typename Rhs>
132 friend constexpr bool operator==(const dense_map_iterator<Lhs> &, const dense_map_iterator<Rhs> &) noexcept;
133
134 template<typename Lhs, typename Rhs>
135 friend constexpr bool operator<(const dense_map_iterator<Lhs> &, const dense_map_iterator<Rhs> &) noexcept;
136
137private:
138 It it;
139};
140
141template<typename Lhs, typename Rhs>
142[[nodiscard]] constexpr std::ptrdiff_t operator-(const dense_map_iterator<Lhs> &lhs, const dense_map_iterator<Rhs> &rhs) noexcept {
143 return lhs.it - rhs.it;
144}
145
146template<typename Lhs, typename Rhs>
147[[nodiscard]] constexpr bool operator==(const dense_map_iterator<Lhs> &lhs, const dense_map_iterator<Rhs> &rhs) noexcept {
148 return lhs.it == rhs.it;
149}
150
151template<typename Lhs, typename Rhs>
152[[nodiscard]] constexpr bool operator!=(const dense_map_iterator<Lhs> &lhs, const dense_map_iterator<Rhs> &rhs) noexcept {
153 return !(lhs == rhs);
154}
155
156template<typename Lhs, typename Rhs>
157[[nodiscard]] constexpr bool operator<(const dense_map_iterator<Lhs> &lhs, const dense_map_iterator<Rhs> &rhs) noexcept {
158 return lhs.it < rhs.it;
159}
160
161template<typename Lhs, typename Rhs>
162[[nodiscard]] constexpr bool operator>(const dense_map_iterator<Lhs> &lhs, const dense_map_iterator<Rhs> &rhs) noexcept {
163 return rhs < lhs;
164}
165
166template<typename Lhs, typename Rhs>
167[[nodiscard]] constexpr bool operator<=(const dense_map_iterator<Lhs> &lhs, const dense_map_iterator<Rhs> &rhs) noexcept {
168 return !(lhs > rhs);
169}
170
171template<typename Lhs, typename Rhs>
172[[nodiscard]] constexpr bool operator>=(const dense_map_iterator<Lhs> &lhs, const dense_map_iterator<Rhs> &rhs) noexcept {
173 return !(lhs < rhs);
174}
175
176template<typename It>
177class dense_map_local_iterator final {
178 template<typename>
179 friend class dense_map_local_iterator;
180
181 using first_type = decltype(std::as_const(std::declval<It>()->element.first));
182 using second_type = decltype((std::declval<It>()->element.second));
183
184public:
185 using value_type = std::pair<first_type, second_type>;
187 using reference = value_type;
188 using difference_type = std::ptrdiff_t;
189 using iterator_category = std::input_iterator_tag;
190 using iterator_concept = std::forward_iterator_tag;
191
192 constexpr dense_map_local_iterator() noexcept
193 : it{},
194 offset{} {}
195
196 constexpr dense_map_local_iterator(It iter, const std::size_t pos) noexcept
197 : it{iter},
198 offset{pos} {}
199
200 template<typename Other, typename = std::enable_if_t<!std::is_same_v<It, Other> && std::is_constructible_v<It, Other>>>
201 constexpr dense_map_local_iterator(const dense_map_local_iterator<Other> &other) noexcept
202 : it{other.it},
203 offset{other.offset} {}
204
205 constexpr dense_map_local_iterator &operator++() noexcept {
206 return offset = it[offset].next, *this;
207 }
208
209 constexpr dense_map_local_iterator operator++(int) noexcept {
210 dense_map_local_iterator orig = *this;
211 return ++(*this), orig;
212 }
213
214 [[nodiscard]] constexpr pointer operator->() const noexcept {
215 return operator*();
216 }
217
218 [[nodiscard]] constexpr reference operator*() const noexcept {
219 return {it[offset].element.first, it[offset].element.second};
220 }
221
222 [[nodiscard]] constexpr std::size_t index() const noexcept {
223 return offset;
224 }
225
226private:
227 It it;
228 std::size_t offset;
229};
230
231template<typename Lhs, typename Rhs>
232[[nodiscard]] constexpr bool operator==(const dense_map_local_iterator<Lhs> &lhs, const dense_map_local_iterator<Rhs> &rhs) noexcept {
233 return lhs.index() == rhs.index();
234}
235
236template<typename Lhs, typename Rhs>
237[[nodiscard]] constexpr bool operator!=(const dense_map_local_iterator<Lhs> &lhs, const dense_map_local_iterator<Rhs> &rhs) noexcept {
238 return !(lhs == rhs);
239}
240
241} // namespace internal
257template<typename Key, typename Type, typename Hash, typename KeyEqual, typename Allocator>
259 static constexpr float default_threshold = 0.875f;
260 static constexpr std::size_t minimum_capacity = 8u;
261
262 using node_type = internal::dense_map_node<Key, Type>;
263 using alloc_traits = std::allocator_traits<Allocator>;
264 static_assert(std::is_same_v<typename alloc_traits::value_type, std::pair<const Key, Type>>, "Invalid value type");
265 using sparse_container_type = std::vector<std::size_t, typename alloc_traits::template rebind_alloc<std::size_t>>;
266 using packed_container_type = std::vector<node_type, typename alloc_traits::template rebind_alloc<node_type>>;
267
268 template<typename Other>
269 [[nodiscard]] std::size_t key_to_bucket(const Other &key) const noexcept {
270 return fast_mod(static_cast<size_type>(sparse.second()(key)), bucket_count());
271 }
272
273 template<typename Other>
274 [[nodiscard]] auto constrained_find(const Other &key, std::size_t bucket) {
275 for(auto it = begin(bucket), last = end(bucket); it != last; ++it) {
276 if(packed.second()(it->first, key)) {
277 return begin() + static_cast<typename iterator::difference_type>(it.index());
278 }
279 }
280
281 return end();
282 }
283
284 template<typename Other>
285 [[nodiscard]] auto constrained_find(const Other &key, std::size_t bucket) const {
286 for(auto it = cbegin(bucket), last = cend(bucket); it != last; ++it) {
287 if(packed.second()(it->first, key)) {
288 return cbegin() + static_cast<typename iterator::difference_type>(it.index());
289 }
290 }
291
292 return cend();
293 }
294
295 template<typename Other, typename... Args>
296 [[nodiscard]] auto insert_or_do_nothing(Other &&key, Args &&...args) {
297 const auto index = key_to_bucket(key);
298
299 if(auto it = constrained_find(key, index); it != end()) {
300 return std::make_pair(it, false);
301 }
302
303 packed.first().emplace_back(sparse.first()[index], std::piecewise_construct, std::forward_as_tuple(std::forward<Other>(key)), std::forward_as_tuple(std::forward<Args>(args)...));
304 sparse.first()[index] = packed.first().size() - 1u;
305 rehash_if_required();
306
307 return std::make_pair(--end(), true);
308 }
309
310 template<typename Other, typename Arg>
311 [[nodiscard]] auto insert_or_overwrite(Other &&key, Arg &&value) {
312 const auto index = key_to_bucket(key);
313
314 if(auto it = constrained_find(key, index); it != end()) {
315 it->second = std::forward<Arg>(value);
316 return std::make_pair(it, false);
317 }
318
319 packed.first().emplace_back(sparse.first()[index], std::forward<Other>(key), std::forward<Arg>(value));
320 sparse.first()[index] = packed.first().size() - 1u;
321 rehash_if_required();
322
323 return std::make_pair(--end(), true);
324 }
325
326 void move_and_pop(const std::size_t pos) {
327 if(const auto last = size() - 1u; pos != last) {
328 size_type *curr = sparse.first().data() + key_to_bucket(packed.first().back().element.first);
329 packed.first()[pos] = std::move(packed.first().back());
330 for(; *curr != last; curr = &packed.first()[*curr].next) {}
331 *curr = pos;
332 }
333
334 packed.first().pop_back();
335 }
336
337 void rehash_if_required() {
338 if(size() > (bucket_count() * max_load_factor())) {
339 rehash(bucket_count() * 2u);
340 }
341 }
342
343public:
345 using key_type = Key;
347 using mapped_type = Type;
349 using value_type = std::pair<const Key, Type>;
351 using size_type = std::size_t;
353 using hasher = Hash;
355 using key_equal = KeyEqual;
357 using allocator_type = Allocator;
359 using iterator = internal::dense_map_iterator<typename packed_container_type::iterator>;
361 using const_iterator = internal::dense_map_iterator<typename packed_container_type::const_iterator>;
363 using local_iterator = internal::dense_map_local_iterator<typename packed_container_type::iterator>;
365 using const_local_iterator = internal::dense_map_local_iterator<typename packed_container_type::const_iterator>;
366
369 : dense_map{minimum_capacity} {}
370
375 explicit dense_map(const allocator_type &allocator)
376 : dense_map{minimum_capacity, hasher{}, key_equal{}, allocator} {}
377
384 dense_map(const size_type cnt, const allocator_type &allocator)
385 : dense_map{cnt, hasher{}, key_equal{}, allocator} {}
386
394 dense_map(const size_type cnt, const hasher &hash, const allocator_type &allocator)
395 : dense_map{cnt, hash, key_equal{}, allocator} {}
396
405 explicit dense_map(const size_type cnt, const hasher &hash = hasher{}, const key_equal &equal = key_equal{}, const allocator_type &allocator = allocator_type{})
406 : sparse{allocator, hash},
407 packed{allocator, equal},
408 threshold{default_threshold} {
409 rehash(cnt);
410 }
411
413 dense_map(const dense_map &) = default;
414
420 dense_map(const dense_map &other, const allocator_type &allocator)
421 : sparse{std::piecewise_construct, std::forward_as_tuple(other.sparse.first(), allocator), std::forward_as_tuple(other.sparse.second())},
422 packed{std::piecewise_construct, std::forward_as_tuple(other.packed.first(), allocator), std::forward_as_tuple(other.packed.second())},
423 threshold{other.threshold} {}
424
426 dense_map(dense_map &&) noexcept(std::is_nothrow_move_constructible_v<compressed_pair<sparse_container_type, hasher>> &&std::is_nothrow_move_constructible_v<compressed_pair<packed_container_type, key_equal>>) = default;
427
433 dense_map(dense_map &&other, const allocator_type &allocator)
434 : sparse{std::piecewise_construct, std::forward_as_tuple(std::move(other.sparse.first()), allocator), std::forward_as_tuple(std::move(other.sparse.second()))},
435 packed{std::piecewise_construct, std::forward_as_tuple(std::move(other.packed.first()), allocator), std::forward_as_tuple(std::move(other.packed.second()))},
436 threshold{other.threshold} {}
437
442 dense_map &operator=(const dense_map &) = default;
443
448 dense_map &operator=(dense_map &&) noexcept(std::is_nothrow_move_assignable_v<compressed_pair<sparse_container_type, hasher>> &&std::is_nothrow_move_assignable_v<compressed_pair<packed_container_type, key_equal>>) = default;
449
454 [[nodiscard]] constexpr allocator_type get_allocator() const noexcept {
455 return sparse.first().get_allocator();
456 }
457
465 [[nodiscard]] const_iterator cbegin() const noexcept {
466 return packed.first().begin();
467 }
468
470 [[nodiscard]] const_iterator begin() const noexcept {
471 return cbegin();
472 }
473
475 [[nodiscard]] iterator begin() noexcept {
476 return packed.first().begin();
477 }
478
484 [[nodiscard]] const_iterator cend() const noexcept {
485 return packed.first().end();
486 }
487
489 [[nodiscard]] const_iterator end() const noexcept {
490 return cend();
491 }
492
494 [[nodiscard]] iterator end() noexcept {
495 return packed.first().end();
496 }
497
502 [[nodiscard]] bool empty() const noexcept {
503 return packed.first().empty();
504 }
505
510 [[nodiscard]] size_type size() const noexcept {
511 return packed.first().size();
512 }
513
518 [[nodiscard]] size_type max_size() const noexcept {
519 return packed.first().max_size();
520 }
521
523 void clear() noexcept {
524 sparse.first().clear();
525 packed.first().clear();
526 rehash(0u);
527 }
528
536 std::pair<iterator, bool> insert(const value_type &value) {
537 return insert_or_do_nothing(value.first, value.second);
538 }
539
541 std::pair<iterator, bool> insert(value_type &&value) {
542 return insert_or_do_nothing(std::move(value.first), std::move(value.second));
543 }
544
549 template<typename Arg>
550 std::enable_if_t<std::is_constructible_v<value_type, Arg &&>, std::pair<iterator, bool>>
551 insert(Arg &&value) {
552 return insert_or_do_nothing(std::forward<Arg>(value).first, std::forward<Arg>(value).second);
553 }
554
561 template<typename It>
562 void insert(It first, It last) {
563 for(; first != last; ++first) {
564 insert(*first);
565 }
566 }
567
577 template<typename Arg>
578 std::pair<iterator, bool> insert_or_assign(const key_type &key, Arg &&value) {
579 return insert_or_overwrite(key, std::forward<Arg>(value));
580 }
581
583 template<typename Arg>
584 std::pair<iterator, bool> insert_or_assign(key_type &&key, Arg &&value) {
585 return insert_or_overwrite(std::move(key), std::forward<Arg>(value));
586 }
587
601 template<typename... Args>
602 std::pair<iterator, bool> emplace([[maybe_unused]] Args &&...args) {
603 if constexpr(sizeof...(Args) == 0u) {
604 return insert_or_do_nothing(key_type{});
605 } else if constexpr(sizeof...(Args) == 1u) {
606 return insert_or_do_nothing(std::forward<Args>(args).first..., std::forward<Args>(args).second...);
607 } else if constexpr(sizeof...(Args) == 2u) {
608 return insert_or_do_nothing(std::forward<Args>(args)...);
609 } else {
610 auto &node = packed.first().emplace_back(packed.first().size(), std::forward<Args>(args)...);
611 const auto index = key_to_bucket(node.element.first);
612
613 if(auto it = constrained_find(node.element.first, index); it != end()) {
614 packed.first().pop_back();
615 return std::make_pair(it, false);
616 }
617
618 std::swap(node.next, sparse.first()[index]);
619 rehash_if_required();
620
621 return std::make_pair(--end(), true);
622 }
623 }
624
636 template<typename... Args>
637 std::pair<iterator, bool> try_emplace(const key_type &key, Args &&...args) {
638 return insert_or_do_nothing(key, std::forward<Args>(args)...);
639 }
640
642 template<typename... Args>
643 std::pair<iterator, bool> try_emplace(key_type &&key, Args &&...args) {
644 return insert_or_do_nothing(std::move(key), std::forward<Args>(args)...);
645 }
646
653 const auto diff = pos - cbegin();
654 erase(pos->first);
655 return begin() + diff;
656 }
657
665 const auto dist = first - cbegin();
666
667 for(auto from = last - cbegin(); from != dist; --from) {
668 erase(packed.first()[from - 1u].element.first);
669 }
670
671 return (begin() + dist);
672 }
673
680 for(size_type *curr = sparse.first().data() + key_to_bucket(key); *curr != (std::numeric_limits<size_type>::max)(); curr = &packed.first()[*curr].next) {
681 if(packed.second()(packed.first()[*curr].element.first, key)) {
682 const auto index = *curr;
683 *curr = packed.first()[*curr].next;
684 move_and_pop(index);
685 return 1u;
686 }
687 }
688
689 return 0u;
690 }
691
696 void swap(dense_map &other) {
697 using std::swap;
698 swap(sparse, other.sparse);
699 swap(packed, other.packed);
700 swap(threshold, other.threshold);
701 }
702
708 [[nodiscard]] mapped_type &at(const key_type &key) {
709 auto it = find(key);
710 ENTT_ASSERT(it != end(), "Invalid key");
711 return it->second;
712 }
713
715 [[nodiscard]] const mapped_type &at(const key_type &key) const {
716 auto it = find(key);
717 ENTT_ASSERT(it != cend(), "Invalid key");
718 return it->second;
719 }
720
726 [[nodiscard]] mapped_type &operator[](const key_type &key) {
727 return insert_or_do_nothing(key).first->second;
728 }
729
735 [[nodiscard]] mapped_type &operator[](key_type &&key) {
736 return insert_or_do_nothing(std::move(key)).first->second;
737 }
738
744 [[nodiscard]] size_type count(const key_type &key) const {
745 return find(key) != end();
746 }
747
754 template<typename Other>
755 [[nodiscard]] std::enable_if_t<is_transparent_v<hasher> && is_transparent_v<key_equal>, std::conditional_t<false, Other, size_type>>
756 count(const Other &key) const {
757 return find(key) != end();
758 }
759
766 [[nodiscard]] iterator find(const key_type &key) {
767 return constrained_find(key, key_to_bucket(key));
768 }
769
771 [[nodiscard]] const_iterator find(const key_type &key) const {
772 return constrained_find(key, key_to_bucket(key));
773 }
774
783 template<typename Other>
784 [[nodiscard]] std::enable_if_t<is_transparent_v<hasher> && is_transparent_v<key_equal>, std::conditional_t<false, Other, iterator>>
785 find(const Other &key) {
786 return constrained_find(key, key_to_bucket(key));
787 }
788
790 template<typename Other>
791 [[nodiscard]] std::enable_if_t<is_transparent_v<hasher> && is_transparent_v<key_equal>, std::conditional_t<false, Other, const_iterator>>
792 find(const Other &key) const {
793 return constrained_find(key, key_to_bucket(key));
794 }
795
802 [[nodiscard]] std::pair<iterator, iterator> equal_range(const key_type &key) {
803 const auto it = find(key);
804 return {it, it + !(it == end())};
805 }
806
808 [[nodiscard]] std::pair<const_iterator, const_iterator> equal_range(const key_type &key) const {
809 const auto it = find(key);
810 return {it, it + !(it == cend())};
811 }
812
821 template<typename Other>
822 [[nodiscard]] std::enable_if_t<is_transparent_v<hasher> && is_transparent_v<key_equal>, std::conditional_t<false, Other, std::pair<iterator, iterator>>>
823 equal_range(const Other &key) {
824 const auto it = find(key);
825 return {it, it + !(it == end())};
826 }
827
829 template<typename Other>
830 [[nodiscard]] std::enable_if_t<is_transparent_v<hasher> && is_transparent_v<key_equal>, std::conditional_t<false, Other, std::pair<const_iterator, const_iterator>>>
831 equal_range(const Other &key) const {
832 const auto it = find(key);
833 return {it, it + !(it == cend())};
834 }
835
841 [[nodiscard]] bool contains(const key_type &key) const {
842 return (find(key) != cend());
843 }
844
852 template<typename Other>
853 [[nodiscard]] std::enable_if_t<is_transparent_v<hasher> && is_transparent_v<key_equal>, std::conditional_t<false, Other, bool>>
854 contains(const Other &key) const {
855 return (find(key) != cend());
856 }
857
863 [[nodiscard]] const_local_iterator cbegin(const size_type index) const {
864 return {packed.first().begin(), sparse.first()[index]};
865 }
866
872 [[nodiscard]] const_local_iterator begin(const size_type index) const {
873 return cbegin(index);
874 }
875
881 [[nodiscard]] local_iterator begin(const size_type index) {
882 return {packed.first().begin(), sparse.first()[index]};
883 }
884
890 [[nodiscard]] const_local_iterator cend([[maybe_unused]] const size_type index) const {
891 return {packed.first().begin(), (std::numeric_limits<size_type>::max)()};
892 }
893
899 [[nodiscard]] const_local_iterator end(const size_type index) const {
900 return cend(index);
901 }
902
908 [[nodiscard]] local_iterator end([[maybe_unused]] const size_type index) {
909 return {packed.first().begin(), (std::numeric_limits<size_type>::max)()};
910 }
911
916 [[nodiscard]] size_type bucket_count() const {
917 return sparse.first().size();
918 }
919
924 [[nodiscard]] size_type max_bucket_count() const {
925 return sparse.first().max_size();
926 }
927
933 [[nodiscard]] size_type bucket_size(const size_type index) const {
934 return static_cast<size_type>(std::distance(begin(index), end(index)));
935 }
936
942 [[nodiscard]] size_type bucket(const key_type &key) const {
943 return key_to_bucket(key);
944 }
945
950 [[nodiscard]] float load_factor() const {
951 return size() / static_cast<float>(bucket_count());
952 }
953
958 [[nodiscard]] float max_load_factor() const {
959 return threshold;
960 }
961
966 void max_load_factor(const float value) {
967 ENTT_ASSERT(value > 0.f, "Invalid load factor");
968 threshold = value;
969 rehash(0u);
970 }
971
977 void rehash(const size_type cnt) {
978 auto value = cnt > minimum_capacity ? cnt : minimum_capacity;
979 const auto cap = static_cast<size_type>(size() / max_load_factor());
980 value = value > cap ? value : cap;
981
982 if(const auto sz = next_power_of_two(value); sz != bucket_count()) {
983 sparse.first().resize(sz);
984
985 for(auto &&elem: sparse.first()) {
986 elem = (std::numeric_limits<size_type>::max)();
987 }
988
989 for(size_type pos{}, last = size(); pos < last; ++pos) {
990 const auto index = key_to_bucket(packed.first()[pos].element.first);
991 packed.first()[pos].next = std::exchange(sparse.first()[index], pos);
992 }
993 }
994 }
995
1001 void reserve(const size_type cnt) {
1002 packed.first().reserve(cnt);
1003 rehash(static_cast<size_type>(std::ceil(cnt / max_load_factor())));
1004 }
1005
1010 [[nodiscard]] hasher hash_function() const {
1011 return sparse.second();
1012 }
1013
1018 [[nodiscard]] key_equal key_eq() const {
1019 return packed.second();
1020 }
1021
1022private:
1025 float threshold;
1026};
1027
1028} // namespace entt
1029
1031namespace std {
1032
1033template<typename Key, typename Value, typename Allocator>
1034struct uses_allocator<entt::internal::dense_map_node<Key, Value>, Allocator>
1035 : std::true_type {};
1036
1037} // namespace std
1040#endif
A compressed pair.
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 key-value pairs with unique keys.
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...
dense_map(const allocator_type &allocator)
Constructs an empty container with a given allocator.
void clear() noexcept
Clears the container.
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.
mapped_type & operator[](key_type &&key)
Accesses or inserts a given element.
std::enable_if_t< is_transparent_v< hasher > &&is_transparent_v< key_equal >, std::conditional_t< false, Other, bool > > contains(const Other &key) const
Checks if the container contains an element with a key that compares equivalent to a given value.
const_iterator cbegin() const noexcept
Returns an iterator to the beginning.
const_local_iterator begin(const size_type index) const
Returns an iterator to the beginning of a given bucket.
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).
size_type size() const noexcept
Returns the number of elements in a container.
std::size_t size_type
Unsigned integer type.
const_local_iterator end(const size_type index) const
Returns an iterator to the end of a given bucket.
void insert(It first, It last)
Inserts elements into the container, if their keys do not exist.
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 &key)
Returns a range containing all elements that compare equivalent to a given key.
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.
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.
std::enable_if_t< is_transparent_v< hasher > &&is_transparent_v< key_equal >, std::conditional_t< false, Other, const_iterator > > find(const Other &key) const
Finds an element with a given key.
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.
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.
std::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 max_load_factor() const
Returns the maximum average number of elements per bucket.
const_iterator find(const key_type &key) const
Finds an element with a given key.
internal::dense_map_iterator< typename packed_container_type::const_iterator > const_iterator
Constant input iterator type.
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.
std::pair< iterator, bool > insert(value_type &&value)
Inserts an element into the container, if the key does not exist.
iterator find(const key_type &key)
Finds an element with a given key.
std::pair< iterator, iterator > equal_range(const key_type &key)
Returns a range containing all elements 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.
dense_map(const dense_map &other, const allocator_type &allocator)
Allocator-extended copy constructor.
std::enable_if_t< std::is_constructible_v< value_type, Arg && >, std::pair< iterator, bool > > insert(Arg &&value)
Inserts an element into the container, if the key does not exist.
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 &key) const
Returns a range containing all elements with a given key.
size_type bucket(const key_type &key) const
Returns the bucket for a given key.
std::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.
mapped_type & operator[](const key_type &key)
Accesses or inserts a given element.
constexpr allocator_type get_allocator() const noexcept
Returns the associated allocator.
const_local_iterator cbegin(const size_type index) const
Returns an iterator to the beginning of a given bucket.
std::pair< const Key, Type > value_type
Key-value type of the container.
dense_map(const dense_map &)=default
Default copy constructor.
dense_map & operator=(dense_map &&) noexcept(std::is_nothrow_move_assignable_v< compressed_pair< sparse_container_type, hasher > > &&std::is_nothrow_move_assignable_v< compressed_pair< packed_container_type, key_equal > >)=default
Default move assignment operator.
std::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.
std::pair< iterator, bool > emplace(Args &&...args)
Constructs an element in-place, if the key does not exist.
std::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.
void swap(dense_map &other)
Exchanges the contents with those of a given container.
local_iterator end(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 &key)
Finds an element with a key that compares equivalent to 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_iterator< typename packed_container_type::iterator > iterator
Input iterator type.
internal::dense_map_local_iterator< typename packed_container_type::iterator > local_iterator
Input iterator type.
internal::dense_map_local_iterator< typename packed_container_type::const_iterator > const_local_iterator
Constant input iterator type.
bool empty() const noexcept
Checks whether a container is empty.
std::pair< iterator, bool > insert(const value_type &value)
Inserts an element into the container, if the key does not exist.
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.
std::pair< const_iterator, const_iterator > equal_range(const key_type &key) const
Returns a range containing all elements with a given key.
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.
iterator end() noexcept
Returns an iterator to the end.
dense_map(dense_map &&) noexcept(std::is_nothrow_move_constructible_v< compressed_pair< sparse_container_type, hasher > > &&std::is_nothrow_move_constructible_v< compressed_pair< packed_container_type, key_equal > >)=default
Default move constructor.
EnTT default namespace.
Definition dense_map.hpp:21
constexpr std::size_t fast_mod(const std::size_t value, const std::size_t mod) noexcept
Fast module utility function (powers of two only).
Definition memory.hpp:47
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 type_list< Type..., Other... > operator+(type_list< Type... >, type_list< Other... >)
Concatenates multiple type lists.
constexpr std::size_t next_power_of_two(const std::size_t value) noexcept
Computes the smallest power of two greater than or equal to a value (waiting for C++20 and std::bit_c...
Definition memory.hpp:30
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.
Helper type to use as pointer with input iterators.
Definition iterator.hpp:16