168 constexpr static double EMH_DEFAULT_LOAD_FACTOR = 0.80f;
169 constexpr static double EMH_MIN_LOAD_FACTOR = 0.25f;
170 constexpr static uint32_t EMH_CACHE_LINE_SIZE = 64;
175 using value_type = std::pair<KeyT, ValueT>;
181 constexpr static size_type INACTIVE = 0xFFFFFFFF;
182 constexpr static size_type END = 0xFFFFFFFF;
189 using iterator_category = std::bidirectional_iterator_tag;
190 using difference_type = std::ptrdiff_t;
191 using value_type =
typename htype::value_type;
192 using pointer = value_type*;
193 using const_pointer =
const value_type*;
194 using reference = value_type&;
195 using const_reference =
const value_type&;
236 reference operator*()
const {
return *kv_; }
237 pointer operator->()
const {
return kv_; }
239 bool operator==(
const iterator&
rhs)
const {
return kv_ ==
rhs.kv_; }
240 bool operator!=(
const iterator&
rhs)
const {
return kv_ !=
rhs.kv_; }
253 using iterator_category = std::bidirectional_iterator_tag;
254 using value_type =
typename htype::value_type;
255 using difference_type = std::ptrdiff_t;
256 using pointer = value_type*;
257 using const_pointer =
const value_type*;
258 using reference = value_type&;
259 using const_reference =
const value_type&;
297 const_reference operator*()
const {
return *kv_; }
298 const_pointer operator->()
const {
return kv_; }
300 bool operator==(
const iterator&
rhs)
const {
return kv_ ==
rhs.kv_; }
301 bool operator!=(
const iterator&
rhs)
const {
return kv_ !=
rhs.kv_; }
307 const value_type* kv_;
314 m_mask = m_num_buckets = 0;
316 m_mlf = (
uint32_t)((1 << 27) / EMH_DEFAULT_LOAD_FACTOR);
317 max_load_factor(
mlf);
328 if (
rhs.load_factor() > EMH_MIN_LOAD_FACTOR) {
329 m_pairs = _allocBucket((size_type)(
rhs.m_num_buckets *
rhs.max_load_factor()) + 4);
330 _allocIndex(
rhs.m_num_buckets);
334 init(rhs.m_num_filled + 2, rhs.max_load_factor());
335 for (
auto it = rhs.begin(); it != rhs.end(); ++it)
336 insert_unique(it->first, it->second);
340 HashTableMap2(HashTableMap2&& rhs)
noexcept
343 *
this = std::move(rhs);
346 HashTableMap2(std::initializer_list<value_type> ilist)
348 init((size_type)ilist.size());
349 for (
auto it = ilist.begin(); it != ilist.end(); ++it)
353 template <
class InputIt>
354 HashTableMap2(InputIt first, InputIt last, size_type bucket_count = 4)
356 init(std::distance(first, last) + bucket_count);
357 for (; first != last; ++first)
361 HashTableMap2& operator=(
const HashTableMap2& rhs)
366 if (rhs.load_factor() < EMH_MIN_LOAD_FACTOR) {
369 rehash(rhs.m_num_filled + 2);
370 for (
auto it = rhs.begin(); it != rhs.end(); ++it)
371 insert_unique(it->first, it->second);
377 if (m_num_buckets != rhs.m_num_buckets) {
380 _allocIndex(rhs.m_num_buckets);
381 m_pairs = _allocBucket((size_type)(rhs.m_num_buckets * rhs.max_load_factor()) + 4);
388 HashTableMap2& operator=(HashTableMap2&& rhs)
noexcept
397 template <
typename Con>
398 bool operator==(
const Con& rhs)
const
400 if (size() != rhs.size())
403 for (
auto it = begin(), last = end(); it != last; ++it) {
404 auto oi = rhs.find(it->first);
405 if (oi == rhs.end() || it->second != oi->second)
411 template <
typename Con>
412 bool operator!=(
const Con& rhs)
const
414 return !(*
this == rhs);
417 ~HashTableMap2() noexcept
424 void clone(
const HashTableMap2& rhs)
427 m_hasher = rhs.m_hasher;
428 m_pairs_allocated_size = rhs.m_pairs_allocated_size;
430 auto opairs = rhs.m_pairs;
431 memcpy((
char*)m_index, (
char*)rhs.m_index, (m_num_buckets + EAD) *
sizeof(Index));
433 if (is_copy_trivially()) {
434 memcpy((
char*)m_pairs, (
char*)opairs, m_num_filled *
sizeof(value_type));
437 for (size_type slot = 0; slot < m_num_filled; slot++)
438 new (m_pairs + slot) value_type(opairs[slot]);
442 void swap(HashTableMap2& rhs)
445 std::swap(m_hasher, rhs.m_hasher);
446 std::swap(m_pairs, rhs.m_pairs);
447 std::swap(m_pairs_allocated_size, rhs.m_pairs_allocated_size);
451 iterator first()
const
455 iterator last()
const
457 return {
this, m_num_filled - 1 };
464 const value_type& front()
const
470 return m_pairs[m_num_filled - 1];
472 const value_type& back()
const
474 return m_pairs[m_num_filled - 1];
490 const_iterator cbegin()
const
494 const_iterator begin()
const
501 return {
this, m_num_filled };
503 const_iterator cend()
const
505 return {
this, m_num_filled };
507 const_iterator end()
const
512 const value_type* values()
const
516 const Index* index()
const
521 size_type size()
const
527 return m_num_filled == 0;
529 size_type bucket_count()
const
531 return m_num_buckets;
537 return static_cast<double>(m_num_filled) / (m_mask + 1);
540 HashT& hash_function()
const
549 void max_load_factor(
double mlf)
551 if (mlf < 0.992 && mlf > EMH_MIN_LOAD_FACTOR) {
552 m_mlf = (
uint32_t)((1 << 27) / mlf);
553 if (m_num_buckets > 0)
554 rehash(m_num_buckets);
558 constexpr double max_load_factor()
const
560 return (1 << 27) /
static_cast<double>(m_mlf);
562 constexpr size_type max_size()
const
564 return (1ull << (
sizeof(size_type) * 8 - 1));
566 constexpr size_type max_bucket_count()
const
572 template <
typename K = KeyT>
573 iterator find(
const K& key)
noexcept
575 return {
this, find_filled_slot(key) };
578 template <
typename K = KeyT>
579 const_iterator find(
const K& key)
const noexcept
581 return {
this, find_filled_slot(key) };
584 template <
typename K = KeyT>
585 ValueT& at(
const K& key)
587 const auto slot = find_filled_slot(key);
589 return m_pairs[slot].second;
592 template <
typename K = KeyT>
593 const ValueT& at(
const K& key)
const
595 const auto slot = find_filled_slot(key);
597 return m_pairs[slot].second;
600 const ValueT& index(
const uint32_t index)
const
602 return m_pairs[index].second;
607 return m_pairs[index].second;
610 template <
typename K = KeyT>
611 bool contains(
const K& key)
const noexcept
613 return find_filled_slot(key) != m_num_filled;
616 template <
typename K = KeyT>
617 size_type count(
const K& key)
const noexcept
619 return find_filled_slot(key) == m_num_filled ? 0 : 1;
622 template <
typename K = KeyT>
623 std::pair<iterator, iterator> equal_range(
const K& key)
625 const auto found = find(key);
626 if (found.second == m_num_filled)
627 return { found, found };
629 return { found, std::next(found) };
632 void merge(HashTableMap2& rhs)
635 *
this = std::move(rhs);
639 for (
auto rit = rhs.begin(); rit != rhs.end();) {
640 auto fit = find(rit->first);
642 insert_unique(rit->first, std::move(rit->second));
643 rit = rhs.erase(rit);
651 std::pair<iterator, bool> add(
const KeyT& key,
const ValueT& value)
653 return insert(std::make_pair(key, value));
656 std::pair<iterator, bool> insert(
const value_type& p)
662 std::pair<iterator, bool> insert(value_type&& p)
665 return _doInsert(std::move(p));
668 void insert(std::initializer_list<value_type> ilist)
670 reserve(ilist.size() + m_num_filled,
false);
671 for (
auto it = ilist.begin(); it != ilist.end(); ++it)
675 template <
typename Iter>
676 void insert(Iter first, Iter last)
678 reserve(std::distance(first, last) + m_num_filled,
false);
679 for (; first != last; ++first)
680 _doInsert(first->first, first->second);
683 template <
class... Args>
684 std::pair<iterator, bool> emplace(Args&&... args)
noexcept
687 return _doInsert(std::forward<Args>(args)...);
691 template <
class... Args>
692 iterator emplace_hint(const_iterator hint, Args&&... args)
696 return _doInsert(std::forward<Args>(args)...).first;
699 template <
class... Args>
700 std::pair<iterator, bool> try_emplace(
const KeyT& k, Args&&... args)
703 return _doInsert(k, std::forward<Args>(args)...);
706 template <
class... Args>
707 std::pair<iterator, bool> try_emplace(KeyT&& k, Args&&... args)
710 return _doInsert(std::move(k), std::forward<Args>(args)...);
713 template <
class... Args>
714 size_type emplace_unique(Args&&... args)
716 return insert_unique(std::forward<Args>(args)...);
719 std::pair<iterator, bool> insert_or_assign(
const KeyT& key, ValueT&& val)
721 return do_assign(key, std::forward<ValueT>(val));
723 std::pair<iterator, bool> insert_or_assign(KeyT&& key, ValueT&& val)
725 return do_assign(std::move(key), std::forward<ValueT>(val));
732 const auto key_hash = hash_key(key);
739 const auto slot = m_index[
bucket].slot & m_mask;
750 const auto key_hash = hash_key(key);
757 const auto slot = m_index[
bucket].slot & m_mask;
758 return m_pairs[slot].second;
764 const auto key_hash = hash_key(key);
770 const auto slot = m_index[
bucket].slot & m_mask;
771 return m_pairs[slot].second;
778 const auto key_hash = hash_key(key);
789 iterator
erase(
const const_iterator&
cit)
noexcept
791 const auto slot = (size_type)(
cit.kv_ - m_pairs);
795 return {
this, slot };
799 iterator
erase(const_iterator first, const_iterator last)
noexcept
801 auto esize = long(last.kv_ - first.kv_);
802 auto tsize = long((m_pairs + m_num_filled) - last.kv_);
804 while (tsize-- > 0) {
807 next = ++
erase(next);
813 next = --
erase(next);
815 return {
this, size_type(next.kv_ - m_pairs) };
818 template <
typename Pred>
819 size_type erase_if(Pred pred)
821 auto old_size = size();
822 for (
auto it = begin(); it != end();) {
828 return old_size - size();
831 static constexpr bool is_triviall_destructable()
833#if __cplusplus >= 201402L || _MSC_VER > 1600
834 return !(std::is_trivially_destructible<KeyT>::value && std::is_trivially_destructible<ValueT>::value);
836 return !(std::is_pod<KeyT>::value && std::is_pod<ValueT>::value);
840 static constexpr bool is_copy_trivially()
842#if __cplusplus >= 201103L || _MSC_VER > 1600
843 return (std::is_trivially_copyable<KeyT>::value && std::is_trivially_copyable<ValueT>::value);
845 return (std::is_pod<KeyT>::value && std::is_pod<ValueT>::value);
854 if (m_num_filled > 0)
855 memset((
char*)m_index, INACTIVE,
sizeof(m_index[0]) * m_num_buckets);
857 m_last = m_num_filled = 0;
862 void shrink_to_fit(
const float min_factor = EMH_DEFAULT_LOAD_FACTOR / 4)
865 rehash(m_num_filled + 1);
889 memset((
char*)m_index, INACTIVE,
sizeof(m_index[0]) * m_num_buckets);
890 for (size_type slot = 0; slot < m_num_filled; slot++) {
891 const auto& key = m_pairs[slot].first;
892 const auto key_hash = hash_key(key);
896 m_index[
bucket] = { 1, slot | ((size_type)(
key_hash) & ~m_mask) };
905 void rehash(
uint64_t required_buckets)
907 if (required_buckets < m_num_filled)
910 assert(required_buckets < max_size());
911 auto num_buckets = m_num_filled > (1u << 16) ? (1u << 16) : 4u;
912 while (num_buckets < required_buckets) {
917 m_mask = num_buckets - 1;
920 num_buckets += num_buckets * EMH_PACK_TAIL / 100;
922 m_num_buckets = num_buckets;
924 rebuild(num_buckets);
927 for (size_type slot = 0; slot < m_num_filled; ++slot) {
928 const auto& key = m_pairs[slot].first;
929 const auto key_hash = hash_key(key);
930 const auto bucket = _findUniqueBucket(key_hash);
931 m_index[bucket] = { bucket, slot | ((size_type)(key_hash) & ~m_mask) };
939 if (is_triviall_destructable()) {
940 while (m_num_filled--)
941 m_pairs[m_num_filled].~value_type();
945 void rebuild(size_type num_buckets)
noexcept
948 auto new_pairs = _allocBucket((size_type)(num_buckets * max_load_factor()) + 4);
949 if (is_copy_trivially()) {
951 memcpy((
char*)new_pairs, (
char*)m_pairs, m_num_filled *
sizeof(value_type));
954 for (size_type slot = 0; slot < m_num_filled; slot++) {
955 new (new_pairs + slot) value_type(std::move(m_pairs[slot]));
956 if (is_triviall_destructable())
957 m_pairs[slot].~value_type();
962 _allocIndex(num_buckets);
964 memset((
char*)m_index, INACTIVE,
sizeof(m_index[0]) * num_buckets);
965 memset((
char*)(m_index + num_buckets), 0,
sizeof(m_index[0]) * EAD);
968 void pack_zero(ValueT zero)
970 m_pairs[m_num_filled] = { KeyT(), zero };
976 const auto slot = find_filled_slot(key);
977 const auto found = slot != m_num_filled;
979 val = m_pairs[slot].second;
987 const auto slot = find_filled_slot(key);
988 return slot != m_num_filled ? &m_pairs[slot].second :
nullptr;
994 const auto slot = find_filled_slot(key);
995 return slot != m_num_filled ? &m_pairs[slot].second :
nullptr;
1001 const auto slot = find_filled_slot(key);
1002 if (slot == m_num_filled)
1005 m_pairs[slot].second =
val;
1012 const auto slot = find_filled_slot(key);
1013 if (slot == m_num_filled)
1016 m_pairs[slot].second = std::move(
val);
1023 const auto slot = find_filled_slot(key);
1024 return slot == m_num_filled ?
ValueT() : m_pairs[slot].second;
1028 std::pair<iterator, bool> _doInsert(
const value_type& value)
noexcept
1030 const auto key_hash = hash_key(value.first);
1037 const auto slot = m_index[
bucket].slot & m_mask;
1038 return { {
this, slot }, bempty };
1041 std::pair<iterator, bool> _doInsert(value_type&& value)
noexcept
1043 const auto key_hash = hash_key(value.first);
1044 const auto bucket = _findOrAllocate(value.first, key_hash);
1045 const auto bempty = EMH_EMPTY(bucket);
1047 EMH_NEW(std::move(value.first), std::move(value.second), bucket, key_hash);
1050 const auto slot = m_index[bucket].slot & m_mask;
1051 return { {
this, slot }, bempty };
1054 template <
typename K,
typename V>
1055 std::pair<iterator, bool> _doInsert(K&& key, V&& val)
noexcept
1057 const auto key_hash = hash_key(key);
1058 const auto bucket = _findOrAllocate(key, key_hash);
1059 const auto bempty = EMH_EMPTY(bucket);
1061 EMH_NEW(std::forward<K>(key), std::forward<V>(val), bucket, key_hash);
1064 const auto slot = m_index[bucket].slot & m_mask;
1065 return { {
this, slot }, bempty };
1068 template <
typename K,
typename V>
1069 std::pair<iterator, bool> do_assign(K&& key, V&& val)
noexcept
1071 check_expand_need();
1072 const auto key_hash = hash_key(key);
1073 const auto bucket = _findOrAllocate(key, key_hash);
1074 const auto bempty = EMH_EMPTY(bucket);
1076 EMH_NEW(std::forward<K>(key), std::forward<V>(val), bucket, key_hash);
1079 m_pairs[m_index[bucket].slot & m_mask].second = std::forward(val);
1082 const auto slot = m_index[bucket].slot & m_mask;
1083 return { {
this, slot }, bempty };
1086 template <
typename K,
typename V>
1087 size_type insert_unique(K&& key, V&& val)
1089 check_expand_need();
1090 const auto key_hash = hash_key(key);
1091 auto bucket = _findUniqueBucket(key_hash);
1092 EMH_NEW(std::forward<K>(key), std::forward<V>(val), bucket, key_hash);
1096 size_type insert_unique(value_type&& value)
1098 return insert_unique(std::move(value.first), std::move(value.second));
1101 size_type insert_unique(
const value_type& value)
1103 return insert_unique(value.first, value.second);
1107 bool check_expand_need()
1109 return reserve(m_num_filled,
false);
1112 static void prefetch_heap_block(
char* ctrl)
1117 __builtin_prefetch(
static_cast<const void*
>(ctrl));
1125 size_type slot_to_bucket(
const size_type slot)
const noexcept
1127 size_type main_bucket;
1128 return find_slot_bucket(slot, main_bucket);
1132 void erase_slot(
const size_type sbucket,
const size_type main_bucket)
noexcept
1134 const auto slot = m_index[sbucket].slot & m_mask;
1135 const auto ebucket = erase_bucket(sbucket, main_bucket);
1136 const auto last_slot = --m_num_filled;
1137 if (EMH_LIKELY(slot != last_slot)) {
1138 const auto last_bucket = (m_etail == INACTIVE || ebucket == m_etail)
1139 ? slot_to_bucket(last_slot)
1142 m_pairs[slot] = std::move(m_pairs[last_slot]);
1143 m_index[last_bucket].slot = slot | (m_index[last_bucket].slot & ~m_mask);
1146 if (is_triviall_destructable())
1147 m_pairs[last_slot].~value_type();
1150 m_index[ebucket] = { INACTIVE, 0 };
1153 size_type erase_bucket(
const size_type bucket,
const size_type main_bucket)
noexcept
1155 const auto next_bucket = m_index[bucket].next;
1156 if (bucket == main_bucket) {
1157 if (main_bucket != next_bucket) {
1158 const auto nbucket = m_index[next_bucket].next;
1159 m_index[main_bucket] = {
1160 (nbucket == next_bucket) ? main_bucket : nbucket,
1161 m_index[next_bucket].slot
1167 const auto prev_bucket = find_prev_bucket(main_bucket, bucket);
1168 m_index[prev_bucket].next = (bucket == next_bucket) ? prev_bucket : next_bucket;
1173 size_type find_slot_bucket(
const size_type slot, size_type& main_bucket)
const
1175 const auto key_hash = hash_key(m_pairs[slot].first);
1176 const auto bucket = main_bucket = size_type(key_hash & m_mask);
1177 if (slot == (m_index[bucket].slot & m_mask))
1180 auto next_bucket = m_index[bucket].next;
1182 if (EMH_LIKELY(slot == (m_index[next_bucket].slot & m_mask)))
1184 next_bucket = m_index[next_bucket].next;
1191 size_type find_filled_bucket(
const KeyT& key,
uint64_t key_hash)
const noexcept
1193 const auto bucket = size_type(key_hash & m_mask);
1194 auto next_bucket = m_index[bucket].next;
1195 if (EMH_UNLIKELY((
int)next_bucket < 0))
1198 const auto slot = m_index[bucket].slot & m_mask;
1200 if (EMH_EQHASH(bucket, key_hash)) {
1201 if (EMH_LIKELY(m_eq(key, m_pairs[slot].first)))
1204 if (next_bucket == bucket)
1208 if (EMH_EQHASH(next_bucket, key_hash)) {
1209 const auto slot = m_index[next_bucket].slot & m_mask;
1210 if (EMH_LIKELY(m_eq(key, m_pairs[slot].first)))
1214 const auto nbucket = m_index[next_bucket].next;
1215 if (nbucket == next_bucket)
1217 next_bucket = nbucket;
1224 template <
typename K = KeyT>
1225 size_type find_filled_slot(
const K& key)
const noexcept
1227 const auto key_hash = hash_key(key);
1228 const auto bucket = size_type(key_hash & m_mask);
1229 auto next_bucket = m_index[bucket].next;
1230 if ((
int)next_bucket < 0)
1231 return m_num_filled;
1233 const auto slot = m_index[bucket].slot & m_mask;
1235 if (EMH_EQHASH(bucket, key_hash)) {
1236 if (EMH_LIKELY(m_eq(key, m_pairs[slot].first)))
1239 if (next_bucket == bucket)
1240 return m_num_filled;
1243 if (EMH_EQHASH(next_bucket, key_hash)) {
1244 const auto slot = m_index[next_bucket].slot & m_mask;
1245 if (EMH_LIKELY(m_eq(key, m_pairs[slot].first)))
1249 const auto nbucket = m_index[next_bucket].next;
1250 if (nbucket == next_bucket)
1251 return m_num_filled;
1252 next_bucket = nbucket;
1255 return m_num_filled;
1262 size_type kickout_bucket(
const size_type kmain,
const size_type bucket)
noexcept
1264 const auto next_bucket = m_index[bucket].next;
1265 const auto new_bucket = _findEmptyBucket(next_bucket, 2);
1266 const auto prev_bucket = find_prev_bucket(kmain, bucket);
1268 const auto last = next_bucket == bucket ? new_bucket : next_bucket;
1269 m_index[new_bucket] = { last, m_index[bucket].slot };
1271 m_index[prev_bucket].next = new_bucket;
1272 m_index[bucket].next = INACTIVE;
1284 template <
typename K = KeyT>
1285 size_type _findOrAllocate(
const K& key,
uint64_t key_hash)
noexcept
1287 const auto bucket = size_type(key_hash & m_mask);
1288 auto next_bucket = m_index[bucket].next;
1289 prefetch_heap_block((
char*)&m_pairs[bucket]);
1290 if ((
int)next_bucket < 0) {
1294 const auto slot = m_index[bucket].slot & m_mask;
1295 if (EMH_EQHASH(bucket, key_hash))
1296 if (EMH_LIKELY(m_eq(key, m_pairs[slot].first)))
1300 const auto kmain = hash_bucket(m_pairs[slot].first);
1301 if (kmain != bucket)
1302 return kickout_bucket(kmain, bucket);
1303 else if (next_bucket == bucket)
1304 return m_index[next_bucket].next = _findEmptyBucket(next_bucket, 1);
1309 const auto eslot = m_index[next_bucket].slot & m_mask;
1310 if (EMH_EQHASH(next_bucket, key_hash)) {
1311 if (EMH_LIKELY(m_eq(key, m_pairs[eslot].first)))
1316 const auto nbucket = m_index[next_bucket].next;
1317 if (nbucket == next_bucket)
1319 next_bucket = nbucket;
1323 const auto new_bucket = _findEmptyBucket(next_bucket, csize);
1324 prefetch_heap_block((
char*)&m_pairs[new_bucket]);
1325 return m_index[next_bucket].next = new_bucket;
1328 size_type _findUniqueBucket(
uint64_t key_hash)
noexcept
1330 const auto bucket = size_type(key_hash & m_mask);
1331 auto next_bucket = m_index[bucket].next;
1332 if ((
int)next_bucket < 0) {
1337 const auto kmain = hash_main(bucket);
1338 if (EMH_UNLIKELY(kmain != bucket))
1339 return kickout_bucket(kmain, bucket);
1340 else if (EMH_UNLIKELY(next_bucket != bucket))
1341 next_bucket = find_last_bucket(next_bucket);
1343 return m_index[next_bucket].next = _findEmptyBucket(next_bucket, 2);
1360 size_type _findEmptyBucket(
const size_type bucket_from,
uint32_t csize)
noexcept
1364 auto bucket = bucket_from;
1365 if (EMH_EMPTY(++bucket) || EMH_EMPTY(++bucket))
1369 constexpr size_type linear_probe_length = 2 * EMH_CACHE_LINE_SIZE /
sizeof(Index);
1370 for (size_type offset = csize + 2, step = 4; offset <= linear_probe_length;) {
1371 bucket = (bucket_from + offset) & m_mask;
1372 if (EMH_EMPTY(bucket) || EMH_EMPTY(++bucket))
1377 constexpr size_type quadratic_probe_length = 6u;
1378 for (size_type offset = 4u, step = 3u; step < quadratic_probe_length;) {
1379 bucket = (bucket_from + offset) & m_mask;
1380 if (EMH_EMPTY(bucket) || EMH_EMPTY(++bucket))
1387 __builtin_prefetch(
static_cast<const void*
>(_index + m_last + 1), 0, EMH_PREFETCH);
1392 if (EMH_EMPTY(++m_last))
1395 auto medium = (m_num_buckets / 2 + m_last) & m_mask;
1396 if (EMH_EMPTY(medium))
1403 size_type find_last_bucket(size_type main_bucket)
const
1405 auto next_bucket = m_index[main_bucket].next;
1406 if (next_bucket == main_bucket)
1410 const auto nbucket = m_index[next_bucket].next;
1411 if (nbucket == next_bucket)
1413 next_bucket = nbucket;
1417 size_type find_prev_bucket(
const size_type main_bucket,
const size_type bucket)
const
1419 auto next_bucket = m_index[main_bucket].next;
1420 if (next_bucket == bucket)
1424 const auto nbucket = m_index[next_bucket].next;
1425 if (nbucket == bucket)
1427 next_bucket = nbucket;
1431 size_type hash_bucket(
const KeyT& key)
const noexcept
1433 return (size_type)hash_key(key) & m_mask;
1436 size_type hash_main(
const size_type bucket)
const noexcept
1438 const auto slot = m_index[bucket].slot & m_mask;
1439 return (size_type)hash_key(m_pairs[slot].first) & m_mask;
1444 template <typename UType, typename std::enable_if<std::is_integral<UType>::value,
uint32_t>::type = 0>
1445 inline uint64_t hash_key(
const UType key)
const
1447 return m_hasher(key);
1450 template <typename UType, typename std::enable_if<std::is_same<UType, std::string>::value,
uint32_t>::type = 0>
1451 inline uint64_t hash_key(
const UType& key)
const
1453 return m_hasher(key);
1456 template <typename UType, typename std::enable_if<!std::is_integral<UType>::value && !std::is_same<UType, std::string>::value,
uint32_t>::type = 0>
1457 inline uint64_t hash_key(
const UType& key)
const
1459 return m_hasher(key);
1464 value_type* m_pairs =
nullptr;
1467 Int64 m_pairs_allocated_size = 0;
1471 value_type* _allocBucket(size_type num_buckets)
1473 m_pairs_allocated_size = (
uint64_t)num_buckets *
sizeof(value_type);
1474 AllocatedMemoryInfo mem_info = m_memory_allocator->
allocate({}, m_pairs_allocated_size);
1475 return reinterpret_cast<value_type*
>(mem_info.baseAddress());
1480 m_memory_allocator->
deallocate({}, { m_pairs, m_pairs_allocated_size });
1482 m_pairs_allocated_size = 0;