6 #ifndef XENIUM_HARRIS_MICHAEL_HASH_MAP_HPP
7 #define XENIUM_HARRIS_MICHAEL_HASH_MAP_HPP
9 #include <xenium/acquire_guard.hpp>
10 #include <xenium/backoff.hpp>
11 #include <xenium/hash.hpp>
12 #include <xenium/parameter.hpp>
13 #include <xenium/policy.hpp>
14 #include <xenium/utils.hpp>
26 template <std::
size_t Value>
86 template <
class Key,
class Value,
class... Policies>
89 using value_type = std::pair<const Key, Value>;
90 using reclaimer = parameter::type_param_t<
policy::reclaimer, parameter::nil, Policies...>;
91 using hash = parameter::type_param_t<policy::hash, xenium::hash<Key>, Policies...>;
92 using map_to_bucket = parameter::type_param_t<policy::map_to_bucket, utils::modulo<std::size_t>, Policies...>;
94 static constexpr std::size_t num_buckets =
95 parameter::value_param_t<std::size_t,
policy::buckets, 512, Policies...>::value;
96 static constexpr
bool memoize_hash =
97 parameter::value_param_t<bool, policy::memoize_hash, !std::is_scalar<Key>::value, Policies...>::value;
99 template <
class... NewPolicies>
102 static_assert(parameter::is_set<reclaimer>::value,
"reclaimer policy must be specified");
124 template <
class... Args>
143 template <
class... Args>
163 template <
class... Args>
164 std::pair<iterator, bool>
get_or_emplace(Key key, Args&&... args);
185 template <
class Factory>
198 bool erase(
const Key& key);
258 using hash_t = std::size_t;
259 using concurrent_ptr =
typename reclaimer::template concurrent_ptr<node, 1>;
260 using marked_ptr =
typename concurrent_ptr::marked_ptr;
261 using guard_ptr =
typename concurrent_ptr::guard_ptr;
263 template <
typename Factory>
264 std::pair<iterator, bool> do_get_or_emplace_lazy(Key key, Factory node_factory);
266 struct construct_without_hash {};
268 struct data_without_hash {
270 template <
class... Args>
271 explicit data_without_hash(hash_t , Args&&... args) : value(std::forward<Args>(args)...) {}
272 template <
class... Args>
273 explicit data_without_hash(construct_without_hash, Args&&... args) : value(std::forward<Args>(args)...) {}
274 [[nodiscard]] hash_t get_hash()
const {
return hash{}(value.first); }
275 [[nodiscard]]
bool greater_or_equal(hash_t ,
const Key& key)
const {
return value.first >= key; }
278 struct data_with_hash {
281 template <
class... Args>
282 explicit data_with_hash(hash_t hash, Args&&... args) : hash(hash), value(std::forward<Args>(args)...) {}
283 template <
class... Args>
284 explicit data_with_hash(construct_without_hash, Args&&... args) : value(std::forward<Args>(args)...) {
285 hash = harris_michael_hash_map::hash{}(value.first);
287 [[nodiscard]] hash_t get_hash()
const {
return hash; }
288 [[nodiscard]]
bool greater_or_equal(hash_t h,
const Key& key)
const {
return hash >= h && value.first >= key; }
291 using data_t = std::conditional_t<memoize_hash, data_with_hash, data_without_hash>;
293 struct node : reclaimer::template enable_concurrent_ptr<node, 1> {
296 template <
class... Args>
297 explicit node(Args&&... args) : data(std::forward<Args>(args)...), next() {}
301 concurrent_ptr* prev;
307 bool find(hash_t hash,
const Key& key, std::size_t bucket, find_info& info, backoff& backoff);
309 concurrent_ptr buckets[num_buckets];
325 template <
class Key,
class Value,
class... Policies>
328 using iterator_category = std::forward_iterator_tag;
329 using value_type = harris_michael_hash_map::value_type;
330 using difference_type = std::ptrdiff_t;
331 using pointer = value_type*;
332 using reference = value_type&;
341 assert(info.cur.get() !=
nullptr);
342 auto next = info.cur->next.load(std::memory_order_relaxed);
345 if (next.mark() == 0 && tmp_guard.acquire_if_equal(info.cur->next, next, std::memory_order_acquire)) {
346 info.prev = &info.cur->next;
347 info.save = std::move(info.cur);
348 info.cur = std::move(tmp_guard);
353 Key key = info.cur->data.value.first;
354 hash_t h = info.cur->data.get_hash();
356 map->find(h, key, bucket, info, backoff);
358 assert(info.prev == &map->buckets[bucket] || info.cur.get() ==
nullptr ||
359 (info.save.get() !=
nullptr && &info.save->next == info.prev));
362 move_to_next_bucket();
372 bool operator==(
const iterator& other)
const {
return info.cur.get() == other.info.cur.get(); }
373 bool operator!=(
const iterator& other)
const {
return !(*
this == other); }
374 reference operator*()
const noexcept {
return info.cur->data.value; }
375 pointer operator->()
const noexcept {
return &info.cur->data.value; }
378 bucket = num_buckets;
390 info.prev = &map->buckets[bucket];
392 info.cur.acquire(*info.prev, std::memory_order_acquire);
395 move_to_next_bucket();
402 info(std::move(info)) {}
404 void move_to_next_bucket() {
406 while (!info.cur && bucket < num_buckets - 1) {
408 info.prev = &map->buckets[bucket];
410 info.cur.acquire(*info.prev, std::memory_order_acquire);
428 template <
class Key,
class Value,
class... Policies>
431 Value* operator->()
const noexcept {
return &guard->data.value.second; }
432 Value& operator*()
const noexcept {
return guard->data.value.second; }
433 void reset() { guard.reset(); }
436 explicit accessor(guard_ptr&& guard) : guard(std::move(guard)) {}
441 template <
class Key,
class Value,
class... Policies>
443 for (std::size_t i = 0; i < num_buckets; ++i) {
445 auto p = buckets[i].load(std::memory_order_acquire);
448 auto next = p->next.load(std::memory_order_acquire);
455 template <
class Key,
class Value,
class... Policies>
461 auto& head = buckets[bucket];
462 assert((info.save ==
nullptr && info.prev == &head) || &info.save->next == info.prev);
463 concurrent_ptr* start = info.prev;
464 guard_ptr start_guard = info.save;
467 info.save = start_guard;
468 info.next = info.prev->load(std::memory_order_relaxed);
469 if (info.next.mark() != 0) {
478 if (!info.cur.acquire_if_equal(*info.prev, info.next, std::memory_order_acquire)) {
486 info.next = info.cur->next.load(std::memory_order_relaxed);
487 if (info.next.mark() != 0) {
491 info.next = info.cur->next.load(std::memory_order_acquire).get();
494 marked_ptr expected = info.cur.get();
498 if (!info.prev->compare_exchange_weak(
499 expected, info.next, std::memory_order_release, std::memory_order_relaxed)) {
505 if (info.prev->load(std::memory_order_relaxed) != info.cur.get()) {
509 const auto& data = info.cur->data;
510 if (data.greater_or_equal(hash, key)) {
511 return data.value.first == key;
514 info.prev = &info.cur->next;
515 std::swap(info.save, info.cur);
520 template <
class Key,
class Value,
class... Policies>
522 auto h = hash{}(key);
523 auto bucket = map_to_bucket{}(h, num_buckets);
524 find_info info{&buckets[bucket]};
526 return find(h, key, bucket, info, backoff);
529 template <
class Key,
class Value,
class... Policies>
531 auto h = hash{}(key);
532 auto bucket = map_to_bucket{}(h, num_buckets);
533 find_info info{&buckets[bucket]};
535 if (find(h, key, bucket, info, backoff)) {
536 return iterator(
this, bucket, std::move(info));
541 template <
class Key,
class Value,
class... Policies>
542 template <
class... Args>
544 auto result = emplace_or_get(std::forward<Args>(args)...);
545 return result.second;
548 template <
class Key,
class Value,
class... Policies>
549 template <
class... Args>
551 -> std::pair<iterator, bool> {
552 return do_get_or_emplace_lazy(std::move(key), [&args...](hash_t
hash, Key key) {
553 return new node(hash,
554 std::piecewise_construct,
555 std::forward_as_tuple(std::move(key)),
556 std::forward_as_tuple(std::forward<Args>(args)...));
560 template <
class Key,
class Value,
class... Policies>
561 template <
typename Factory>
563 -> std::pair<iterator, bool> {
564 return do_get_or_emplace_lazy(
565 std::move(key), [&value_factory](hash_t hash, Key key) {
return new node(hash, std::move(key), value_factory()); });
568 template <
class Key,
class Value,
class... Policies>
569 template <
typename Factory>
570 auto harris_michael_hash_map<Key, Value, Policies...>::do_get_or_emplace_lazy(Key key, Factory node_factory)
571 -> std::pair<iterator, bool> {
573 auto h = hash{}(key);
574 auto bucket = map_to_bucket{}(h, num_buckets);
576 const Key* pkey = &key;
577 find_info info{&buckets[bucket]};
580 if (find(h, *pkey, bucket, info, backoff)) {
582 return {iterator(
this, bucket, std::move(info)),
false};
585 n = node_factory(h, std::move(key));
586 pkey = &n->data.value.first;
590 marked_ptr cur = info.cur.get();
592 info.cur = guard_ptr(n);
593 n->next.store(cur, std::memory_order_relaxed);
598 if (info.prev->compare_exchange_weak(cur, n, std::memory_order_release, std::memory_order_relaxed)) {
599 return {iterator(
this, bucket, std::move(info)),
true};
606 template <
class Key,
class Value,
class... Policies>
607 template <
class... Args>
609 node* n =
new node(construct_without_hash{}, std::forward<Args>(args)...);
611 auto h = n->data.get_hash();
612 auto bucket = map_to_bucket{}(h, num_buckets);
614 find_info info{&buckets[bucket]};
617 if (find(h, n->data.value.first, bucket, info, backoff)) {
619 return {iterator(
this, bucket, std::move(info)),
false};
622 marked_ptr expected = info.cur.get();
623 n->next.store(expected, std::memory_order_relaxed);
624 guard_ptr new_guard(n);
629 if (info.prev->compare_exchange_weak(expected, n, std::memory_order_release, std::memory_order_relaxed)) {
630 info.cur = std::move(new_guard);
631 return {iterator(
this, bucket, std::move(info)),
true};
638 template <
class Key,
class Value,
class... Policies>
640 auto h = hash{}(key);
641 auto bucket = map_to_bucket{}(h, num_buckets);
643 find_info info{&buckets[bucket]};
646 if (!find(h, key, bucket, info, backoff)) {
651 }
while (!info.cur->next.compare_exchange_weak(
652 info.next, marked_ptr(info.next.get(), 1), std::memory_order_acquire, std::memory_order_relaxed));
654 assert(info.next.mark() == 0);
655 assert(info.cur.mark() == 0);
658 marked_ptr expected = info.cur;
662 if (info.prev->compare_exchange_weak(
663 expected, info.next.get(), std::memory_order_release, std::memory_order_relaxed)) {
668 find(h, key, bucket, info, backoff);
674 template <
class Key,
class Value,
class... Policies>
678 auto next = pos.info.cur->next.load(std::memory_order_acquire);
679 while (next.mark() == 0) {
682 if (pos.info.cur->next.compare_exchange_weak(next, marked_ptr(next.get(), 1), std::memory_order_acquire)) {
689 guard_ptr next_guard(next.get());
690 assert(pos.info.cur.mark() == 0);
693 marked_ptr expected = pos.info.cur;
697 if (pos.info.prev->compare_exchange_weak(
698 expected, next_guard, std::memory_order_release, std::memory_order_relaxed)) {
699 pos.info.cur.reclaim();
700 pos.info.cur = std::move(next_guard);
703 Key key = pos.info.cur->data.value.first;
704 hash_t h = pos.info.cur->data.get_hash();
707 find(h, key, pos.bucket, pos.info, backoff);
711 pos.move_to_next_bucket();
717 template <
class Key,
class Value,
class... Policies>
719 auto result = get_or_emplace_lazy(key, []() {
return Value{}; });
720 return accessor(std::move(result.first.info.cur));
723 template <
class Key,
class Value,
class... Policies>
728 template <
class Key,
class Value,
class... Policies>