24#ifndef TSL_HOPSCOTCH_HASH_H
25#define TSL_HOPSCOTCH_HASH_H
37#include <initializer_list>
48#if (defined(__GNUC__) && (__GNUC__ == 4) && (__GNUC_MINOR__ < 9))
49#define TSL_NO_RANGE_ERASE_WITH_CONST_ITERATOR
61 #define tsl_assert(expr) assert(expr)
63 #define tsl_assert(expr) (static_cast<void>(0))
81 throw std::length_error(
"The map exceeds its maxmimum size.");
84 static_assert(MIN_BUCKETS_SIZE > 0,
"");
85 const std::size_t min_bucket_count = MIN_BUCKETS_SIZE;
87 min_bucket_count_in_out = std::max(min_bucket_count, min_bucket_count_in_out);
88 min_bucket_count_in_out = round_up_to_power_of_two(min_bucket_count_in_out);
89 m_mask = min_bucket_count_in_out - 1;
104 throw std::length_error(
"The map exceeds its maxmimum size.");
107 return (m_mask + 1) * 2;
114 return std::numeric_limits<std::size_t>::max()/2 + 1;
118 static std::size_t round_up_to_power_of_two(std::size_t value) {
123 if(is_power_of_two(value)) {
128 for(std::size_t i = 1; i <
sizeof(std::size_t) * CHAR_BIT; i *= 2) {
135 static constexpr bool is_power_of_two(std::size_t value) {
136 return value != 0 && (value & (value - 1)) == 0;
140 static const std::size_t MIN_BUCKETS_SIZE = 2;
149template<
class GrowthFactor = std::ratio<3, 2>>
153 if(min_bucket_count_in_out > max_bucket_count()) {
154 throw std::length_error(
"The map exceeds its maxmimum size.");
157 static_assert(MIN_BUCKETS_SIZE > 0,
"");
158 const std::size_t min_bucket_count = MIN_BUCKETS_SIZE;
160 min_bucket_count_in_out = std::max(min_bucket_count, min_bucket_count_in_out);
161 m_bucket_count = min_bucket_count_in_out;
164 std::size_t bucket_for_hash(std::size_t hash)
const {
165 tsl_assert(m_bucket_count != 0);
166 return hash % m_bucket_count;
169 std::size_t next_bucket_count()
const {
170 if(m_bucket_count == max_bucket_count()) {
171 throw std::length_error(
"The map exceeds its maxmimum size.");
174 const double next_bucket_count = std::ceil(
double(m_bucket_count) * REHASH_SIZE_MULTIPLICATION_FACTOR);
175 if(!std::isnormal(next_bucket_count)) {
176 throw std::length_error(
"The map exceeds its maxmimum size.");
179 if(next_bucket_count >
double(max_bucket_count())) {
180 return max_bucket_count();
183 return std::size_t(next_bucket_count);
187 std::size_t max_bucket_count()
const {
188 return MAX_BUCKET_COUNT;
192 static const std::size_t MIN_BUCKETS_SIZE = 2;
193 static constexpr double REHASH_SIZE_MULTIPLICATION_FACTOR = 1.0*GrowthFactor::num/GrowthFactor::den;
194 static const std::size_t MAX_BUCKET_COUNT =
196 std::numeric_limits<std::size_t>::max()/REHASH_SIZE_MULTIPLICATION_FACTOR
199 static_assert(REHASH_SIZE_MULTIPLICATION_FACTOR >= 1.1,
"Grow factor should be >= 1.1.");
201 std::size_t m_bucket_count;
206namespace detail_hopscotch_hash {
208static constexpr const std::array<std::size_t, 39> PRIMES = {{
209 5ul, 17ul, 29ul, 37ul, 53ul, 67ul, 79ul, 97ul, 131ul, 193ul, 257ul, 389ul, 521ul, 769ul, 1031ul, 1543ul, 2053ul,
210 3079ul, 6151ul, 12289ul, 24593ul, 49157ul, 98317ul, 196613ul, 393241ul, 786433ul, 1572869ul, 3145739ul,
211 6291469ul, 12582917ul, 25165843ul, 50331653ul, 100663319ul, 201326611ul, 402653189ul, 805306457ul,
212 1610612741ul, 3221225473ul, 4294967291ul
215template<
unsigned int IPrime>
216static std::size_t mod(std::size_t hash) {
return hash % PRIMES[IPrime]; }
220static constexpr const std::array<std::size_t(*)(std::size_t), 39> MOD_PRIME = {{
221 &mod<0>, &mod<1>, &mod<2>, &mod<3>, &mod<4>, &mod<5>, &mod<6>, &mod<7>, &mod<8>, &mod<9>, &mod<10>,
222 &mod<11>, &mod<12>, &mod<13>, &mod<14>, &mod<15>, &mod<16>, &mod<17>, &mod<18>, &mod<19>, &mod<20>,
223 &mod<21>, &mod<22>, &mod<23>, &mod<24>, &mod<25>, &mod<26>, &mod<27>, &mod<28>, &mod<29>, &mod<30>,
224 &mod<31>, &mod<32>, &mod<33>, &mod<34>, &mod<35>, &mod<36>, &mod<37> , &mod<38>
236 auto it_prime = std::lower_bound(tsl::detail_hopscotch_hash::PRIMES.begin(),
237 tsl::detail_hopscotch_hash::PRIMES.end(), min_bucket_count_in_out);
238 if(it_prime == tsl::detail_hopscotch_hash::PRIMES.end()) {
239 throw std::length_error(
"The map exceeds its maxmimum size.");
242 m_iprime =
static_cast<unsigned int>(std::distance(tsl::detail_hopscotch_hash::PRIMES.begin(), it_prime));
243 min_bucket_count_in_out = *it_prime;
246 std::size_t bucket_for_hash(std::size_t hash)
const {
247 return bucket_for_hash_iprime(hash, m_iprime);
250 std::size_t next_bucket_count()
const {
251 if(m_iprime + 1 >= tsl::detail_hopscotch_hash::PRIMES.size()) {
252 throw std::length_error(
"The map exceeds its maxmimum size.");
255 return tsl::detail_hopscotch_hash::PRIMES[m_iprime + 1];
258 std::size_t max_bucket_count()
const {
259 return tsl::detail_hopscotch_hash::PRIMES.back();
263 std::size_t bucket_for_hash_iprime(std::size_t hash,
unsigned int iprime)
const {
264 tsl_assert(iprime < tsl::detail_hopscotch_hash::MOD_PRIME.size());
265 return tsl::detail_hopscotch_hash::MOD_PRIME[iprime](hash);
269 unsigned int m_iprime;
273namespace detail_hopscotch_hash {
283template<
typename T,
typename =
void>
292template<
typename T,
typename =
void>
307static const size_t SMALLEST_TYPE_MAX_BITS_SUPPORTED = 64;
308template<
unsigned int MinBits,
typename Enable =
void>
312template<
unsigned int MinBits>
315 using type = std::uint_least8_t;
318template<
unsigned int MinBits>
321 using type = std::uint_least16_t;
324template<
unsigned int MinBits>
327 using type = std::uint_least32_t;
330template<
unsigned int MinBits>
333 using type = std::uint_least64_t;
353static const std::size_t NB_RESERVED_BITS_IN_NEIGHBORHOOD = 2;
356template<
bool StoreHash>
359 using hash_type = std::false_type;
361 bool bucket_hash_equal(std::size_t )
const noexcept {
365 std::size_t truncated_bucket_hash()
const noexcept {
374 void set_hash(std::size_t )
noexcept {
381 using hash_type = std::uint_least32_t;
382 static_assert(
sizeof(hash_type) <=
sizeof(std::size_t),
"");
384 bool bucket_hash_equal(std::size_t hash)
const noexcept {
385 return m_hash == hash_type(hash);
388 std::size_t truncated_bucket_hash()
const noexcept {
394 m_hash = bucket.m_hash;
397 void set_hash(std::size_t hash)
noexcept {
398 m_hash = hash_type(hash);
405template<
typename ValueType,
unsigned int NeighborhoodSize,
bool StoreHash>
408 static const size_t MIN_NEIGHBORHOOD_SIZE = 4;
409 static const size_t MAX_NEIGHBORHOOD_SIZE = SMALLEST_TYPE_MAX_BITS_SUPPORTED - NB_RESERVED_BITS_IN_NEIGHBORHOOD;
412 static_assert(NeighborhoodSize >= 4,
"NeighborhoodSize should be >= 4.");
414 static_assert(MIN_NEIGHBORHOOD_SIZE == 4,
"");
416 static_assert(NeighborhoodSize <= 62,
"NeighborhoodSize should be <= 62.");
418 static_assert(MAX_NEIGHBORHOOD_SIZE == 62,
"");
421 static_assert(!StoreHash || NeighborhoodSize <= 30,
422 "NeighborhoodSize should be <= 30 if StoreHash is true.");
424 static_assert(MAX_NEIGHBORHOOD_SIZE - 32 == 30,
"");
429 using value_type = ValueType;
430 using neighborhood_bitmap =
440 noexcept(std::is_nothrow_copy_constructible<value_type>::value):
bucket_hash(bucket),
441 m_neighborhood_infos(0)
443 if(!bucket.empty()) {
444 ::new (
static_cast<void*
>(std::addressof(m_value))) value_type(bucket.value());
447 m_neighborhood_infos = bucket.m_neighborhood_infos;
451 noexcept(std::is_nothrow_move_constructible<value_type>::value) :
bucket_hash(std::move(bucket)),
452 m_neighborhood_infos(0)
454 if(!bucket.empty()) {
455 ::new (
static_cast<void*
>(std::addressof(m_value))) value_type(std::move(bucket.value()));
458 m_neighborhood_infos = bucket.m_neighborhood_infos;
462 noexcept(std::is_nothrow_copy_constructible<value_type>::value)
464 if(
this != &bucket) {
465 bucket_hash::operator=(bucket);
472 if(!bucket.empty()) {
473 ::new (
static_cast<void*
>(std::addressof(m_value))) value_type(bucket.value());
476 m_neighborhood_infos = bucket.m_neighborhood_infos;
490 neighborhood_bitmap neighborhood_infos()
const noexcept {
491 return neighborhood_bitmap(m_neighborhood_infos >> NB_RESERVED_BITS_IN_NEIGHBORHOOD);
494 void set_overflow(
bool has_overflow)
noexcept {
496 m_neighborhood_infos = neighborhood_bitmap(m_neighborhood_infos | 2);
499 m_neighborhood_infos = neighborhood_bitmap(m_neighborhood_infos & ~2);
503 bool has_overflow()
const noexcept {
504 return (m_neighborhood_infos & 2) != 0;
507 bool empty()
const noexcept {
508 return (m_neighborhood_infos & 1) == 0;
511 void toggle_neighbor_presence(std::size_t ineighbor)
noexcept {
512 tsl_assert(ineighbor <= NeighborhoodSize);
513 m_neighborhood_infos = neighborhood_bitmap(
514 m_neighborhood_infos ^ (1ull << (ineighbor + NB_RESERVED_BITS_IN_NEIGHBORHOOD)));
517 bool check_neighbor_presence(std::size_t ineighbor)
const noexcept {
518 tsl_assert(ineighbor <= NeighborhoodSize);
519 if(((m_neighborhood_infos >> (ineighbor + NB_RESERVED_BITS_IN_NEIGHBORHOOD)) & 1) == 1) {
526 value_type& value()
noexcept {
527 tsl_assert(!empty());
528 return *
reinterpret_cast<value_type*
>(std::addressof(m_value));
531 const value_type& value()
const noexcept {
532 tsl_assert(!empty());
533 return *
reinterpret_cast<const value_type*
>(std::addressof(m_value));
537 void set_value_of_empty_bucket(
P&& value, std::size_t hash) {
540 ::new (
static_cast<void*
>(std::addressof(m_value))) value_type(std::forward<P>(value));
542 this->set_hash(hash);
546 tsl_assert(empty_bucket.empty());
548 ::new (
static_cast<void*
>(std::addressof(empty_bucket.m_value))) value_type(std::move(value()));
549 empty_bucket.copy_hash(*
this);
550 empty_bucket.set_empty(
false);
557 void remove_value()
noexcept {
564 void clear()
noexcept {
569 m_neighborhood_infos = 0;
573 static std::size_t max_size()
noexcept {
575 return std::numeric_limits<typename bucket_hash::hash_type>::max();
578 return std::numeric_limits<std::size_t>::max();
583 void set_empty(
bool is_empty)
noexcept {
585 m_neighborhood_infos = neighborhood_bitmap(m_neighborhood_infos & ~1);
588 m_neighborhood_infos = neighborhood_bitmap(m_neighborhood_infos | 1);
592 void destroy_value()
noexcept {
594 tsl_assert(!empty());
596 value().~value_type();
604 using storage =
typename std::aligned_storage<
sizeof(value_type),
alignof(value_type)>::type;
606 neighborhood_bitmap m_neighborhood_infos;
624template<
class ValueType,
630 unsigned int NeighborhoodSize,
633 class OverflowContainer>
637 using has_mapped_type =
typename std::integral_constant<bool, !std::is_same<U, void>::value>;
640 template<
bool is_const>
643 using key_type =
typename KeySelect::key_type;
644 using value_type = ValueType;
645 using size_type = std::size_t;
646 using difference_type = std::ptrdiff_t;
648 using key_equal = KeyEqual;
649 using allocator_type = Allocator;
650 using reference = value_type&;
651 using const_reference =
const value_type&;
652 using pointer = value_type*;
653 using const_pointer =
const value_type*;
659 using neighborhood_bitmap =
typename hopscotch_bucket::neighborhood_bitmap;
661 using buckets_allocator =
typename std::allocator_traits<allocator_type>::template rebind_alloc<hopscotch_bucket>;
662 using buckets_container_type = std::vector<hopscotch_bucket, buckets_allocator>;
664 using overflow_container_type = OverflowContainer;
666 static_assert(std::is_same<typename overflow_container_type::value_type, ValueType>::value,
667 "OverflowContainer should have ValueType as type.");
669 static_assert(std::is_same<typename overflow_container_type::allocator_type, Allocator>::value,
670 "Invalid allocator, not the same type as the value_type.");
673 using iterator_buckets =
typename buckets_container_type::iterator;
674 using const_iterator_buckets =
typename buckets_container_type::const_iterator;
676 using iterator_overflow =
typename overflow_container_type::iterator;
677 using const_iterator_overflow =
typename overflow_container_type::const_iterator;
687 template<
bool is_const>
691 using iterator_bucket =
typename std::conditional<is_const,
692 typename hopscotch_hash::const_iterator_buckets,
693 typename hopscotch_hash::iterator_buckets>::type;
694 using iterator_overflow =
typename std::conditional<is_const,
695 typename hopscotch_hash::const_iterator_overflow,
696 typename hopscotch_hash::iterator_overflow>::type;
699 hopscotch_iterator(iterator_bucket buckets_iterator, iterator_bucket buckets_end_iterator,
700 iterator_overflow overflow_iterator) noexcept :
701 m_buckets_iterator(buckets_iterator), m_buckets_end_iterator(buckets_end_iterator),
702 m_overflow_iterator(overflow_iterator)
707 using iterator_category = std::forward_iterator_tag;
708 using value_type =
const typename hopscotch_hash::value_type;
709 using difference_type = std::ptrdiff_t;
710 using reference = value_type&;
711 using pointer = value_type*;
718 m_buckets_iterator(other.m_buckets_iterator), m_buckets_end_iterator(other.m_buckets_end_iterator),
719 m_overflow_iterator(other.m_overflow_iterator)
723 const typename hopscotch_hash::key_type& key()
const {
724 if(m_buckets_iterator != m_buckets_end_iterator) {
725 return KeySelect()(m_buckets_iterator->value());
728 return KeySelect()(*m_overflow_iterator);
731 template<class U = ValueSelect, typename std::enable_if<has_mapped_type<U>::value>::type* =
nullptr>
732 typename std::conditional<
734 const typename U::value_type&,
735 typename U::value_type&>::type value()
const
737 if(m_buckets_iterator != m_buckets_end_iterator) {
738 return U()(m_buckets_iterator->value());
741 return U()(*m_overflow_iterator);
744 reference operator*()
const {
745 if(m_buckets_iterator != m_buckets_end_iterator) {
746 return m_buckets_iterator->value();
749 return *m_overflow_iterator;
752 pointer operator->()
const {
753 if(m_buckets_iterator != m_buckets_end_iterator) {
754 return std::addressof(m_buckets_iterator->value());
757 return std::addressof(*m_overflow_iterator);
761 if(m_buckets_iterator == m_buckets_end_iterator) {
762 ++m_overflow_iterator;
767 ++m_buckets_iterator;
768 }
while(m_buckets_iterator != m_buckets_end_iterator && m_buckets_iterator->empty());
781 return lhs.m_buckets_iterator == rhs.m_buckets_iterator &&
782 lhs.m_overflow_iterator == rhs.m_overflow_iterator;
786 return !(lhs == rhs);
790 iterator_bucket m_buckets_iterator;
791 iterator_bucket m_buckets_end_iterator;
792 iterator_overflow m_overflow_iterator;
798 template<class OC = OverflowContainer, typename std::enable_if<!has_key_compare<OC>::value>::type* =
nullptr>
801 const KeyEqual& equal,
802 const Allocator& alloc,
803 float max_load_factor) : Hash(hash),
805 GrowthPolicy(bucket_count),
807 m_overflow_elements(alloc),
810 if(bucket_count > max_bucket_count()) {
811 throw std::length_error(
"The map exceeds its maxmimum size.");
814 static_assert(NeighborhoodSize - 1 > 0,
"");
815 m_buckets.resize(bucket_count + NeighborhoodSize - 1);
818 this->max_load_factor(max_load_factor);
821 template<class OC = OverflowContainer, typename std::enable_if<has_key_compare<OC>::value>::type* =
nullptr>
822 hopscotch_hash(size_type bucket_count,
824 const KeyEqual& equal,
825 const Allocator& alloc,
826 float max_load_factor,
827 const typename OC::key_compare& comp) : Hash(hash),
829 GrowthPolicy(bucket_count),
831 m_overflow_elements(comp, alloc),
835 if(bucket_count > max_bucket_count()) {
836 throw std::length_error(
"The map exceeds its maxmimum size.");
839 static_assert(NeighborhoodSize - 1 > 0,
"");
840 m_buckets.resize(bucket_count + NeighborhoodSize - 1);
843 this->max_load_factor(max_load_factor);
846 hopscotch_hash(
const hopscotch_hash& other) =
default;
848 hopscotch_hash(hopscotch_hash&& other)
850 std::is_nothrow_move_constructible<Hash>::value &&
851 std::is_nothrow_move_constructible<KeyEqual>::value &&
852 std::is_nothrow_move_constructible<GrowthPolicy>::value &&
853 std::is_nothrow_move_constructible<buckets_container_type>::value &&
854 std::is_nothrow_move_constructible<overflow_container_type>::value
856 : Hash(std::move(static_cast<Hash&>(other))),
857 KeyEqual(std::move(static_cast<KeyEqual&>(other))),
858 GrowthPolicy(std::move(static_cast<GrowthPolicy&>(other))),
859 m_buckets(std::move(other.m_buckets)),
860 m_overflow_elements(std::move(other.m_overflow_elements)),
861 m_nb_elements(other.m_nb_elements),
862 m_max_load_factor(other.m_max_load_factor),
863 m_load_threshold(other.m_load_threshold),
864 m_min_load_factor_rehash_threshold(other.m_min_load_factor_rehash_threshold)
869 hopscotch_hash& operator=(
const hopscotch_hash& other) =
default;
871 hopscotch_hash& operator=(hopscotch_hash&& other) {
878 allocator_type get_allocator()
const {
879 return m_buckets.get_allocator();
886 iterator begin() noexcept {
887 auto begin = m_buckets.begin();
888 while(begin != m_buckets.end() && begin->empty()) {
892 return iterator(begin, m_buckets.end(), m_overflow_elements.begin());
895 const_iterator begin() const noexcept {
899 const_iterator cbegin() const noexcept {
900 auto begin = m_buckets.cbegin();
901 while(begin != m_buckets.cend() && begin->empty()) {
905 return const_iterator(begin, m_buckets.cend(), m_overflow_elements.cbegin());
908 iterator end() noexcept {
909 return iterator(m_buckets.end(), m_buckets.end(), m_overflow_elements.end());
912 const_iterator end() const noexcept {
916 const_iterator cend() const noexcept {
917 return const_iterator(m_buckets.cend(), m_buckets.cend(), m_overflow_elements.cend());
924 bool empty() const noexcept {
925 return m_nb_elements == 0;
928 size_type size() const noexcept {
929 return m_nb_elements;
932 size_type max_size() const noexcept {
933 return hopscotch_bucket::max_size();
939 void clear() noexcept {
940 for(
auto& bucket : m_buckets) {
944 m_overflow_elements.clear();
949 std::pair<iterator, bool> insert(
const value_type& value) {
950 return insert_impl(value);
953 template<class P, typename std::enable_if<std::is_constructible<value_type, P&&>::value>::type* =
nullptr>
954 std::pair<iterator, bool> insert(
P&& value) {
955 return emplace(std::forward<P>(value));
958 std::pair<iterator, bool> insert(value_type&& value) {
959 return insert_impl(std::move(value));
963 iterator insert(const_iterator hint,
const value_type& value) {
964 if(hint != cend() && compare_keys(KeySelect()(*hint), KeySelect()(value))) {
965 return mutable_iterator(hint);
968 return insert(value).first;
971 template<class P, typename std::enable_if<std::is_constructible<value_type, P&&>::value>::type* =
nullptr>
972 iterator insert(const_iterator hint,
P&& value) {
973 return emplace_hint(hint, std::forward<P>(value));
976 iterator insert(const_iterator hint, value_type&& value) {
977 if(hint != cend() && compare_keys(KeySelect()(*hint), KeySelect()(value))) {
978 return mutable_iterator(hint);
981 return insert(std::move(value)).first;
985 template<
class InputIt>
986 void insert(InputIt first, InputIt last) {
987 if(std::is_base_of<std::forward_iterator_tag,
988 typename std::iterator_traits<InputIt>::iterator_category>::value)
990 const auto nb_elements_insert = std::distance(first, last);
991 const std::size_t nb_elements_in_buckets = m_nb_elements - m_overflow_elements.size();
992 const std::size_t nb_free_buckets = m_load_threshold - nb_elements_in_buckets;
993 tsl_assert(m_nb_elements >= m_overflow_elements.size());
994 tsl_assert(m_load_threshold >= nb_elements_in_buckets);
996 if(nb_elements_insert > 0 && nb_free_buckets < std::size_t(nb_elements_insert)) {
997 reserve(nb_elements_in_buckets + std::size_t(nb_elements_insert));
1001 for(; first != last; ++first) {
1008 std::pair<iterator, bool> insert_or_assign(
const key_type& k, M&& obj) {
1009 return insert_or_assign_impl(k, std::forward<M>(obj));
1013 std::pair<iterator, bool> insert_or_assign(key_type&& k, M&& obj) {
1014 return insert_or_assign_impl(std::move(k), std::forward<M>(obj));
1019 iterator insert_or_assign(const_iterator hint,
const key_type& k, M&& obj) {
1020 if(hint != cend() && compare_keys(KeySelect()(*hint), k)) {
1021 auto it = mutable_iterator(hint);
1022 it.value() = std::forward<M>(obj);
1027 return insert_or_assign(k, std::forward<M>(obj)).first;
1031 iterator insert_or_assign(const_iterator hint, key_type&& k, M&& obj) {
1032 if(hint != cend() && compare_keys(KeySelect()(*hint), k)) {
1033 auto it = mutable_iterator(hint);
1034 it.value() = std::forward<M>(obj);
1039 return insert_or_assign(std::move(k), std::forward<M>(obj)).first;
1043 template<
class... Args>
1044 std::pair<iterator, bool> emplace(Args&&... args) {
1045 return insert(value_type(std::forward<Args>(args)...));
1048 template<
class... Args>
1049 iterator emplace_hint(const_iterator hint, Args&&... args) {
1050 return insert(hint, value_type(std::forward<Args>(args)...));
1053 template<
class... Args>
1054 std::pair<iterator, bool> try_emplace(
const key_type& k, Args&&... args) {
1055 return try_emplace_impl(k, std::forward<Args>(args)...);
1058 template<
class... Args>
1059 std::pair<iterator, bool> try_emplace(key_type&& k, Args&&... args) {
1060 return try_emplace_impl(std::move(k), std::forward<Args>(args)...);
1063 template<
class... Args>
1064 iterator try_emplace(const_iterator hint,
const key_type& k, Args&&... args) {
1065 if(hint != cend() && compare_keys(KeySelect()(*hint), k)) {
1066 return mutable_iterator(hint);
1069 return try_emplace(k, std::forward<Args>(args)...).first;
1072 template<
class... Args>
1073 iterator try_emplace(const_iterator hint, key_type&& k, Args&&... args) {
1074 if(hint != cend() && compare_keys(KeySelect()(*hint), k)) {
1075 return mutable_iterator(hint);
1078 return try_emplace(std::move(k), std::forward<Args>(args)...).first;
1083 iterator erase(iterator pos) {
1084 return erase(const_iterator(pos));
1087 iterator erase(const_iterator pos) {
1088 const std::size_t ibucket_for_hash = bucket_for_hash(hash_key(pos.key()));
1090 if(pos.m_buckets_iterator != pos.m_buckets_end_iterator) {
1091 auto it_bucket = m_buckets.begin() + std::distance(m_buckets.cbegin(), pos.m_buckets_iterator);
1092 erase_from_bucket(it_bucket, ibucket_for_hash);
1094 return ++iterator(it_bucket, m_buckets.end(), m_overflow_elements.begin());
1097 auto it_next_overflow = erase_from_overflow(pos.m_overflow_iterator, ibucket_for_hash);
1098 return iterator(m_buckets.end(), m_buckets.end(), it_next_overflow);
1102 iterator erase(const_iterator first, const_iterator last) {
1104 return mutable_iterator(first);
1107 auto to_delete = erase(first);
1108 while(to_delete != last) {
1109 to_delete = erase(to_delete);
1116 size_type erase(
const K& key) {
1117 return erase(key, hash_key(key));
1121 size_type erase(
const K& key, std::size_t hash) {
1122 const std::size_t ibucket_for_hash = bucket_for_hash(hash);
1124 auto it_find = find_in_buckets(key, hash, m_buckets.begin() + ibucket_for_hash);
1125 if(it_find != m_buckets.end()) {
1126 erase_from_bucket(it_find, ibucket_for_hash);
1131 if(m_buckets[ibucket_for_hash].has_overflow()) {
1132 auto it_overflow = find_in_overflow(key);
1133 if(it_overflow != m_overflow_elements.end()) {
1134 erase_from_overflow(it_overflow, ibucket_for_hash);
1143 void swap(hopscotch_hash& other) {
1146 swap(
static_cast<Hash&
>(*
this),
static_cast<Hash&
>(other));
1147 swap(
static_cast<KeyEqual&
>(*
this),
static_cast<KeyEqual&
>(other));
1148 swap(
static_cast<GrowthPolicy&
>(*
this),
static_cast<GrowthPolicy&
>(other));
1149 swap(m_buckets, other.m_buckets);
1150 swap(m_overflow_elements, other.m_overflow_elements);
1151 swap(m_nb_elements, other.m_nb_elements);
1152 swap(m_max_load_factor, other.m_max_load_factor);
1153 swap(m_load_threshold, other.m_load_threshold);
1154 swap(m_min_load_factor_rehash_threshold, other.m_min_load_factor_rehash_threshold);
1161 template<class K, class U = ValueSelect, typename std::enable_if<has_mapped_type<U>::value>::type* =
nullptr>
1162 typename U::value_type& at(
const K& key) {
1163 return at(key, hash_key(key));
1166 template<class K, class U = ValueSelect, typename std::enable_if<has_mapped_type<U>::value>::type* =
nullptr>
1167 typename U::value_type& at(
const K& key, std::size_t hash) {
1168 return const_cast<typename U::value_type&
>(
static_cast<const hopscotch_hash*
>(
this)->at(key, hash));
1172 template<class K, class U = ValueSelect, typename std::enable_if<has_mapped_type<U>::value>::type* =
nullptr>
1173 const typename U::value_type& at(
const K& key)
const {
1174 return at(key, hash_key(key));
1177 template<class K, class U = ValueSelect, typename std::enable_if<has_mapped_type<U>::value>::type* =
nullptr>
1178 const typename U::value_type& at(
const K& key, std::size_t hash)
const {
1179 using T =
typename U::value_type;
1181 const T* value = find_value_impl(key, hash, m_buckets.begin() + bucket_for_hash(hash));
1182 if(value ==
nullptr) {
1183 throw std::out_of_range(
"Couldn't find key.");
1191 template<class K, class U = ValueSelect, typename std::enable_if<has_mapped_type<U>::value>::type* =
nullptr>
1192 typename U::value_type& operator[](K&& key) {
1193 using T =
typename U::value_type;
1195 const std::size_t hash = hash_key(key);
1196 const std::size_t ibucket_for_hash = bucket_for_hash(hash);
1198 T* value = find_value_impl(key, hash, m_buckets.begin() + ibucket_for_hash);
1199 if(value !=
nullptr) {
1203 return insert_impl(std::make_pair(std::forward<K>(key), T()), hash, ibucket_for_hash).first.value();
1209 size_type count(
const K& key)
const {
1210 return count(key, hash_key(key));
1214 size_type count(
const K& key, std::size_t hash)
const {
1215 return count_impl(key, hash, m_buckets.cbegin() + bucket_for_hash(hash));
1220 iterator find(
const K& key) {
1221 return find(key, hash_key(key));
1225 iterator find(
const K& key, std::size_t hash) {
1226 return find_impl(key, hash, m_buckets.begin() + bucket_for_hash(hash));
1231 const_iterator find(
const K& key)
const {
1232 return find(key, hash_key(key));
1236 const_iterator find(
const K& key, std::size_t hash)
const {
1237 return find_impl(key, hash, m_buckets.begin() + bucket_for_hash(hash));
1242 std::pair<iterator, iterator> equal_range(
const K& key) {
1243 return equal_range(key, hash_key(key));
1247 std::pair<iterator, iterator> equal_range(
const K& key, std::size_t hash) {
1248 iterator it = find(key, hash);
1249 return std::make_pair(it, (it == end())?it:std::next(it));
1254 std::pair<const_iterator, const_iterator> equal_range(
const K& key)
const {
1255 return equal_range(key, hash_key(key));
1259 std::pair<const_iterator, const_iterator> equal_range(
const K& key, std::size_t hash)
const {
1260 const_iterator it = find(key, hash);
1261 return std::make_pair(it, (it == cend())?it:std::next(it));
1267 size_type bucket_count()
const {
1273 return m_buckets.size() - NeighborhoodSize + 1;
1276 size_type max_bucket_count()
const {
1277 const std::size_t max_bucket_count = std::min(GrowthPolicy::max_bucket_count(), m_buckets.max_size());
1278 return max_bucket_count - NeighborhoodSize + 1;
1285 float load_factor()
const {
1286 return float(m_nb_elements)/float(bucket_count());
1289 float max_load_factor()
const {
1290 return m_max_load_factor;
1293 void max_load_factor(
float ml) {
1294 m_max_load_factor = ml;
1295 m_load_threshold = size_type(
float(bucket_count())*m_max_load_factor);
1296 m_min_load_factor_rehash_threshold = size_type(bucket_count()*MIN_LOAD_FACTOR_FOR_REHASH);
1299 void rehash(size_type count) {
1300 count = std::max(count, size_type(std::ceil(
float(size())/max_load_factor())));
1304 void reserve(size_type count) {
1305 rehash(size_type(std::ceil(
float(count)/max_load_factor())));
1312 hasher hash_function()
const {
1313 return static_cast<Hash
>(*this);
1317 return static_cast<KeyEqual
>(*this);
1323 size_type overflow_size() const noexcept {
1324 return m_overflow_elements.size();
1327 template<class U = OverflowContainer, typename std::enable_if<has_key_compare<U>::value>::type* =
nullptr>
1328 typename U::key_compare key_comp()
const {
1329 return m_overflow_elements.key_comp();
1334 std::size_t hash_key(
const K& key)
const {
1335 return Hash::operator()(key);
1338 template<
class K1,
class K2>
1339 bool compare_keys(
const K1& key1,
const K2& key2)
const {
1340 return KeyEqual::operator()(key1, key2);
1343 std::size_t bucket_for_hash(std::size_t hash)
const {
1344 return GrowthPolicy::bucket_for_hash(hash);
1347 iterator mutable_iterator(const_iterator pos) {
1348 if(pos.m_buckets_iterator != pos.m_buckets_end_iterator) {
1350 auto it = m_buckets.begin() + std::distance(m_buckets.cbegin(), pos.m_buckets_iterator);
1351 return iterator(it, m_buckets.end(), m_overflow_elements.begin());
1355 auto it = mutable_overflow_iterator(pos.m_overflow_iterator);
1357 return iterator(m_buckets.end(), m_buckets.end(), it);
1362 static_assert(std::is_nothrow_move_constructible<value_type>::value ||
1363 std::is_copy_constructible<value_type>::value,
1364 "value_type must be either copy constructible or nothrow move constructible.");
1366 template<
typename U = value_type,
1367 typename std::enable_if<std::is_nothrow_move_constructible<U>::value>::type* =
nullptr>
1368 void rehash_impl(size_type count) {
1369 hopscotch_hash new_map = new_hopscotch_hash(count);
1371 if(!m_overflow_elements.empty()) {
1372 new_map.m_overflow_elements.swap(m_overflow_elements);
1373 new_map.m_nb_elements += new_map.m_overflow_elements.size();
1375 for(
const value_type& value : new_map.m_overflow_elements) {
1376 const std::size_t ibucket_for_hash = new_map.bucket_for_hash(new_map.hash_key(KeySelect()(value)));
1377 new_map.m_buckets[ibucket_for_hash].set_overflow(
true);
1382 for(
auto it_bucket = m_buckets.begin(); it_bucket != m_buckets.end(); ++it_bucket) {
1383 if(it_bucket->empty()) {
1387 const std::size_t hash = USE_STORED_HASH_ON_REHASH?
1388 it_bucket->truncated_bucket_hash():
1389 new_map.hash_key(KeySelect()(it_bucket->value()));
1390 const std::size_t ibucket_for_hash = new_map.bucket_for_hash(hash);
1392 new_map.insert_impl(std::move(it_bucket->value()), hash, ibucket_for_hash);
1395 erase_from_bucket(it_bucket, bucket_for_hash(hash));
1403 m_overflow_elements.swap(new_map.m_overflow_elements);
1405 for(
auto it_bucket = new_map.m_buckets.begin(); it_bucket != new_map.m_buckets.end(); ++it_bucket) {
1406 if(it_bucket->empty()) {
1410 const std::size_t hash = USE_STORED_HASH_ON_REHASH?
1411 it_bucket->truncated_bucket_hash():
1412 hash_key(KeySelect()(it_bucket->value()));
1413 const std::size_t ibucket_for_hash = bucket_for_hash(hash);
1417 insert_impl(std::move(it_bucket->value()), hash, ibucket_for_hash);
1423 new_map.swap(*
this);
1426 template<
typename U = value_type,
1427 typename std::enable_if<std::is_copy_constructible<U>::value &&
1428 !std::is_nothrow_move_constructible<U>::value>::type* =
nullptr>
1429 void rehash_impl(size_type count) {
1430 hopscotch_hash new_map = new_hopscotch_hash(count);
1432 for(
const hopscotch_bucket& bucket: m_buckets) {
1433 if(bucket.empty()) {
1437 const std::size_t hash = USE_STORED_HASH_ON_REHASH?
1438 bucket.truncated_bucket_hash():
1439 new_map.hash_key(KeySelect()(bucket.value()));
1440 const std::size_t ibucket_for_hash = new_map.bucket_for_hash(hash);
1442 new_map.insert_impl(bucket.value(), hash, ibucket_for_hash);
1445 for(
const value_type& value: m_overflow_elements) {
1446 const std::size_t hash = new_map.hash_key(KeySelect()(value));
1447 const std::size_t ibucket_for_hash = new_map.bucket_for_hash(hash);
1449 new_map.insert_impl(value, hash, ibucket_for_hash);
1452 new_map.swap(*
this);
1455#ifdef TSL_NO_RANGE_ERASE_WITH_CONST_ITERATOR
1456 iterator_overflow mutable_overflow_iterator(const_iterator_overflow it) {
1457 return std::next(m_overflow_elements.begin(), std::distance(m_overflow_elements.cbegin(), it));
1460 iterator_overflow mutable_overflow_iterator(const_iterator_overflow it) {
1461 return m_overflow_elements.erase(it, it);
1466 iterator_overflow erase_from_overflow(const_iterator_overflow pos, std::size_t ibucket_for_hash) {
1467#ifdef TSL_NO_RANGE_ERASE_WITH_CONST_ITERATOR
1468 auto it_next = m_overflow_elements.erase(mutable_overflow_iterator(pos));
1470 auto it_next = m_overflow_elements.erase(pos);
1476 tsl_assert(m_buckets[ibucket_for_hash].has_overflow());
1477 for(
const value_type& value: m_overflow_elements) {
1478 const std::size_t bucket_for_value = bucket_for_hash(hash_key(KeySelect()(value)));
1479 if(bucket_for_value == ibucket_for_hash) {
1484 m_buckets[ibucket_for_hash].set_overflow(
false);
1489 void erase_from_bucket(iterator_buckets pos, std::size_t ibucket_for_hash)
noexcept {
1490 const std::size_t ibucket_for_pos = std::distance(m_buckets.begin(), pos);
1491 tsl_assert(ibucket_for_pos >= ibucket_for_hash);
1493 m_buckets[ibucket_for_pos].remove_value();
1494 m_buckets[ibucket_for_hash].toggle_neighbor_presence(ibucket_for_pos - ibucket_for_hash);
1500 template<
class K,
class M>
1501 std::pair<iterator, bool> insert_or_assign_impl(K&& key, M&& obj) {
1502 const std::size_t hash = hash_key(key);
1503 const std::size_t ibucket_for_hash = bucket_for_hash(hash);
1506 auto it_find = find_impl(key, hash, m_buckets.begin() + ibucket_for_hash);
1507 if(it_find != end()) {
1508 it_find.value() = std::forward<M>(obj);
1509 return std::make_pair(it_find,
false);
1513 return insert_impl(value_type(std::forward<K>(key), std::forward<M>(obj)), hash, ibucket_for_hash);
1516 template<
typename P,
class... Args>
1517 std::pair<iterator, bool> try_emplace_impl(
P&& key, Args&&... args_value) {
1518 const std::size_t hash = hash_key(key);
1519 const std::size_t ibucket_for_hash = bucket_for_hash(hash);
1522 auto it_find = find_impl(key, hash, m_buckets.begin() + ibucket_for_hash);
1523 if(it_find != end()) {
1524 return std::make_pair(it_find,
false);
1528 return insert_impl(value_type(std::piecewise_construct,
1529 std::forward_as_tuple(std::forward<P>(key)),
1530 std::forward_as_tuple(std::forward<Args>(args_value)...)),
1531 hash, ibucket_for_hash);
1534 template<
typename P>
1535 std::pair<iterator, bool> insert_impl(
P&& value) {
1536 const std::size_t hash = hash_key(KeySelect()(value));
1537 const std::size_t ibucket_for_hash = bucket_for_hash(hash);
1540 auto it_find = find_impl(KeySelect()(value), hash, m_buckets.begin() + ibucket_for_hash);
1541 if(it_find != end()) {
1542 return std::make_pair(it_find,
false);
1546 return insert_impl(std::forward<P>(value), hash, ibucket_for_hash);
1549 template<
typename P>
1550 std::pair<iterator, bool> insert_impl(
P&& value, std::size_t hash, std::size_t ibucket_for_hash) {
1551 if((m_nb_elements - m_overflow_elements.size()) >= m_load_threshold) {
1552 rehash(GrowthPolicy::next_bucket_count());
1553 ibucket_for_hash = bucket_for_hash(hash);
1556 std::size_t ibucket_empty = find_empty_bucket(ibucket_for_hash);
1557 if(ibucket_empty < m_buckets.size()) {
1559 tsl_assert(ibucket_empty >= ibucket_for_hash);
1562 if(ibucket_empty - ibucket_for_hash < NeighborhoodSize) {
1563 auto it = insert_in_bucket(std::forward<P>(value), hash, ibucket_empty, ibucket_for_hash);
1564 return std::make_pair(iterator(it, m_buckets.end(), m_overflow_elements.begin()),
true);
1568 while(swap_empty_bucket_closer(ibucket_empty));
1572 if(size() < m_min_load_factor_rehash_threshold || !will_neighborhood_change_on_rehash(ibucket_for_hash)) {
1573 auto it_insert = m_overflow_elements.insert(m_overflow_elements.end(), std::forward<P>(value));
1574 m_buckets[ibucket_for_hash].set_overflow(
true);
1577 return std::make_pair(iterator(m_buckets.end(), m_buckets.end(), it_insert),
true);
1580 rehash(GrowthPolicy::next_bucket_count());
1582 ibucket_for_hash = bucket_for_hash(hash);
1583 return insert_impl(std::forward<P>(value), hash, ibucket_for_hash);
1590 bool will_neighborhood_change_on_rehash(
size_t ibucket_neighborhood_check)
const {
1591 std::size_t expand_bucket_count = GrowthPolicy::next_bucket_count();
1592 GrowthPolicy expand_growth_policy(expand_bucket_count);
1594 for(
size_t ibucket = ibucket_neighborhood_check;
1595 ibucket < m_buckets.size() && (ibucket - ibucket_neighborhood_check) < NeighborhoodSize;
1598 tsl_assert(!m_buckets[ibucket].empty());
1600 const size_t hash = USE_STORED_HASH_ON_REHASH?
1601 m_buckets[ibucket].truncated_bucket_hash():
1602 hash_key(KeySelect()(m_buckets[ibucket].value()));
1603 if(bucket_for_hash(hash) != expand_growth_policy.bucket_for_hash(hash)) {
1615 std::size_t find_empty_bucket(std::size_t ibucket_start)
const {
1616 const std::size_t limit = std::min(ibucket_start + MAX_PROBES_FOR_EMPTY_BUCKET, m_buckets.size());
1617 for(; ibucket_start < limit; ibucket_start++) {
1618 if(m_buckets[ibucket_start].empty()) {
1619 return ibucket_start;
1623 return m_buckets.size();
1631 template<
typename P>
1632 iterator_buckets insert_in_bucket(
P&& value, std::size_t hash,
1633 std::size_t ibucket_empty, std::size_t ibucket_for_hash)
1635 tsl_assert(ibucket_empty >= ibucket_for_hash );
1636 tsl_assert(m_buckets[ibucket_empty].empty());
1637 m_buckets[ibucket_empty].set_value_of_empty_bucket(std::forward<P>(value), hash);
1639 tsl_assert(!m_buckets[ibucket_for_hash].empty());
1640 m_buckets[ibucket_for_hash].toggle_neighbor_presence(ibucket_empty - ibucket_for_hash);
1643 return m_buckets.begin() + ibucket_empty;
1652 bool swap_empty_bucket_closer(std::size_t& ibucket_empty_in_out) {
1653 tsl_assert(ibucket_empty_in_out >= NeighborhoodSize);
1654 const std::size_t neighborhood_start = ibucket_empty_in_out - NeighborhoodSize + 1;
1656 for(std::size_t to_check = neighborhood_start; to_check < ibucket_empty_in_out; to_check++) {
1657 neighborhood_bitmap neighborhood_infos = m_buckets[to_check].neighborhood_infos();
1658 std::size_t to_swap = to_check;
1660 while(neighborhood_infos != 0 && to_swap < ibucket_empty_in_out) {
1661 if((neighborhood_infos & 1) == 1) {
1662 tsl_assert(m_buckets[ibucket_empty_in_out].empty());
1663 tsl_assert(!m_buckets[to_swap].empty());
1665 m_buckets[to_swap].swap_value_into_empty_bucket(m_buckets[ibucket_empty_in_out]);
1667 tsl_assert(!m_buckets[to_check].check_neighbor_presence(ibucket_empty_in_out - to_check));
1668 tsl_assert(m_buckets[to_check].check_neighbor_presence(to_swap - to_check));
1670 m_buckets[to_check].toggle_neighbor_presence(ibucket_empty_in_out - to_check);
1671 m_buckets[to_check].toggle_neighbor_presence(to_swap - to_check);
1674 ibucket_empty_in_out = to_swap;
1680 neighborhood_infos = neighborhood_bitmap(neighborhood_infos >> 1);
1689 template<class K, class U = ValueSelect, typename std::enable_if<has_mapped_type<U>::value>::type* =
nullptr>
1690 typename U::value_type* find_value_impl(
const K& key, std::size_t hash, iterator_buckets it_bucket) {
1691 return const_cast<typename U::value_type*
>(
1692 static_cast<const hopscotch_hash*
>(
this)->find_value_impl(key, hash, it_bucket));
1700 template<class K, class U = ValueSelect, typename std::enable_if<has_mapped_type<U>::value>::type* =
nullptr>
1701 const typename U::value_type* find_value_impl(
const K& key, std::size_t hash,
1702 const_iterator_buckets it_bucket)
const
1704 auto it_find = find_in_buckets(key, hash, it_bucket);
1705 if(it_find != m_buckets.cend()) {
1706 return std::addressof(ValueSelect()(it_find->value()));
1709 if(it_bucket->has_overflow()) {
1710 auto it_overflow = find_in_overflow(key);
1711 if(it_overflow != m_overflow_elements.end()) {
1712 return std::addressof(ValueSelect()(*it_overflow));
1720 size_type count_impl(
const K& key, std::size_t hash, const_iterator_buckets it_bucket)
const {
1721 if(find_in_buckets(key, hash, it_bucket) != m_buckets.cend()) {
1724 else if(it_bucket->has_overflow() && find_in_overflow(key) != m_overflow_elements.cend()) {
1733 iterator find_impl(
const K& key, std::size_t hash, iterator_buckets it_bucket) {
1734 auto it = find_in_buckets(key, hash, it_bucket);
1735 if(it != m_buckets.end()) {
1736 return iterator(it, m_buckets.end(), m_overflow_elements.begin());
1739 if(!it_bucket->has_overflow()) {
1743 return iterator(m_buckets.end(), m_buckets.end(), find_in_overflow(key));
1747 const_iterator find_impl(
const K& key, std::size_t hash, const_iterator_buckets it_bucket)
const {
1748 auto it = find_in_buckets(key, hash, it_bucket);
1749 if(it != m_buckets.cend()) {
1750 return const_iterator(it, m_buckets.cend(), m_overflow_elements.cbegin());
1753 if(!it_bucket->has_overflow()) {
1758 return const_iterator(m_buckets.cend(), m_buckets.cend(), find_in_overflow(key));
1762 iterator_buckets find_in_buckets(
const K& key, std::size_t hash, iterator_buckets it_bucket) {
1763 auto it_find =
static_cast<const hopscotch_hash*
>(
this)->find_in_buckets(key, hash, it_bucket);
1764 return m_buckets.begin() + std::distance(m_buckets.cbegin(), it_find);
1769 const_iterator_buckets find_in_buckets(
const K& key, std::size_t hash, const_iterator_buckets it_bucket)
const {
1776 neighborhood_bitmap neighborhood_infos = it_bucket->neighborhood_infos();
1777 while(neighborhood_infos != 0) {
1778 if((neighborhood_infos & 1) == 1) {
1782 if((!StoreHash || it_bucket->bucket_hash_equal(hash)) &&
1783 compare_keys(KeySelect()(it_bucket->value()), key))
1790 neighborhood_infos = neighborhood_bitmap(neighborhood_infos >> 1);
1793 return m_buckets.end();
1800 template<class K, class U = OverflowContainer, typename std::enable_if<!has_key_compare<U>::value>::type* =
nullptr>
1801 iterator_overflow find_in_overflow(
const K& key) {
1802 return std::find_if(m_overflow_elements.begin(), m_overflow_elements.end(),
1803 [&](
const value_type& value) {
1804 return compare_keys(key, KeySelect()(value));
1808 template<class K, class U = OverflowContainer, typename std::enable_if<!has_key_compare<U>::value>::type* =
nullptr>
1809 const_iterator_overflow find_in_overflow(
const K& key)
const {
1810 return std::find_if(m_overflow_elements.cbegin(), m_overflow_elements.cend(),
1811 [&](
const value_type& value) {
1812 return compare_keys(key, KeySelect()(value));
1816 template<class K, class U = OverflowContainer, typename std::enable_if<has_key_compare<U>::value>::type* =
nullptr>
1817 iterator_overflow find_in_overflow(
const K& key) {
1818 return m_overflow_elements.find(key);
1821 template<class K, class U = OverflowContainer, typename std::enable_if<has_key_compare<U>::value>::type* =
nullptr>
1822 const_iterator_overflow find_in_overflow(
const K& key)
const {
1823 return m_overflow_elements.find(key);
1826 template<class U = OverflowContainer, typename std::enable_if<!has_key_compare<U>::value>::type* =
nullptr>
1827 hopscotch_hash new_hopscotch_hash(size_type bucket_count) {
1828 return hopscotch_hash(bucket_count,
static_cast<Hash&
>(*
this),
static_cast<KeyEqual&
>(*
this),
1829 get_allocator(), m_max_load_factor);
1832 template<class U = OverflowContainer, typename std::enable_if<has_key_compare<U>::value>::type* =
nullptr>
1833 hopscotch_hash new_hopscotch_hash(size_type bucket_count) {
1834 return hopscotch_hash(bucket_count,
static_cast<Hash&
>(*
this),
static_cast<KeyEqual&
>(*
this),
1835 get_allocator(), m_max_load_factor, m_overflow_elements.key_comp());
1839 static const size_type DEFAULT_INIT_BUCKETS_SIZE = 16;
1840 static constexpr float DEFAULT_MAX_LOAD_FACTOR = 0.9f;
1843 static const std::size_t MAX_PROBES_FOR_EMPTY_BUCKET = 12*NeighborhoodSize;
1844 static constexpr float MIN_LOAD_FACTOR_FOR_REHASH = 0.1f;
1846 static const bool USE_STORED_HASH_ON_REHASH =
1847 StoreHash && std::is_same<GrowthPolicy, tsl::power_of_two_growth_policy>::value;
1850 buckets_container_type m_buckets;
1851 overflow_container_type m_overflow_elements;
1853 size_type m_nb_elements;
1855 float m_max_load_factor;
1856 size_type m_load_threshold;
1857 size_type m_min_load_factor_rehash_threshold;
Test structure used for several test.
std::size_t max_bucket_count() const
std::size_t bucket_for_hash(std::size_t hash) const
power_of_two_growth_policy(std::size_t &min_bucket_count_in_out)
std::size_t next_bucket_count() const
Structure required for the Sph Harmonic amplitude dictionary arguments.