koutil
Loading...
Searching...
No Matches
template_hash_array.h
Go to the documentation of this file.
1#ifndef KOUTIL_CONTAINER_TEMPLATE_HASH_ARRAY_H
2#define KOUTIL_CONTAINER_TEMPLATE_HASH_ARRAY_H
3
4#include <cassert>
5#include <concepts>
6#include <cstddef>
7#include <iterator>
8#include <memory>
9#include <type_traits>
10#include <utility>
11#include <vector>
12
13namespace koutil::container {
14
22template <typename T, typename Key, typename ComptimeData>
23concept is_template_hash = requires(T hash, const Key& key) {
24 requires std::is_trivially_constructible_v<T>;
25
26 { hash.template hash<ComptimeData {}>(key) } -> std::same_as<std::size_t>;
27};
28
34template <typename Bucket, typename KeyID>
35concept is_bucket = requires(Bucket& bucket, std::size_t v) {
36 { *bucket.begin() } -> std::same_as<std::pair<std::size_t, KeyID>&>;
37 { *bucket.end() } -> std::same_as<std::pair<std::size_t, KeyID>&>;
38 { bucket.size() } -> std::same_as<std::size_t>;
39 { bucket.emplace_back(v, v) };
40 { bucket.clear() };
41 { bucket.erase(bucket.begin()) };
42
43 typename Bucket::iterator;
44
45 requires std::forward_iterator<typename Bucket::iterator>;
46 requires std::is_same_v<typename Bucket::iterator, decltype(bucket.begin())>
47 && std::is_same_v<typename Bucket::iterator, decltype(bucket.end())>;
48};
49
57template <typename T, typename Key, typename KeyID, typename ComptimeData>
58concept is_template_key_adapter = requires(const Key& key, T adapter, const KeyID& index) {
59 requires std::move_constructible<T> && std::is_trivially_copy_constructible_v<T>;
60 { adapter.template eql<ComptimeData {}>(key, index) } -> std::same_as<bool>;
61};
62
68template <typename T, typename Bucket>
69concept is_allocator = requires(T alloc, std::size_t n, Bucket* buckets) {
70 requires std::is_constructible_v<T>;
71 { alloc.allocate(n) } -> std::same_as<Bucket*>;
72 { alloc.deallocate(buckets, n) };
73};
74
75template <
76 typename Key,
77 typename KeyID,
78 typename ComptimeData,
81 is_bucket<KeyID> Bucket = std::vector<std::pair<std::size_t, KeyID>>,
82 is_allocator<Bucket> Allocator = std::allocator<Bucket>>
84private:
85 using key_t = Key;
86 using key_id_t = KeyID;
87 using value_t = std::size_t;
88 using bucket_t = Bucket;
89 using hash_t = Hash;
90 using bucket_iter = bucket_t::iterator;
91 using bucket_const_iter = bucket_t::const_iterator;
92 using adapter_t = KeyAdapter;
93 using allocator_t = Allocator;
94 using comptime_t = ComptimeData;
95
101 template <bool is_const> class iterator {
102 private:
103 using ref_t = std::conditional_t<is_const, const bucket_t*, bucket_t*>;
104
105 public:
106 using value_type = KeyID;
107 using reference = std::conditional_t<is_const, const value_type&, value_type&>;
109
110 using iterator_tag = std::forward_iterator_tag;
111 using difference_type = std::ptrdiff_t;
112
117 : m_ref(nullptr) { }
118
126 iterator(ref_t bucket, std::size_t bucket_item, ref_t bucket_end)
127 : m_ref(bucket)
128 , m_item(bucket_item)
129 , m_bucket_end(bucket_end) {
131 }
132
133 iterator(iterator&&) = default;
134 iterator(const iterator&) = default;
136 iterator& operator=(const iterator&) = default;
137
143 operator iterator<true>() const
144 requires(!is_const)
145 {
146 return { m_ref, m_item };
147 }
148
149 bool operator==(const iterator& other) const { return m_ref == other.m_ref && m_item == other.m_item; }
150
151 reference operator*() { return m_ref->at(m_item).second; }
152
153 constant_reference operator*() const { return m_ref->at(m_item); }
154
156 increment();
157 return *this;
158 }
159
161 iterator tmp = *this;
162
163 increment();
164 return tmp;
165 }
166
167 private:
169 std::size_t m_item;
171
175 void increment() {
176 m_item += 1;
177 if (m_item >= m_ref->size()) {
178 m_item = 0;
179 m_ref += 1;
180
182 }
183 }
184
187 m_ref += 1;
188 }
189 }
190 };
191
192public:
195
200 : m_buckets_count(1) {
201 m_buckets = new (allocator_t().allocate(1)) bucket_t[1];
202 }
203
213
221 , m_size(other.m_size)
223
224 m_buckets = allocator_t().allocate(other.m_buckets_count);
225
226 std::uninitialized_copy_n(other.m_buckets, other.m_buckets_count, m_buckets);
227 }
228
235 : m_buckets(other.m_buckets)
237 , m_size(other.m_size)
239
240 other.m_buckets = nullptr;
241 other.m_size = 0;
242 other.m_buckets_count = 0;
243 }
244
249
257 if (&other == this) {
258 return *this;
259 }
260
261 destroy();
262
264 m_size = other.m_size;
266
267 m_buckets = allocator_t().allocate(other.m_buckets_count);
268
269 std::uninitialized_copy_n(other.m_buckets, other.m_buckets_count, m_buckets);
270
271 return *this;
272 }
273
281 if (&other == this) {
282 return *this;
283 }
284
285 destroy();
286
287 m_buckets = other.m_buckets;
288 m_buckets_count = other.m_buckets_count;
289 m_size = other.m_size;
290 m_max_load_factor = other.m_max_load_factor;
291
292 other.m_buckets = nullptr;
293 other.m_size = 0;
294 other.m_buckets_count = 0;
295
296 return *this;
297 }
298
303 [[nodiscard]] bool empty() const { return m_size == 0; }
304
309 [[nodiscard]] std::size_t size() const { return m_size; }
310
315 [[nodiscard]] std::size_t bucket_count() const { return m_buckets_count; }
316
321 [[nodiscard]] float max_load_factor() const { return m_max_load_factor; }
322
327 void set_max_load_factor(float factor) {
328 assert(factor > 0);
329 m_max_load_factor = factor;
330 }
331
336
344 template <comptime_t Data> bool try_insert(const key_t& key, const key_id_t& key_id, adapter_t adapter) {
345 return insert<Data>(key, key_id, adapter);
346 }
347
355 template <comptime_t Data> bool try_set(const key_t& key, const key_id_t& new_key_id, adapter_t adapter) {
356 return set<Data>(key, new_key_id, adapter);
357 }
358
367 template <comptime_t Data> void erase(const key_t& key, adapter_t adapter) { remove<Data>(key, adapter); }
368
376 template <comptime_t Data> iterator_t find(const key_t& key, adapter_t adapter) {
377 auto& bucket = get_bucket<Data>(key);
378 auto it = find_bucket_item<Data>(key, bucket, adapter);
379
380 if (it != bucket.end()) {
381 return iterator_t { &bucket,
382 static_cast<std::size_t>(std::distance(bucket.begin(), it)),
384 }
385
386 return end();
387 }
388
396 template <comptime_t Data> const_iterator_t find(const key_t& key, adapter_t adapter) const {
397 auto& bucket = get_bucket<Data>(key);
398 auto it = find_bucket_item<Data>(key, bucket, adapter);
399
400 if (it != bucket.end()) {
401 return const_iterator_t { &bucket,
402 static_cast<std::size_t>(std::distance(bucket.begin(), it)),
404 }
405
406 return cend();
407 }
408
414
420
426
432
438
444
445private:
447 std::size_t m_buckets_count;
448 std::size_t m_size = 0;
449 float m_max_load_factor = 1.0F;
450
451 template <comptime_t Data> value_t hash_key(const Key& key) {
452 value_t hash = hash_t().template hash<Data>(key);
453 return hash % m_buckets_count;
454 }
455
456 template <comptime_t Data> bucket_t& get_bucket(const key_t& key) { return m_buckets[hash_key<Data>(key)]; }
457
458 template <comptime_t Data> bucket_iter find_bucket_item(const key_t& key, bucket_t& bucket, adapter_t adapter) {
459 auto bucket_end = bucket.end();
460 auto bucket_start = bucket.begin();
461
462 for (auto start = bucket_start; start != bucket_end; ++start) {
463 if (adapter.template eql<Data>(key, start->second)) {
464 return start;
465 }
466 }
467
468 return bucket_end;
469 }
470
471 template <comptime_t Data>
472 bucket_const_iter find_bucket_item(const key_t& key, const bucket_t& bucket, adapter_t adapter) const {
473 auto bucket_end = bucket.end();
474 auto bucket_start = bucket.begin();
475
476 for (auto start = bucket_start; start != bucket_end; ++start) {
477 if (adapter.template eql<Data>(key, start->second)) {
478 return start;
479 }
480 }
481
482 return bucket_end;
483 }
484
485 template <comptime_t Data> bool insert(const key_t& key, const key_id_t& key_id, adapter_t adapter) {
486 value_t hash = hash_t().template hash<Data>(key);
487 auto& bucket = m_buckets[hash % m_buckets_count];
488
489 auto it = find_bucket_item<Data>(key, bucket, adapter);
490
491 if (it != bucket.end()) {
492 return false;
493 }
494
495 bucket.emplace_back(hash, key_id);
496 m_size += 1;
498 return true;
499 }
500
501 template <comptime_t Data> void remove(const key_t& key, adapter_t adapter) {
502 auto& bucket = get_bucket<Data>(key);
503 auto it = find_bucket_item<Data>(key, bucket, adapter);
504
505 if (it != bucket.end()) {
506 bucket.erase(it);
507 m_size -= 1;
508 }
509 }
510
511 template <comptime_t Data> bool set(const key_t& key, const key_id_t& new_key_id, adapter_t adapter) {
512 auto& bucket = get_bucket<Data>(key);
513 auto it = find_bucket_item<Data>(key, bucket, adapter);
514
515 if (it != bucket.end()) {
516 it->second = new_key_id;
517 return true;
518 }
519 return false;
520 }
521
523 if (static_cast<float>(m_size) / m_buckets_count <= m_max_load_factor) {
524 return;
525 }
526
527 std::size_t new_buckets_count = m_buckets_count * 2;
528
529 auto alloc = allocator_t();
530 auto new_buckets_mem = alloc.allocate(new_buckets_count);
531 auto new_buckets = new (new_buckets_mem) bucket_t[new_buckets_count];
532
533 for (std::size_t i = 0; i < m_buckets_count; ++i) {
534 for (auto&& [hash, key_index] : m_buckets[i]) {
535 new_buckets[hash % new_buckets_count].emplace_back(hash, key_index);
536 }
537 }
538
539 std::destroy_n(m_buckets, m_buckets_count);
540 alloc.deallocate(m_buckets, m_buckets_count);
541
542 m_buckets_count = new_buckets_count;
543 m_buckets = new_buckets;
544 }
545
547 for (std::size_t i = 0; i < m_buckets_count; ++i) {
548 m_buckets[i].clear();
549 }
550 m_size = 0;
551 }
552
553 void destroy() {
554
555 if (m_buckets_count > 0) {
556 auto alloc = allocator_t();
557 std::destroy_n(m_buckets, m_buckets_count);
558 alloc.deallocate(m_buckets, m_buckets_count);
559 }
560 }
561};
562
563}
564
565#endif
Iterator class template for hash_array.
Definition template_hash_array.h:101
KeyID value_type
Definition template_hash_array.h:106
iterator()
Default constructor.
Definition template_hash_array.h:116
reference operator*()
Definition template_hash_array.h:151
iterator & operator++()
Definition template_hash_array.h:155
const value_type & constant_reference
Definition template_hash_array.h:108
void increment()
Helper function to increment the iterator.
Definition template_hash_array.h:175
iterator(ref_t bucket, std::size_t bucket_item, ref_t bucket_end)
Constructor with parameters.
Definition template_hash_array.h:126
iterator & operator=(const iterator &)=default
ref_t m_ref
Definition template_hash_array.h:168
bool operator==(const iterator &other) const
Definition template_hash_array.h:149
iterator & operator=(iterator &&)=default
constant_reference operator*() const
Definition template_hash_array.h:153
std::size_t m_item
Definition template_hash_array.h:169
std::conditional_t< is_const, const value_type &, value_type & > reference
Definition template_hash_array.h:107
std::forward_iterator_tag iterator_tag
Definition template_hash_array.h:110
iterator operator++(int)
Definition template_hash_array.h:160
void find_non_empty()
Definition template_hash_array.h:185
std::ptrdiff_t difference_type
Definition template_hash_array.h:111
ref_t m_bucket_end
Definition template_hash_array.h:170
std::conditional_t< is_const, const bucket_t *, bucket_t * > ref_t
Definition template_hash_array.h:103
Definition template_hash_array.h:83
template_hash_array(std::size_t bucket_count)
Constructor with bucket count.
Definition template_hash_array.h:209
value_t hash_key(const Key &key)
Definition template_hash_array.h:451
bucket_t::const_iterator bucket_const_iter
Definition template_hash_array.h:91
bool empty() const
Check if the hash_array is empty.
Definition template_hash_array.h:303
bool try_set(const key_t &key, const key_id_t &new_key_id, adapter_t adapter)
Try to set new key ID.
Definition template_hash_array.h:355
template_hash_array & operator=(const template_hash_array &other)
Copy assignment operator.
Definition template_hash_array.h:256
bool set(const key_t &key, const key_id_t &new_key_id, adapter_t adapter)
Definition template_hash_array.h:511
Allocator allocator_t
Definition template_hash_array.h:93
bool insert(const key_t &key, const key_id_t &key_id, adapter_t adapter)
Definition template_hash_array.h:485
template_hash_array(template_hash_array &&other)
Move constructor.
Definition template_hash_array.h:234
bucket_const_iter find_bucket_item(const key_t &key, const bucket_t &bucket, adapter_t adapter) const
Definition template_hash_array.h:472
std::size_t m_buckets_count
Definition template_hash_array.h:447
std::size_t value_t
Definition template_hash_array.h:87
ComptimeData comptime_t
Definition template_hash_array.h:94
iterator_t begin()
Get an iterator to the beginning of the hash_array.
Definition template_hash_array.h:413
template_hash_array & operator=(template_hash_array &&other)
Move assignment operator.
Definition template_hash_array.h:280
KeyAdapter adapter_t
Definition template_hash_array.h:92
void destroy()
Definition template_hash_array.h:553
const_iterator_t begin() const
Get a constant iterator to the beginning of the hash_array.
Definition template_hash_array.h:425
float m_max_load_factor
Definition template_hash_array.h:449
~template_hash_array()
Destructor.
Definition template_hash_array.h:248
void rehash_if_needed()
Definition template_hash_array.h:522
std::size_t bucket_count() const
Returns the number of buckets.
Definition template_hash_array.h:315
template_hash_array()
Default constructor.
Definition template_hash_array.h:199
std::size_t m_size
Definition template_hash_array.h:448
void remove(const key_t &key, adapter_t adapter)
Definition template_hash_array.h:501
const_iterator_t end() const
Get a constant iterator to the end of the hash_array.
Definition template_hash_array.h:431
void clear()
Clear all elements from the hash_array.
Definition template_hash_array.h:335
iterator_t find(const key_t &key, adapter_t adapter)
Finds an element in the hash table.
Definition template_hash_array.h:376
std::size_t size() const
Get the number of elements in the hash_array.
Definition template_hash_array.h:309
bucket_t::iterator bucket_iter
Definition template_hash_array.h:90
bucket_t * m_buckets
Definition template_hash_array.h:446
KeyID key_id_t
Definition template_hash_array.h:86
bucket_t & get_bucket(const key_t &key)
Definition template_hash_array.h:456
const_iterator_t cend() const
Get a constant iterator to the end of the hash_array.
Definition template_hash_array.h:443
const_iterator_t find(const key_t &key, adapter_t adapter) const
Finds an element in the hash table.
Definition template_hash_array.h:396
void set_max_load_factor(float factor)
Sets a new maximum load factor.
Definition template_hash_array.h:327
const_iterator_t cbegin() const
Get a constant iterator to the beginning of the hash_array.
Definition template_hash_array.h:437
void clear_all_buckets()
Definition template_hash_array.h:546
Hash hash_t
Definition template_hash_array.h:89
bucket_iter find_bucket_item(const key_t &key, bucket_t &bucket, adapter_t adapter)
Definition template_hash_array.h:458
template_hash_array(const template_hash_array &other)
Copy constructor.
Definition template_hash_array.h:219
iterator_t end()
Get an iterator to the end of the hash_array.
Definition template_hash_array.h:419
Bucket bucket_t
Definition template_hash_array.h:88
float max_load_factor() const
Returns the maximum load factor.
Definition template_hash_array.h:321
bool try_insert(const key_t &key, const key_id_t &key_id, adapter_t adapter)
Try to insert a key and key ID into the hash_array.
Definition template_hash_array.h:344
void erase(const key_t &key, adapter_t adapter)
Erases an element from the hash table.
Definition template_hash_array.h:367
Key key_t
Definition template_hash_array.h:85
Concept to check if a type is a valid allocator for a given bucket type.
Definition template_hash_array.h:69
Concept to check if a type is a valid bucket type.
Definition template_hash_array.h:35
Concept to check if a type is a valid hash function for a given Key type.
Definition template_hash_array.h:23
Concept to check if a type is a valid key adapter.
Definition template_hash_array.h:58
Definition comptime_map.h:11