24 #ifndef TSL_ROBIN_HASH_H 25 #define TSL_ROBIN_HASH_H 39 #include <type_traits> 54 template<
typename T,
typename =
void>
55 struct has_is_transparent: std::false_type {
59 struct has_is_transparent<T,
typename make_void<
typename T::is_transparent>::type>: std::true_type {
63 struct is_power_of_two_policy: std::false_type {
66 template<std::size_t GrowthFactor>
72 const T&
clamp(
const T& v,
const T& lo,
const T& hi) {
73 return std::min(hi, std::max(lo, v));
77 using truncated_hash_type = std::uint_least32_t;
82 template<
bool StoreHash>
83 class bucket_entry_hash {
85 bool bucket_hash_equal(std::size_t )
const noexcept {
89 truncated_hash_type truncated_hash()
const noexcept {
94 void set_hash(truncated_hash_type )
noexcept {
99 class bucket_entry_hash<
true> {
101 bool bucket_hash_equal(std::size_t hash)
const noexcept {
102 return m_hash == truncated_hash_type(hash);
105 truncated_hash_type truncated_hash()
const noexcept {
110 void set_hash(truncated_hash_type hash)
noexcept {
111 m_hash = truncated_hash_type(hash);
115 truncated_hash_type m_hash;
135 template<
typename ValueType,
bool StoreHash>
136 class bucket_entry:
public bucket_entry_hash<StoreHash> {
137 using bucket_hash = bucket_entry_hash<StoreHash>;
140 using value_type = ValueType;
141 using distance_type = std::int_least16_t;
144 bucket_entry()
noexcept: bucket_hash(), m_dist_from_ideal_bucket(EMPTY_MARKER_DIST_FROM_IDEAL_BUCKET),
150 bucket_entry(
bool last_bucket)
noexcept: bucket_hash(), m_dist_from_ideal_bucket(EMPTY_MARKER_DIST_FROM_IDEAL_BUCKET),
151 m_last_bucket(last_bucket)
156 bucket_entry(
const bucket_entry& other)
noexcept(std::is_nothrow_copy_constructible<value_type>::value):
158 m_dist_from_ideal_bucket(EMPTY_MARKER_DIST_FROM_IDEAL_BUCKET),
159 m_last_bucket(other.m_last_bucket)
162 ::
new (
static_cast<
void*>(std::addressof(m_value))) value_type(other.value());
163 m_dist_from_ideal_bucket = other.m_dist_from_ideal_bucket;
171 bucket_entry(bucket_entry&& other)
noexcept(std::is_nothrow_move_constructible<value_type>::value):
172 bucket_hash(std::move(other)),
173 m_dist_from_ideal_bucket(EMPTY_MARKER_DIST_FROM_IDEAL_BUCKET),
174 m_last_bucket(other.m_last_bucket)
177 ::
new (
static_cast<
void*>(std::addressof(m_value))) value_type(std::move(other.value()));
178 m_dist_from_ideal_bucket = other.m_dist_from_ideal_bucket;
182 bucket_entry& operator=(
const bucket_entry& other)
183 noexcept(std::is_nothrow_copy_constructible<value_type>::value)
188 bucket_hash::operator=(other);
190 ::
new (
static_cast<
void*>(std::addressof(m_value))) value_type(other.value());
193 m_dist_from_ideal_bucket = other.m_dist_from_ideal_bucket;
194 m_last_bucket = other.m_last_bucket;
200 bucket_entry& operator=(bucket_entry&& ) =
delete;
202 ~bucket_entry()
noexcept {
206 void clear()
noexcept {
209 m_dist_from_ideal_bucket = EMPTY_MARKER_DIST_FROM_IDEAL_BUCKET;
213 bool empty()
const noexcept {
214 return m_dist_from_ideal_bucket == EMPTY_MARKER_DIST_FROM_IDEAL_BUCKET;
217 value_type& value()
noexcept {
219 return *
reinterpret_cast<value_type*>(std::addressof(m_value));
222 const value_type& value()
const noexcept {
224 return *
reinterpret_cast<
const value_type*>(std::addressof(m_value));
227 distance_type dist_from_ideal_bucket()
const noexcept {
228 return m_dist_from_ideal_bucket;
231 bool last_bucket()
const noexcept {
232 return m_last_bucket;
235 void set_as_last_bucket()
noexcept {
236 m_last_bucket =
true;
239 template<
typename... Args>
240 void set_value_of_empty_bucket(distance_type dist_from_ideal_bucket,
241 truncated_hash_type hash, Args&&... value_type_args)
246 ::
new (
static_cast<
void*>(std::addressof(m_value))) value_type(std::forward<Args>(value_type_args)...);
247 this->set_hash(hash);
248 m_dist_from_ideal_bucket = dist_from_ideal_bucket;
253 void swap_with_value_in_bucket(distance_type& dist_from_ideal_bucket,
254 truncated_hash_type& hash, value_type& value)
259 swap(value,
this->value());
260 swap(dist_from_ideal_bucket, m_dist_from_ideal_bucket);
265 const truncated_hash_type tmp_hash =
this->truncated_hash();
266 this->set_hash(hash);
271 static truncated_hash_type truncate_hash(std::size_t hash)
noexcept {
272 return truncated_hash_type(hash);
276 void destroy_value()
noexcept {
278 value().~value_type();
282 using storage =
typename std::aligned_storage<
sizeof(value_type),
alignof(value_type)>::type;
284 static const distance_type EMPTY_MARKER_DIST_FROM_IDEAL_BUCKET = -1;
286 distance_type m_dist_from_ideal_bucket;
309 template<
class ValueType,
317 class robin_hash:
private Hash,
private KeyEqual,
private GrowthPolicy {
320 using has_mapped_type =
typename std::integral_constant<
bool, !std::is_same<U,
void>::value>;
322 static_assert(
noexcept(std::declval<GrowthPolicy>().bucket_for_hash(std::size_t(0))),
"GrowthPolicy::bucket_for_hash must be noexcept.");
323 static_assert(
noexcept(std::declval<GrowthPolicy>().clear()),
"GrowthPolicy::clear must be noexcept.");
326 template<
bool IsConst>
329 using key_type =
typename KeySelect::key_type;
330 using value_type = ValueType;
331 using size_type = std::size_t;
332 using difference_type = std::ptrdiff_t;
334 using key_equal = KeyEqual;
335 using allocator_type = Allocator;
336 using reference = value_type&;
337 using const_reference =
const value_type&;
338 using pointer = value_type*;
339 using const_pointer =
const value_type*;
349 static constexpr bool STORE_HASH = StoreHash ||
354 (
sizeof(std::size_t) ==
sizeof(truncated_hash_type) ||
355 is_power_of_two_policy<GrowthPolicy>::value)
358 (!std::is_arithmetic<key_type>::value ||
359 !std::is_same<Hash, std::hash<key_type>>::value)
366 static constexpr bool USE_STORED_HASH_ON_LOOKUP = StoreHash;
374 static bool USE_STORED_HASH_ON_REHASH(size_type bucket_count) {
376 if(STORE_HASH &&
sizeof(std::size_t) ==
sizeof(truncated_hash_type)) {
379 else if(STORE_HASH && is_power_of_two_policy<GrowthPolicy>::value) {
381 return (bucket_count - 1) <= std::numeric_limits<truncated_hash_type>::max();
389 using distance_type =
typename bucket_entry::distance_type;
391 using buckets_allocator =
typename std::allocator_traits<allocator_type>::
template rebind_alloc<bucket_entry>;
392 using buckets_container_type = std::vector<bucket_entry, buckets_allocator>;
406 template<
bool IsConst>
411 using bucket_entry_ptr =
typename std::conditional<IsConst,
413 bucket_entry*>::type;
416 robin_iterator(bucket_entry_ptr bucket)
noexcept: m_bucket(bucket) {
420 using iterator_category = std::forward_iterator_tag;
422 using difference_type = std::ptrdiff_t;
423 using reference = value_type&;
424 using pointer = value_type*;
431 template<
bool TIsConst = IsConst,
typename std::enable_if<TIsConst>::type* =
nullptr>
441 return KeySelect()(m_bucket->value());
444 template<
class U = ValueSelect,
typename std::enable_if<has_mapped_type<U>::value && IsConst>::type* =
nullptr>
446 return U()(m_bucket->value());
449 template<
class U = ValueSelect,
typename std::enable_if<has_mapped_type<U>::value && !IsConst>::type* =
nullptr>
451 return U()(m_bucket->value());
455 return m_bucket->value();
459 return std::addressof(m_bucket->value());
464 if(m_bucket->last_bucket()) {
470 if(!m_bucket->empty()) {
484 return lhs.m_bucket == rhs.m_bucket;
488 return !(lhs == rhs);
492 bucket_entry_ptr m_bucket;
497 #if defined(__cplusplus
) && __cplusplus
>= 201402L
511 "The map exceeds its maximum bucket count.");
542 const KeyEqual& equal,
543 const Allocator& alloc,
548 GrowthPolicy(bucket_count),
549 m_buckets_data(alloc),
550 m_buckets(static_empty_bucket_ptr()),
551 m_bucket_count(bucket_count),
553 m_grow_on_next_insert(
false),
554 m_try_skrink_on_next_insert(
false)
560 if(m_bucket_count > 0) {
561 m_buckets_data.resize(m_bucket_count);
562 m_buckets = m_buckets_data.data();
565 m_buckets_data.back().set_as_last_bucket();
568 this->min_load_factor(min_load_factor);
569 this->max_load_factor(max_load_factor);
576 m_buckets_data(other.m_buckets_data),
577 m_buckets(m_buckets_data.empty()?static_empty_bucket_ptr():m_buckets_data.data()),
578 m_bucket_count(other.m_bucket_count),
579 m_nb_elements(other.m_nb_elements),
580 m_load_threshold(other.m_load_threshold),
581 m_max_load_factor(other.m_max_load_factor),
582 m_grow_on_next_insert(other.m_grow_on_next_insert),
583 m_min_load_factor(other.m_min_load_factor),
584 m_try_skrink_on_next_insert(other.m_try_skrink_on_next_insert)
592 : Hash(std::move(
static_cast<Hash&>(other))),
593 KeyEqual(std::move(
static_cast<KeyEqual&>(other))),
594 GrowthPolicy(std::move(
static_cast<GrowthPolicy&>(other))),
595 m_buckets_data(std::move(other.m_buckets_data)),
596 m_buckets(m_buckets_data.empty()?static_empty_bucket_ptr():m_buckets_data.data()),
597 m_bucket_count(other.m_bucket_count),
598 m_nb_elements(other.m_nb_elements),
599 m_load_threshold(other.m_load_threshold),
600 m_max_load_factor(other.m_max_load_factor),
601 m_grow_on_next_insert(other.m_grow_on_next_insert),
602 m_min_load_factor(other.m_min_load_factor),
603 m_try_skrink_on_next_insert(other.m_try_skrink_on_next_insert)
605 other.GrowthPolicy::clear();
606 other.m_buckets_data.clear();
607 other.m_buckets = static_empty_bucket_ptr();
608 other.m_bucket_count = 0;
609 other.m_nb_elements = 0;
610 other.m_load_threshold = 0;
611 other.m_grow_on_next_insert =
false;
612 other.m_try_skrink_on_next_insert =
false;
617 Hash::operator=(other);
618 KeyEqual::operator=(other);
619 GrowthPolicy::operator=(other);
621 m_buckets_data = other.m_buckets_data;
622 m_buckets = m_buckets_data.empty()?static_empty_bucket_ptr():
623 m_buckets_data.data();
624 m_bucket_count = other.m_bucket_count;
625 m_nb_elements = other.m_nb_elements;
627 m_load_threshold = other.m_load_threshold;
628 m_max_load_factor = other.m_max_load_factor;
629 m_grow_on_next_insert = other.m_grow_on_next_insert;
631 m_min_load_factor = other.m_min_load_factor;
632 m_try_skrink_on_next_insert = other.m_try_skrink_on_next_insert;
646 return m_buckets_data.get_allocator();
655 while(i < m_bucket_count && m_buckets[i].empty()) {
659 return iterator(m_buckets + i);
662 const_iterator
begin()
const noexcept {
668 while(i < m_bucket_count && m_buckets[i].empty()) {
672 return const_iterator(m_buckets + i);
676 return iterator(m_buckets + m_bucket_count);
679 const_iterator
end()
const noexcept {
683 const_iterator
cend()
const noexcept {
684 return const_iterator(m_buckets + m_bucket_count);
692 return m_nb_elements == 0;
695 size_type
size()
const noexcept {
696 return m_nb_elements;
700 return m_buckets_data.max_size();
707 for(
auto& bucket: m_buckets_data) {
712 m_grow_on_next_insert =
false;
718 std::pair<iterator,
bool>
insert(P&& value) {
719 return insert_impl(KeySelect()(value), std::forward<P>(value));
724 if(hint !=
cend() && compare_keys(KeySelect()(*hint), KeySelect()(value))) {
728 return insert(std::forward<P>(value)).first;
731 template<
class InputIt>
732 void insert(InputIt first, InputIt last) {
733 if(std::is_base_of<std::forward_iterator_tag,
734 typename std::iterator_traits<InputIt>::iterator_category>::value)
736 const auto nb_elements_insert = std::distance(first, last);
737 const size_type nb_free_buckets = m_load_threshold -
size();
740 if(nb_elements_insert > 0 && nb_free_buckets < size_type(nb_elements_insert)) {
745 for(; first != last; ++first) {
752 template<
class K,
class M>
754 auto it = try_emplace(std::forward<K>(key), std::forward<M>(obj));
756 it.first.value() = std::forward<M>(obj);
762 template<
class K,
class M>
764 if(hint !=
cend() && compare_keys(KeySelect()(*hint), key)) {
766 it.value() = std::forward<M>(obj);
771 return insert_or_assign(std::forward<K>(key), std::forward<M>(obj)).first;
775 template<
class... Args>
776 std::pair<iterator,
bool>
emplace(Args&&... args) {
777 return insert(value_type(std::forward<Args>(args)...));
780 template<
class... Args>
782 return insert_hint(hint, value_type(std::forward<Args>(args)...));
787 template<
class K,
class... Args>
789 return insert_impl(key, std::piecewise_construct,
790 std::forward_as_tuple(std::forward<K>(key)),
791 std::forward_as_tuple(std::forward<Args>(args)...));
794 template<
class K,
class... Args>
796 if(hint !=
cend() && compare_keys(KeySelect()(*hint), key)) {
800 return try_emplace(std::forward<K>(key), std::forward<Args>(args)...).first;
808 erase_from_bucket(pos);
814 if(pos.m_bucket->empty()) {
818 m_try_skrink_on_next_insert =
true;
823 iterator
erase(const_iterator pos) {
827 iterator
erase(const_iterator first, const_iterator last) {
834 for(
auto it = first_mutable.m_bucket; it != last_mutable.m_bucket; ++it) {
841 if(last_mutable == end()) {
850 std::size_t icloser_bucket =
static_cast<std::size_t>(first_mutable.m_bucket - m_buckets);
851 std::size_t ito_move_closer_value =
static_cast<std::size_t>(last_mutable.m_bucket - m_buckets);
854 const std::size_t ireturn_bucket = ito_move_closer_value -
855 std::min(ito_move_closer_value - icloser_bucket,
856 std::size_t(m_buckets[ito_move_closer_value].dist_from_ideal_bucket()));
858 while(ito_move_closer_value < m_bucket_count && m_buckets[ito_move_closer_value].dist_from_ideal_bucket() > 0) {
859 icloser_bucket = ito_move_closer_value -
860 std::min(ito_move_closer_value - icloser_bucket,
861 std::size_t(m_buckets[ito_move_closer_value].dist_from_ideal_bucket()));
865 const distance_type new_distance = distance_type(m_buckets[ito_move_closer_value].dist_from_ideal_bucket() -
866 (ito_move_closer_value - icloser_bucket));
867 m_buckets[icloser_bucket].set_value_of_empty_bucket(new_distance,
868 m_buckets[ito_move_closer_value].truncated_hash(),
869 std::move(m_buckets[ito_move_closer_value].value()));
870 m_buckets[ito_move_closer_value].clear();
874 ++ito_move_closer_value;
877 m_try_skrink_on_next_insert =
true;
879 return iterator(m_buckets + ireturn_bucket);
885 return erase(key, hash_key(key));
889 size_type
erase(
const K& key, std::size_t hash) {
890 auto it = find(key, hash);
892 erase_from_bucket(it);
893 m_try_skrink_on_next_insert =
true;
909 swap(
static_cast<Hash&>(*
this),
static_cast<Hash&>(other));
910 swap(
static_cast<KeyEqual&>(*
this),
static_cast<KeyEqual&>(other));
911 swap(
static_cast<GrowthPolicy&>(*
this),
static_cast<GrowthPolicy&>(other));
912 swap(m_buckets_data, other.m_buckets_data);
913 swap(m_buckets, other.m_buckets);
914 swap(m_bucket_count, other.m_bucket_count);
915 swap(m_nb_elements, other.m_nb_elements);
916 swap(m_load_threshold, other.m_load_threshold);
917 swap(m_max_load_factor, other.m_max_load_factor);
918 swap(m_grow_on_next_insert, other.m_grow_on_next_insert);
919 swap(m_min_load_factor, other.m_min_load_factor);
920 swap(m_try_skrink_on_next_insert, other.m_try_skrink_on_next_insert);
927 template<
class K,
class U = ValueSelect,
typename std::enable_if<has_mapped_type<U>::value>::type* =
nullptr>
929 return at(key, hash_key(key));
932 template<
class K,
class U = ValueSelect,
typename std::enable_if<has_mapped_type<U>::value>::type* =
nullptr>
934 return const_cast<
typename U::value_type&>(
static_cast<
const robin_hash*>(
this)->at(key, hash));
938 template<
class K,
class U = ValueSelect,
typename std::enable_if<has_mapped_type<U>::value>::type* =
nullptr>
940 return at(key, hash_key(key));
943 template<
class K,
class U = ValueSelect,
typename std::enable_if<has_mapped_type<U>::value>::type* =
nullptr>
944 const typename U::
value_type&
at(
const K& key, std::size_t hash)
const {
945 auto it = find(key, hash);
954 template<
class K,
class U = ValueSelect,
typename std::enable_if<has_mapped_type<U>::value>::type* =
nullptr>
956 return try_emplace(std::forward<K>(key)).first.value();
961 size_type
count(
const K& key)
const {
962 return count(key, hash_key(key));
966 size_type
count(
const K& key, std::size_t hash)
const {
967 if(find(key, hash) !=
cend()) {
978 return find_impl(key, hash_key(key));
982 iterator
find(
const K& key, std::size_t hash) {
983 return find_impl(key, hash);
988 const_iterator
find(
const K& key)
const {
989 return find_impl(key, hash_key(key));
993 const_iterator
find(
const K& key, std::size_t hash)
const {
994 return find_impl(key, hash);
1000 return equal_range(key, hash_key(key));
1004 std::pair<iterator, iterator>
equal_range(
const K& key, std::size_t hash) {
1005 iterator it = find(key, hash);
1006 return std::make_pair(it, (it == end())?it:std::next(it));
1011 std::pair<const_iterator, const_iterator>
equal_range(
const K& key)
const {
1012 return equal_range(key, hash_key(key));
1016 std::pair<const_iterator, const_iterator>
equal_range(
const K& key, std::size_t hash)
const {
1017 const_iterator it = find(key, hash);
1018 return std::make_pair(it, (it ==
cend())?it:std::next(it));
1025 return m_bucket_count;
1029 return std::min(GrowthPolicy::max_bucket_count(), m_buckets_data.max_size());
1044 return m_min_load_factor;
1048 return m_max_load_factor;
1059 m_load_threshold = size_type(
float(
bucket_count())*m_max_load_factor);
1063 count = std::max(count, size_type(std::ceil(
float(
size())/max_load_factor())));
1068 rehash(size_type(std::ceil(
float(count)/max_load_factor()))
);
1075 return static_cast<
const Hash&>(*
this);
1079 return static_cast<
const KeyEqual&>(*
this);
1087 return iterator(
const_cast<bucket_entry*>(pos.m_bucket));
1092 std::size_t hash_key(
const K& key)
const {
1093 return Hash::operator()(key);
1096 template<
class K1,
class K2>
1097 bool compare_keys(
const K1& key1,
const K2& key2)
const {
1098 return KeyEqual::operator()(key1, key2);
1101 std::size_t bucket_for_hash(std::size_t hash)
const {
1102 const std::size_t bucket = GrowthPolicy::bucket_for_hash(hash);
1103 tsl_rh_assert(bucket < m_bucket_count || (bucket == 0 && m_bucket_count == 0));
1108 template<
class U = GrowthPolicy,
typename std::enable_if<is_power_of_two_policy<U>::value>::type* =
nullptr>
1109 std::size_t next_bucket(std::size_t index)
const noexcept {
1112 return (index + 1) &
this->m_mask;
1115 template<
class U = GrowthPolicy,
typename std::enable_if<!is_power_of_two_policy<U>::value>::type* =
nullptr>
1116 std::size_t next_bucket(std::size_t index)
const noexcept {
1126 iterator find_impl(
const K& key, std::size_t hash) {
1131 const_iterator find_impl(
const K& key, std::size_t hash)
const {
1132 std::size_t ibucket = bucket_for_hash(hash);
1133 distance_type dist_from_ideal_bucket = 0;
1135 while(dist_from_ideal_bucket <= m_buckets[ibucket].dist_from_ideal_bucket()) {
1136 if(
TSL_RH_LIKELY((!USE_STORED_HASH_ON_LOOKUP || m_buckets[ibucket].bucket_hash_equal(hash)) &&
1137 compare_keys(KeySelect()(m_buckets[ibucket].value()), key)))
1139 return const_iterator(m_buckets + ibucket);
1142 ibucket = next_bucket(ibucket);
1143 dist_from_ideal_bucket++;
1149 void erase_from_bucket(iterator pos) {
1150 pos.m_bucket->clear();
1159 std::size_t previous_ibucket =
static_cast<std::size_t>(pos.m_bucket - m_buckets);
1160 std::size_t ibucket = next_bucket(previous_ibucket);
1162 while(m_buckets[ibucket].dist_from_ideal_bucket() > 0) {
1165 const distance_type new_distance = distance_type(m_buckets[ibucket].dist_from_ideal_bucket() - 1);
1166 m_buckets[previous_ibucket].set_value_of_empty_bucket(new_distance, m_buckets[ibucket].truncated_hash(),
1167 std::move(m_buckets[ibucket].value()));
1168 m_buckets[ibucket].clear();
1170 previous_ibucket = ibucket;
1171 ibucket = next_bucket(ibucket);
1175 template<
class K,
class... Args>
1176 std::pair<iterator,
bool> insert_impl(
const K& key, Args&&... value_type_args) {
1177 const std::size_t hash = hash_key(key);
1179 std::size_t ibucket = bucket_for_hash(hash);
1180 distance_type dist_from_ideal_bucket = 0;
1182 while(dist_from_ideal_bucket <= m_buckets[ibucket].dist_from_ideal_bucket()) {
1183 if((!USE_STORED_HASH_ON_LOOKUP || m_buckets[ibucket].bucket_hash_equal(hash)) &&
1184 compare_keys(KeySelect()(m_buckets[ibucket].value()), key))
1186 return std::make_pair(iterator(m_buckets + ibucket),
false);
1189 ibucket = next_bucket(ibucket);
1190 dist_from_ideal_bucket++;
1193 if(rehash_on_extreme_load()) {
1194 ibucket = bucket_for_hash(hash);
1195 dist_from_ideal_bucket = 0;
1197 while(dist_from_ideal_bucket <= m_buckets[ibucket].dist_from_ideal_bucket()) {
1198 ibucket = next_bucket(ibucket);
1199 dist_from_ideal_bucket++;
1204 if(m_buckets[ibucket].empty()) {
1205 m_buckets[ibucket].set_value_of_empty_bucket(dist_from_ideal_bucket, bucket_entry::truncate_hash(hash),
1206 std::forward<Args>(value_type_args)...);
1209 insert_value(ibucket, dist_from_ideal_bucket, bucket_entry::truncate_hash(hash),
1210 std::forward<Args>(value_type_args)...);
1219 return std::make_pair(iterator(m_buckets + ibucket),
true);
1223 template<
class... Args>
1224 void insert_value(std::size_t ibucket, distance_type dist_from_ideal_bucket,
1225 truncated_hash_type hash, Args&&... value_type_args)
1227 value_type value(std::forward<Args>(value_type_args)...);
1228 insert_value_impl(ibucket, dist_from_ideal_bucket, hash, value);
1231 void insert_value(std::size_t ibucket, distance_type dist_from_ideal_bucket,
1232 truncated_hash_type hash, value_type&& value)
1234 insert_value_impl(ibucket, dist_from_ideal_bucket, hash, value);
1244 void insert_value_impl(std::size_t ibucket, distance_type dist_from_ideal_bucket,
1245 truncated_hash_type hash, value_type& value)
1247 m_buckets[ibucket].swap_with_value_in_bucket(dist_from_ideal_bucket, hash, value);
1248 ibucket = next_bucket(ibucket);
1249 dist_from_ideal_bucket++;
1251 while(!m_buckets[ibucket].empty()) {
1252 if(dist_from_ideal_bucket > m_buckets[ibucket].dist_from_ideal_bucket()) {
1253 if(dist_from_ideal_bucket >= REHASH_ON_HIGH_NB_PROBES__NPROBES &&
1260 m_grow_on_next_insert =
true;
1263 m_buckets[ibucket].swap_with_value_in_bucket(dist_from_ideal_bucket, hash, value);
1266 ibucket = next_bucket(ibucket);
1267 dist_from_ideal_bucket++;
1270 m_buckets[ibucket].set_value_of_empty_bucket(dist_from_ideal_bucket, hash, std::move(value));
1274 void rehash_impl(size_type count) {
1275 robin_hash new_table(count,
static_cast<Hash&>(*
this),
static_cast<KeyEqual&>(*
this),
1278 const bool use_stored_hash = USE_STORED_HASH_ON_REHASH(new_table.bucket_count());
1279 for(
auto& bucket: m_buckets_data) {
1280 if(bucket.empty()) {
1284 const std::size_t hash = use_stored_hash?bucket.truncated_hash():
1285 new_table.hash_key(KeySelect()(bucket.value()));
1287 new_table.insert_value_on_rehash(new_table.bucket_for_hash(hash), 0,
1288 bucket_entry::truncate_hash(hash), std::move(bucket.value()));
1291 new_table.m_nb_elements = m_nb_elements;
1292 new_table.swap(*
this);
1295 void insert_value_on_rehash(std::size_t ibucket, distance_type dist_from_ideal_bucket,
1296 truncated_hash_type hash, value_type&& value)
1299 if(dist_from_ideal_bucket > m_buckets[ibucket].dist_from_ideal_bucket()) {
1300 if(m_buckets[ibucket].empty()) {
1301 m_buckets[ibucket].set_value_of_empty_bucket(dist_from_ideal_bucket, hash, std::move(value));
1305 m_buckets[ibucket].swap_with_value_in_bucket(dist_from_ideal_bucket, hash, value);
1309 dist_from_ideal_bucket++;
1310 ibucket = next_bucket(ibucket);
1323 bool rehash_on_extreme_load() {
1324 if(m_grow_on_next_insert ||
size() >= m_load_threshold) {
1325 rehash_impl(GrowthPolicy::next_bucket_count());
1326 m_grow_on_next_insert =
false;
1331 if(m_try_skrink_on_next_insert) {
1332 m_try_skrink_on_next_insert =
false;
1333 if(m_min_load_factor != 0.0f &&
load_factor() < m_min_load_factor) {
1356 "MINIMUM_MAX_LOAD_FACTOR should be < MAXIMUM_MAX_LOAD_FACTOR");
1358 "MINIMUM_MIN_LOAD_FACTOR should be < MAXIMUM_MIN_LOAD_FACTOR");
1360 "MAXIMUM_MIN_LOAD_FACTOR should be < MINIMUM_MAX_LOAD_FACTOR");
1363 static const distance_type REHASH_ON_HIGH_NB_PROBES__NPROBES = 128;
1364 static constexpr float REHASH_ON_HIGH_NB_PROBES__MIN_LOAD_FACTOR = 0.15f;
1370 bucket_entry* static_empty_bucket_ptr() {
1371 static bucket_entry empty_bucket(
true);
1372 return &empty_bucket;
1376 buckets_container_type m_buckets_data;
1386 bucket_entry* m_buckets;
1391 size_type m_bucket_count;
1393 size_type m_nb_elements;
1395 size_type m_load_threshold;
1396 float m_max_load_factor;
1398 bool m_grow_on_next_insert;
1400 float m_min_load_factor;
1408 bool m_try_skrink_on_next_insert;
std::pair< iterator, iterator > equal_range(const K &key)
Definition: robin_hash.h:999
iterator end() noexcept
Definition: robin_hash.h:675
robin_hash & operator=(robin_hash &&other)
Definition: robin_hash.h:638
friend bool operator!=(const robin_iterator &lhs, const robin_iterator &rhs)
Definition: robin_hash.h:487
Definition: robin_growth_policy.h:69
friend bool operator==(const robin_iterator &lhs, const robin_iterator &rhs)
Definition: robin_hash.h:483
size_type bucket_count() const
Definition: robin_hash.h:1024
size_type max_bucket_count() const
Definition: robin_hash.h:1028
std::pair< iterator, bool > try_emplace(K &&key, Args &&... args)
Definition: robin_hash.h:788
const_iterator find(const K &key) const
Definition: robin_hash.h:988
#define TSL_RH_LIKELY(exp)
Definition: robin_growth_policy.h:64
std::pair< iterator, bool > insert(P &&value)
Definition: robin_hash.h:718
float load_factor() const
Definition: robin_hash.h:1035
iterator find(const K &key, std::size_t hash)
Definition: robin_hash.h:982
Definition: robin_growth_policy.h:68
const robin_hash::key_type & key() const
Definition: robin_hash.h:440
U::value_type & value()
Definition: robin_hash.h:450
robin_hash(size_type bucket_count, const Hash &hash, const KeyEqual &equal, const Allocator &alloc, float min_load_factor=DEFAULT_MIN_LOAD_FACTOR, float max_load_factor=DEFAULT_MAX_LOAD_FACTOR)
Definition: robin_hash.h:540
const T & clamp(const T &v, const T &lo, const T &hi)
Definition: robin_hash.h:72
iterator insert_hint(const_iterator hint, P &&value)
Definition: robin_hash.h:723
iterator begin() noexcept
Definition: robin_hash.h:653
iterator erase(const_iterator pos)
Definition: robin_hash.h:823
const U::value_type & at(const K &key) const
Definition: robin_hash.h:939
size_type count(const K &key) const
Definition: robin_hash.h:961
void clear() noexcept
Definition: robin_hash.h:706
void insert(InputIt first, InputIt last)
Definition: robin_hash.h:732
static constexpr float DEFAULT_MIN_LOAD_FACTOR
Definition: robin_hash.h:1351
pointer operator->() const
Definition: robin_hash.h:458
std::pair< const_iterator, const_iterator > equal_range(const K &key) const
Definition: robin_hash.h:1011
const U::value_type & at(const K &key, std::size_t hash) const
Definition: robin_hash.h:944
robin_iterator operator++(int)
Definition: robin_hash.h:476
iterator erase(iterator pos)
Definition: robin_hash.h:807
const_iterator cbegin() const noexcept
Definition: robin_hash.h:666
size_type size() const noexcept
Definition: robin_hash.h:695
robin_iterator(robin_iterator &&other)=default
robin_hash(const robin_hash &other)
Definition: robin_hash.h:573
const_iterator cend() const noexcept
Definition: robin_hash.h:683
std::pair< iterator, bool > insert_or_assign(K &&key, M &&obj)
Definition: robin_hash.h:753
void reserve(size_type count)
Definition: robin_hash.h:1067
reference operator*() const
Definition: robin_hash.h:454
iterator mutable_iterator(const_iterator pos)
Definition: robin_hash.h:1086
void min_load_factor(float ml)
Definition: robin_hash.h:1051
size_type count(const K &key, std::size_t hash) const
Definition: robin_hash.h:966
iterator emplace_hint(const_iterator hint, Args &&... args)
Definition: robin_hash.h:781
friend class robin_hash
Definition: robin_hash.h:408
size_type max_size() const noexcept
Definition: robin_hash.h:699
void swap(robin_hash &other)
Definition: robin_hash.h:906
const U::value_type & value() const
Definition: robin_hash.h:445
std::pair< const_iterator, const_iterator > equal_range(const K &key, std::size_t hash) const
Definition: robin_hash.h:1016
static constexpr float MINIMUM_MIN_LOAD_FACTOR
Definition: robin_hash.h:1352
const_iterator begin() const noexcept
Definition: robin_hash.h:662
void max_load_factor(float ml)
Definition: robin_hash.h:1056
#define tsl_rh_assert(expr)
Definition: robin_growth_policy.h:42
iterator erase(const_iterator first, const_iterator last)
Definition: robin_hash.h:827
const_iterator find(const K &key, std::size_t hash) const
Definition: robin_hash.h:993
U::value_type & at(const K &key)
Definition: robin_hash.h:928
iterator find(const K &key)
Definition: robin_hash.h:977
robin_hash & operator=(const robin_hash &other)
Definition: robin_hash.h:615
static constexpr float MINIMUM_MAX_LOAD_FACTOR
Definition: robin_hash.h:1348
robin_iterator(const robin_iterator<!TIsConst > &other) noexcept
Definition: robin_hash.h:432
static constexpr float MAXIMUM_MAX_LOAD_FACTOR
Definition: robin_hash.h:1349
std::pair< iterator, iterator > equal_range(const K &key, std::size_t hash)
Definition: robin_hash.h:1004
#define TSL_RH_THROW_OR_TERMINATE(ex, msg)
Definition: robin_growth_policy.h:56
key_equal key_eq() const
Definition: robin_hash.h:1078
U::value_type & operator[](K &&key)
Definition: robin_hash.h:955
robin_iterator & operator=(const robin_iterator &other)=default
robin_iterator() noexcept
Definition: robin_hash.h:427
static const size_type DEFAULT_INIT_BUCKETS_SIZE
Definition: robin_hash.h:1345
static constexpr float MAXIMUM_MIN_LOAD_FACTOR
Definition: robin_hash.h:1353
static constexpr float DEFAULT_MAX_LOAD_FACTOR
Definition: robin_hash.h:1347
hasher hash_function() const
Definition: robin_hash.h:1074
robin_hash(robin_hash &&other) noexcept(std::is_nothrow_move_constructible< Hash >::value &&std::is_nothrow_move_constructible< KeyEqual >::value &&std::is_nothrow_move_constructible< GrowthPolicy >::value &&std::is_nothrow_move_constructible< buckets_container_type >::value)
Definition: robin_hash.h:588
std::pair< iterator, bool > emplace(Args &&... args)
Definition: robin_hash.h:776
Definition: robin_hash.h:317
iterator insert_or_assign(const_iterator hint, K &&key, M &&obj)
Definition: robin_hash.h:763
size_type erase(const K &key)
Definition: robin_hash.h:884
iterator try_emplace_hint(const_iterator hint, K &&key, Args &&... args)
Definition: robin_hash.h:795
void rehash(size_type count)
Definition: robin_hash.h:1062
robin_iterator & operator=(robin_iterator &&other)=default
float max_load_factor() const
Definition: robin_hash.h:1047
const_iterator end() const noexcept
Definition: robin_hash.h:679
Definition: robin_growth_policy.h:78
robin_iterator & operator++()
Definition: robin_hash.h:462
robin_iterator(const robin_iterator &other)=default
float min_load_factor() const
Definition: robin_hash.h:1043
bool empty() const noexcept
Definition: robin_hash.h:691
allocator_type get_allocator() const
Definition: robin_hash.h:645
Definition: robin_hash.h:47
size_type erase(const K &key, std::size_t hash)
Definition: robin_hash.h:889
U::value_type & at(const K &key, std::size_t hash)
Definition: robin_hash.h:933