Iros
 
Loading...
Searching...
No Matches
node_hash_table.h
Go to the documentation of this file.
1#pragma once
2
13#include "di/function/not_fn.h"
14#include "di/meta/algorithm.h"
15#include "di/meta/relation.h"
16#include "di/meta/util.h"
17#include "di/util/declval.h"
18#include "di/util/get.h"
21
22namespace di::container {
23namespace detail {
24 template<typename Value, typename Eq>
26 template<typename U>
31 };
32
33 template<typename Key, typename Value, typename Eq>
42
43 template<typename Value, bool is_map>
45 using Type = Value;
46 };
47
48 template<typename Value>
49 struct NodeHashTableKey<Value, true> {
51 };
52}
53
58template<typename Value, typename Eq, concepts::Hasher Hasher, typename Buckets, typename Tag, typename Interface,
59 bool is_multi, bool is_map, typename Self = Void>
60class NodeHashTable : public Interface {
61private:
62 template<typename, typename, concepts::Hasher, typename, typename, typename, bool, bool, typename>
63 friend class NodeHashTable;
64
65protected:
69
70 using ConcreteNode = decltype(Tag::node_type(in_place_type<Value>));
71
73
74 constexpr auto down_cast_self() -> decltype(auto) {
75 if constexpr (concepts::SameAs<Void, Self>) {
76 return *this;
77 } else {
78 return static_cast<Self&>(*this);
79 }
80 }
81
82 constexpr static bool allow_rehashing_in_insert = false;
83
84public:
85 NodeHashTable() = default;
86 NodeHashTable(NodeHashTable const&) = delete;
87 auto operator=(NodeHashTable const&) -> NodeHashTable& = delete;
88
89 constexpr explicit NodeHashTable(Eq eq, Hasher hasher = {}) : m_eq(util::move(eq)), m_hasher(util::move(hasher)) {}
90
91 constexpr NodeHashTable(NodeHashTable&& other)
92 : m_buckets(util::move(other.m_buckets))
93 , m_size(util::exchange(other.m_size, 0))
94 , m_eq(util::move(other.m_eq))
95 , m_hasher(util::move(other.m_hasher)) {}
96
97 constexpr auto operator=(NodeHashTable&& other) -> NodeHashTable& {
98 this->clear();
99 m_buckets = util::move(other.m_buckets);
100 m_size = util::exchange(other.m_size, 0);
101 m_eq = util::move(other.m_eq);
102 m_hasher = util::move(other.m_hasher);
103 return *this;
104 }
105
106 constexpr ~NodeHashTable() { this->clear(); }
107
108 constexpr auto size() const -> usize { return m_size; }
109 constexpr auto empty() const -> bool { return m_size == 0; }
110 constexpr auto bucket_count() const -> usize { return vector::size(m_buckets); }
111
112 constexpr auto begin() -> Iterator { return unconst_iterator(util::as_const(*this).begin()); }
113 constexpr auto begin() const -> ConstIterator {
114 auto const buckets = m_buckets.span();
116 if (it == m_buckets.end()) {
117 return {};
118 }
119 auto const bucket_index = it - buckets.begin();
120 return Iterator(buckets, bucket_index);
121 }
122 constexpr auto end() -> Iterator { return unconst_iterator(util::as_const(*this).end()); }
123 constexpr auto end() const -> ConstIterator { return Iterator(m_buckets.span(), m_buckets.size()); }
124
125 constexpr auto unconst_iterator(ConstIterator it) -> Iterator { return it.base(); }
126
127 constexpr auto insert_node(Node& node)
129 {
131 }
132
133 constexpr auto insert_node(ConstIterator, Node& node) {
134 if constexpr (!is_multi) {
135 return as_fallible(this->insert_node(node)) % [](auto&& tuple) {
136 return util::get<0>(tuple);
137 } | try_infallible;
138 } else {
139 return this->insert_node(node);
140 }
141 }
142
143 constexpr auto erase_impl(ConstIterator it) -> Iterator {
144 auto bucket_index = it.base().bucket_index();
145 auto& bucket = m_buckets[bucket_index];
146 auto& node = static_cast<ConcreteNode&>(it.base().node());
147 auto const next = bucket.erase_after(it.base().before_current());
148 --m_size;
149
150 Tag::did_remove(down_cast_self(), node);
151 if (next == bucket.end()) {
152 for (++bucket_index; bucket_index < bucket_count(); ++bucket_index) {
153 if (!vector::lookup(m_buckets, bucket_index).empty()) {
154 return Iterator(m_buckets.span(), bucket_index);
155 }
156 }
157 return end();
158 }
159 return Iterator { m_buckets.span(), bucket_index, it.base().before_current() };
160 }
161
162 template<typename U>
164 constexpr auto equal_range_impl(U&& needle) const {
165 if (empty()) {
166 return View<ConstIterator> { end(), end() };
167 }
168
169 auto const hash = this->hash(needle);
170 auto const bucket_index = hash % m_buckets.size();
171 auto const& bucket = m_buckets[bucket_index];
172 auto before_it = bucket.before_begin();
173 while (container::next(before_it) != bucket.end()) {
174 auto&& current = *container::next(before_it);
175 if (this->equal(current, needle)) {
176 break;
177 }
178 ++before_it;
179 }
180
181 auto it = container::next(before_it);
182 if (it == bucket.end()) {
183 return View<ConstIterator> { end(), end() };
184 }
185
186 auto const first = ConstIterator { m_buckets.span(), bucket_index, before_it };
187 auto last = ConstIterator { m_buckets.span(), bucket_index, it };
188 if constexpr (is_multi) {
189 while (container::next(last.before_current()) != bucket.end()) {
190 auto&& current = *container::next(last.before_current());
191 if (!this->equal(node_value(current), needle)) {
192 break;
193 }
194 ++last;
195 }
196 }
197 return View<ConstIterator> { first, last };
198 }
199
200 template<typename U>
202 constexpr auto find_impl(U&& needle) const -> ConstIterator {
203 if (empty()) {
204 return end();
205 }
206
207 auto const hash = this->hash(needle);
208 auto const bucket_index = hash % m_buckets.size();
209 auto const& bucket = m_buckets[bucket_index];
210 auto before_it = bucket.before_begin();
211 while (container::next(before_it) != bucket.end()) {
212 auto&& current = *container::next(before_it);
213 if (this->equal(this->node_value(current), needle)) {
214 return Iterator(m_buckets.span(), bucket_index, before_it.base());
215 }
216 ++before_it;
217 }
218 return end();
219 }
220
221 constexpr auto reserve(usize new_capacity)
222 -> decltype(util::declval<Buckets&>().reserve_from_nothing(new_capacity)) {
224 if constexpr (concepts::LanguageVoid<decltype(util::declval<Buckets&>().reserve_from_nothing(
225 new_capacity))>) {
226 m_buckets.reserve_from_nothing(new_capacity);
227 } else {
228 DI_TRY(m_buckets.reserve_from_nothing(new_capacity));
229 }
230 m_buckets.assume_size(new_capacity);
232 if constexpr (concepts::LanguageVoid<decltype(util::declval<Buckets&>().reserve_from_nothing(
233 new_capacity))>) {
234 return;
235 } else {
236 return {};
237 }
238 }
239
240 if (new_capacity <= m_buckets.size()) {
241 return;
242 }
243
244 auto new_buckets = Buckets {};
245 if constexpr (concepts::LanguageVoid<decltype(util::declval<Buckets&>().reserve_from_nothing(new_capacity))>) {
246 new_buckets.reserve_from_nothing(new_capacity);
247 } else {
248 DI_TRY(new_buckets.reserve_from_nothing(new_capacity));
249 }
250 new_buckets.assume_size(new_capacity);
251 m_size = 0;
253
254 auto old_buckets = util::move(m_buckets);
255 m_buckets = util::move(new_buckets);
256 for (auto& bucket : old_buckets) {
257 while (!bucket.empty()) {
258 auto& node = static_cast<Node&>(*bucket.begin().node());
259 bucket.pop_front();
261 }
262 }
263 if constexpr (concepts::LanguageVoid<decltype(util::declval<Buckets&>().reserve_from_nothing(new_capacity))>) {
264 return;
265 } else {
266 return {};
267 }
268 }
269
270 constexpr void merge_impl(NodeHashTable&& other)
272 {
273 merge_impl_without_rehashing(util::move(other));
274 }
275
276protected:
277 constexpr auto node_value(Node& node) const -> Value& {
278 return Tag::down_cast(in_place_type<Value>, static_cast<ConcreteNode&>(node));
279 }
280 constexpr auto node_value(Node const& node) const -> Value const& {
281 return const_cast<NodeHashTable&>(*this).node_value(const_cast<Node&>(node));
282 }
283
285 auto it = other.begin();
286 auto const end = other.end();
287 for (; it != end;) {
288 auto& node = it.node();
289 ++it;
290 this->insert_node_without_rehashing(node, false);
291 }
292 }
293
294 constexpr auto insert_node_without_rehashing(Node& node, bool call_insertion_hook = true) {
296
297 auto const hash = this->hash(node_value(node));
298 auto const bucket_index = hash % vector::size(m_buckets);
299 auto& bucket = vector::lookup(m_buckets, bucket_index);
300
301 auto before_it = bucket.before_begin();
302 while (container::next(before_it) != bucket.end()) {
303 auto&& current = *container::next(before_it);
304 if (this->equal(node_value(current), node_value(node))) {
305 break;
306 }
307 ++before_it;
308 }
309 auto it = container::next(before_it);
310 if constexpr (is_multi) {
311 ++m_size;
312 if (it != bucket.end()) {
313 bucket.insert_after(it, node);
314 if (call_insertion_hook) {
315 Tag::did_insert(down_cast_self(), static_cast<ConcreteNode&>(node));
316 }
317 return Iterator { m_buckets.span(), bucket_index, it };
318 }
319 bucket.push_front(node);
320 if (call_insertion_hook) {
321 Tag::did_insert(down_cast_self(), static_cast<ConcreteNode&>(node));
322 }
323 return Iterator { m_buckets.span(), bucket_index, bucket.before_begin() };
324
325 } else {
326 if (it == bucket.end()) {
327 ++m_size;
328 bucket.push_front(node);
329 if (call_insertion_hook) {
330 Tag::did_insert(down_cast_self(), static_cast<ConcreteNode&>(node));
331 }
332 return vocab::Tuple(Iterator { m_buckets.span(), bucket_index, bucket.before_begin() }, true);
333 }
334 return vocab::Tuple(Iterator { m_buckets.span(), bucket_index, before_it }, false);
335 }
336 }
337
338 template<typename U>
339 constexpr auto hash(U const& value) const -> u64 {
340 auto hasher = m_hasher;
341 return container::hash(hasher, value);
342 }
343
344 constexpr auto hash(Value const& value) const -> u64 {
345 auto hasher = m_hasher;
346 if constexpr (is_map) {
347 return container::hash(hasher, util::get<0>(value));
348 } else {
349 return container::hash(hasher, value);
350 }
351 }
352
353 template<typename T, typename U>
354 constexpr auto equal(T const& a, U const& b) const -> bool {
355 return m_eq(a, b);
356 }
357
358 template<typename T>
359 constexpr auto equal(T const& a, Value const& b) const -> bool {
360 if constexpr (is_map) {
361 return m_eq(a, util::get<0>(b));
362 } else {
363 return m_eq(a, b);
364 }
365 }
366
367 template<typename T>
368 constexpr auto equal(Value const& a, T const& b) const -> bool {
369 if constexpr (is_map) {
370 return m_eq(util::get<0>(a), b);
371 } else {
372 return m_eq(a, b);
373 }
374 }
375
376 constexpr auto equal(Value const& a, Value const& b) const -> bool {
377 if constexpr (is_map) {
378 return m_eq(util::get<0>(a), util::get<0>(b));
379 } else {
380 return m_eq(a, b);
381 }
382 }
383
384 Buckets m_buckets {};
386 [[no_unique_address]] Eq m_eq {};
387 [[no_unique_address]] Hasher m_hasher {};
388};
389}
#define DI_ASSERT(...)
Definition assert_bool.h:7
Definition const_iterator_impl.h:19
Definition hash_node_iterator.h:15
constexpr auto bucket_count() const -> usize
Definition node_hash_table.h:110
constexpr auto insert_node_without_rehashing(Node &node, bool call_insertion_hook=true)
Definition node_hash_table.h:294
constexpr ~NodeHashTable()
Definition node_hash_table.h:106
static constexpr bool allow_rehashing_in_insert
Definition node_hash_table.h:82
constexpr auto end() -> Iterator
Definition node_hash_table.h:122
meta::Type< detail::NodeHashTableKey< Value, is_map > > Key
Definition node_hash_table.h:72
constexpr auto equal(Value const &a, Value const &b) const -> bool
Definition node_hash_table.h:376
constexpr auto begin() const -> ConstIterator
Definition node_hash_table.h:113
usize m_size
Definition node_hash_table.h:385
constexpr auto begin() -> Iterator
Definition node_hash_table.h:112
HashNode< Tag > Node
Definition node_hash_table.h:66
NodeHashTable(NodeHashTable const &)=delete
constexpr auto equal(T const &a, U const &b) const -> bool
Definition node_hash_table.h:354
constexpr auto hash(U const &value) const -> u64
Definition node_hash_table.h:339
constexpr void merge_impl_without_rehashing(NodeHashTable &&other)
Definition node_hash_table.h:284
constexpr auto down_cast_self() -> decltype(auto)
Definition node_hash_table.h:74
constexpr auto unconst_iterator(ConstIterator it) -> Iterator
Definition node_hash_table.h:125
constexpr auto find_impl(U &&needle) const -> ConstIterator
Definition node_hash_table.h:202
auto operator=(NodeHashTable const &) -> NodeHashTable &=delete
constexpr auto size() const -> usize
Definition node_hash_table.h:108
constexpr auto insert_node(Node &node)
Definition node_hash_table.h:127
constexpr auto equal(Value const &a, T const &b) const -> bool
Definition node_hash_table.h:368
constexpr auto node_value(Node const &node) const -> Value const &
Definition node_hash_table.h:280
constexpr auto end() const -> ConstIterator
Definition node_hash_table.h:123
constexpr NodeHashTable(NodeHashTable &&other)
Definition node_hash_table.h:91
constexpr auto erase_impl(ConstIterator it) -> Iterator
Definition node_hash_table.h:143
constexpr auto reserve(usize new_capacity) -> decltype(util::declval< Buckets & >().reserve_from_nothing(new_capacity))
Definition node_hash_table.h:221
constexpr auto equal(T const &a, Value const &b) const -> bool
Definition node_hash_table.h:359
constexpr auto equal_range_impl(U &&needle) const
Definition node_hash_table.h:164
constexpr auto hash(Value const &value) const -> u64
Definition node_hash_table.h:344
constexpr auto empty() const -> bool
Definition node_hash_table.h:109
Buckets m_buckets
Definition node_hash_table.h:384
constexpr auto insert_node(ConstIterator, Node &node)
Definition node_hash_table.h:133
Hasher m_hasher
Definition node_hash_table.h:387
constexpr auto node_value(Node &node) const -> Value &
Definition node_hash_table.h:277
HashNodeIterator< Value, Tag > Iterator
Definition node_hash_table.h:67
decltype(Tag::node_type(in_place_type< Value >)) ConcreteNode
Definition node_hash_table.h:70
constexpr auto operator=(NodeHashTable &&other) -> NodeHashTable &
Definition node_hash_table.h:97
container::ConstIteratorImpl< Iterator > ConstIterator
Definition node_hash_table.h:68
friend class NodeHashTable
Definition node_hash_table.h:63
constexpr NodeHashTable(Eq eq, Hasher hasher={})
Definition node_hash_table.h:89
constexpr void merge_impl(NodeHashTable &&other)
Definition node_hash_table.h:270
Eq m_eq
Definition node_hash_table.h:386
Definition view.h:35
Definition tuple_forward_declaration.h:5
Definition hash_same.h:43
Definition hasher.h:8
Definition core.h:128
Definition relation.h:7
Definition core.h:114
#define DI_TRY(...)
Definition monad_try.h:13
Definition sequence.h:13
constexpr auto lookup(concepts::detail::ConstantVector auto &vector, size_t index) -> decltype(auto)
Definition vector_lookup.h:10
constexpr auto size(concepts::detail::ConstantVector auto const &vector) -> size_t
Definition vector_size.h:7
constexpr auto empty(concepts::detail::ConstantVector auto const &vector) -> bool
Definition vector_empty.h:7
Definition sequence.h:12
constexpr auto next
Definition next.h:35
constexpr auto empty
Definition empty.h:45
constexpr auto move
Definition move.h:38
constexpr auto find_if
Definition find_if.h:31
constexpr auto equal
Definition equal.h:46
constexpr auto uninitialized_default_construct
Definition uninitialized_default_construct.h:34
constexpr auto hash
Definition hash.h:21
constexpr auto not_fn(F &&function)
Definition not_fn.h:55
T::Type Type
Definition core.h:26
T::Front Front
Definition list.h:100
size_t usize
Definition integers.h:33
__UINT64_TYPE__ u64
Definition integers.h:12
di::meta::Decay< decltype(T)> Tag
Definition tag_invoke.h:28
Definition vocab.h:96
constexpr auto get(T &&value) -> decltype(auto)
Definition get.h:8
auto declval() -> meta::AddRValueReference< T >
Definition declval.h:8
constexpr auto exchange(T &object, U &&new_value) -> T
Definition exchange.h:8
Tuple(Types...) -> Tuple< Types... >
constexpr auto exchange(T &object, U &&new_value) -> T
Definition exchange.h:8
constexpr auto as_fallible
Definition as_fallible.h:26
constexpr auto try_infallible
Definition try_infallible.h:31
constexpr auto in_place_type
Definition in_place_type.h:12
Definition hash_node.h:7
Definition node_hash_table.h:44
meta::Front< meta::AsList< Value > > Type
Definition node_hash_table.h:50
Value Type
Definition node_hash_table.h:45
static constexpr bool value
Definition node_hash_table.h:37
static constexpr bool value
Definition node_hash_table.h:28
Definition void.h:6