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);
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;
863 void shrink_to_fit(
const float min_factor = EMH_DEFAULT_LOAD_FACTOR / 4)
865 if (
load_factor() < min_factor && bucket_count() > 10)
866 rehash(m_num_filled + 1);
874 const auto required_buckets = num_elems * m_mlf >> 27;
875 if (EMH_LIKELY(required_buckets < m_mask))
879 rehash(required_buckets + 2);
885 if (m_num_filled != required_buckets)
886 return reserve(required_buckets,
true);
890 memset((
char*)m_index, INACTIVE,
sizeof(m_index[0]) * m_num_buckets);
891 for (
size_type slot = 0; slot < m_num_filled; slot++) {
892 const auto& key = m_pairs[slot].first;
893 const auto key_hash = hash_key(key);
894 const auto bucket =
size_type(key_hash & m_mask);
895 auto& next_bucket = m_index[bucket].next;
896 if ((
int)next_bucket < 0)
897 m_index[bucket] = { 1, slot | ((
size_type)(key_hash) & ~m_mask) };
899 m_index[bucket].slot |= (
size_type)(key_hash) & ~m_mask;
906 void rehash(uint64_t required_buckets)
908 if (required_buckets < m_num_filled)
911 assert(required_buckets < max_size());
912 auto num_buckets = m_num_filled > (1u << 16) ? (1u << 16) : 4u;
913 while (num_buckets < required_buckets) {
918 m_mask = num_buckets - 1;
921 num_buckets += num_buckets * EMH_PACK_TAIL / 100;
923 m_num_buckets = num_buckets;
925 rebuild(num_buckets);
928 for (size_type slot = 0; slot < m_num_filled; ++slot) {
929 const auto& key = m_pairs[slot].first;
930 const auto key_hash = hash_key(key);
931 const auto bucket = _findUniqueBucket(key_hash);
932 m_index[bucket] = { bucket, slot | ((size_type)(key_hash) & ~m_mask) };
940 if (is_triviall_destructable()) {
941 while (m_num_filled--)
942 m_pairs[m_num_filled].~value_type();
946 void rebuild(size_type num_buckets)
noexcept
949 auto new_pairs = _allocBucket((size_type)(num_buckets * max_load_factor()) + 4);
950 if (is_copy_trivially()) {
952 memcpy((
char*)new_pairs, (
char*)m_pairs, m_num_filled *
sizeof(value_type));
955 for (size_type slot = 0; slot < m_num_filled; slot++) {
956 new (new_pairs + slot) value_type(std::move(m_pairs[slot]));
957 if (is_triviall_destructable())
958 m_pairs[slot].~value_type();
963 _allocIndex(num_buckets);
965 memset((
char*)m_index, INACTIVE,
sizeof(m_index[0]) * num_buckets);
966 memset((
char*)(m_index + num_buckets), 0,
sizeof(m_index[0]) * EAD);
969 void pack_zero(ValueT zero)
971 m_pairs[m_num_filled] = { KeyT(), zero };
975 bool try_get(
const KeyT& key, ValueT& val)
const noexcept
977 const auto slot = find_filled_slot(key);
978 const auto found = slot != m_num_filled;
980 val = m_pairs[slot].second;
988 const auto slot = find_filled_slot(key);
989 return slot != m_num_filled ? &m_pairs[slot].second :
nullptr;
993 ValueT*
try_get(
const KeyT& key)
const noexcept
995 const auto slot = find_filled_slot(key);
996 return slot != m_num_filled ? &m_pairs[slot].second :
nullptr;
1000 bool try_set(
const KeyT& key,
const ValueT& val)
noexcept
1002 const auto slot = find_filled_slot(key);
1003 if (slot == m_num_filled)
1006 m_pairs[slot].second = val;
1011 bool try_set(
const KeyT& key, ValueT&& val)
noexcept
1013 const auto slot = find_filled_slot(key);
1014 if (slot == m_num_filled)
1017 m_pairs[slot].second = std::move(val);
1024 const auto slot = find_filled_slot(key);
1025 return slot == m_num_filled ? ValueT() : m_pairs[slot].second;
1029 std::pair<iterator, bool> _doInsert(
const value_type& value)
noexcept
1031 const auto key_hash = hash_key(value.first);
1032 const auto bucket = _findOrAllocate(value.first, key_hash);
1033 const auto bempty = EMH_EMPTY(bucket);
1035 EMH_NEW(value.first, value.second, bucket, key_hash);
1038 const auto slot = m_index[bucket].slot & m_mask;
1039 return { {
this, slot }, bempty };
1042 std::pair<iterator, bool> _doInsert(value_type&& value)
noexcept
1044 const auto key_hash = hash_key(value.first);
1045 const auto bucket = _findOrAllocate(value.first, key_hash);
1046 const auto bempty = EMH_EMPTY(bucket);
1048 EMH_NEW(std::move(value.first), std::move(value.second), bucket, key_hash);
1051 const auto slot = m_index[bucket].slot & m_mask;
1052 return { {
this, slot }, bempty };
1055 template <
typename K,
typename V>
1056 std::pair<iterator, bool> _doInsert(K&& key, V&& val)
noexcept
1058 const auto key_hash = hash_key(key);
1059 const auto bucket = _findOrAllocate(key, key_hash);
1060 const auto bempty = EMH_EMPTY(bucket);
1062 EMH_NEW(std::forward<K>(key), std::forward<V>(val), bucket, key_hash);
1065 const auto slot = m_index[bucket].slot & m_mask;
1066 return { {
this, slot }, bempty };
1069 template <
typename K,
typename V>
1070 std::pair<iterator, bool> do_assign(K&& key, V&& val)
noexcept
1072 check_expand_need();
1073 const auto key_hash = hash_key(key);
1074 const auto bucket = _findOrAllocate(key, key_hash);
1075 const auto bempty = EMH_EMPTY(bucket);
1077 EMH_NEW(std::forward<K>(key), std::forward<V>(val), bucket, key_hash);
1080 m_pairs[m_index[bucket].slot & m_mask].second = std::forward(val);
1083 const auto slot = m_index[bucket].slot & m_mask;
1084 return { {
this, slot }, bempty };
1087 template <
typename K,
typename V>
1088 size_type insert_unique(K&& key, V&& val)
1090 check_expand_need();
1091 const auto key_hash = hash_key(key);
1092 auto bucket = _findUniqueBucket(key_hash);
1093 EMH_NEW(std::forward<K>(key), std::forward<V>(val), bucket, key_hash);
1097 size_type insert_unique(value_type&& value)
1099 return insert_unique(std::move(value.first), std::move(value.second));
1102 size_type insert_unique(
const value_type& value)
1104 return insert_unique(value.first, value.second);
1108 bool check_expand_need()
1110 return reserve(m_num_filled,
false);
1113 static void prefetch_heap_block(
char* ctrl)
1118 __builtin_prefetch(
static_cast<const void*
>(ctrl));
1126 size_type slot_to_bucket(
const size_type slot)
const noexcept
1128 size_type main_bucket;
1129 return find_slot_bucket(slot, main_bucket);
1133 void erase_slot(
const size_type sbucket,
const size_type main_bucket)
noexcept
1135 const auto slot = m_index[sbucket].slot & m_mask;
1136 const auto ebucket = erase_bucket(sbucket, main_bucket);
1137 const auto last_slot = --m_num_filled;
1138 if (EMH_LIKELY(slot != last_slot)) {
1139 const auto last_bucket = (m_etail == INACTIVE || ebucket == m_etail)
1140 ? slot_to_bucket(last_slot)
1143 m_pairs[slot] = std::move(m_pairs[last_slot]);
1144 m_index[last_bucket].slot = slot | (m_index[last_bucket].slot & ~m_mask);
1147 if (is_triviall_destructable())
1148 m_pairs[last_slot].~value_type();
1151 m_index[ebucket] = { INACTIVE, 0 };
1154 size_type erase_bucket(
const size_type bucket,
const size_type main_bucket)
noexcept
1156 const auto next_bucket = m_index[bucket].next;
1157 if (bucket == main_bucket) {
1158 if (main_bucket != next_bucket) {
1159 const auto nbucket = m_index[next_bucket].next;
1160 m_index[main_bucket] = {
1161 (nbucket == next_bucket) ? main_bucket : nbucket,
1162 m_index[next_bucket].slot
1168 const auto prev_bucket = find_prev_bucket(main_bucket, bucket);
1169 m_index[prev_bucket].next = (bucket == next_bucket) ? prev_bucket : next_bucket;
1174 size_type find_slot_bucket(
const size_type slot, size_type& main_bucket)
const
1176 const auto key_hash = hash_key(m_pairs[slot].first);
1177 const auto bucket = main_bucket = size_type(key_hash & m_mask);
1178 if (slot == (m_index[bucket].slot & m_mask))
1181 auto next_bucket = m_index[bucket].next;
1183 if (EMH_LIKELY(slot == (m_index[next_bucket].slot & m_mask)))
1185 next_bucket = m_index[next_bucket].next;
1192 size_type find_filled_bucket(
const KeyT& key, uint64_t key_hash)
const noexcept
1194 const auto bucket = size_type(key_hash & m_mask);
1195 auto next_bucket = m_index[bucket].next;
1196 if (EMH_UNLIKELY((
int)next_bucket < 0))
1199 const auto slot = m_index[bucket].slot & m_mask;
1201 if (EMH_EQHASH(bucket, key_hash)) {
1202 if (EMH_LIKELY(m_eq(key, m_pairs[slot].first)))
1205 if (next_bucket == bucket)
1209 if (EMH_EQHASH(next_bucket, key_hash)) {
1210 const auto slot = m_index[next_bucket].slot & m_mask;
1211 if (EMH_LIKELY(m_eq(key, m_pairs[slot].first)))
1215 const auto nbucket = m_index[next_bucket].next;
1216 if (nbucket == next_bucket)
1218 next_bucket = nbucket;
1225 template <
typename K = KeyT>
1226 size_type find_filled_slot(
const K& key)
const noexcept
1228 const auto key_hash = hash_key(key);
1229 const auto bucket = size_type(key_hash & m_mask);
1230 auto next_bucket = m_index[bucket].next;
1231 if ((
int)next_bucket < 0)
1232 return m_num_filled;
1234 const auto slot = m_index[bucket].slot & m_mask;
1236 if (EMH_EQHASH(bucket, key_hash)) {
1237 if (EMH_LIKELY(m_eq(key, m_pairs[slot].first)))
1240 if (next_bucket == bucket)
1241 return m_num_filled;
1244 if (EMH_EQHASH(next_bucket, key_hash)) {
1245 const auto slot = m_index[next_bucket].slot & m_mask;
1246 if (EMH_LIKELY(m_eq(key, m_pairs[slot].first)))
1250 const auto nbucket = m_index[next_bucket].next;
1251 if (nbucket == next_bucket)
1252 return m_num_filled;
1253 next_bucket = nbucket;
1256 return m_num_filled;
1263 size_type kickout_bucket(
const size_type kmain,
const size_type bucket)
noexcept
1265 const auto next_bucket = m_index[bucket].next;
1266 const auto new_bucket = _findEmptyBucket(next_bucket, 2);
1267 const auto prev_bucket = find_prev_bucket(kmain, bucket);
1269 const auto last = next_bucket == bucket ? new_bucket : next_bucket;
1270 m_index[new_bucket] = { last, m_index[bucket].slot };
1272 m_index[prev_bucket].next = new_bucket;
1273 m_index[bucket].next = INACTIVE;
1285 template <
typename K = KeyT>
1286 size_type _findOrAllocate(
const K& key, uint64_t key_hash)
noexcept
1288 const auto bucket = size_type(key_hash & m_mask);
1289 auto next_bucket = m_index[bucket].next;
1290 prefetch_heap_block((
char*)&m_pairs[bucket]);
1291 if ((
int)next_bucket < 0) {
1295 const auto slot = m_index[bucket].slot & m_mask;
1296 if (EMH_EQHASH(bucket, key_hash))
1297 if (EMH_LIKELY(m_eq(key, m_pairs[slot].first)))
1301 const auto kmain = hash_bucket(m_pairs[slot].first);
1302 if (kmain != bucket)
1303 return kickout_bucket(kmain, bucket);
1304 else if (next_bucket == bucket)
1305 return m_index[next_bucket].next = _findEmptyBucket(next_bucket, 1);
1310 const auto eslot = m_index[next_bucket].slot & m_mask;
1311 if (EMH_EQHASH(next_bucket, key_hash)) {
1312 if (EMH_LIKELY(m_eq(key, m_pairs[eslot].first)))
1317 const auto nbucket = m_index[next_bucket].next;
1318 if (nbucket == next_bucket)
1320 next_bucket = nbucket;
1324 const auto new_bucket = _findEmptyBucket(next_bucket, csize);
1325 prefetch_heap_block((
char*)&m_pairs[new_bucket]);
1326 return m_index[next_bucket].next = new_bucket;
1329 size_type _findUniqueBucket(uint64_t key_hash)
noexcept
1331 const auto bucket = size_type(key_hash & m_mask);
1332 auto next_bucket = m_index[bucket].next;
1333 if ((
int)next_bucket < 0) {
1338 const auto kmain = hash_main(bucket);
1339 if (EMH_UNLIKELY(kmain != bucket))
1340 return kickout_bucket(kmain, bucket);
1341 else if (EMH_UNLIKELY(next_bucket != bucket))
1342 next_bucket = find_last_bucket(next_bucket);
1344 return m_index[next_bucket].next = _findEmptyBucket(next_bucket, 2);
1361 size_type _findEmptyBucket(
const size_type bucket_from, uint32_t csize)
noexcept
1365 auto bucket = bucket_from;
1366 if (EMH_EMPTY(++bucket) || EMH_EMPTY(++bucket))
1370 constexpr size_type linear_probe_length = 2 * EMH_CACHE_LINE_SIZE /
sizeof(
Index);
1371 for (size_type offset = csize + 2, step = 4; offset <= linear_probe_length;) {
1372 bucket = (bucket_from + offset) & m_mask;
1373 if (EMH_EMPTY(bucket) || EMH_EMPTY(++bucket))
1378 constexpr size_type quadratic_probe_length = 6u;
1379 for (size_type offset = 4u, step = 3u; step < quadratic_probe_length;) {
1380 bucket = (bucket_from + offset) & m_mask;
1381 if (EMH_EMPTY(bucket) || EMH_EMPTY(++bucket))
1388 __builtin_prefetch(
static_cast<const void*
>(_index + m_last + 1), 0, EMH_PREFETCH);
1393 if (EMH_EMPTY(++m_last))
1396 auto medium = (m_num_buckets / 2 + m_last) & m_mask;
1397 if (EMH_EMPTY(medium))
1404 size_type find_last_bucket(size_type main_bucket)
const
1406 auto next_bucket = m_index[main_bucket].next;
1407 if (next_bucket == main_bucket)
1411 const auto nbucket = m_index[next_bucket].next;
1412 if (nbucket == next_bucket)
1414 next_bucket = nbucket;
1418 size_type find_prev_bucket(
const size_type main_bucket,
const size_type bucket)
const
1420 auto next_bucket = m_index[main_bucket].next;
1421 if (next_bucket == bucket)
1425 const auto nbucket = m_index[next_bucket].next;
1426 if (nbucket == bucket)
1428 next_bucket = nbucket;
1432 size_type hash_bucket(
const KeyT& key)
const noexcept
1434 return (size_type)hash_key(key) & m_mask;
1437 size_type hash_main(
const size_type bucket)
const noexcept
1439 const auto slot = m_index[bucket].slot & m_mask;
1440 return (size_type)hash_key(m_pairs[slot].first) & m_mask;
1445 template <typename UType, typename std::enable_if<std::is_integral<UType>::value, uint32_t>::type = 0>
1446 inline uint64_t hash_key(
const UType key)
const
1448 return m_hasher(key);
1451 template <typename UType, typename std::enable_if<std::is_same<UType, std::string>::value, uint32_t>::type = 0>
1452 inline uint64_t hash_key(
const UType& key)
const
1454 return m_hasher(key);
1457 template <typename UType, typename std::enable_if<!std::is_integral<UType>::value && !std::is_same<UType, std::string>::value, uint32_t>::type = 0>
1458 inline uint64_t hash_key(
const UType& key)
const
1460 return m_hasher(key);
1465 value_type* m_pairs =
nullptr;
1468 Int64 m_pairs_allocated_size = 0;
1472 value_type* _allocBucket(size_type num_buckets)
1474 m_pairs_allocated_size = (uint64_t)num_buckets *
sizeof(value_type);
1475 AllocatedMemoryInfo mem_info = m_memory_allocator->allocate({}, m_pairs_allocated_size);
1476 return reinterpret_cast<value_type*
>(mem_info.baseAddress());
1481 m_memory_allocator->deallocate({}, { m_pairs, m_pairs_allocated_size });
1483 m_pairs_allocated_size = 0;