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