sandbox
Loading...
Searching...
No Matches
dense_map.hpp
1// SPDX-License-Identifier: MIT
2#ifndef LIBSBX_CONTAINERS_DENSE_MAP_HPP_
3#define LIBSBX_CONTAINERS_DENSE_MAP_HPP_
4
5#include <memory>
6#include <vector>
7#include <cmath>
8#include <bit>
9
10#include <libsbx/utility/fast_mod.hpp>
11#include <libsbx/utility/assert.hpp>
12
13#include <libsbx/memory/iterable_adaptor.hpp>
14#include <libsbx/memory/concepts.hpp>
15
16#include <libsbx/containers/compressed_pair.hpp>
17
18namespace sbx::containers {
19
20namespace detail {
21
22template<typename Key, typename Type>
23struct dense_map_node final {
24
25 using value_type = std::pair<Key, Type>;
26 using size_type = std::size_t;
27
28 template<typename... Args>
29 dense_map_node(const size_type position, Args&&... args)
30 : element{std::forward<Args>(args)...},
31 next{position} { }
32
33 template<typename Allocator, typename... Args>
34 dense_map_node(std::allocator_arg_t, const Allocator& allocator, const size_type position, Args&&... args)
35 : element{std::make_obj_using_allocator<value_type>(allocator, std::forward<Args>(args)...)},
36 next{position} { }
37
38 template<typename Allocator>
39 dense_map_node(std::allocator_arg_t, const Allocator& allocator, const dense_map_node& other)
40 : element{std::make_obj_using_allocator<value_type>(allocator, other.element)},
41 next{other.next} { }
42
43 template<typename Allocator>
44 dense_map_node(std::allocator_arg_t, const Allocator& allocator, dense_map_node&& other)
45 : element{std::make_obj_using_allocator<value_type>(allocator, std::move(other.element))},
46 next{other.next} { }
47
48 value_type element;
49 size_type next;
50
51}; // struct dense_map_node
52
53template<typename Iterator>
54class dense_map_iterator final {
55
56 template<typename>
57 friend class dense_map_iterator;
58
59 template<typename Lhs, typename Rhs>
60 friend constexpr auto operator-(const dense_map_iterator<Lhs>&, const dense_map_iterator<Rhs>&) noexcept -> std::ptrdiff_t;
61
62 template<typename Lhs, typename Rhs>
63 friend constexpr auto operator==(const dense_map_iterator<Lhs>&, const dense_map_iterator<Rhs>&) noexcept -> bool;
64
65 template<typename Lhs, typename Rhs>
66 friend constexpr auto operator<=>(const dense_map_iterator<Lhs>&, const dense_map_iterator<Rhs>&) noexcept -> std::strong_ordering;
67
68 using first_type = decltype(std::as_const(std::declval<Iterator>()->element.first));
69 using second_type = decltype((std::declval<Iterator>()->element.second));
70
71public:
72
73 using value_type = std::pair<first_type, second_type>;
75 using reference = value_type;
76 using difference_type = std::ptrdiff_t;
77 using iterator_category = std::input_iterator_tag;
78 using iterator_concept = std::random_access_iterator_tag;
79
80 constexpr dense_map_iterator() noexcept
81 : _iterator{} { }
82
83 constexpr dense_map_iterator(const Iterator iterator) noexcept
84 : _iterator{iterator} {}
85
86 template<typename Other, typename = std::enable_if_t<!std::is_same_v<Iterator, Other> && std::is_constructible_v<Iterator, Other>>>
87 constexpr dense_map_iterator(const dense_map_iterator<Other>& other) noexcept
88 : _iterator{other._iterator} {}
89
90 constexpr auto operator++() noexcept -> dense_map_iterator& {
91 ++_iterator;
92 return *this;
93 }
94
95 constexpr auto operator++(int) noexcept -> dense_map_iterator {
96 const auto original = *this;
97 ++(*this);
98 return original;
99 }
100
101 constexpr auto operator--() noexcept -> dense_map_iterator& {
102 --_iterator;
103 return *this;
104 }
105
106 constexpr auto operator--(int) noexcept -> dense_map_iterator {
107 const auto original = *this;
108 --(*this);
109 return original;
110 }
111
112 constexpr auto operator+=(const difference_type value) noexcept -> dense_map_iterator& {
113 _iterator += value;
114 return *this;
115 }
116
117 constexpr auto operator+(const difference_type value) const noexcept -> dense_map_iterator {
118 auto copy = *this;
119 return (copy += value);
120 }
121
122 constexpr auto operator-=(const difference_type value) noexcept -> dense_map_iterator& {
123 return (*this += -value);
124 }
125
126 constexpr auto operator-(const difference_type value) const noexcept -> dense_map_iterator {
127 return (*this + -value);
128 }
129
130 [[nodiscard]] constexpr auto operator[](const difference_type value) const noexcept -> reference {
131 return {_iterator[value].element.first, _iterator[value].element.second};
132 }
133
134 [[nodiscard]] constexpr auto operator->() const noexcept -> pointer {
135 return operator*();
136 }
137
138 [[nodiscard]] constexpr auto operator*() const noexcept -> reference {
139 return operator[](0);
140 }
141
142private:
143
144 Iterator _iterator;
145
146}; // struct dense_map_iterator
147
148template<typename Lhs, typename Rhs>
149[[nodiscard]] constexpr auto operator-(const dense_map_iterator<Lhs>& lhs, const dense_map_iterator<Rhs>& rhs) noexcept -> std::ptrdiff_t {
150 return lhs._iterator - rhs._iterator;
151}
152
153template<typename Lhs, typename Rhs>
154[[nodiscard]] constexpr auto operator==(const dense_map_iterator<Lhs>& lhs, const dense_map_iterator<Rhs>& rhs) noexcept -> bool {
155 return lhs._iterator == rhs._iterator;
156}
157
158template<typename Lhs, typename Rhs>
159[[nodiscard]] constexpr auto operator<=>(const dense_map_iterator<Lhs>& lhs, const dense_map_iterator<Rhs>& rhs) noexcept -> std::strong_ordering {
160 return lhs._iterator <=> rhs._iterator;
161}
162
163template<typename Iterator>
165
166 template<typename>
167 friend class dense_map_local_iterator;
168
169 using first_type = decltype(std::as_const(std::declval<Iterator>()->element.first));
170 using second_type = decltype((std::declval<Iterator>()->element.second));
171
172public:
173
174 using value_type = std::pair<first_type, second_type>;
175 using size_type = std::size_t;
177 using reference = value_type;
178 using difference_type = std::ptrdiff_t;
179 using iterator_category = std::input_iterator_tag;
180 using iterator_concept = std::forward_iterator_tag;
181
182 constexpr dense_map_local_iterator() noexcept
183 : _iterator{},
184 _offset{} { }
185
186 constexpr dense_map_local_iterator(Iterator iterator, const std::size_t position) noexcept
187 : _iterator{iterator},
188 _offset{position} {}
189
190 template<typename Other, typename = std::enable_if_t<!std::is_same_v<Iterator, Other> && std::is_constructible_v<Iterator, Other>>>
191 constexpr dense_map_local_iterator(const dense_map_local_iterator<Other>& other) noexcept
192 : _iterator{other._iterator},
193 _offset{other._offset} {}
194
195 constexpr auto operator++() noexcept -> dense_map_local_iterator& {
196 return (_offset = _iterator[static_cast<typename Iterator::difference_type>(_offset)].next), *this;
197 }
198
199 constexpr auto operator++(int) noexcept -> dense_map_local_iterator {
200 const auto original = *this;
201 ++(*this);
202 return original;
203 }
204
205 [[nodiscard]] constexpr auto operator->() const noexcept -> pointer {
206 return operator*();
207 }
208
209 [[nodiscard]] constexpr auto operator*() const noexcept -> reference {
210 const auto index = static_cast<typename Iterator::difference_type>(_offset);
211 return {_iterator[index].element.first, _iterator[index].element.second};
212 }
213
214 [[nodiscard]] constexpr auto index() const noexcept -> size_type {
215 return _offset;
216 }
217
218private:
219
220 Iterator _iterator;
221 size_type _offset;
222
223}; // struct dense_map_local_iterator
224
225template<typename Lhs, typename Rhs>
226[[nodiscard]] constexpr auto operator==(const dense_map_local_iterator<Lhs>& lhs, const dense_map_local_iterator<Rhs>& rhs) noexcept -> bool{
227 return lhs.index() == rhs.index();
228}
229
230} // namespace detail
231
232template<typename Key, typename Type, typename Hash = std::hash<Key>, typename KeyEqual = std::equal_to<Key>, typename Allocator = std::allocator<std::pair<Key, Type>>>
234
235 static constexpr auto default_threshold = std::float_t{0.875f};
236 static constexpr auto minimum_capacity = std::size_t{8};
237
239 using allocator_traits = std::allocator_traits<Allocator>;
240
241 using sparse_container_type = std::vector<std::size_t, memory::rebound_allocator_t<Allocator, std::size_t>>;
242 using dense_container_type = std::vector<node_type, memory::rebound_allocator_t<Allocator, node_type>>;
243
244public:
245
246 using allocator_type = Allocator;
247 using value_type = std::pair<Key, Type>;
248 using key_type = Key;
249 using mapped_type = Type;
250 using size_type = std::size_t;
251 using difference_type = std::ptrdiff_t;
252 using hasher = Hash;
253 using key_equal = KeyEqual;
258
259 dense_map()
260 : dense_map{minimum_capacity} { }
261
262 explicit dense_map(const allocator_type& allocator)
263 : dense_map{minimum_capacity, hasher{}, key_equal{}, allocator} { }
264
265 dense_map(const size_type count, const allocator_type& allocator)
266 : dense_map{count, hasher{}, key_equal{}, allocator} { }
267
268 dense_map(const size_type count, const hasher& hash, const allocator_type& allocator)
269 : dense_map{count, hash, key_equal{}, allocator} { }
270
271 explicit dense_map(const size_type count, const hasher& hash = hasher{}, const key_equal& equal = key_equal{}, const allocator_type& allocator = allocator_type{})
272 : _sparse{allocator, hash},
273 _dense{allocator, equal},
274 _threshold{default_threshold} {
275 rehash(count);
276 }
277
278 dense_map(const dense_map& other) = default;
279
280 dense_map(const dense_map& other, const allocator_type& allocator)
281 : _sparse{std::piecewise_construct, std::forward_as_tuple(other._sparse.first(), allocator), std::forward_as_tuple(other._sparse.second())},
282 _dense{std::piecewise_construct, std::forward_as_tuple(other._dense.first(), allocator), std::forward_as_tuple(other._dense.second())},
283 _threshold{other._threshold} { }
284
285 dense_map(dense_map&& other) noexcept = default;
286
287 dense_map(dense_map&& other, const allocator_type& allocator)
288 : _sparse{std::piecewise_construct, std::forward_as_tuple(std::move(other._sparse.first()), allocator), std::forward_as_tuple(std::move(other._sparse.second()))},
289 _dense{std::piecewise_construct, std::forward_as_tuple(std::move(other._dense.first()), allocator), std::forward_as_tuple(std::move(other._dense.second()))},
290 _threshold{other._threshold} { }
291
292 ~dense_map() = default;
293
294 auto operator=(const dense_map& other) -> dense_map& = default;
295
296 auto operator=(dense_map&& other) noexcept -> dense_map& = default;
297
298 auto swap(dense_map& other) noexcept -> void {
299 using std::swap;
300 swap(_sparse, other._sparse);
301 swap(_dense, other._dense);
302 swap(_threshold, other._threshold);
303 }
304
305 [[nodiscard]] constexpr auto get_allocator() const noexcept -> allocator_type {
306 return _sparse.first().get_allocator();
307 }
308
309 [[nodiscard]] auto cbegin() const noexcept -> const_iterator {
310 return _dense.first().begin();
311 }
312
313 [[nodiscard]] auto begin() const noexcept -> const_iterator {
314 return cbegin();
315 }
316
317 [[nodiscard]] auto begin() noexcept -> iterator {
318 return _dense.first().begin();
319 }
320
321 [[nodiscard]] auto cend() const noexcept -> const_iterator {
322 auto& first = _dense.first();
323 return first.end();
324 }
325
326 [[nodiscard]] auto end() const noexcept -> const_iterator {
327 return cend();
328 }
329
330 [[nodiscard]] auto end() noexcept -> iterator {
331 return _dense.first().end();
332 }
333
334 [[nodiscard]] bool empty() const noexcept {
335 return _dense.first().empty();
336 }
337
338 [[nodiscard]] auto size() const noexcept -> size_type {
339 return _dense.first().size();
340 }
341
342 [[nodiscard]] auto max_size() const noexcept -> size_type {
343 return _dense.first().max_size();
344 }
345
346 auto clear() noexcept -> void {
347 _sparse.first().clear();
348 _dense.first().clear();
349 rehash(0u);
350 }
351
352 auto insert(const value_type& value) -> std::pair<iterator, bool> {
353 return _insert_or_do_nothing(value.first, value.second);
354 }
355
356 auto insert(value_type&& value) -> std::pair<iterator, bool> {
357 return _insert_or_do_nothing(std::move(value.first), std::move(value.second));
358 }
359
360 template<typename Arg>
361 requires(std::is_constructible_v<value_type, Arg&&>)
362 auto insert(Arg&& value) -> std::pair<iterator, bool> {
363 return _insert_or_do_nothing(std::forward<Arg>(value).first, std::forward<Arg>(value).second);
364 }
365
366 template<typename... Args>
367 auto emplace([[maybe_unused]] Args&&... args) -> std::pair<iterator, bool> {
368 if constexpr (sizeof...(Args) == 0u) {
369 return _insert_or_do_nothing(key_type{});
370 } else if constexpr (sizeof...(Args) == 1u) {
371 return _insert_or_do_nothing(std::forward<Args>(args).first..., std::forward<Args>(args).second...);
372 } else if constexpr (sizeof...(Args) == 2u) {
373 return _insert_or_do_nothing(std::forward<Args>(args)...);
374 } else {
375 auto& node = _dense.first().emplace_back(_dense.first().size(), std::forward<Args>(args)...);
376 const auto index = _key_to_bucket(node.element.first);
377
378 if (auto it = _constrained_find(node.element.first, index); it != end()) {
379 _dense.first().pop_back();
380 return std::make_pair(it, false);
381 }
382
383 std::swap(node.next, _sparse.first()[index]);
384 _rehash_if_required();
385
386 return std::make_pair(--end(), true);
387 }
388 }
389
390 auto erase(const_iterator position) -> iterator {
391 const auto offset = position - cbegin();
392 erase(position->first);
393 return begin() + offset;
394 }
395
396 auto erase(const key_type& key) -> bool {
397 for (auto* current = &_sparse.first()[key_to_bucket(key)]; *current != (std::numeric_limits<size_type>::max)(); current = &_dense.first()[*current].next) {
398 if (_dense.second()(_dense.first()[*current].element.first, key)) {
399 const auto index = *current;
400 *current = _dense.first()[*current].next;
401 _move_and_pop(index);
402
403 return true;
404 }
405 }
406
407 return false;
408 }
409
410 [[nodiscard]] auto at(const key_type& key) -> mapped_type& {
411 auto it = find(key);
412 utility::assert_that(it != end(), "Invalid key");
413
414 return it->second;
415 }
416
418 [[nodiscard]] auto at(const key_type &key) const -> const mapped_type& {
419 auto it = find(key);
420 utility::assert_that(it != cend(), "Invalid key");
421 return it->second;
422 }
423
424 [[nodiscard]] auto operator[](const key_type& key) -> mapped_type& {
425 return _insert_or_do_nothing(key).first->second;
426 }
427
428 [[nodiscard]] auto operator[](key_type&& key) -> mapped_type& {
429 return _insert_or_do_nothing(std::move(key)).first->second;
430 }
431
432 [[nodiscard]] auto find(const key_type& key) -> iterator {
433 return _constrained_find(key, _key_to_bucket(key));
434 }
435
436 [[nodiscard]] auto find(const key_type& key) const -> const_iterator {
437 return _constrained_find(key, _key_to_bucket(key));
438 }
439
440 [[nodiscard]] auto contains(const key_type& key) const -> bool {
441 return find(key) != cend();
442 }
443
444 [[nodiscard]] auto cbegin(const size_type index) const -> const_local_iterator {
445 return {_dense.first().begin(), _sparse.first()[index]};
446 }
447
448 [[nodiscard]] auto begin(const size_type index) const -> const_local_iterator {
449 return cbegin(index);
450 }
451
452 [[nodiscard]] auto begin(const size_type index) -> local_iterator {
453 return {_dense.first().begin(), _sparse.first()[index]};
454 }
455
456 [[nodiscard]] auto cend([[maybe_unused]] const size_type index) const -> const_local_iterator {
457 return {_dense.first().begin(), (std::numeric_limits<size_type>::max)()};
458 }
459
460 [[nodiscard]] auto end(const size_type index) const -> const_local_iterator {
461 return cend(index);
462 }
463
464 [[nodiscard]] auto end([[maybe_unused]] const size_type index) -> local_iterator {
465 return {_dense.first().begin(), (std::numeric_limits<size_type>::max)()};
466 }
467
468 [[nodiscard]] auto max_load_factor() const -> std::float_t {
469 return _threshold;
470 }
471
472 auto set_max_load_factor(const std::float_t value) -> void {
473 utility::assert_that(value > 0.f, "Invalid load factor");
474 _threshold = value;
475 rehash(0u);
476 }
477
478 [[nodiscard]] auto bucket_count() const -> size_type {
479 return _sparse.first().size();
480 }
481
482 void rehash(const size_type count) {
483 auto value = count > minimum_capacity ? count : minimum_capacity;
484 const auto capacity = static_cast<size_type>(static_cast<std::float_t>(size()) / max_load_factor());
485 value = value > capacity ? value : capacity;
486
487 if (const auto length = std::bit_ceil(value); length != bucket_count()) {
488 _sparse.first().resize(length);
489
490 for (auto&& elem: _sparse.first()) {
491 elem = (std::numeric_limits<size_type>::max)();
492 }
493
494 for (auto position = 0u, last = size(); position < last; ++position) {
495 const auto index = _key_to_bucket(_dense.first()[position].element.first);
496 _dense.first()[position].next = std::exchange(_sparse.first()[index], position);
497 }
498 }
499 }
500
501 void reserve(const size_type count) {
502 _dense.first().reserve(count);
503 rehash(static_cast<size_type>(std::ceil(static_cast<std::float_t>(count) / max_load_factor())));
504 }
505
506 [[nodiscard]] auto hash_function() const -> hasher {
507 return _sparse.second();
508 }
509
510 [[nodiscard]] auto key_eq() const -> key_equal {
511 return _dense.second();
512 }
513
514private:
515
516 template<typename Other>
517 [[nodiscard]] auto _key_to_bucket(const Other& key) const noexcept -> size_type {
518 return utility::fast_mod(static_cast<size_type>(_sparse.second()(key)), bucket_count());
519 }
520
521 template<typename Other>
522 [[nodiscard]] auto _constrained_find(const Other& key, const size_type bucket) -> iterator {
523 for (auto it = begin(bucket), last = end(bucket); it != last; ++it) {
524 if (_dense.second()(it->first, key)) {
525 return begin() + static_cast<difference_type>(it.index());
526 }
527 }
528
529 return end();
530 }
531
532 template<typename Other>
533 [[nodiscard]] auto _constrained_find(const Other& key, const size_type bucket) const -> const_iterator {
534 for (auto it = cbegin(bucket), last = cend(bucket); it != last; ++it) {
535 if (_dense.second()(it->first, key)) {
536 return cbegin() + static_cast<difference_type>(it.index());
537 }
538 }
539
540 return cend();
541 }
542
543 template<typename Other, typename... Args>
544 [[nodiscard]] auto _insert_or_do_nothing(Other &&key, Args &&...args) -> std::pair<iterator, bool> {
545 const auto index = _key_to_bucket(key);
546
547 if (auto it = _constrained_find(key, index); it != end()) {
548 return std::make_pair(it, false);
549 }
550
551 _dense.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)...));
552 _sparse.first()[index] = _dense.first().size() - 1u;
553 _rehash_if_required();
554
555 return std::make_pair(--end(), true);
556 }
557
558 template<typename Other, typename Arg>
559 [[nodiscard]] auto _insert_or_overwrite(Other&& key, Arg&& value) -> std::pair<iterator, bool> {
560 const auto index = key_to_bucket(key);
561
562 if (auto it = _constrained_find(key, index); it != end()) {
563 it->second = std::forward<Arg>(value);
564 return std::make_pair(it, false);
565 }
566
567 _dense.first().emplace_back(_sparse.first()[index], std::forward<Other>(key), std::forward<Arg>(value));
568 _sparse.first()[index] = _dense.first().size() - 1u;
569 _rehash_if_required();
570
571 return std::make_pair(--end(), true);
572 }
573
574 auto _move_and_pop(const size_type position) -> void {
575 if (const auto last = size() - 1u; position != last) {
576 auto* current = &_sparse.first()[_key_to_bucket(_dense.first().back().element.first)];
577 _dense.first()[position] = std::move(_dense.first().back());
578 for (; *current != last; current = &_dense.first()[*current].next) {}
579 *current = position;
580 }
581
582 _dense.first().pop_back();
583 }
584
585 auto _rehash_if_required() -> void {
586 if (const auto count = bucket_count(); size() > static_cast<size_type>(static_cast<std::float_t>(count) * max_load_factor())) {
587 rehash(count * 2u);
588 }
589 }
590
591 compressed_pair<sparse_container_type, hasher> _sparse;
592 compressed_pair<dense_container_type, key_equal> _dense;
593 std::float_t _threshold;
594
595}; // class dense_map
596
597} // namespace sbx::containers
598
599template<typename Key, typename Value, typename Allocator>
600struct std::uses_allocator<sbx::containers::detail::dense_map_node<Key, Value>, Allocator> : std::true_type { };
601
602#endif // LIBSBX_CONTAINERS_DENSE_MAP_HPP_
Definition: tests.cpp:6
Definition: dense_map.hpp:233
auto at(const key_type &key) const -> const mapped_type &
Definition: dense_map.hpp:418
Definition: iterable_adaptor.hpp:51
Definition: dense_map.hpp:23