IceShard 1
A personal game engine project, with development focused on 2D/2.5D games.
Loading...
Searching...
No Matches
hashmap_details.hxx
Go to the documentation of this file.
1
3
4#pragma
5#include <ice/array.hxx>
8
9namespace ice
10{
11
13 {
14
15 template<ice::concepts::AssociativeContainerType T>
17
19 template<typename T>
21 typename std::remove_reference_t<T>::EntryType;
22 { std::remove_reference_t<T>::OperationLogic } -> std::convertible_to<ice::ContainerLogic>;
23 { t._capacity } -> std::convertible_to<ice::u32>;
24 { t._count } -> std::convertible_to<ice::u32>;
25 { t._hashes } -> std::convertible_to<ice::u32 const*>;
26 { t._entries } -> std::convertible_to<ice::detail::hashmap::HashMapEntryType<T>*>;
27 { t._data } -> std::convertible_to<ice::container::ValuePtr<T>>;
28 };
29
30 static constexpr ice::f32 Constant_HashMapMaxFill = 0.7f;
31 static constexpr ice::u32 Constant_EndOfList = 0xffffffffu;
32
39
40 constexpr auto calc_required_capacity(ice::ncount max_count) noexcept -> ice::ncount
41 {
42 return static_cast<ice::ncount::base_type>(
43 max_count.native() / Constant_HashMapMaxFill + 0.99f /* magic */
44 );
45 }
46
47 constexpr auto capacity_with_overhead(ice::ncount max_count) noexcept -> ice::ncount
48 {
49 return calc_required_capacity(max_count);
50 }
51
52 template<typename EntryType, typename ValueType>
53 constexpr auto calc_meminfo(ice::ncount capacity) noexcept -> ice::meminfo
54 {
55 ice::ncount const new_internal_capacity = ice::detail::hashmap::capacity_with_overhead(capacity);
56
57 ice::meminfo alloc_info = ice::meminfo_of<ice::u32> * new_internal_capacity;
58 alloc_info += ice::meminfo_of<EntryType> * capacity;
59 alloc_info += ice::meminfo_of<ValueType> * capacity;
60 return alloc_info;
61 }
62
63 template<ice::detail::hashmap::HashMapContainer ContainerT>
64 inline auto entries(ContainerT const& map) noexcept -> ice::Span<typename ContainerT::EntryType const>
65 {
66 return ice::Span{ map._entries, map._count };
67 }
68
69 template<ice::detail::hashmap::HashMapContainer ContainerT>
70 inline auto find(ContainerT const& map, ice::u64 key) noexcept -> FindResult
71 {
72 FindResult fr{
73 .hash_i = Constant_EndOfList,
74 .entry_prev = Constant_EndOfList,
75 .entry_i = Constant_EndOfList,
76 };
77
78 if (map._count == 0)
79 {
80 return fr;
81 }
82
83 fr.hash_i = key % map._capacity;
84 fr.entry_i = map._hashes[fr.hash_i];
85
86 while (fr.entry_i != Constant_EndOfList)
87 {
88 if (map._entries[fr.entry_i].key == key)
89 {
90 return fr;
91 }
92
93 fr.entry_prev = fr.entry_i;
94 fr.entry_i = map._entries[fr.entry_i].next;
95 }
96 return fr;
97 }
98
99 template<ice::detail::hashmap::HashMapContainer ContainerT>
100 inline auto make(ContainerT& map, ice::u64 key) noexcept -> ice::u32
101 {
103 if (fr.hash_i == Constant_EndOfList)
104 {
105 fr.hash_i = key % map._capacity;
106 }
107
108 // The count is now the new index.
109 ice::u32 const index = map._count;
110
111 // Set the key we are use to make the new entry.
112 map._entries[index].key = key;
113
114 // ... and the next to the previous index stored in the hashes
115 map._entries[index].next = map._hashes[fr.hash_i];
116
117 // ... then this is the first entry on this hash table.
118 map._hashes[fr.hash_i] = index;
119
120 // ... increase the entry count
121 map._count += 1;
122
123 return index;
124 }
125
126 template<ice::detail::hashmap::HashMapContainer ContainerT>
127 inline void erase(ContainerT& map, FindResult const fr) noexcept
128 {
129 using Entry = typename ContainerT::EntryType;
130 using Type = typename ContainerT::ValueType;
131
132 // We only update the hash index if we remove the first entry.
133 if (fr.entry_prev == Constant_EndOfList)
134 {
135 map._hashes[fr.hash_i] = map._entries[fr.entry_i].next;
136 }
137 else
138 {
139 map._entries[fr.entry_prev].next = map._entries[fr.entry_i].next;
140 }
141
142 // Destroy the object...
143 if constexpr (ContainerT::OperationLogic == ContainerLogic::Complex)
144 {
145 ice::mem_destruct_at(map._data + fr.entry_i);
146 }
147
148 map._count -= 1;
149 if (fr.entry_i == map._count)
150 {
151 return;
152 }
153
154 if constexpr (ContainerT::OperationLogic == ContainerLogic::Complex)
155 {
156 // Move construct the last object to the now empty location...
158 Memory{
159 .location = map._data + fr.entry_i,
160 .size = ice::size_of<Type>,
161 .alignment = ice::align_of<Type>
162 },
163 ice::move(map._data[map._count])
164 );
165
166 // ... then destroy the object at the last location.
167 ice::mem_destruct_at(map._data + map._count);
168 }
169 else
170 {
171 map._data[fr.entry_i] = map._data[map._count];
172 }
173
174 // Copy entry data...
175 map._entries[fr.entry_i] = map._entries[map._count];
176
177 // The first and last entry are the same...
178 FindResult const last_key_first_entry = ice::detail::hashmap::find(map, map._entries[map._count].key);
179 if (last_key_first_entry.entry_prev == Constant_EndOfList)
180 {
181 // ... update the hashes
182 map._hashes[last_key_first_entry.hash_i] = fr.entry_i;
183 }
184 else
185 {
186 Entry* prev_entry = map._entries + last_key_first_entry.entry_prev;
187
188 // Until 'next' holds the last value index...
189 while (prev_entry->next != map._count)
190 {
191 // ... we move forward
192 prev_entry = map._entries + prev_entry->next;
193 }
194
195 // ... we can fix the 'next' value
196 prev_entry->next = fr.entry_i;
197 }
198 }
199
200 template<ice::detail::hashmap::HashMapContainer ContainerT>
201 inline auto find_or_fail(ContainerT const& map, ice::container::KeyType<ContainerT> key) noexcept -> ice::u32
202 {
203 return ice::detail::hashmap::find(map, key).entry_i;
204 }
205
206 template<ice::detail::hashmap::HashMapContainer ContainerT>
207 inline auto find_or_make(ContainerT& map, ice::u64 key, bool& found) noexcept -> ice::u32
208 {
210
211 // If entry index is not valid we still might have a previous element on the same hash index.
212 if (fr.entry_i != Constant_EndOfList)
213 {
214 found = true;
215 return fr.entry_i;
216 }
217
218 if (fr.hash_i == Constant_EndOfList)
219 {
220 fr.hash_i = key % map._capacity;
221 }
222
223 // The count is now the new index.
224 ice::u32 const index = map._count;
225
226 // Set the key we are use to make the new entry.
227 map._entries[index].key = key;
228
229 // ... and the next to the previous index stored in the hashes
230 map._entries[index].next = map._hashes[fr.hash_i];
231
232 // ... then this is the first entry on this hash table.
233 map._hashes[fr.hash_i] = index;
234
235 // ... increase the entry count
236 map._count += 1;
237
238 return index;
239 }
240
241 template<ice::detail::hashmap::HashMapContainer ContainerT>
242 inline bool find_and_erase(ContainerT& map, ice::u64 key) noexcept
243 {
244 FindResult const fr = ice::detail::hashmap::find(map, key);
245 if (fr.entry_i != Constant_EndOfList)
246 {
248 }
249 return fr.entry_i != Constant_EndOfList;
250 }
251
252 template<ice::detail::hashmap::HashMapContainer ContainerT>
253 inline auto find(ContainerT& map, ice::u32 entry_index) noexcept -> FindResult
254 {
255 FindResult fr{
256 .hash_i = Constant_EndOfList,
257 .entry_prev = Constant_EndOfList,
258 .entry_i = Constant_EndOfList,
259 };
260
261 if (map._count == 0)
262 {
263 return fr;
264 }
265 ICE_ASSERT_CORE(entry_index < map._count);
266 HashMapEntryType<ContainerT>* const entry = map._entries + entry_index;
267
268 fr.hash_i = entry->key % map._capacity;
269 fr.entry_i = map._hashes[fr.hash_i];
270
271 while (fr.entry_i != Constant_EndOfList)
272 {
273 if ((map._entries + fr.entry_i) == entry)
274 {
275 return fr;
276 }
277
278 fr.entry_prev = fr.entry_i;
279 fr.entry_i = map._entries[fr.entry_i].next;
280 }
281
282 ICE_ASSERT_CORE(fr.entry_i == entry_index);
283 return fr;
284 }
285
286 } // namespace hashmap::detail
287
288} // namespace ice
#define ICE_ASSERT_CORE(expression)
Definition assert_core.hxx:43
Definition container_concepts.hxx:22
A concept used to enable access to read-only operations for all compatible types.
Definition hashmap_details.hxx:20
typename std::remove_reference_t< ContainerT >::KeyType KeyType
Definition container_concepts.hxx:135
Definition hashmap_details.hxx:13
constexpr auto calc_meminfo(ice::ncount capacity) noexcept -> ice::meminfo
Definition hashmap_details.hxx:53
auto find(ContainerT const &map, ice::u64 key) noexcept -> FindResult
Definition hashmap_details.hxx:70
auto make(ContainerT &map, ice::u64 key) noexcept -> ice::u32
Definition hashmap_details.hxx:100
constexpr auto calc_required_capacity(ice::ncount max_count) noexcept -> ice::ncount
Definition hashmap_details.hxx:40
bool find_and_erase(ContainerT &map, ice::u64 key) noexcept
Definition hashmap_details.hxx:242
constexpr auto capacity_with_overhead(ice::ncount max_count) noexcept -> ice::ncount
Definition hashmap_details.hxx:47
auto find_or_make(ContainerT &map, ice::u64 key, bool &found) noexcept -> ice::u32
Definition hashmap_details.hxx:207
static constexpr ice::f32 Constant_HashMapMaxFill
Definition hashmap_details.hxx:30
void erase(ContainerT &map, FindResult const fr) noexcept
Definition hashmap_details.hxx:127
auto entries(ContainerT const &map) noexcept -> ice::Span< typename ContainerT::EntryType const >
Definition hashmap_details.hxx:64
auto find_or_fail(ContainerT const &map, ice::container::KeyType< ContainerT > key) noexcept -> ice::u32
Definition hashmap_details.hxx:201
static constexpr ice::u32 Constant_EndOfList
Definition hashmap_details.hxx:31
ice::const_correct_t< T, typename std::remove_reference_t< T >::EntryType > HashMapEntryType
Definition hashmap_details.hxx:16
SPDX-License-Identifier: MIT.
Definition array.hxx:12
constexpr ice::usize size_of
Definition mem_info.hxx:12
auto mem_move_construct_at(void *memory_ptr, T &&other) noexcept -> T *
Definition mem_initializers.hxx:26
@ Complex
The collection handles complex data types and properly implements copy and move semantics.
Definition container_logic.hxx:19
std::uint64_t u64
Definition types.hxx:27
constexpr ice::ualign align_of
Definition mem_info.hxx:15
typename ice::const_correct< OwnerT, ValueT >::type const_correct_t
Definition utility.hxx:24
std::uint32_t u32
Definition types.hxx:26
void mem_destruct_at(T *location) noexcept
Definition mem_initializers.hxx:107
constexpr ice::meminfo meminfo_of
Definition mem_info.hxx:18
float f32
Definition types.hxx:16
Definition mem_memory.hxx:13
A view into an array of objects laid out in contiguous memory.
Definition span.hxx:17
Definition hashmap_details.hxx:34
ice::u32 entry_i
Definition hashmap_details.hxx:37
ice::u32 entry_prev
Definition hashmap_details.hxx:36
ice::u32 hash_i
Definition hashmap_details.hxx:35
Definition mem_size_types.hxx:59
Definition ncount.hxx:14
ice::detail::nvalue_base_utype base_type
Definition nvalue.hxx:75