1 #ifndef ENTT_CONTAINER_DENSE_MAP_HPP
2 #define ENTT_CONTAINER_DENSE_MAP_HPP
12 #include <type_traits>
15 #include "../config/config.h"
16 #include "../core/compressed_pair.hpp"
17 #include "../core/iterator.hpp"
18 #include "../core/memory.hpp"
19 #include "../core/type_traits.hpp"
31 template<
typename Key,
typename Type>
32 struct dense_map_node final {
33 using value_type = std::pair<Key, Type>;
35 template<
typename... Args>
36 dense_map_node(
const std::size_t pos, Args &&...args)
38 element{std::forward<Args>(args)...} {}
40 template<
typename Allocator,
typename... Args>
41 dense_map_node(std::allocator_arg_t,
const Allocator &allocator,
const std::size_t pos, Args &&...args)
43 element{entt::make_obj_using_allocator<value_type>(allocator, std::forward<Args>(args)...)} {}
45 template<
typename Allocator>
46 dense_map_node(std::allocator_arg_t,
const Allocator &allocator,
const dense_map_node &other)
48 element{entt::make_obj_using_allocator<value_type>(allocator, other.element)} {}
50 template<
typename Allocator>
51 dense_map_node(std::allocator_arg_t,
const Allocator &allocator, dense_map_node &&other)
53 element{entt::make_obj_using_allocator<value_type>(allocator, std::move(other.element))} {}
60 class dense_map_iterator final {
62 friend class dense_map_iterator;
64 using first_type = decltype(std::as_const(std::declval<It>()->element.first));
65 using second_type = decltype((std::declval<It>()->element.second));
68 using value_type = std::pair<first_type, second_type>;
70 using reference = value_type;
71 using difference_type = std::ptrdiff_t;
72 using iterator_category = std::input_iterator_tag;
74 dense_map_iterator() ENTT_NOEXCEPT
77 dense_map_iterator(
const It iter) ENTT_NOEXCEPT
80 template<
typename Other,
typename = std::enable_if_t<!std::is_same_v<It, Other> && std::is_constructible_v<It, Other>>>
81 dense_map_iterator(
const dense_map_iterator<Other> &other) ENTT_NOEXCEPT
84 dense_map_iterator &operator++() ENTT_NOEXCEPT {
88 dense_map_iterator operator++(
int) ENTT_NOEXCEPT {
89 dense_map_iterator orig = *
this;
90 return ++(*this), orig;
93 dense_map_iterator &operator--() ENTT_NOEXCEPT {
97 dense_map_iterator operator--(
int) ENTT_NOEXCEPT {
98 dense_map_iterator orig = *
this;
99 return operator--(), orig;
102 dense_map_iterator &operator+=(
const difference_type value) ENTT_NOEXCEPT {
107 dense_map_iterator
operator+(
const difference_type value)
const ENTT_NOEXCEPT {
108 dense_map_iterator copy = *
this;
109 return (copy += value);
112 dense_map_iterator &operator-=(
const difference_type value) ENTT_NOEXCEPT {
113 return (*
this += -value);
116 dense_map_iterator operator-(
const difference_type value)
const ENTT_NOEXCEPT {
117 return (*
this + -value);
120 [[nodiscard]] reference operator[](
const difference_type value)
const ENTT_NOEXCEPT {
121 return {it[value].element.first, it[value].element.second};
124 [[nodiscard]] pointer operator->()
const ENTT_NOEXCEPT {
128 [[nodiscard]] reference operator*()
const ENTT_NOEXCEPT {
129 return {it->element.first, it->element.second};
132 template<
typename ILhs,
typename IRhs>
133 friend std::ptrdiff_t operator-(
const dense_map_iterator<ILhs> &,
const dense_map_iterator<IRhs> &) ENTT_NOEXCEPT;
135 template<
typename ILhs,
typename IRhs>
136 friend bool operator==(
const dense_map_iterator<ILhs> &,
const dense_map_iterator<IRhs> &) ENTT_NOEXCEPT;
138 template<
typename ILhs,
typename IRhs>
139 friend bool operator<(
const dense_map_iterator<ILhs> &,
const dense_map_iterator<IRhs> &) ENTT_NOEXCEPT;
145 template<
typename ILhs,
typename IRhs>
146 [[nodiscard]] std::ptrdiff_t operator-(
const dense_map_iterator<ILhs> &lhs,
const dense_map_iterator<IRhs> &rhs) ENTT_NOEXCEPT {
147 return lhs.it - rhs.it;
150 template<
typename ILhs,
typename IRhs>
151 [[nodiscard]]
bool operator==(
const dense_map_iterator<ILhs> &lhs,
const dense_map_iterator<IRhs> &rhs) ENTT_NOEXCEPT {
152 return lhs.it == rhs.it;
155 template<
typename ILhs,
typename IRhs>
156 [[nodiscard]]
bool operator!=(
const dense_map_iterator<ILhs> &lhs,
const dense_map_iterator<IRhs> &rhs) ENTT_NOEXCEPT {
157 return !(lhs == rhs);
160 template<
typename ILhs,
typename IRhs>
161 [[nodiscard]]
bool operator<(
const dense_map_iterator<ILhs> &lhs,
const dense_map_iterator<IRhs> &rhs) ENTT_NOEXCEPT {
162 return lhs.it < rhs.it;
165 template<
typename ILhs,
typename IRhs>
166 [[nodiscard]]
bool operator>(
const dense_map_iterator<ILhs> &lhs,
const dense_map_iterator<IRhs> &rhs) ENTT_NOEXCEPT {
170 template<
typename ILhs,
typename IRhs>
171 [[nodiscard]]
bool operator<=(
const dense_map_iterator<ILhs> &lhs,
const dense_map_iterator<IRhs> &rhs) ENTT_NOEXCEPT {
175 template<
typename ILhs,
typename IRhs>
176 [[nodiscard]]
bool operator>=(
const dense_map_iterator<ILhs> &lhs,
const dense_map_iterator<IRhs> &rhs) ENTT_NOEXCEPT {
180 template<
typename It>
181 class dense_map_local_iterator final {
183 friend class dense_map_local_iterator;
185 using first_type = decltype(std::as_const(std::declval<It>()->element.first));
186 using second_type = decltype((std::declval<It>()->element.second));
189 using value_type = std::pair<first_type, second_type>;
191 using reference = value_type;
192 using difference_type = std::ptrdiff_t;
193 using iterator_category = std::input_iterator_tag;
195 dense_map_local_iterator() ENTT_NOEXCEPT
199 dense_map_local_iterator(It iter,
const std::size_t pos) ENTT_NOEXCEPT
203 template<
typename Other,
typename = std::enable_if_t<!std::is_same_v<It, Other> && std::is_constructible_v<It, Other>>>
204 dense_map_local_iterator(
const dense_map_local_iterator<Other> &other) ENTT_NOEXCEPT
206 offset{other.offset} {}
208 dense_map_local_iterator &operator++() ENTT_NOEXCEPT {
209 return offset = it[offset].next, *
this;
212 dense_map_local_iterator operator++(
int) ENTT_NOEXCEPT {
213 dense_map_local_iterator orig = *
this;
214 return ++(*this), orig;
217 [[nodiscard]] pointer operator->()
const ENTT_NOEXCEPT {
221 [[nodiscard]] reference operator*()
const ENTT_NOEXCEPT {
222 return {it[offset].element.first, it[offset].element.second};
225 [[nodiscard]] std::size_t index()
const ENTT_NOEXCEPT {
234 template<
typename ILhs,
typename IRhs>
235 [[nodiscard]]
bool operator==(
const dense_map_local_iterator<ILhs> &lhs,
const dense_map_local_iterator<IRhs> &rhs) ENTT_NOEXCEPT {
236 return lhs.index() == rhs.index();
239 template<
typename ILhs,
typename IRhs>
240 [[nodiscard]]
bool operator!=(
const dense_map_local_iterator<ILhs> &lhs,
const dense_map_local_iterator<IRhs> &rhs) ENTT_NOEXCEPT {
241 return !(lhs == rhs);
264 template<
typename Key,
typename Type,
typename Hash,
typename KeyEqual,
typename Allocator>
266 static constexpr
float default_threshold = 0.875f;
267 static constexpr std::size_t minimum_capacity = 8u;
269 using node_type = internal::dense_map_node<Key, Type>;
270 using alloc_traits =
typename std::allocator_traits<Allocator>;
271 static_assert(std::is_same_v<
typename alloc_traits::value_type, std::pair<const Key, Type>>,
"Invalid value type");
272 using sparse_container_type = std::vector<std::size_t, typename alloc_traits::template rebind_alloc<std::size_t>>;
273 using packed_container_type = std::vector<node_type, typename alloc_traits::template rebind_alloc<node_type>>;
275 template<
typename Other>
276 [[nodiscard]] std::size_t key_to_bucket(
const Other &key)
const ENTT_NOEXCEPT {
280 template<
typename Other>
281 [[nodiscard]]
auto constrained_find(
const Other &key, std::size_t
bucket) {
283 if(packed.
second()(it->first, key)) {
284 return begin() +
static_cast<typename iterator::difference_type
>(it.index());
291 template<
typename Other>
292 [[nodiscard]]
auto constrained_find(
const Other &key, std::size_t
bucket)
const {
294 if(packed.
second()(it->first, key)) {
295 return cbegin() +
static_cast<typename iterator::difference_type
>(it.index());
302 template<
typename Other,
typename... Args>
303 [[nodiscard]]
auto insert_or_do_nothing(Other &&key, Args &&...args) {
304 const auto index = key_to_bucket(key);
306 if(
auto it = constrained_find(key, index); it !=
end()) {
307 return std::make_pair(it,
false);
310 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)...));
311 sparse.
first()[index] = packed.
first().size() - 1u;
312 rehash_if_required();
314 return std::make_pair(--
end(),
true);
317 template<
typename Other,
typename Arg>
318 [[nodiscard]]
auto insert_or_overwrite(Other &&key, Arg &&value) {
319 const auto index = key_to_bucket(key);
321 if(
auto it = constrained_find(key, index); it !=
end()) {
322 it->second = std::forward<Arg>(value);
323 return std::make_pair(it,
false);
326 packed.
first().emplace_back(sparse.
first()[index], std::forward<Other>(key), std::forward<Arg>(value));
327 sparse.
first()[index] = packed.
first().size() - 1u;
328 rehash_if_required();
330 return std::make_pair(--
end(),
true);
333 void move_and_pop(
const std::size_t pos) {
334 if(
const auto last =
size() - 1u; pos != last) {
335 packed.
first()[pos] = std::move(packed.
first().back());
336 size_type *curr = sparse.
first().data() + key_to_bucket(packed.
first().back().element.first);
337 for(; *curr != last; curr = &packed.
first()[*curr].next) {}
341 packed.
first().pop_back();
344 void rehash_if_required() {
366 using iterator = internal::dense_map_iterator<typename packed_container_type::iterator>;
368 using const_iterator = internal::dense_map_iterator<typename packed_container_type::const_iterator>;
370 using local_iterator = internal::dense_map_local_iterator<typename packed_container_type::iterator>;
372 using const_local_iterator = internal::dense_map_local_iterator<typename packed_container_type::const_iterator>;
413 : sparse{allocator, hash},
414 packed{allocator, equal},
415 threshold{default_threshold} {
428 : sparse{std::piecewise_construct, std::forward_as_tuple(other.sparse.first(), allocator), std::forward_as_tuple(other.sparse.second())},
429 packed{std::piecewise_construct, std::forward_as_tuple(other.packed.first(), allocator), std::forward_as_tuple(other.packed.second())},
430 threshold{other.threshold} {}
441 : sparse{std::piecewise_construct, std::forward_as_tuple(std::move(other.sparse.first()), allocator), std::forward_as_tuple(std::move(other.sparse.second()))},
442 packed{std::piecewise_construct, std::forward_as_tuple(std::move(other.packed.first()), allocator), std::forward_as_tuple(std::move(other.packed.second()))},
443 threshold{other.threshold} {}
462 return sparse.
first().get_allocator();
474 return packed.
first().begin();
484 return packed.
first().begin();
498 return packed.
first().end();
508 return packed.
first().end();
515 [[nodiscard]]
bool empty() const ENTT_NOEXCEPT {
516 return packed.
first().empty();
524 return packed.
first().size();
529 sparse.
first().clear();
530 packed.
first().clear();
542 return insert_or_do_nothing(value.first, value.second);
547 return insert_or_do_nothing(std::move(value.first), std::move(value.second));
554 template<
typename Arg>
555 std::enable_if_t<std::is_constructible_v<value_type, Arg &&>, std::pair<iterator, bool>>
557 return insert_or_do_nothing(std::forward<Arg>(value).first, std::forward<Arg>(value).second);
566 template<
typename It>
568 for(; first != last; ++first) {
582 template<
typename Arg>
584 return insert_or_overwrite(key, std::forward<Arg>(value));
588 template<
typename Arg>
590 return insert_or_overwrite(std::move(key), std::forward<Arg>(value));
606 template<
typename... Args>
607 std::pair<iterator, bool>
emplace([[maybe_unused]] Args &&...args) {
608 if constexpr(
sizeof...(Args) == 0u) {
609 return insert_or_do_nothing(
key_type{});
610 }
else if constexpr(
sizeof...(Args) == 1u) {
611 return insert_or_do_nothing(std::forward<Args>(args).first..., std::forward<Args>(args).second...);
612 }
else if constexpr(
sizeof...(Args) == 2u) {
613 return insert_or_do_nothing(std::forward<Args>(args)...);
615 auto &node = packed.
first().emplace_back(packed.
first().size(), std::forward<Args>(args)...);
616 const auto index = key_to_bucket(node.element.first);
618 if(
auto it = constrained_find(node.element.first, index); it !=
end()) {
619 packed.
first().pop_back();
620 return std::make_pair(it,
false);
623 std::swap(node.next, sparse.
first()[index]);
624 rehash_if_required();
626 return std::make_pair(--
end(),
true);
641 template<
typename... Args>
643 return insert_or_do_nothing(key, std::forward<Args>(args)...);
647 template<
typename... Args>
649 return insert_or_do_nothing(std::move(key), std::forward<Args>(args)...);
658 const auto diff = pos -
cbegin();
660 return begin() + diff;
670 const auto dist = first -
cbegin();
672 for(
auto from = last -
cbegin(); from != dist; --from) {
673 erase(packed.
first()[from - 1u].element.first);
676 return (
begin() + dist);
685 for(
size_type *curr = sparse.
first().data() + key_to_bucket(key); *curr != (std::numeric_limits<size_type>::max)(); curr = &packed.
first()[*curr].next) {
686 if(packed.
second()(packed.
first()[*curr].element.first, key)) {
687 const auto index = *curr;
688 *curr = packed.
first()[*curr].next;
703 swap(sparse, other.sparse);
704 swap(packed, other.packed);
705 swap(threshold, other.threshold);
715 ENTT_ASSERT(it !=
end(),
"Invalid key");
722 ENTT_ASSERT(it !=
cend(),
"Invalid key");
732 return insert_or_do_nothing(key).first->second;
741 return insert_or_do_nothing(std::move(key)).first->second;
751 return constrained_find(key, key_to_bucket(key));
756 return constrained_find(key, key_to_bucket(key));
767 template<
typename Other>
768 [[nodiscard]] std::enable_if_t<is_transparent_v<hasher> && is_transparent_v<key_equal>, std::conditional_t<false, Other, iterator>>
770 return constrained_find(key, key_to_bucket(key));
774 template<
typename Other>
775 [[nodiscard]] std::enable_if_t<is_transparent_v<hasher> && is_transparent_v<key_equal>, std::conditional_t<false, Other, const_iterator>>
777 return constrained_find(key, key_to_bucket(key));
796 template<
typename Other>
797 [[nodiscard]] std::enable_if_t<is_transparent_v<hasher> && is_transparent_v<key_equal>, std::conditional_t<false, Other, bool>>
808 return {packed.
first().begin(), sparse.
first()[index]};
826 return {packed.
first().begin(), sparse.
first()[index]};
835 return {packed.
first().begin(), (std::numeric_limits<size_type>::max)()};
853 return {packed.
first().begin(), (std::numeric_limits<size_type>::max)()};
861 return sparse.
first().size();
869 return sparse.
first().max_size();
887 return key_to_bucket(key);
911 ENTT_ASSERT(value > 0.f,
"Invalid load factor");
922 auto value = (std::max)(count, minimum_capacity);
926 sparse.
first().resize(sz);
927 std::fill(sparse.
first().begin(), sparse.
first().end(), (std::numeric_limits<size_type>::max)());
930 const auto index = key_to_bucket(packed.
first()[pos].element.first);
931 packed.
first()[pos].next = std::exchange(sparse.
first()[index], pos);
942 packed.
first().reserve(count);
977 template<
typename Key,
typename Value,
typename Allocator>
978 struct uses_allocator<
entt::internal::dense_map_node<Key, Value>, Allocator>
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 key-value pairs with unique keys.
mapped_type & at(const key_type &key)
Accesses a given element with bounds checking.
dense_map(const allocator_type &allocator)
Constructs an empty container with a given allocator.
dense_map & operator=(dense_map &&)=default
Default move assignment operator.
float load_factor() const
Returns the average number of elements per bucket.
const_iterator cend() const
Returns an iterator to the end.
size_type erase(const key_type &key)
Removes the element associated with a given key.
mapped_type & operator[](const key_type &key)
Accesses or inserts a given element.
const_iterator begin() const
Returns an iterator to the beginning.
dense_map(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...
const_local_iterator begin(const size_type index) const
Returns an iterator to the beginning of a given bucket.
dense_map(dense_map &&other, const allocator_type &allocator)
Allocator-extended move constructor.
std::size_t size_type
Unsigned integer type.
void insert(It first, It last)
Inserts elements into the container, if their keys do not exist.
KeyEqual key_equal
Type of function to use to compare the keys for equality.
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.
iterator end()
Returns an iterator to the end.
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.
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.
iterator begin()
Returns an iterator to the beginning.
std::pair< iterator, bool > insert(const value_type &value)
Inserts an element into the container, if the key does not exist.
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.
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.
void max_load_factor(const float value)
Sets the desired maximum average number of elements per bucket.
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.
Type mapped_type
Mapped type of the container.
const_iterator cbegin() const
Returns an iterator to the beginning.
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.
iterator erase(const_iterator pos)
Removes an element from a given position.
void clear()
Clears the container.
dense_map(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 ...
dense_map(const dense_map &other, const allocator_type &allocator)
Allocator-extended copy constructor.
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.
const_local_iterator end([[maybe_unused]] const size_type index) const
Returns an iterator to the end of a given bucket.
const mapped_type & at(const key_type &key) const
Accesses a given element with bounds checking.
local_iterator end([[maybe_unused]] const size_type index)
Returns an iterator to the end of a given bucket.
const_iterator end() const
Returns an iterator to the end.
size_type bucket(const key_type &key) const
Returns the bucket for a given key.
const_local_iterator cbegin(const size_type index) const
Returns an iterator to the beginning of a given bucket.
dense_map & operator=(const dense_map &)=default
Default copy assignment operator.
std::pair< const Key, Type > value_type
Key-value type of the container.
dense_map(const dense_map &)=default
Default copy constructor.
std::pair< iterator, bool > emplace([[maybe_unused]] Args &&...args)
Constructs an element in-place, if the key does not exist.
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 value.
void swap(dense_map &other)
Exchanges the contents with those of a given container.
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.
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.
key_equal key_eq() const
Returns the function used to compare keys for equality.
internal::dense_map_iterator< typename packed_container_type::iterator > iterator
Input iterator type.
void rehash(const size_type count)
Reserves at least the specified number of buckets and regenerates the hash table.
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.
mapped_type & operator[](key_type &&key)
Accesses or inserts a given element.
Allocator allocator_type
Allocator type.
constexpr allocator_type get_allocator() const
Returns the associated allocator.
size_type bucket_size(const size_type index) const
Returns the number of elements in a given bucket.
dense_map(dense_map &&)=default
Default move constructor.
Hash hasher
Type of function to use to hash the keys.
bool empty() const
Checks whether a container is empty.
Key key_type
Key type of the container.
size_type bucket_count() const
Returns the number of buckets.
void reserve(const size_type count)
Reserves space for at least the specified number of elements and regenerates the hash table.
dense_map()
Default constructor.
const_local_iterator cend([[maybe_unused]] const size_type index) const
Returns an iterator to the end of a given bucket.
dense_map(const size_type bucket_count, 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 keys.
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.
size_type size() const
Returns the number of elements in a container.
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.