169 constexpr static float EMH_DEFAULT_LOAD_FACTOR = 0.80f;
170 constexpr static float EMH_MIN_LOAD_FACTOR = 0.25f;
171 constexpr static uint32_t EMH_CACHE_LINE_SIZE = 64;
175 using htype = HashTableMap2<KeyT, ValueT, HashT, EqT>;
176 using value_type = std::pair<KeyT, ValueT>;
177 using key_type = KeyT;
178 using mapped_type = ValueT;
179 using hasher = HashT;
180 using key_equal = EqT;
182 constexpr static size_type INACTIVE = 0xFFFFFFFF;
183 constexpr static size_type END = 0xFFFFFFFF;
190 using iterator_category = std::bidirectional_iterator_tag;
191 using difference_type = std::ptrdiff_t;
192 using value_type =
typename htype::value_type;
193 using pointer = value_type*;
194 using const_pointer =
const value_type*;
195 using reference = value_type&;
196 using const_reference =
const value_type&;
206 iterator(
const htype* hash_map, size_type bucket)
208 kv_ = hash_map->m_pairs + (int)bucket;
211 iterator& operator++()
217 iterator operator++(
int)
224 iterator& operator--()
230 iterator operator--(
int)
237 reference operator*()
const {
return *kv_; }
238 pointer operator->()
const {
return kv_; }
240 bool operator==(
const iterator& rhs)
const {
return kv_ == rhs.kv_; }
241 bool operator!=(
const iterator& rhs)
const {
return kv_ != rhs.kv_; }
242 bool operator==(
const const_iterator& rhs)
const {
return kv_ == rhs.kv_; }
243 bool operator!=(
const const_iterator& rhs)
const {
return kv_ != rhs.kv_; }
254 using iterator_category = std::bidirectional_iterator_tag;
255 using value_type =
typename htype::value_type;
256 using difference_type = std::ptrdiff_t;
257 using pointer = value_type*;
258 using const_pointer =
const value_type*;
259 using reference = value_type&;
260 using const_reference =
const value_type&;
267 const_iterator(
const htype* hash_map, size_type bucket)
269 kv_ = hash_map->m_pairs + (int)bucket;
272 const_iterator& operator++()
278 const_iterator operator++(
int)
285 const_iterator& operator--()
291 const_iterator operator--(
int)
298 const_reference operator*()
const {
return *kv_; }
299 const_pointer operator->()
const {
return kv_; }
301 bool operator==(
const iterator& rhs)
const {
return kv_ == rhs.kv_; }
302 bool operator!=(
const iterator& rhs)
const {
return kv_ != rhs.kv_; }
303 bool operator==(
const const_iterator& rhs)
const {
return kv_ == rhs.kv_; }
304 bool operator!=(
const const_iterator& rhs)
const {
return kv_ != rhs.kv_; }
308 const value_type* kv_;
311 void init(size_type bucket,
float mlf = EMH_DEFAULT_LOAD_FACTOR)
315 m_mask = m_num_buckets = 0;
317 m_mlf = (uint32_t)((1 << 27) / EMH_DEFAULT_LOAD_FACTOR);
318 max_load_factor(mlf);
327 HashTableMap2(
const HashTableMap2& rhs)
329 if (rhs.load_factor() > EMH_MIN_LOAD_FACTOR) {
330 m_pairs = _allocBucket((
size_type)(rhs.m_num_buckets * rhs.max_load_factor()) + 4);
331 _allocIndex(rhs.m_num_buckets);
335 init(rhs.m_num_filled + 2, rhs.max_load_factor());
336 for (
auto it = rhs.begin(); it != rhs.end(); ++it)
337 insert_unique(it->first, it->second);
341 HashTableMap2(HashTableMap2&& rhs)
noexcept
344 *
this = std::move(rhs);
347 HashTableMap2(std::initializer_list<value_type> ilist)
349 init((size_type)ilist.size());
350 for (
auto it = ilist.begin(); it != ilist.end(); ++it)
354 template <
class InputIt>
355 HashTableMap2(InputIt first, InputIt last, size_type bucket_count = 4)
357 init(std::distance(first, last) + bucket_count);
358 for (; first != last; ++first)
362 HashTableMap2& operator=(
const HashTableMap2& rhs)
367 if (rhs.load_factor() < EMH_MIN_LOAD_FACTOR) {
370 rehash(rhs.m_num_filled + 2);
371 for (
auto it = rhs.begin(); it != rhs.end(); ++it)
372 insert_unique(it->first, it->second);
378 if (m_num_buckets != rhs.m_num_buckets) {
381 _allocIndex(rhs.m_num_buckets);
382 m_pairs = _allocBucket((size_type)(rhs.m_num_buckets * rhs.max_load_factor()) + 4);
389 HashTableMap2& operator=(HashTableMap2&& rhs)
noexcept
398 template <
typename Con>
399 bool operator==(
const Con& rhs)
const
401 if (size() != rhs.size())
404 for (
auto it = begin(), last = end(); it != last; ++it) {
405 auto oi = rhs.find(it->first);
406 if (oi == rhs.end() || it->second != oi->second)
412 template <
typename Con>
413 bool operator!=(
const Con& rhs)
const
415 return !(*
this == rhs);
418 ~HashTableMap2() noexcept
425 void clone(
const HashTableMap2& rhs)
428 m_hasher = rhs.m_hasher;
429 m_pairs_allocated_size = rhs.m_pairs_allocated_size;
431 auto opairs = rhs.m_pairs;
432 memcpy((
char*)m_index, (
char*)rhs.m_index, (m_num_buckets + EAD) *
sizeof(
Index));
434 if (is_copy_trivially()) {
435 memcpy((
char*)m_pairs, (
char*)opairs, m_num_filled *
sizeof(value_type));
438 for (size_type slot = 0; slot < m_num_filled; slot++)
439 new (m_pairs + slot) value_type(opairs[slot]);
443 void swap(HashTableMap2& rhs)
446 std::swap(m_hasher, rhs.m_hasher);
447 std::swap(m_pairs, rhs.m_pairs);
448 std::swap(m_pairs_allocated_size, rhs.m_pairs_allocated_size);
458 return {
this, m_num_filled - 1 };
465 const value_type& front()
const
471 return m_pairs[m_num_filled - 1];
473 const value_type& back()
const
475 return m_pairs[m_num_filled - 1];
502 return {
this, m_num_filled };
506 return {
this, m_num_filled };
513 const value_type* values()
const
517 const Index* index()
const
522 size_type size()
const
528 return m_num_filled == 0;
530 size_type bucket_count()
const
532 return m_num_buckets;
538 return static_cast<double>(m_num_filled) / (m_mask + 1);
541 HashT& hash_function()
const
550 void max_load_factor(
double mlf)
552 if (mlf < 0.992 && mlf > EMH_MIN_LOAD_FACTOR) {
553 m_mlf = (uint32_t)((1 << 27) / mlf);
554 if (m_num_buckets > 0)
555 rehash(m_num_buckets);
559 constexpr double max_load_factor()
const
561 return (1 << 27) /
static_cast<double>(m_mlf);
563 constexpr size_type max_size()
const
565 return (1ull << (
sizeof(size_type) * 8 - 1));
567 constexpr size_type max_bucket_count()
const
573 template <
typename K = KeyT>
574 iterator find(
const K& key)
noexcept
576 return {
this, find_filled_slot(key) };
579 template <
typename K = KeyT>
582 return {
this, find_filled_slot(key) };
585 template <
typename K = KeyT>
586 ValueT& at(
const K& key)
588 const auto slot = find_filled_slot(key);
590 return m_pairs[slot].second;
593 template <
typename K = KeyT>
594 const ValueT& at(
const K& key)
const
596 const auto slot = find_filled_slot(key);
598 return m_pairs[slot].second;
601 const ValueT& index(
const uint32_t index)
const
603 return m_pairs[index].second;
606 ValueT& index(
const uint32_t index)
608 return m_pairs[index].second;
611 template <
typename K = KeyT>
612 bool contains(
const K& key)
const noexcept
614 return find_filled_slot(key) != m_num_filled;
617 template <
typename K = KeyT>
618 size_type count(
const K& key)
const noexcept
620 return find_filled_slot(key) == m_num_filled ? 0 : 1;
623 template <
typename K = KeyT>
624 std::pair<iterator, iterator> equal_range(
const K& key)
626 const auto found = find(key);
627 if (found.second == m_num_filled)
628 return { found, found };
630 return { found, std::next(found) };
633 void merge(HashTableMap2& rhs)
636 *
this = std::move(rhs);
640 for (
auto rit = rhs.begin(); rit != rhs.end();) {
641 auto fit = find(rit->first);
643 insert_unique(rit->first, std::move(rit->second));
644 rit = rhs.erase(rit);
652 std::pair<iterator, bool> add(
const KeyT& key,
const ValueT& value)
654 return insert(std::make_pair(key, value));
657 std::pair<iterator, bool> insert(
const value_type& p)
663 std::pair<iterator, bool> insert(value_type&& p)
666 return _doInsert(std::move(p));
669 void insert(std::initializer_list<value_type> ilist)
671 reserve(ilist.size() + m_num_filled,
false);
672 for (
auto it = ilist.begin(); it != ilist.end(); ++it)
676 template <
typename Iter>
677 void insert(Iter first, Iter last)
679 reserve(std::distance(first, last) + m_num_filled,
false);
680 for (; first != last; ++first)
681 _doInsert(first->first, first->second);
684 template <
class... Args>
685 std::pair<iterator, bool> emplace(Args&&... args)
noexcept
688 return _doInsert(std::forward<Args>(args)...);
692 template <
class... Args>
697 return _doInsert(std::forward<Args>(args)...).first;
700 template <
class... Args>
701 std::pair<iterator, bool> try_emplace(
const KeyT& k, Args&&... args)
704 return _doInsert(k, std::forward<Args>(args)...);
707 template <
class... Args>
708 std::pair<iterator, bool> try_emplace(KeyT&& k, Args&&... args)
711 return _doInsert(std::move(k), std::forward<Args>(args)...);
714 template <
class... Args>
715 size_type emplace_unique(Args&&... args)
717 return insert_unique(std::forward<Args>(args)...);
720 std::pair<iterator, bool> insert_or_assign(
const KeyT& key, ValueT&& val)
722 return do_assign(key, std::forward<ValueT>(val));
724 std::pair<iterator, bool> insert_or_assign(KeyT&& key, ValueT&& val)
726 return do_assign(std::move(key), std::forward<ValueT>(val));
730 ValueT
set_get(
const KeyT& key,
const ValueT& val)
733 const auto key_hash = hash_key(key);
734 const auto bucket = _findOrAllocate(key, key_hash);
735 if (EMH_EMPTY(bucket)) {
736 EMH_NEW(key, val, bucket, key_hash);
740 const auto slot = m_index[bucket].slot & m_mask;
741 ValueT old_value(val);
742 std::swap(m_pairs[slot].second, old_value);
751 const auto key_hash = hash_key(key);
752 const auto bucket = _findOrAllocate(key, key_hash);
753 if (EMH_EMPTY(bucket)) {
755 EMH_NEW(key, std::move(ValueT()), bucket, key_hash);
758 const auto slot = m_index[bucket].slot & m_mask;
759 return m_pairs[slot].second;
765 const auto key_hash = hash_key(key);
766 const auto bucket = _findOrAllocate(key, key_hash);
767 if (EMH_EMPTY(bucket)) {
768 EMH_NEW(std::move(key), std::move(ValueT()), bucket, key_hash);
771 const auto slot = m_index[bucket].slot & m_mask;
772 return m_pairs[slot].second;
777 size_type
erase(
const KeyT& key)
noexcept
779 const auto key_hash = hash_key(key);
780 const auto sbucket = find_filled_bucket(key, key_hash);
781 if (sbucket == INACTIVE)
784 const auto main_bucket = key_hash & m_mask;
785 erase_slot(sbucket, (size_type)main_bucket);
790 iterator
erase(
const const_iterator& cit)
noexcept
792 const auto slot = (
size_type)(cit.kv_ - m_pairs);
794 const auto sbucket = find_slot_bucket(slot, main_bucket);
795 erase_slot(sbucket, main_bucket);
796 return {
this, slot };
800 iterator
erase(const_iterator first, const_iterator last)
noexcept
802 auto esize = long(last.kv_ - first.kv_);
803 auto tsize = long((m_pairs + m_num_filled) - last.kv_);
805 while (tsize-- > 0) {
808 next = ++
erase(next);
814 next = --
erase(next);
816 return {
this, size_type(next.kv_ - m_pairs) };
819 template <
typename Pred>
820 size_type erase_if(Pred pred)
822 auto old_size = size();
823 for (
auto it = begin(); it != end();) {
829 return old_size - size();
832 static constexpr bool is_triviall_destructable()
834#if __cplusplus >= 201402L || _MSC_VER > 1600
835 return !(std::is_trivially_destructible<KeyT>::value && std::is_trivially_destructible<ValueT>::value);
837 return !(std::is_pod<KeyT>::value && std::is_pod<ValueT>::value);
841 static constexpr bool is_copy_trivially()
843#if __cplusplus >= 201103L || _MSC_VER > 1600
844 return (std::is_trivially_copyable<KeyT>::value && std::is_trivially_copyable<ValueT>::value);
846 return (std::is_pod<KeyT>::value && std::is_pod<ValueT>::value);
855 if (m_num_filled > 0)
856 memset((
char*)m_index, INACTIVE,
sizeof(m_index[0]) * m_num_buckets);
858 m_last = m_num_filled = 0;
862 void shrink_to_fit(
const float min_factor = EMH_DEFAULT_LOAD_FACTOR / 4)
864 if (
load_factor() < min_factor && bucket_count() > 10)
865 rehash(m_num_filled + 1);
872 const auto required_buckets = num_elems * m_mlf >> 27;
873 if (EMH_LIKELY(required_buckets < m_mask))
877 rehash(required_buckets + 2);
883 if (m_num_filled != required_buckets)
884 return reserve(required_buckets,
true);
888 memset((
char*)m_index, INACTIVE,
sizeof(m_index[0]) * m_num_buckets);
889 for (
size_type slot = 0; slot < m_num_filled; slot++) {
890 const auto& key = m_pairs[slot].first;
891 const auto key_hash = hash_key(key);
892 const auto bucket =
size_type(key_hash & m_mask);
893 auto& next_bucket = m_index[bucket].next;
894 if ((
int)next_bucket < 0)
895 m_index[bucket] = { 1, slot | ((
size_type)(key_hash) & ~m_mask) };
897 m_index[bucket].slot |= (
size_type)(key_hash) & ~m_mask;
904 void rehash(uint64_t required_buckets)
906 if (required_buckets < m_num_filled)
909 assert(required_buckets < max_size());
910 auto num_buckets = m_num_filled > (1u << 16) ? (1u << 16) : 4u;
911 while (num_buckets < required_buckets) {
916 m_mask = num_buckets - 1;
919 num_buckets += num_buckets * EMH_PACK_TAIL / 100;
921 m_num_buckets = num_buckets;
923 rebuild(num_buckets);
926 for (size_type slot = 0; slot < m_num_filled; ++slot) {
927 const auto& key = m_pairs[slot].first;
928 const auto key_hash = hash_key(key);
929 const auto bucket = _findUniqueBucket(key_hash);
930 m_index[bucket] = { bucket, slot | ((size_type)(key_hash) & ~m_mask) };
938 if (is_triviall_destructable()) {
939 while (m_num_filled--)
940 m_pairs[m_num_filled].~value_type();
944 void rebuild(size_type num_buckets)
noexcept
947 auto new_pairs = _allocBucket((size_type)(num_buckets * max_load_factor()) + 4);
948 if (is_copy_trivially()) {
950 memcpy((
char*)new_pairs, (
char*)m_pairs, m_num_filled *
sizeof(value_type));
953 for (size_type slot = 0; slot < m_num_filled; slot++) {
954 new (new_pairs + slot) value_type(std::move(m_pairs[slot]));
955 if (is_triviall_destructable())
956 m_pairs[slot].~value_type();
961 _allocIndex(num_buckets);
963 memset((
char*)m_index, INACTIVE,
sizeof(m_index[0]) * num_buckets);
964 memset((
char*)(m_index + num_buckets), 0,
sizeof(m_index[0]) * EAD);
967 void pack_zero(ValueT zero)
969 m_pairs[m_num_filled] = { KeyT(), zero };
973 bool try_get(
const KeyT& key, ValueT& val)
const noexcept
975 const auto slot = find_filled_slot(key);
976 const auto found = slot != m_num_filled;
978 val = m_pairs[slot].second;
984 ValueT* try_get(
const KeyT& key)
noexcept
986 const auto slot = find_filled_slot(key);
987 return slot != m_num_filled ? &m_pairs[slot].second :
nullptr;
991 ValueT* try_get(
const KeyT& key)
const noexcept
993 const auto slot = find_filled_slot(key);
994 return slot != m_num_filled ? &m_pairs[slot].second :
nullptr;
998 bool try_set(
const KeyT& key,
const ValueT& val)
noexcept
1000 const auto slot = find_filled_slot(key);
1001 if (slot == m_num_filled)
1004 m_pairs[slot].second = val;
1009 bool try_set(
const KeyT& key, ValueT&& val)
noexcept
1011 const auto slot = find_filled_slot(key);
1012 if (slot == m_num_filled)
1015 m_pairs[slot].second = std::move(val);
1020 ValueT get_or_return_default(
const KeyT& key)
const noexcept
1022 const auto slot = find_filled_slot(key);
1023 return slot == m_num_filled ? ValueT() : m_pairs[slot].second;
1027 std::pair<iterator, bool> _doInsert(
const value_type& value)
noexcept
1029 const auto key_hash = hash_key(value.first);
1030 const auto bucket = _findOrAllocate(value.first, key_hash);
1031 const auto bempty = EMH_EMPTY(bucket);
1033 EMH_NEW(value.first, value.second, bucket, key_hash);
1036 const auto slot = m_index[bucket].slot & m_mask;
1037 return { {
this, slot }, bempty };
1040 std::pair<iterator, bool> _doInsert(value_type&& value)
noexcept
1042 const auto key_hash = hash_key(value.first);
1043 const auto bucket = _findOrAllocate(value.first, key_hash);
1044 const auto bempty = EMH_EMPTY(bucket);
1046 EMH_NEW(std::move(value.first), std::move(value.second), bucket, key_hash);
1049 const auto slot = m_index[bucket].slot & m_mask;
1050 return { {
this, slot }, bempty };
1053 template <
typename K,
typename V>
1054 std::pair<iterator, bool> _doInsert(K&& key, V&& val)
noexcept
1056 const auto key_hash = hash_key(key);
1057 const auto bucket = _findOrAllocate(key, key_hash);
1058 const auto bempty = EMH_EMPTY(bucket);
1060 EMH_NEW(std::forward<K>(key), std::forward<V>(val), bucket, key_hash);
1063 const auto slot = m_index[bucket].slot & m_mask;
1064 return { {
this, slot }, bempty };
1067 template <
typename K,
typename V>
1068 std::pair<iterator, bool> do_assign(K&& key, V&& val)
noexcept
1070 check_expand_need();
1071 const auto key_hash = hash_key(key);
1072 const auto bucket = _findOrAllocate(key, key_hash);
1073 const auto bempty = EMH_EMPTY(bucket);
1075 EMH_NEW(std::forward<K>(key), std::forward<V>(val), bucket, key_hash);
1078 m_pairs[m_index[bucket].slot & m_mask].second = std::forward(val);
1081 const auto slot = m_index[bucket].slot & m_mask;
1082 return { {
this, slot }, bempty };
1085 template <
typename K,
typename V>
1086 size_type insert_unique(K&& key, V&& val)
1088 check_expand_need();
1089 const auto key_hash = hash_key(key);
1090 auto bucket = _findUniqueBucket(key_hash);
1091 EMH_NEW(std::forward<K>(key), std::forward<V>(val), bucket, key_hash);
1095 size_type insert_unique(value_type&& value)
1097 return insert_unique(std::move(value.first), std::move(value.second));
1100 size_type insert_unique(
const value_type& value)
1102 return insert_unique(value.first, value.second);
1106 bool check_expand_need()
1108 return reserve(m_num_filled,
false);
1111 static void prefetch_heap_block(
char* ctrl)
1116 __builtin_prefetch(
static_cast<const void*
>(ctrl));
1124 size_type slot_to_bucket(
const size_type slot)
const noexcept
1126 size_type main_bucket;
1127 return find_slot_bucket(slot, main_bucket);
1131 void erase_slot(
const size_type sbucket,
const size_type main_bucket)
noexcept
1133 const auto slot = m_index[sbucket].slot & m_mask;
1134 const auto ebucket = erase_bucket(sbucket, main_bucket);
1135 const auto last_slot = --m_num_filled;
1136 if (EMH_LIKELY(slot != last_slot)) {
1137 const auto last_bucket = (m_etail == INACTIVE || ebucket == m_etail)
1138 ? slot_to_bucket(last_slot)
1141 m_pairs[slot] = std::move(m_pairs[last_slot]);
1142 m_index[last_bucket].slot = slot | (m_index[last_bucket].slot & ~m_mask);
1145 if (is_triviall_destructable())
1146 m_pairs[last_slot].~value_type();
1149 m_index[ebucket] = { INACTIVE, 0 };
1152 size_type erase_bucket(
const size_type bucket,
const size_type main_bucket)
noexcept
1154 const auto next_bucket = m_index[bucket].next;
1155 if (bucket == main_bucket) {
1156 if (main_bucket != next_bucket) {
1157 const auto nbucket = m_index[next_bucket].next;
1158 m_index[main_bucket] = {
1159 (nbucket == next_bucket) ? main_bucket : nbucket,
1160 m_index[next_bucket].slot
1166 const auto prev_bucket = find_prev_bucket(main_bucket, bucket);
1167 m_index[prev_bucket].next = (bucket == next_bucket) ? prev_bucket : next_bucket;
1172 size_type find_slot_bucket(
const size_type slot, size_type& main_bucket)
const
1174 const auto key_hash = hash_key(m_pairs[slot].first);
1175 const auto bucket = main_bucket = size_type(key_hash & m_mask);
1176 if (slot == (m_index[bucket].slot & m_mask))
1179 auto next_bucket = m_index[bucket].next;
1181 if (EMH_LIKELY(slot == (m_index[next_bucket].slot & m_mask)))
1183 next_bucket = m_index[next_bucket].next;
1190 size_type find_filled_bucket(
const KeyT& key, uint64_t key_hash)
const noexcept
1192 const auto bucket = size_type(key_hash & m_mask);
1193 auto next_bucket = m_index[bucket].next;
1194 if (EMH_UNLIKELY((
int)next_bucket < 0))
1197 const auto slot = m_index[bucket].slot & m_mask;
1199 if (EMH_EQHASH(bucket, key_hash)) {
1200 if (EMH_LIKELY(m_eq(key, m_pairs[slot].first)))
1203 if (next_bucket == bucket)
1207 if (EMH_EQHASH(next_bucket, key_hash)) {
1208 const auto slot = m_index[next_bucket].slot & m_mask;
1209 if (EMH_LIKELY(m_eq(key, m_pairs[slot].first)))
1213 const auto nbucket = m_index[next_bucket].next;
1214 if (nbucket == next_bucket)
1216 next_bucket = nbucket;
1223 template <
typename K = KeyT>
1224 size_type find_filled_slot(
const K& key)
const noexcept
1226 const auto key_hash = hash_key(key);
1227 const auto bucket = size_type(key_hash & m_mask);
1228 auto next_bucket = m_index[bucket].next;
1229 if ((
int)next_bucket < 0)
1230 return m_num_filled;
1232 const auto slot = m_index[bucket].slot & m_mask;
1234 if (EMH_EQHASH(bucket, key_hash)) {
1235 if (EMH_LIKELY(m_eq(key, m_pairs[slot].first)))
1238 if (next_bucket == bucket)
1239 return m_num_filled;
1242 if (EMH_EQHASH(next_bucket, key_hash)) {
1243 const auto slot = m_index[next_bucket].slot & m_mask;
1244 if (EMH_LIKELY(m_eq(key, m_pairs[slot].first)))
1248 const auto nbucket = m_index[next_bucket].next;
1249 if (nbucket == next_bucket)
1250 return m_num_filled;
1251 next_bucket = nbucket;
1254 return m_num_filled;
1261 size_type kickout_bucket(
const size_type kmain,
const size_type bucket)
noexcept
1263 const auto next_bucket = m_index[bucket].next;
1264 const auto new_bucket = _findEmptyBucket(next_bucket, 2);
1265 const auto prev_bucket = find_prev_bucket(kmain, bucket);
1267 const auto last = next_bucket == bucket ? new_bucket : next_bucket;
1268 m_index[new_bucket] = { last, m_index[bucket].slot };
1270 m_index[prev_bucket].next = new_bucket;
1271 m_index[bucket].next = INACTIVE;
1283 template <
typename K = KeyT>
1284 size_type _findOrAllocate(
const K& key, uint64_t key_hash)
noexcept
1286 const auto bucket = size_type(key_hash & m_mask);
1287 auto next_bucket = m_index[bucket].next;
1288 prefetch_heap_block((
char*)&m_pairs[bucket]);
1289 if ((
int)next_bucket < 0) {
1293 const auto slot = m_index[bucket].slot & m_mask;
1294 if (EMH_EQHASH(bucket, key_hash))
1295 if (EMH_LIKELY(m_eq(key, m_pairs[slot].first)))
1299 const auto kmain = hash_bucket(m_pairs[slot].first);
1300 if (kmain != bucket)
1301 return kickout_bucket(kmain, bucket);
1302 else if (next_bucket == bucket)
1303 return m_index[next_bucket].next = _findEmptyBucket(next_bucket, 1);
1308 const auto eslot = m_index[next_bucket].slot & m_mask;
1309 if (EMH_EQHASH(next_bucket, key_hash)) {
1310 if (EMH_LIKELY(m_eq(key, m_pairs[eslot].first)))
1315 const auto nbucket = m_index[next_bucket].next;
1316 if (nbucket == next_bucket)
1318 next_bucket = nbucket;
1322 const auto new_bucket = _findEmptyBucket(next_bucket, csize);
1323 prefetch_heap_block((
char*)&m_pairs[new_bucket]);
1324 return m_index[next_bucket].next = new_bucket;
1327 size_type _findUniqueBucket(uint64_t key_hash)
noexcept
1329 const auto bucket = size_type(key_hash & m_mask);
1330 auto next_bucket = m_index[bucket].next;
1331 if ((
int)next_bucket < 0) {
1336 const auto kmain = hash_main(bucket);
1337 if (EMH_UNLIKELY(kmain != bucket))
1338 return kickout_bucket(kmain, bucket);
1339 else if (EMH_UNLIKELY(next_bucket != bucket))
1340 next_bucket = find_last_bucket(next_bucket);
1342 return m_index[next_bucket].next = _findEmptyBucket(next_bucket, 2);
1359 size_type _findEmptyBucket(
const size_type bucket_from, uint32_t csize)
noexcept
1363 auto bucket = bucket_from;
1364 if (EMH_EMPTY(++bucket) || EMH_EMPTY(++bucket))
1368 constexpr size_type linear_probe_length = 2 * EMH_CACHE_LINE_SIZE /
sizeof(
Index);
1369 for (size_type offset = csize + 2, step = 4; offset <= linear_probe_length;) {
1370 bucket = (bucket_from + offset) & m_mask;
1371 if (EMH_EMPTY(bucket) || EMH_EMPTY(++bucket))
1376 constexpr size_type quadratic_probe_length = 6u;
1377 for (size_type offset = 4u, step = 3u; step < quadratic_probe_length;) {
1378 bucket = (bucket_from + offset) & m_mask;
1379 if (EMH_EMPTY(bucket) || EMH_EMPTY(++bucket))
1386 __builtin_prefetch(
static_cast<const void*
>(_index + m_last + 1), 0, EMH_PREFETCH);
1391 if (EMH_EMPTY(++m_last))
1394 auto medium = (m_num_buckets / 2 + m_last) & m_mask;
1395 if (EMH_EMPTY(medium))
1402 size_type find_last_bucket(size_type main_bucket)
const
1404 auto next_bucket = m_index[main_bucket].next;
1405 if (next_bucket == main_bucket)
1409 const auto nbucket = m_index[next_bucket].next;
1410 if (nbucket == next_bucket)
1412 next_bucket = nbucket;
1416 size_type find_prev_bucket(
const size_type main_bucket,
const size_type bucket)
const
1418 auto next_bucket = m_index[main_bucket].next;
1419 if (next_bucket == bucket)
1423 const auto nbucket = m_index[next_bucket].next;
1424 if (nbucket == bucket)
1426 next_bucket = nbucket;
1430 size_type hash_bucket(
const KeyT& key)
const noexcept
1432 return (size_type)hash_key(key) & m_mask;
1435 size_type hash_main(
const size_type bucket)
const noexcept
1437 const auto slot = m_index[bucket].slot & m_mask;
1438 return (size_type)hash_key(m_pairs[slot].first) & m_mask;
1443 template <typename UType, typename std::enable_if<std::is_integral<UType>::value, uint32_t>::type = 0>
1444 inline uint64_t hash_key(
const UType key)
const
1446 return m_hasher(key);
1449 template <typename UType, typename std::enable_if<std::is_same<UType, std::string>::value, uint32_t>::type = 0>
1450 inline uint64_t hash_key(
const UType& key)
const
1452 return m_hasher(key);
1455 template <typename UType, typename std::enable_if<!std::is_integral<UType>::value && !std::is_same<UType, std::string>::value, uint32_t>::type = 0>
1456 inline uint64_t hash_key(
const UType& key)
const
1458 return m_hasher(key);
1463 value_type* m_pairs =
nullptr;
1466 Int64 m_pairs_allocated_size = 0;
1470 value_type* _allocBucket(size_type num_buckets)
1472 m_pairs_allocated_size = (uint64_t)num_buckets *
sizeof(value_type);
1473 AllocatedMemoryInfo mem_info = m_memory_allocator->allocate({}, m_pairs_allocated_size);
1474 return reinterpret_cast<value_type*
>(mem_info.baseAddress());
1479 m_memory_allocator->deallocate({}, { m_pairs, m_pairs_allocated_size });
1481 m_pairs_allocated_size = 0;