1#ifndef ENTT_CONTAINER_DENSE_MAP_HPP
2#define ENTT_CONTAINER_DENSE_MAP_HPP
14#include "../config/config.h"
15#include "../core/bit.hpp"
16#include "../core/compressed_pair.hpp"
17#include "../core/iterator.hpp"
18#include "../core/memory.hpp"
19#include "../core/type_traits.hpp"
27template<
typename Key,
typename Type>
28struct dense_map_node final {
29 using value_type = std::pair<Key, Type>;
31 template<
typename... Args>
32 dense_map_node(
const std::size_t pos, Args &&...args)
34 element{std::forward<Args>(args)...} {}
36 template<
typename Allocator,
typename... Args>
37 dense_map_node(std::allocator_arg_t,
const Allocator &allocator,
const std::size_t pos, Args &&...args)
41 template<
typename Allocator>
42 dense_map_node(std::allocator_arg_t,
const Allocator &allocator,
const dense_map_node &other)
46 template<
typename Allocator>
47 dense_map_node(std::allocator_arg_t,
const Allocator &allocator, dense_map_node &&other)
56class dense_map_iterator final {
58 friend class dense_map_iterator;
60 using first_type =
decltype(std::as_const(std::declval<It>()->element.first));
61 using second_type =
decltype((std::declval<It>()->element.second));
64 using value_type = std::pair<first_type, second_type>;
66 using reference = value_type;
67 using difference_type = std::ptrdiff_t;
68 using iterator_category = std::input_iterator_tag;
69 using iterator_concept = std::random_access_iterator_tag;
71 constexpr dense_map_iterator() noexcept
74 constexpr dense_map_iterator(
const It iter) noexcept
77 template<
typename Other,
typename = std::enable_if_t<!std::is_same_v<It, Other> && std::is_constructible_v<It, Other>>>
78 constexpr dense_map_iterator(
const dense_map_iterator<Other> &other) noexcept
81 constexpr dense_map_iterator &operator++()
noexcept {
85 constexpr dense_map_iterator operator++(
int)
noexcept {
86 dense_map_iterator orig = *
this;
87 return ++(*this), orig;
90 constexpr dense_map_iterator &operator--()
noexcept {
94 constexpr dense_map_iterator operator--(
int)
noexcept {
95 dense_map_iterator orig = *
this;
96 return operator--(), orig;
99 constexpr dense_map_iterator &operator+=(
const difference_type value)
noexcept {
104 constexpr dense_map_iterator
operator+(
const difference_type value)
const noexcept {
105 dense_map_iterator copy = *
this;
106 return (copy += value);
109 constexpr dense_map_iterator &operator-=(
const difference_type value)
noexcept {
110 return (*
this += -value);
113 constexpr dense_map_iterator operator-(
const difference_type value)
const noexcept {
114 return (*
this + -value);
117 [[nodiscard]]
constexpr reference operator[](
const difference_type value)
const noexcept {
118 return {it[value].element.first, it[value].element.second};
121 [[nodiscard]]
constexpr pointer operator->()
const noexcept {
125 [[nodiscard]]
constexpr reference operator*()
const noexcept {
126 return operator[](0);
129 template<
typename Lhs,
typename Rhs>
130 friend constexpr std::ptrdiff_t operator-(
const dense_map_iterator<Lhs> &,
const dense_map_iterator<Rhs> &)
noexcept;
132 template<
typename Lhs,
typename Rhs>
133 friend constexpr bool operator==(
const dense_map_iterator<Lhs> &,
const dense_map_iterator<Rhs> &)
noexcept;
135 template<
typename Lhs,
typename Rhs>
136 friend constexpr bool operator<(
const dense_map_iterator<Lhs> &,
const dense_map_iterator<Rhs> &)
noexcept;
142template<
typename Lhs,
typename Rhs>
143[[nodiscard]]
constexpr std::ptrdiff_t operator-(
const dense_map_iterator<Lhs> &lhs,
const dense_map_iterator<Rhs> &rhs)
noexcept {
144 return lhs.it - rhs.it;
147template<
typename Lhs,
typename Rhs>
148[[nodiscard]]
constexpr bool operator==(
const dense_map_iterator<Lhs> &lhs,
const dense_map_iterator<Rhs> &rhs)
noexcept {
149 return lhs.it == rhs.it;
152template<
typename Lhs,
typename Rhs>
153[[nodiscard]]
constexpr bool operator!=(
const dense_map_iterator<Lhs> &lhs,
const dense_map_iterator<Rhs> &rhs)
noexcept {
154 return !(lhs == rhs);
157template<
typename Lhs,
typename Rhs>
158[[nodiscard]]
constexpr bool operator<(
const dense_map_iterator<Lhs> &lhs,
const dense_map_iterator<Rhs> &rhs)
noexcept {
159 return lhs.it < rhs.it;
162template<
typename Lhs,
typename Rhs>
163[[nodiscard]]
constexpr bool operator>(
const dense_map_iterator<Lhs> &lhs,
const dense_map_iterator<Rhs> &rhs)
noexcept {
167template<
typename Lhs,
typename Rhs>
168[[nodiscard]]
constexpr bool operator<=(
const dense_map_iterator<Lhs> &lhs,
const dense_map_iterator<Rhs> &rhs)
noexcept {
172template<
typename Lhs,
typename Rhs>
173[[nodiscard]]
constexpr bool operator>=(
const dense_map_iterator<Lhs> &lhs,
const dense_map_iterator<Rhs> &rhs)
noexcept {
178class dense_map_local_iterator final {
180 friend class dense_map_local_iterator;
182 using first_type =
decltype(std::as_const(std::declval<It>()->element.first));
183 using second_type =
decltype((std::declval<It>()->element.second));
186 using value_type = std::pair<first_type, second_type>;
188 using reference = value_type;
189 using difference_type = std::ptrdiff_t;
190 using iterator_category = std::input_iterator_tag;
191 using iterator_concept = std::forward_iterator_tag;
193 constexpr dense_map_local_iterator() noexcept
197 constexpr dense_map_local_iterator(It iter,
const std::size_t pos) noexcept
201 template<
typename Other,
typename = std::enable_if_t<!std::is_same_v<It, Other> && std::is_constructible_v<It, Other>>>
202 constexpr dense_map_local_iterator(
const dense_map_local_iterator<Other> &other) noexcept
204 offset{other.offset} {}
206 constexpr dense_map_local_iterator &operator++()
noexcept {
207 return offset = it[offset].next, *
this;
210 constexpr dense_map_local_iterator operator++(
int)
noexcept {
211 dense_map_local_iterator orig = *
this;
212 return ++(*this), orig;
215 [[nodiscard]]
constexpr pointer operator->()
const noexcept {
219 [[nodiscard]]
constexpr reference operator*()
const noexcept {
220 return {it[offset].element.first, it[offset].element.second};
223 [[nodiscard]]
constexpr std::size_t index()
const noexcept {
232template<
typename Lhs,
typename Rhs>
233[[nodiscard]]
constexpr bool operator==(
const dense_map_local_iterator<Lhs> &lhs,
const dense_map_local_iterator<Rhs> &rhs)
noexcept {
234 return lhs.index() == rhs.index();
237template<
typename Lhs,
typename Rhs>
238[[nodiscard]]
constexpr bool operator!=(
const dense_map_local_iterator<Lhs> &lhs,
const dense_map_local_iterator<Rhs> &rhs)
noexcept {
239 return !(lhs == rhs);
258template<
typename Key,
typename Type,
typename Hash,
typename KeyEqual,
typename Allocator>
260 static constexpr float default_threshold = 0.875f;
261 static constexpr std::size_t minimum_capacity = 8u;
263 using node_type = internal::dense_map_node<Key, Type>;
264 using alloc_traits = std::allocator_traits<Allocator>;
265 static_assert(std::is_same_v<typename alloc_traits::value_type, std::pair<const Key, Type>>,
"Invalid value type");
266 using sparse_container_type = std::vector<std::size_t, typename alloc_traits::template rebind_alloc<std::size_t>>;
267 using packed_container_type = std::vector<node_type, typename alloc_traits::template rebind_alloc<node_type>>;
269 template<
typename Other>
275 template<
typename Other>
279 return begin() +
static_cast<typename iterator::difference_type
>(
it.index());
286 template<
typename Other>
290 return cbegin() +
static_cast<typename iterator::difference_type
>(
it.index());
297 template<
typename Other,
typename...
Args>
299 const auto index = key_to_bucket(
key);
301 if(
auto it = constrained_find(
key, index);
it !=
end()) {
302 return std::make_pair(
it,
false);
305 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)...));
306 sparse.
first()[index] = packed.
first().size() - 1u;
307 rehash_if_required();
309 return std::make_pair(--
end(),
true);
312 template<
typename Other,
typename Arg>
314 const auto index = key_to_bucket(
key);
316 if(
auto it = constrained_find(
key, index);
it !=
end()) {
317 it->second = std::forward<Arg>(value);
318 return std::make_pair(
it,
false);
321 packed.
first().emplace_back(sparse.
first()[index], std::forward<Other>(
key), std::forward<Arg>(value));
322 sparse.
first()[index] = packed.
first().size() - 1u;
323 rehash_if_required();
325 return std::make_pair(--
end(),
true);
328 void move_and_pop(
const std::size_t
pos) {
329 if(
const auto last =
size() - 1u;
pos != last) {
336 packed.
first().pop_back();
339 void rehash_if_required() {
361 using iterator = internal::dense_map_iterator<typename packed_container_type::iterator>;
363 using const_iterator = internal::dense_map_iterator<typename packed_container_type::const_iterator>;
365 using local_iterator = internal::dense_map_local_iterator<typename packed_container_type::iterator>;
367 using const_local_iterator = internal::dense_map_local_iterator<typename packed_container_type::const_iterator>;
424 threshold{
other.threshold} {}
435 : sparse{std::piecewise_construct, std::forward_as_tuple(std::move(
other.sparse.first()),
allocator), std::forward_as_tuple(std::move(
other.sparse.second()))},
436 packed{std::piecewise_construct, std::forward_as_tuple(std::move(
other.packed.first()),
allocator), std::forward_as_tuple(std::move(
other.packed.second()))},
437 threshold{
other.threshold} {}
459 return sparse.
first().get_allocator();
470 return packed.
first().begin();
480 return packed.
first().begin();
489 return packed.
first().end();
499 return packed.
first().end();
507 return packed.
first().empty();
515 return packed.
first().size();
523 return packed.
first().max_size();
528 sparse.
first().clear();
529 packed.
first().clear();
541 return insert_or_do_nothing(value.first, value.second);
546 return insert_or_do_nothing(std::move(value.first), std::move(value.second));
553 template<
typename Arg>
554 std::enable_if_t<std::is_constructible_v<value_type, Arg &&>, std::pair<iterator, bool>>
556 return insert_or_do_nothing(std::forward<Arg>(value).first, std::forward<Arg>(value).second);
565 template<
typename It>
567 for(; first != last; ++first) {
581 template<
typename Arg>
583 return insert_or_overwrite(
key, std::forward<Arg>(value));
587 template<
typename Arg>
589 return insert_or_overwrite(std::move(
key), std::forward<Arg>(value));
605 template<
typename...
Args>
607 if constexpr(
sizeof...(Args) == 0
u) {
608 return insert_or_do_nothing(
key_type{});
609 }
else if constexpr(
sizeof...(Args) == 1u) {
610 return insert_or_do_nothing(std::forward<Args>(
args).first..., std::forward<Args>(
args).second...);
611 }
else if constexpr(
sizeof...(Args) == 2u) {
612 return insert_or_do_nothing(std::forward<Args>(
args)...);
614 auto &node = packed.
first().emplace_back(packed.
first().size(), std::forward<Args>(
args)...);
615 const auto index = key_to_bucket(node.element.first);
617 if(
auto it = constrained_find(node.element.first, index);
it !=
end()) {
618 packed.
first().pop_back();
619 return std::make_pair(
it,
false);
622 std::swap(node.next, sparse.
first()[index]);
623 rehash_if_required();
625 return std::make_pair(--
end(),
true);
640 template<
typename...
Args>
642 return insert_or_do_nothing(
key, std::forward<Args>(
args)...);
646 template<
typename...
Args>
648 return insert_or_do_nothing(std::move(
key), std::forward<Args>(
args)...);
686 const auto index = *
curr;
714 ENTT_ASSERT(
it !=
end(),
"Invalid key");
721 ENTT_ASSERT(
it !=
cend(),
"Invalid key");
731 return insert_or_do_nothing(
key).first->second;
740 return insert_or_do_nothing(std::move(
key)).first->second;
758 template<
typename Other>
771 return constrained_find(
key, key_to_bucket(
key));
776 return constrained_find(
key, key_to_bucket(
key));
787 template<
typename Other>
790 return constrained_find(
key, key_to_bucket(
key));
794 template<
typename Other>
797 return constrained_find(
key, key_to_bucket(
key));
825 template<
typename Other>
833 template<
typename Other>
856 template<
typename Other>
868 return {packed.
first().begin(), sparse.
first()[index]};
886 return {packed.
first().begin(), sparse.
first()[index]};
895 return {packed.
first().begin(), (std::numeric_limits<size_type>::max)()};
913 return {packed.
first().begin(), (std::numeric_limits<size_type>::max)()};
921 return sparse.
first().size();
929 return sparse.
first().max_size();
947 return key_to_bucket(
key);
971 ENTT_ASSERT(value > 0.f,
"Invalid load factor");
982 auto value =
cnt > minimum_capacity ?
cnt : minimum_capacity;
984 value = value >
cap ? value :
cap;
990 elem = (std::numeric_limits<size_type>::max)();
994 const auto index = key_to_bucket(packed.
first()[
pos].element.first);
1029 float threshold{default_threshold};
1037template<
typename Key,
typename Value,
typename Allocator>
1038struct uses_allocator<
entt::internal::dense_map_node<Key, Value>, Allocator>
1039 : std::true_type {};
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.
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.
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.
void swap(dense_map &other) noexcept
Exchanges the contents with those of a given container.
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.
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.
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.
dense_map(dense_map &&) noexcept=default
Default move constructor.
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.
~dense_map()=default
Default destructor.
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.
constexpr Type make_obj_using_allocator(const Allocator &allocator, Args &&...args)
Uses-allocator construction utility (waiting for C++20).
constexpr std::enable_if_t< std::is_unsigned_v< Type >, Type > fast_mod(const Type value, const std::size_t mod) noexcept
Fast module utility function (powers of two only).
constexpr bool operator<=(const basic_hashed_string< Char > &lhs, const basic_hashed_string< Char > &rhs) noexcept
Compares two hashed strings.
constexpr std::enable_if_t< std::is_unsigned_v< Type >, Type > next_power_of_two(const Type value) noexcept
Computes the smallest power of two greater than or equal to a value (waiting for C++20 and std::bit_c...
constexpr bool operator<(const basic_hashed_string< Char > &lhs, const basic_hashed_string< Char > &rhs) noexcept
Compares two hashed strings.
constexpr type_list< Type..., Other... > operator+(type_list< Type... >, type_list< Other... >)
Concatenates multiple type lists.
constexpr bool operator!=(const basic_hashed_string< Char > &lhs, const basic_hashed_string< Char > &rhs) noexcept
Compares two hashed strings.
constexpr bool operator>=(const basic_hashed_string< Char > &lhs, const basic_hashed_string< Char > &rhs) noexcept
Compares two hashed strings.
constexpr bool operator>(const basic_hashed_string< Char > &lhs, const basic_hashed_string< Char > &rhs) noexcept
Compares two hashed strings.
constexpr bool operator==(const basic_hashed_string< Char > &lhs, const basic_hashed_string< Char > &rhs) noexcept
Compares two hashed strings.