IOSS 2.0
Loading...
Searching...
No Matches
hopscotch_hash.h
Go to the documentation of this file.
1/**
2 * MIT License
3 *
4 * Copyright (c) 2017, 2022, 2023 Thibaut Goetghebuer-Planchon <tessil@gmx.com>
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a copy
7 * of this software and associated documentation files (the "Software"), to deal
8 * in the Software without restriction, including without limitation the rights
9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 * copies of the Software, and to permit persons to whom the Software is
11 * furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 * SOFTWARE.
23 */
24#ifndef TSL_HOPSCOTCH_HASH_H
25#define TSL_HOPSCOTCH_HASH_H
26
27#include <algorithm>
28#include <cassert>
29#include <cmath>
30#include <cstddef>
31#include <cstdint>
32#include <exception>
33#include <functional>
34#include <initializer_list>
35#include <iterator>
36#include <limits>
37#include <memory>
38#include <stdexcept>
39#include <tuple>
40#include <type_traits>
41#include <utility>
42#include <vector>
43
45
46#if (defined(__GNUC__) && (__GNUC__ == 4) && (__GNUC_MINOR__ < 9))
47#define TSL_HH_NO_RANGE_ERASE_WITH_CONST_ITERATOR
48#endif
49
50// NOLINTBEGIN
51namespace tsl {
52 namespace detail_hopscotch_hash {
53
54 template <typename T> struct make_void
55 {
56 using type = void;
57 };
58
59 template <typename T, typename = void> struct has_is_transparent : std::false_type
60 {
61 };
62
63 template <typename T>
64 struct has_is_transparent<T, typename make_void<typename T::is_transparent>::type>
65 : std::true_type
66 {
67 };
68
69 template <typename T, typename = void> struct has_key_compare : std::false_type
70 {
71 };
72
73 template <typename T>
74 struct has_key_compare<T, typename make_void<typename T::key_compare>::type> : std::true_type
75 {
76 };
77
78 template <typename U> struct is_power_of_two_policy : std::false_type
79 {
80 };
81
82 template <std::size_t GrowthFactor>
84 : std::true_type
85 {
86 };
87
88 template <typename T, typename U>
89 T numeric_cast(U value, const char *error_message = "numeric_cast() failed.")
90 {
91 T ret = static_cast<T>(value);
92 if (static_cast<U>(ret) != value) {
93 TSL_HH_THROW_OR_TERMINATE(std::runtime_error, error_message);
94 }
95
96 const bool is_same_signedness = (std::is_unsigned<T>::value && std::is_unsigned<U>::value) ||
97 (std::is_signed<T>::value && std::is_signed<U>::value);
98 if (!is_same_signedness && (ret < T{}) != (value < U{})) {
99 TSL_HH_THROW_OR_TERMINATE(std::runtime_error, error_message);
100 }
101
102 return ret;
103 }
104
105 /*
106 * smallest_type_for_min_bits::type returns the smallest type that can fit
107 * MinBits.
108 */
109 static const std::size_t SMALLEST_TYPE_MAX_BITS_SUPPORTED = 64;
110 template <unsigned int MinBits, typename Enable = void> class smallest_type_for_min_bits
111 {
112 };
113
114 template <unsigned int MinBits>
116 typename std::enable_if<(MinBits > 0) && (MinBits <= 8)>::type>
117 {
118 public:
119 using type = std::uint_least8_t;
120 };
121
122 template <unsigned int MinBits>
124 MinBits, typename std::enable_if<(MinBits > 8) && (MinBits <= 16)>::type>
125 {
126 public:
127 using type = std::uint_least16_t;
128 };
129
130 template <unsigned int MinBits>
132 MinBits, typename std::enable_if<(MinBits > 16) && (MinBits <= 32)>::type>
133 {
134 public:
135 using type = std::uint_least32_t;
136 };
137
138 template <unsigned int MinBits>
140 MinBits, typename std::enable_if<(MinBits > 32) && (MinBits <= 64)>::type>
141 {
142 public:
143 using type = std::uint_least64_t;
144 };
145
146 /*
147 * Each bucket may store up to three elements:
148 * - An aligned storage to store a value_type object with placement-new.
149 * - An (optional) hash of the value in the bucket.
150 * - An unsigned integer of type neighborhood_bitmap used to tell us which
151 * buckets in the neighborhood of the current bucket contain a value with a hash
152 * belonging to the current bucket.
153 *
154 * For a bucket 'bct', a bit 'i' (counting from 0 and from the least significant
155 * bit to the most significant) set to 1 means that the bucket 'bct + i'
156 * contains a value with a hash belonging to bucket 'bct'. The bits used for
157 * that, start from the third least significant bit. The two least significant
158 * bits are reserved:
159 * - The least significant bit is set to 1 if there is a value in the bucket
160 * storage.
161 * - The second least significant bit is set to 1 if there is an overflow. More
162 * than NeighborhoodSize values give the same hash, all overflow values are
163 * stored in the m_overflow_elements list of the map.
164 *
165 * Details regarding hopscotch hashing an its implementation can be found here:
166 * https://tessil.github.io/2016/08/29/hopscotch-hashing.html
167 */
168 static const std::size_t NB_RESERVED_BITS_IN_NEIGHBORHOOD = 2;
169
170 using truncated_hash_type = std::uint_least32_t;
171
172 /**
173 * Helper class that stores a truncated hash if StoreHash is true and nothing
174 * otherwise.
175 */
176 template <bool StoreHash> class hopscotch_bucket_hash
177 {
178 public:
179 bool bucket_hash_equal(std::size_t /*hash*/) const noexcept { return true; }
180
181 truncated_hash_type truncated_bucket_hash() const noexcept { return 0; }
182
183 protected:
184 void copy_hash(const hopscotch_bucket_hash &) noexcept {}
185
186 void set_hash(truncated_hash_type /*hash*/) noexcept {}
187 };
188
189 template <> class hopscotch_bucket_hash<true>
190 {
191 public:
192 bool bucket_hash_equal(std::size_t my_hash) const noexcept
193 {
194 return m_hash == truncated_hash_type(my_hash);
195 }
196
197 truncated_hash_type truncated_bucket_hash() const noexcept { return m_hash; }
198
199 protected:
200 void copy_hash(const hopscotch_bucket_hash &bucket) noexcept { m_hash = bucket.m_hash; }
201
202 void set_hash(truncated_hash_type my_hash) noexcept { m_hash = my_hash; }
203
204 private:
206 };
207
208 template <typename ValueType, unsigned int NeighborhoodSize, bool StoreHash>
209 class hopscotch_bucket : public hopscotch_bucket_hash<StoreHash>
210 {
211 private:
212 static const std::size_t MIN_NEIGHBORHOOD_SIZE = 4;
213 static const std::size_t MAX_NEIGHBORHOOD_SIZE =
215
216 static_assert(NeighborhoodSize >= 4, "NeighborhoodSize should be >= 4.");
217 // We can't put a variable in the message, ensure coherence
218 static_assert(MIN_NEIGHBORHOOD_SIZE == 4, "");
219
220 static_assert(NeighborhoodSize <= 62, "NeighborhoodSize should be <= 62.");
221 // We can't put a variable in the message, ensure coherence
222 static_assert(MAX_NEIGHBORHOOD_SIZE == 62, "");
223
224 static_assert(!StoreHash || NeighborhoodSize <= 30,
225 "NeighborhoodSize should be <= 30 if StoreHash is true.");
226 // We can't put a variable in the message, ensure coherence
227 static_assert(MAX_NEIGHBORHOOD_SIZE - 32 == 30, "");
228
230
231 public:
232 using value_type = ValueType;
234 typename smallest_type_for_min_bits<NeighborhoodSize +
236
241
242 hopscotch_bucket(const hopscotch_bucket &bucket) noexcept(
243 std::is_nothrow_copy_constructible<value_type>::value)
245 {
246 if (!bucket.empty()) {
247 ::new (static_cast<void *>(std::addressof(m_value))) value_type(bucket.value());
248 }
249
250 m_neighborhood_infos = bucket.m_neighborhood_infos;
251 }
252
254 std::is_nothrow_move_constructible<value_type>::value)
255 : bucket_hash(std::move(bucket)), m_neighborhood_infos(0)
256 {
257 if (!bucket.empty()) {
258 ::new (static_cast<void *>(std::addressof(m_value)))
259 value_type(std::move(bucket.value()));
260 }
261
262 m_neighborhood_infos = bucket.m_neighborhood_infos;
263 }
264
266 std::is_nothrow_copy_constructible<value_type>::value)
267 {
268 if (this != &bucket) {
269 remove_value();
270
271 bucket_hash::operator=(bucket);
272 if (!bucket.empty()) {
273 ::new (static_cast<void *>(std::addressof(m_value))) value_type(bucket.value());
274 }
275
276 m_neighborhood_infos = bucket.m_neighborhood_infos;
277 }
278
279 return *this;
280 }
281
283
285 {
286 if (!empty()) {
288 }
289 }
290
295
305
306 bool has_overflow() const noexcept { return (m_neighborhood_infos & 2) != 0; }
307
308 bool empty() const noexcept { return (m_neighborhood_infos & 1) == 0; }
309
310 void toggle_neighbor_presence(std::size_t ineighbor) noexcept
311 {
312 tsl_hh_assert(ineighbor <= NeighborhoodSize);
315 }
316
317 bool check_neighbor_presence(std::size_t ineighbor) const noexcept
318 {
319 tsl_hh_assert(ineighbor <= NeighborhoodSize);
320 if (((m_neighborhood_infos >> (ineighbor + NB_RESERVED_BITS_IN_NEIGHBORHOOD)) & 1) == 1) {
321 return true;
322 }
323
324 return false;
325 }
326
327 value_type &value() noexcept
328 {
330 return *reinterpret_cast<value_type *>(std::addressof(m_value));
331 }
332
333 const value_type &value() const noexcept
334 {
336 return *reinterpret_cast<const value_type *>(std::addressof(m_value));
337 }
338
339 template <typename... Args>
340 void set_value_of_empty_bucket(truncated_hash_type my_hash, Args &&...value_type_args)
341 {
343
344 ::new (static_cast<void *>(std::addressof(m_value)))
345 value_type(std::forward<Args>(value_type_args)...);
346 set_empty(false);
347 this->set_hash(my_hash);
348 }
349
351 {
352 tsl_hh_assert(empty_bucket.empty());
353 if (!empty()) {
354 ::new (static_cast<void *>(std::addressof(empty_bucket.m_value)))
355 value_type(std::move(value()));
356 empty_bucket.copy_hash(*this);
357 empty_bucket.set_empty(false);
358
360 set_empty(true);
361 }
362 }
363
364 void remove_value() noexcept
365 {
366 if (!empty()) {
368 set_empty(true);
369 }
370 }
371
372 void clear() noexcept
373 {
374 if (!empty()) {
376 }
377
380 }
381
382 static truncated_hash_type truncate_hash(std::size_t my_hash) noexcept
383 {
384 return truncated_hash_type(my_hash);
385 }
386
387 private:
388 void set_empty(bool is_empty) noexcept
389 {
390 if (is_empty) {
392 }
393 else {
395 }
396 }
397
398 void destroy_value() noexcept
399 {
401 value().~value_type();
402 }
403
404 private:
405 using storage = typename std::aligned_storage<sizeof(value_type), alignof(value_type)>::type;
406
409 };
410
411 /**
412 * Internal common class used by (b)hopscotch_map and (b)hopscotch_set.
413 *
414 * ValueType is what will be stored by hopscotch_hash (usually std::pair<Key, T>
415 * for a map and Key for a set).
416 *
417 * KeySelect should be a FunctionObject which takes a ValueType in parameter and
418 * returns a reference to the key.
419 *
420 * ValueSelect should be a FunctionObject which takes a ValueType in parameter
421 * and returns a reference to the value. ValueSelect should be void if there is
422 * no value (in a set for example).
423 *
424 * OverflowContainer will be used as containers for overflown elements. Usually
425 * it should be a list<ValueType> or a set<Key>/map<Key, T>.
426 */
427 template <class ValueType, class KeySelect, class ValueSelect, class Hash, class KeyEqual,
428 class Allocator, unsigned int NeighborhoodSize, bool StoreHash, class GrowthPolicy,
429 class OverflowContainer>
430 class hopscotch_hash : private Hash, private KeyEqual, private GrowthPolicy
431 {
432 private:
433 template <typename U>
434 using has_mapped_type = typename std::integral_constant<bool, !std::is_same<U, void>::value>;
435
436 static_assert(noexcept(std::declval<GrowthPolicy>().bucket_for_hash(std::size_t(0))),
437 "GrowthPolicy::bucket_for_hash must be noexcept.");
438 static_assert(noexcept(std::declval<GrowthPolicy>().clear()),
439 "GrowthPolicy::clear must be noexcept.");
440
441 public:
442 template <bool IsConst> class hopscotch_iterator;
443
444 using key_type = typename KeySelect::key_type;
445 using value_type = ValueType;
446 using size_type = std::size_t;
447 using difference_type = std::ptrdiff_t;
448 using hasher = Hash;
449 using key_equal = KeyEqual;
450 using allocator_type = Allocator;
454 using const_pointer = const value_type *;
457
458 private:
462
464 typename std::allocator_traits<allocator_type>::template rebind_alloc<hopscotch_bucket>;
465 using buckets_container_type = std::vector<hopscotch_bucket, buckets_allocator>;
466
467 using overflow_container_type = OverflowContainer;
468
469 static_assert(std::is_same<typename overflow_container_type::value_type, ValueType>::value,
470 "OverflowContainer should have ValueType as type.");
471
472 static_assert(
473 std::is_same<typename overflow_container_type::allocator_type, Allocator>::value,
474 "Invalid allocator, not the same type as the value_type.");
475
476 using iterator_buckets = typename buckets_container_type::iterator;
477 using const_iterator_buckets = typename buckets_container_type::const_iterator;
478
479 using iterator_overflow = typename overflow_container_type::iterator;
480 using const_iterator_overflow = typename overflow_container_type::const_iterator;
481
482 public:
483 /**
484 * The `operator*()` and `operator->()` methods return a const reference and
485 * const pointer respectively to the stored value type.
486 *
487 * In case of a map, to get a modifiable reference to the value associated to
488 * a key (the `.second` in the stored pair), you have to call `value()`.
489 */
490 template <bool IsConst> class hopscotch_iterator
491 {
492 friend class hopscotch_hash;
493
494 private:
496 typename std::conditional<IsConst, typename hopscotch_hash::const_iterator_buckets,
497 typename hopscotch_hash::iterator_buckets>::type;
499 typename std::conditional<IsConst, typename hopscotch_hash::const_iterator_overflow,
500 typename hopscotch_hash::iterator_overflow>::type;
501
502 hopscotch_iterator(iterator_bucket buckets_iterator, iterator_bucket buckets_end_iterator,
503 iterator_overflow overflow_iterator) noexcept
504 : m_buckets_iterator(buckets_iterator), m_buckets_end_iterator(buckets_end_iterator),
505 m_overflow_iterator(overflow_iterator)
506 {
507 }
508
509 public:
510 using iterator_category = std::forward_iterator_tag;
512 using difference_type = std::ptrdiff_t;
515
516 hopscotch_iterator() noexcept {}
517
518 // Copy constructor from iterator to const_iterator.
519 template <bool TIsConst = IsConst, typename std::enable_if<TIsConst>::type * = nullptr>
521 : m_buckets_iterator(other.m_buckets_iterator),
522 m_buckets_end_iterator(other.m_buckets_end_iterator),
523 m_overflow_iterator(other.m_overflow_iterator)
524 {
525 }
526
527 hopscotch_iterator(const hopscotch_iterator &other) = default;
531
532 const typename hopscotch_hash::key_type &key() const
533 {
535 return KeySelect()(m_buckets_iterator->value());
536 }
537
538 return KeySelect()(*m_overflow_iterator);
539 }
540
541 template <class U = ValueSelect,
542 typename std::enable_if<has_mapped_type<U>::value>::type * = nullptr>
543 typename std::conditional<IsConst, const typename U::value_type &,
544 typename U::value_type &>::type
545 value() const
546 {
548 return U()(m_buckets_iterator->value());
549 }
550
551 return U()(*m_overflow_iterator);
552 }
553
555 {
557 return m_buckets_iterator->value();
558 }
559
560 return *m_overflow_iterator;
561 }
562
564 {
566 return std::addressof(m_buckets_iterator->value());
567 }
568
569 return std::addressof(*m_overflow_iterator);
570 }
571
573 {
576 return *this;
577 }
578
579 do {
582
583 return *this;
584 }
585
587 {
588 hopscotch_iterator tmp(*this);
589 ++*this;
590
591 return tmp;
592 }
593
594 friend bool operator==(const hopscotch_iterator &lhs, const hopscotch_iterator &rhs)
595 {
596 return lhs.m_buckets_iterator == rhs.m_buckets_iterator &&
598 }
599
600 friend bool operator!=(const hopscotch_iterator &lhs, const hopscotch_iterator &rhs)
601 {
602 return !(lhs == rhs);
603 }
604
605 private:
609 };
610
611 public:
612 template <class OC = OverflowContainer,
613 typename std::enable_if<!has_key_compare<OC>::value>::type * = nullptr>
614 hopscotch_hash(size_type bucket_count, const Hash &my_hash, const KeyEqual &equal,
615 const Allocator &alloc, float max_load_factor)
616 : Hash(my_hash), KeyEqual(equal), GrowthPolicy(bucket_count), m_buckets_data(alloc),
618 {
620 TSL_HH_THROW_OR_TERMINATE(std::length_error, "The map exceeds its maximum size.");
621 }
622
623 if (bucket_count > 0) {
624 static_assert(NeighborhoodSize - 1 > 0, "");
625
626 // Can't directly construct with the appropriate size in the initializer
627 // as m_buckets_data(bucket_count, alloc) is not supported by GCC 4.8
628 m_buckets_data.resize(bucket_count + NeighborhoodSize - 1);
629 m_buckets = m_buckets_data.data();
630 }
631
632 this->max_load_factor(max_load_factor);
633
634 // Check in the constructor instead of outside of a function to avoid
635 // compilation issues when value_type is not complete.
636 static_assert(std::is_nothrow_move_constructible<value_type>::value ||
637 std::is_copy_constructible<value_type>::value,
638 "value_type must be either copy constructible or nothrow "
639 "move constructible.");
640 }
641
642 template <class OC = OverflowContainer,
643 typename std::enable_if<has_key_compare<OC>::value>::type * = nullptr>
644 hopscotch_hash(size_type bucket_count, const Hash &p_hash, const KeyEqual &equal,
645 const Allocator &alloc, float max_load_factor,
646 const typename OC::key_compare &comp)
647 : Hash(p_hash), KeyEqual(equal), GrowthPolicy(bucket_count), m_buckets_data(alloc),
649 {
651 TSL_HH_THROW_OR_TERMINATE(std::length_error, "The map exceeds its maximum size.");
652 }
653
654 if (bucket_count > 0) {
655 static_assert(NeighborhoodSize - 1 > 0, "");
656
657 // Can't directly construct with the appropriate size in the initializer
658 // as m_buckets_data(bucket_count, alloc) is not supported by GCC 4.8
659 m_buckets_data.resize(bucket_count + NeighborhoodSize - 1);
660 m_buckets = m_buckets_data.data();
661 }
662
663 this->max_load_factor(max_load_factor);
664
665 // Check in the constructor instead of outside of a function to avoid
666 // compilation issues when value_type is not complete.
667 static_assert(std::is_nothrow_move_constructible<value_type>::value ||
668 std::is_copy_constructible<value_type>::value,
669 "value_type must be either copy constructible or nothrow "
670 "move constructible.");
671 }
672
683
685 std::is_nothrow_move_constructible<Hash>::value &&
686 std::is_nothrow_move_constructible<KeyEqual>::value &&
687 std::is_nothrow_move_constructible<GrowthPolicy>::value &&
688 std::is_nothrow_move_constructible<buckets_container_type>::value &&
689 std::is_nothrow_move_constructible<overflow_container_type>::value)
690 : Hash(std::move(static_cast<Hash &>(other))),
691 KeyEqual(std::move(static_cast<KeyEqual &>(other))),
692 GrowthPolicy(std::move(static_cast<GrowthPolicy &>(other))),
693 m_buckets_data(std::move(other.m_buckets_data)),
700 {
701 other.GrowthPolicy::clear();
702 other.m_buckets_data.clear();
703 other.m_overflow_elements.clear();
704 other.m_buckets = static_empty_bucket_ptr();
705 other.m_nb_elements = 0;
706 other.m_min_load_threshold_rehash = 0;
707 other.m_max_load_threshold_rehash = 0;
708 }
709
711 {
712 if (&other != this) {
713 Hash::operator=(other);
714 KeyEqual::operator=(other);
715 GrowthPolicy::operator=(other);
716
721
725 }
726
727 return *this;
728 }
729
731 {
732 other.swap(*this);
733 other.clear();
734
735 return *this;
736 }
737
738 allocator_type get_allocator() const { return m_buckets_data.get_allocator(); }
739
740 /*
741 * Iterators
742 */
743 iterator begin() noexcept
744 {
745 auto l_begin = m_buckets_data.begin();
746 while (l_begin != m_buckets_data.end() && l_begin->empty()) {
747 ++l_begin;
748 }
749
750 return iterator(l_begin, m_buckets_data.end(), m_overflow_elements.begin());
751 }
752
753 const_iterator begin() const noexcept { return cbegin(); }
754
755 const_iterator cbegin() const noexcept
756 {
757 auto l_begin = m_buckets_data.cbegin();
758 while (l_begin != m_buckets_data.cend() && l_begin->empty()) {
759 ++l_begin;
760 }
761
762 return const_iterator(l_begin, m_buckets_data.cend(), m_overflow_elements.cbegin());
763 }
764
765 iterator end() noexcept
766 {
767 return iterator(m_buckets_data.end(), m_buckets_data.end(), m_overflow_elements.end());
768 }
769
770 const_iterator end() const noexcept { return cend(); }
771
772 const_iterator cend() const noexcept
773 {
774 return const_iterator(m_buckets_data.cend(), m_buckets_data.cend(),
775 m_overflow_elements.cend());
776 }
777
778 /*
779 * Capacity
780 */
781 bool empty() const noexcept { return m_nb_elements == 0; }
782
783 size_type size() const noexcept { return m_nb_elements; }
784
785 size_type max_size() const noexcept { return m_buckets_data.max_size(); }
786
787 /*
788 * Modifiers
789 */
790 void clear() noexcept
791 {
792 for (auto &bucket : m_buckets_data) {
793 bucket.clear();
794 }
795
796 m_overflow_elements.clear();
797 m_nb_elements = 0;
798 }
799
800 std::pair<iterator, bool> insert(const value_type &value) { return insert_impl(value); }
801
802 template <class P, typename std::enable_if<
803 std::is_constructible<value_type, P &&>::value>::type * = nullptr>
804 std::pair<iterator, bool> insert(P &&value)
805 {
806 return insert_impl(value_type(std::forward<P>(value)));
807 }
808
809 std::pair<iterator, bool> insert(value_type &&value) { return insert_impl(std::move(value)); }
810
812 {
813 if (hint != cend() && compare_keys(KeySelect()(*hint), KeySelect()(value))) {
814 return mutable_iterator(hint);
815 }
816
817 return insert(value).first;
818 }
819
820 template <class P, typename std::enable_if<
821 std::is_constructible<value_type, P &&>::value>::type * = nullptr>
823 {
824 return emplace_hint(hint, std::forward<P>(value));
825 }
826
828 {
829 if (hint != cend() && compare_keys(KeySelect()(*hint), KeySelect()(value))) {
830 return mutable_iterator(hint);
831 }
832
833 return insert(std::move(value)).first;
834 }
835
836 template <class InputIt> void insert(InputIt first, InputIt last)
837 {
838 if (std::is_base_of<std::forward_iterator_tag,
839 typename std::iterator_traits<InputIt>::iterator_category>::value) {
840 const auto nb_elements_insert = std::distance(first, last);
841 const std::size_t nb_elements_in_buckets = m_nb_elements - m_overflow_elements.size();
842 const std::size_t nb_free_buckets = m_max_load_threshold_rehash - nb_elements_in_buckets;
844 tsl_hh_assert(m_max_load_threshold_rehash >= nb_elements_in_buckets);
845
846 if (nb_elements_insert > 0 && nb_free_buckets < std::size_t(nb_elements_insert)) {
847 reserve(nb_elements_in_buckets + std::size_t(nb_elements_insert));
848 }
849 }
850
851 for (; first != last; ++first) {
852 insert(*first);
853 }
854 }
855
856 template <class M> std::pair<iterator, bool> insert_or_assign(const key_type &k, M &&obj)
857 {
858 return insert_or_assign_impl(k, std::forward<M>(obj));
859 }
860
861 template <class M> std::pair<iterator, bool> insert_or_assign(key_type &&k, M &&obj)
862 {
863 return insert_or_assign_impl(std::move(k), std::forward<M>(obj));
864 }
865
866 template <class M> iterator insert_or_assign(const_iterator hint, const key_type &k, M &&obj)
867 {
868 if (hint != cend() && compare_keys(KeySelect()(*hint), k)) {
869 auto it = mutable_iterator(hint);
870 it.value() = std::forward<M>(obj);
871
872 return it;
873 }
874
875 return insert_or_assign(k, std::forward<M>(obj)).first;
876 }
877
878 template <class M> iterator insert_or_assign(const_iterator hint, key_type &&k, M &&obj)
879 {
880 if (hint != cend() && compare_keys(KeySelect()(*hint), k)) {
881 auto it = mutable_iterator(hint);
882 it.value() = std::forward<M>(obj);
883
884 return it;
885 }
886
887 return insert_or_assign(std::move(k), std::forward<M>(obj)).first;
888 }
889
890 template <class... Args> std::pair<iterator, bool> emplace(Args &&...args)
891 {
892 return insert(value_type(std::forward<Args>(args)...));
893 }
894
895 template <class... Args> iterator emplace_hint(const_iterator hint, Args &&...args)
896 {
897 return insert(hint, value_type(std::forward<Args>(args)...));
898 }
899
900 template <class... Args>
901 std::pair<iterator, bool> try_emplace(const key_type &k, Args &&...args)
902 {
903 return try_emplace_impl(k, std::forward<Args>(args)...);
904 }
905
906 template <class... Args> std::pair<iterator, bool> try_emplace(key_type &&k, Args &&...args)
907 {
908 return try_emplace_impl(std::move(k), std::forward<Args>(args)...);
909 }
910
911 template <class... Args>
912 iterator try_emplace(const_iterator hint, const key_type &k, Args &&...args)
913 {
914 if (hint != cend() && compare_keys(KeySelect()(*hint), k)) {
915 return mutable_iterator(hint);
916 }
917
918 return try_emplace(k, std::forward<Args>(args)...).first;
919 }
920
921 template <class... Args>
922 iterator try_emplace(const_iterator hint, key_type &&k, Args &&...args)
923 {
924 if (hint != cend() && compare_keys(KeySelect()(*hint), k)) {
925 return mutable_iterator(hint);
926 }
927
928 return try_emplace(std::move(k), std::forward<Args>(args)...).first;
929 }
930
931 /**
932 * Here to avoid `template<class K> size_type erase(const K& key)` being used
933 * when we use an iterator instead of a const_iterator.
934 */
936
938 {
939 const std::size_t ibucket_for_hash = bucket_for_hash(hash_key(pos.key()));
940
942 auto it_bucket = m_buckets_data.begin() +
943 std::distance(m_buckets_data.cbegin(), pos.m_buckets_iterator);
944 erase_from_bucket(*it_bucket, ibucket_for_hash);
945
946 return ++iterator(it_bucket, m_buckets_data.end(), m_overflow_elements.begin());
947 }
948 else {
949 auto it_next_overflow = erase_from_overflow(pos.m_overflow_iterator, ibucket_for_hash);
950 return iterator(m_buckets_data.end(), m_buckets_data.end(), it_next_overflow);
951 }
952 }
953
955 {
956 if (first == last) {
957 return mutable_iterator(first);
958 }
959
960 auto to_delete = erase(first);
961 while (to_delete != last) {
962 to_delete = erase(to_delete);
963 }
964
965 return to_delete;
966 }
967
968 template <class K> size_type erase(const K &key) { return erase(key, hash_key(key)); }
969
970 template <class K> size_type erase(const K &key, std::size_t my_hash)
971 {
972 const std::size_t ibucket_for_hash = bucket_for_hash(my_hash);
973
974 hopscotch_bucket *bucket_found =
975 find_in_buckets(key, my_hash, m_buckets + ibucket_for_hash);
976 if (bucket_found != nullptr) {
977 erase_from_bucket(*bucket_found, ibucket_for_hash);
978
979 return 1;
980 }
981
982 if (m_buckets[ibucket_for_hash].has_overflow()) {
983 auto it_overflow = find_in_overflow(key);
984 if (it_overflow != m_overflow_elements.end()) {
985 erase_from_overflow(it_overflow, ibucket_for_hash);
986
987 return 1;
988 }
989 }
990
991 return 0;
992 }
993
994 void swap(hopscotch_hash &other)
995 {
996 using std::swap;
997
998 swap(static_cast<Hash &>(*this), static_cast<Hash &>(other));
999 swap(static_cast<KeyEqual &>(*this), static_cast<KeyEqual &>(other));
1000 swap(static_cast<GrowthPolicy &>(*this), static_cast<GrowthPolicy &>(other));
1003 swap(m_buckets, other.m_buckets);
1008 }
1009
1010 /*
1011 * Lookup
1012 */
1013 template <class K, class U = ValueSelect,
1014 typename std::enable_if<has_mapped_type<U>::value>::type * = nullptr>
1015 typename U::value_type &at(const K &key)
1016 {
1017 return at(key, hash_key(key));
1018 }
1019
1020 template <class K, class U = ValueSelect,
1021 typename std::enable_if<has_mapped_type<U>::value>::type * = nullptr>
1022 typename U::value_type &at(const K &key, std::size_t my_hash)
1023 {
1024 return const_cast<typename U::value_type &>(
1025 static_cast<const hopscotch_hash *>(this)->at(key, my_hash));
1026 }
1027
1028 template <class K, class U = ValueSelect,
1029 typename std::enable_if<has_mapped_type<U>::value>::type * = nullptr>
1030 const typename U::value_type &at(const K &key) const
1031 {
1032 return at(key, hash_key(key));
1033 }
1034
1035 template <class K, class U = ValueSelect,
1036 typename std::enable_if<has_mapped_type<U>::value>::type * = nullptr>
1037 const typename U::value_type &at(const K &key, std::size_t my_hash) const
1038 {
1039 using T = typename U::value_type;
1040
1041 const T *value = find_value_impl(key, my_hash, m_buckets + bucket_for_hash(my_hash));
1042 if (value == nullptr) {
1043 TSL_HH_THROW_OR_TERMINATE(std::out_of_range, "Couldn't find key.");
1044 }
1045 else {
1046 return *value;
1047 }
1048 }
1049
1050 template <class K, class U = ValueSelect,
1051 typename std::enable_if<has_mapped_type<U>::value>::type * = nullptr>
1052 typename U::value_type &operator[](K &&key)
1053 {
1054 using T = typename U::value_type;
1055
1056 const std::size_t my_hash = hash_key(key);
1057 const std::size_t ibucket_for_hash = bucket_for_hash(my_hash);
1058
1059 T *value = find_value_impl(key, my_hash, m_buckets + ibucket_for_hash);
1060 if (value != nullptr) {
1061 return *value;
1062 }
1063 else {
1064 return insert_value(ibucket_for_hash, my_hash, std::piecewise_construct,
1065 std::forward_as_tuple(std::forward<K>(key)), std::forward_as_tuple())
1066 .first.value();
1067 }
1068 }
1069
1070 template <class K> size_type count(const K &key) const { return count(key, hash_key(key)); }
1071
1072 template <class K> size_type count(const K &key, std::size_t my_hash) const
1073 {
1074 return count_impl(key, my_hash, m_buckets + bucket_for_hash(my_hash));
1075 }
1076
1077 template <class K> iterator find(const K &key) { return find(key, hash_key(key)); }
1078
1079 template <class K> iterator find(const K &key, std::size_t my_hash)
1080 {
1081 return find_impl(key, my_hash, m_buckets + bucket_for_hash(my_hash));
1082 }
1083
1084 template <class K> const_iterator find(const K &key) const
1085 {
1086 return find(key, hash_key(key));
1087 }
1088
1089 template <class K> const_iterator find(const K &key, std::size_t my_hash) const
1090 {
1091 return find_impl(key, my_hash, m_buckets + bucket_for_hash(my_hash));
1092 }
1093
1094 template <class K> bool contains(const K &key) const { return contains(key, hash_key(key)); }
1095
1096 template <class K> bool contains(const K &key, std::size_t my_hash) const
1097 {
1098 return count(key, my_hash) != 0;
1099 }
1100
1101 template <class K> std::pair<iterator, iterator> equal_range(const K &key)
1102 {
1103 return equal_range(key, hash_key(key));
1104 }
1105
1106 template <class K>
1107 std::pair<iterator, iterator> equal_range(const K &key, std::size_t my_hash)
1108 {
1109 iterator it = find(key, my_hash);
1110 return std::make_pair(it, (it == end()) ? it : std::next(it));
1111 }
1112
1113 template <class K> std::pair<const_iterator, const_iterator> equal_range(const K &key) const
1114 {
1115 return equal_range(key, hash_key(key));
1116 }
1117
1118 template <class K>
1119 std::pair<const_iterator, const_iterator> equal_range(const K &key, std::size_t my_hash) const
1120 {
1121 const_iterator it = find(key, my_hash);
1122 return std::make_pair(it, (it == cend()) ? it : std::next(it));
1123 }
1124
1125 /*
1126 * Bucket interface
1127 */
1129 {
1130 /*
1131 * So that the last bucket can have NeighborhoodSize neighbors, the size of
1132 * the bucket array is a little bigger than the real number of buckets when
1133 * not empty. We could use some of the buckets at the beginning, but it is
1134 * faster this way as we avoid extra checks.
1135 */
1136 if (m_buckets_data.empty()) {
1137 return 0;
1138 }
1139
1140 return m_buckets_data.size() - NeighborhoodSize + 1;
1141 }
1142
1144 {
1145 const std::size_t max_bucket_count =
1146 std::min(GrowthPolicy::max_bucket_count(), m_buckets_data.max_size());
1147 return max_bucket_count - NeighborhoodSize + 1;
1148 }
1149
1150 /*
1151 * Hash policy
1152 */
1153 float load_factor() const
1154 {
1155 if (bucket_count() == 0) {
1156 return 0;
1157 }
1158
1159 return float(m_nb_elements) / float(bucket_count());
1160 }
1161
1162 float max_load_factor() const { return m_max_load_factor; }
1163
1164 void max_load_factor(float ml)
1165 {
1166 m_max_load_factor = std::max(0.1f, std::min(ml, 0.95f));
1169 }
1170
1171 void rehash(size_type count_)
1172 {
1173 count_ = std::max(count_, size_type(std::ceil(float(size()) / max_load_factor())));
1174 rehash_impl(count_);
1175 }
1176
1177 void reserve(size_type count_)
1178 {
1179 rehash(size_type(std::ceil(float(count_) / max_load_factor())));
1180 }
1181
1182 /*
1183 * Observers
1184 */
1185 hasher hash_function() const { return static_cast<const Hash &>(*this); }
1186
1187 key_equal key_eq() const { return static_cast<const KeyEqual &>(*this); }
1188
1189 /*
1190 * Other
1191 */
1193 {
1195 // Get a non-const iterator
1196 auto it = m_buckets_data.begin() +
1197 std::distance(m_buckets_data.cbegin(), pos.m_buckets_iterator);
1198 return iterator(it, m_buckets_data.end(), m_overflow_elements.begin());
1199 }
1200 else {
1201 // Get a non-const iterator
1203 return iterator(m_buckets_data.end(), m_buckets_data.end(), it);
1204 }
1205 }
1206
1207 size_type overflow_size() const noexcept { return m_overflow_elements.size(); }
1208
1209 template <class U = OverflowContainer,
1210 typename std::enable_if<has_key_compare<U>::value>::type * = nullptr>
1211 typename U::key_compare key_comp() const
1212 {
1213 return m_overflow_elements.key_comp();
1214 }
1215
1216 private:
1217 template <class K> std::size_t hash_key(const K &key) const { return Hash::operator()(key); }
1218
1219 template <class K1, class K2> bool compare_keys(const K1 &key1, const K2 &key2) const
1220 {
1221 return KeyEqual::operator()(key1, key2);
1222 }
1223
1224 std::size_t bucket_for_hash(std::size_t my_hash) const
1225 {
1226 const std::size_t bucket = GrowthPolicy::bucket_for_hash(my_hash);
1227 tsl_hh_assert(bucket < m_buckets_data.size() || (bucket == 0 && m_buckets_data.empty()));
1228
1229 return bucket;
1230 }
1231
1232 template <
1233 typename U = value_type,
1234 typename std::enable_if<std::is_nothrow_move_constructible<U>::value>::type * = nullptr>
1236 {
1237 hopscotch_hash new_map = new_hopscotch_hash(count_);
1238
1239 if (!m_overflow_elements.empty()) {
1241 new_map.m_nb_elements += new_map.m_overflow_elements.size();
1242
1243 for (const value_type &value : new_map.m_overflow_elements) {
1244 const std::size_t ibucket_for_hash =
1245 new_map.bucket_for_hash(new_map.hash_key(KeySelect()(value)));
1246 new_map.m_buckets[ibucket_for_hash].set_overflow(true);
1247 }
1248 }
1249
1250#ifndef TSL_HH_NO_EXCEPTIONS
1251 try {
1252#endif
1253 const bool use_stored_hash = USE_STORED_HASH_ON_REHASH(new_map.bucket_count());
1254 for (auto it_bucket = m_buckets_data.begin(); it_bucket != m_buckets_data.end();
1255 ++it_bucket) {
1256 if (it_bucket->empty()) {
1257 continue;
1258 }
1259
1260 const std::size_t my_hash = use_stored_hash
1261 ? it_bucket->truncated_bucket_hash()
1262 : new_map.hash_key(KeySelect()(it_bucket->value()));
1263 const std::size_t ibucket_for_hash = new_map.bucket_for_hash(my_hash);
1264
1265 new_map.insert_value(ibucket_for_hash, my_hash, std::move(it_bucket->value()));
1266
1267 erase_from_bucket(*it_bucket, bucket_for_hash(my_hash));
1268 }
1269#ifndef TSL_HH_NO_EXCEPTIONS
1270 }
1271 /*
1272 * The call to insert_value may throw an exception if an element is added to
1273 * the overflow list and the memory allocation fails. Rollback the elements
1274 * in this case.
1275 */
1276 catch (...) {
1278
1279 const bool use_stored_hash = USE_STORED_HASH_ON_REHASH(new_map.bucket_count());
1280 for (auto it_bucket = new_map.m_buckets_data.begin();
1281 it_bucket != new_map.m_buckets_data.end(); ++it_bucket) {
1282 if (it_bucket->empty()) {
1283 continue;
1284 }
1285
1286 const std::size_t my_hash = use_stored_hash ? it_bucket->truncated_bucket_hash()
1287 : hash_key(KeySelect()(it_bucket->value()));
1288 const std::size_t ibucket_for_hash = bucket_for_hash(my_hash);
1289
1290 // The elements we insert were not in the overflow list before the
1291 // switch. They will not be go in the overflow list if we rollback the
1292 // switch.
1293 insert_value(ibucket_for_hash, my_hash, std::move(it_bucket->value()));
1294 }
1295
1296 throw;
1297 }
1298#endif
1299
1300 new_map.swap(*this);
1301 }
1302
1303 template <
1304 typename U = value_type,
1305 typename std::enable_if<std::is_copy_constructible<U>::value &&
1306 !std::is_nothrow_move_constructible<U>::value>::type * = nullptr>
1308 {
1309 hopscotch_hash new_map = new_hopscotch_hash(count_);
1310
1311 const bool use_stored_hash = USE_STORED_HASH_ON_REHASH(new_map.bucket_count());
1312 for (const hopscotch_bucket &bucket : m_buckets_data) {
1313 if (bucket.empty()) {
1314 continue;
1315 }
1316
1317 const std::size_t my_hash = use_stored_hash
1318 ? bucket.truncated_bucket_hash()
1319 : new_map.hash_key(KeySelect()(bucket.value()));
1320 const std::size_t ibucket_for_hash = new_map.bucket_for_hash(my_hash);
1321
1322 new_map.insert_value(ibucket_for_hash, my_hash, bucket.value());
1323 }
1324
1325 for (const value_type &value : m_overflow_elements) {
1326 const std::size_t my_hash = new_map.hash_key(KeySelect()(value));
1327 const std::size_t ibucket_for_hash = new_map.bucket_for_hash(my_hash);
1328
1329 new_map.insert_value(ibucket_for_hash, my_hash, value);
1330 }
1331
1332 new_map.swap(*this);
1333 }
1334
1335#ifdef TSL_HH_NO_RANGE_ERASE_WITH_CONST_ITERATOR
1337 {
1338 return std::next(m_overflow_elements.begin(),
1339 std::distance(m_overflow_elements.cbegin(), it));
1340 }
1341#else
1346#endif
1347
1348 // iterator is in overflow list
1350 std::size_t ibucket_for_hash)
1351 {
1352#ifdef TSL_HH_NO_RANGE_ERASE_WITH_CONST_ITERATOR
1353 auto it_next = m_overflow_elements.erase(mutable_overflow_iterator(pos));
1354#else
1355 auto it_next = m_overflow_elements.erase(pos);
1356#endif
1357 m_nb_elements--;
1358
1359 // Check if we can remove the overflow flag
1360 tsl_hh_assert(m_buckets[ibucket_for_hash].has_overflow());
1361 for (const value_type &value : m_overflow_elements) {
1362 const std::size_t bucket_for_value = bucket_for_hash(hash_key(KeySelect()(value)));
1363 if (bucket_for_value == ibucket_for_hash) {
1364 return it_next;
1365 }
1366 }
1367
1368 m_buckets[ibucket_for_hash].set_overflow(false);
1369 return it_next;
1370 }
1371
1372 /**
1373 * bucket_for_value is the bucket in which the value is.
1374 * ibucket_for_hash is the bucket where the value belongs.
1375 */
1376 void erase_from_bucket(hopscotch_bucket &bucket_for_value,
1377 std::size_t ibucket_for_hash) noexcept
1378 {
1379 const std::size_t ibucket_for_value =
1380 std::distance(m_buckets_data.data(), &bucket_for_value);
1381 tsl_hh_assert(ibucket_for_value >= ibucket_for_hash);
1382
1383 bucket_for_value.remove_value();
1384 m_buckets[ibucket_for_hash].toggle_neighbor_presence(ibucket_for_value - ibucket_for_hash);
1385 m_nb_elements--;
1386 }
1387
1388 template <class K, class M> std::pair<iterator, bool> insert_or_assign_impl(K &&key, M &&obj)
1389 {
1390 auto it = try_emplace_impl(std::forward<K>(key), std::forward<M>(obj));
1391 if (!it.second) {
1392 it.first.value() = std::forward<M>(obj);
1393 }
1394
1395 return it;
1396 }
1397
1398 template <typename P, class... Args>
1399 std::pair<iterator, bool> try_emplace_impl(P &&key, Args &&...args_value)
1400 {
1401 const std::size_t my_hash = hash_key(key);
1402 const std::size_t ibucket_for_hash = bucket_for_hash(my_hash);
1403
1404 // Check if already presents
1405 auto it_find = find_impl(key, my_hash, m_buckets + ibucket_for_hash);
1406 if (it_find != end()) {
1407 return std::make_pair(it_find, false);
1408 }
1409
1410 return insert_value(ibucket_for_hash, my_hash, std::piecewise_construct,
1411 std::forward_as_tuple(std::forward<P>(key)),
1412 std::forward_as_tuple(std::forward<Args>(args_value)...));
1413 }
1414
1415 template <typename P> std::pair<iterator, bool> insert_impl(P &&value)
1416 {
1417 const std::size_t my_hash = hash_key(KeySelect()(value));
1418 const std::size_t ibucket_for_hash = bucket_for_hash(my_hash);
1419
1420 // Check if already presents
1421 auto it_find = find_impl(KeySelect()(value), my_hash, m_buckets + ibucket_for_hash);
1422 if (it_find != end()) {
1423 return std::make_pair(it_find, false);
1424 }
1425
1426 return insert_value(ibucket_for_hash, my_hash, std::forward<P>(value));
1427 }
1428
1429 template <typename... Args>
1430 std::pair<iterator, bool> insert_value(std::size_t ibucket_for_hash, std::size_t my_hash,
1431 Args &&...value_type_args)
1432 {
1434 rehash(GrowthPolicy::next_bucket_count());
1435 ibucket_for_hash = bucket_for_hash(my_hash);
1436 }
1437
1438 std::size_t ibucket_empty = find_empty_bucket(ibucket_for_hash);
1439 if (ibucket_empty < m_buckets_data.size()) {
1440 do {
1441 tsl_hh_assert(ibucket_empty >= ibucket_for_hash);
1442
1443 // Empty bucket is in range of NeighborhoodSize, use it
1444 if (ibucket_empty - ibucket_for_hash < NeighborhoodSize) {
1445 auto it = insert_in_bucket(ibucket_empty, ibucket_for_hash, my_hash,
1446 std::forward<Args>(value_type_args)...);
1447 return std::make_pair(iterator(it, m_buckets_data.end(), m_overflow_elements.begin()),
1448 true);
1449 }
1450 }
1451 // else, try to swap values to get a closer empty bucket
1452 while (swap_empty_bucket_closer(ibucket_empty));
1453 }
1454
1455 // Load factor is too low or a rehash will not change the neighborhood, put
1456 // the value in overflow list
1458 !will_neighborhood_change_on_rehash(ibucket_for_hash)) {
1459 auto it = insert_in_overflow(ibucket_for_hash, std::forward<Args>(value_type_args)...);
1460 return std::make_pair(iterator(m_buckets_data.end(), m_buckets_data.end(), it), true);
1461 }
1462
1463 rehash(GrowthPolicy::next_bucket_count());
1464 ibucket_for_hash = bucket_for_hash(my_hash);
1465
1466 return insert_value(ibucket_for_hash, my_hash, std::forward<Args>(value_type_args)...);
1467 }
1468
1469 /*
1470 * Return true if a rehash will change the position of a key-value in the
1471 * neighborhood of ibucket_neighborhood_check. In this case a rehash is needed
1472 * instead of putting the value in overflow list.
1473 */
1474 bool will_neighborhood_change_on_rehash(size_t ibucket_neighborhood_check) const
1475 {
1476 std::size_t expand_bucket_count = GrowthPolicy::next_bucket_count();
1477 GrowthPolicy expand_growth_policy(expand_bucket_count);
1478
1479 const bool use_stored_hash = USE_STORED_HASH_ON_REHASH(expand_bucket_count);
1480 for (size_t ibucket = ibucket_neighborhood_check;
1481 ibucket < m_buckets_data.size() &&
1482 (ibucket - ibucket_neighborhood_check) < NeighborhoodSize;
1483 ++ibucket) {
1484 tsl_hh_assert(!m_buckets[ibucket].empty());
1485
1486 const size_t my_hash = use_stored_hash
1487 ? m_buckets[ibucket].truncated_bucket_hash()
1488 : hash_key(KeySelect()(m_buckets[ibucket].value()));
1489 if (bucket_for_hash(my_hash) != expand_growth_policy.bucket_for_hash(my_hash)) {
1490 return true;
1491 }
1492 }
1493
1494 return false;
1495 }
1496
1497 /*
1498 * Return the index of an empty bucket in m_buckets_data.
1499 * If none, the returned index equals m_buckets_data.size()
1500 */
1501 std::size_t find_empty_bucket(std::size_t ibucket_start) const
1502 {
1503 const std::size_t limit =
1504 std::min(ibucket_start + MAX_PROBES_FOR_EMPTY_BUCKET, m_buckets_data.size());
1505 for (; ibucket_start < limit; ibucket_start++) {
1506 if (m_buckets[ibucket_start].empty()) {
1507 return ibucket_start;
1508 }
1509 }
1510
1511 return m_buckets_data.size();
1512 }
1513
1514 /*
1515 * Insert value in ibucket_empty where value originally belongs to
1516 * ibucket_for_hash
1517 *
1518 * Return bucket iterator to ibucket_empty
1519 */
1520 template <typename... Args>
1521 iterator_buckets insert_in_bucket(std::size_t ibucket_empty, std::size_t ibucket_for_hash,
1522 std::size_t my_hash, Args &&...value_type_args)
1523 {
1524 tsl_hh_assert(ibucket_empty >= ibucket_for_hash);
1525 tsl_hh_assert(m_buckets[ibucket_empty].empty());
1527 std::forward<Args>(value_type_args)...);
1528
1529 tsl_hh_assert(!m_buckets[ibucket_for_hash].empty());
1530 m_buckets[ibucket_for_hash].toggle_neighbor_presence(ibucket_empty - ibucket_for_hash);
1531 m_nb_elements++;
1532
1533 return m_buckets_data.begin() + ibucket_empty;
1534 }
1535
1536 template <class... Args, class U = OverflowContainer,
1537 typename std::enable_if<!has_key_compare<U>::value>::type * = nullptr>
1538 iterator_overflow insert_in_overflow(std::size_t ibucket_for_hash, Args &&...value_type_args)
1539 {
1540 auto it = m_overflow_elements.emplace(m_overflow_elements.end(),
1541 std::forward<Args>(value_type_args)...);
1542
1543 m_buckets[ibucket_for_hash].set_overflow(true);
1544 m_nb_elements++;
1545
1546 return it;
1547 }
1548
1549 template <class... Args, class U = OverflowContainer,
1550 typename std::enable_if<has_key_compare<U>::value>::type * = nullptr>
1551 iterator_overflow insert_in_overflow(std::size_t ibucket_for_hash, Args &&...value_type_args)
1552 {
1553 auto it = m_overflow_elements.emplace(std::forward<Args>(value_type_args)...).first;
1554
1555 m_buckets[ibucket_for_hash].set_overflow(true);
1556 m_nb_elements++;
1557
1558 return it;
1559 }
1560
1561 /*
1562 * Try to swap the bucket ibucket_empty_in_out with a bucket preceding it
1563 * while keeping the neighborhood conditions correct.
1564 *
1565 * If a swap was possible, the position of ibucket_empty_in_out will be closer
1566 * to 0 and true will re returned.
1567 */
1568 bool swap_empty_bucket_closer(std::size_t &ibucket_empty_in_out)
1569 {
1570 tsl_hh_assert(ibucket_empty_in_out >= NeighborhoodSize);
1571 const std::size_t neighborhood_start = ibucket_empty_in_out - NeighborhoodSize + 1;
1572
1573 for (std::size_t to_check = neighborhood_start; to_check < ibucket_empty_in_out;
1574 to_check++) {
1575 neighborhood_bitmap neighborhood_infos = m_buckets[to_check].neighborhood_infos();
1576 std::size_t to_swap = to_check;
1577
1578 while (neighborhood_infos != 0 && to_swap < ibucket_empty_in_out) {
1579 if ((neighborhood_infos & 1) == 1) {
1580 tsl_hh_assert(m_buckets[ibucket_empty_in_out].empty());
1581 tsl_hh_assert(!m_buckets[to_swap].empty());
1582
1583 m_buckets[to_swap].swap_value_into_empty_bucket(m_buckets[ibucket_empty_in_out]);
1584
1586 !m_buckets[to_check].check_neighbor_presence(ibucket_empty_in_out - to_check));
1587 tsl_hh_assert(m_buckets[to_check].check_neighbor_presence(to_swap - to_check));
1588
1589 m_buckets[to_check].toggle_neighbor_presence(ibucket_empty_in_out - to_check);
1590 m_buckets[to_check].toggle_neighbor_presence(to_swap - to_check);
1591
1592 ibucket_empty_in_out = to_swap;
1593
1594 return true;
1595 }
1596
1597 to_swap++;
1598 neighborhood_infos = neighborhood_bitmap(neighborhood_infos >> 1);
1599 }
1600 }
1601
1602 return false;
1603 }
1604
1605 template <class K, class U = ValueSelect,
1606 typename std::enable_if<has_mapped_type<U>::value>::type * = nullptr>
1607 typename U::value_type *find_value_impl(const K &key, std::size_t my_hash,
1609 {
1610 return const_cast<typename U::value_type *>(
1611 static_cast<const hopscotch_hash *>(this)->find_value_impl(key, my_hash,
1613 }
1614
1615 /*
1616 * Avoid the creation of an iterator to just get the value for operator[] and
1617 * at() in maps. Faster this way.
1618 *
1619 * Return null if no value for the key (TODO use std::optional when
1620 * available).
1621 */
1622 template <class K, class U = ValueSelect,
1623 typename std::enable_if<has_mapped_type<U>::value>::type * = nullptr>
1624 const typename U::value_type *find_value_impl(const K &key, std::size_t my_hash,
1625 const hopscotch_bucket *bucket_for_hash) const
1626 {
1627 const hopscotch_bucket *bucket_found = find_in_buckets(key, my_hash, bucket_for_hash);
1628 if (bucket_found != nullptr) {
1629 return std::addressof(ValueSelect()(bucket_found->value()));
1630 }
1631
1632 if (bucket_for_hash->has_overflow()) {
1633 auto it_overflow = find_in_overflow(key);
1634 if (it_overflow != m_overflow_elements.end()) {
1635 return std::addressof(ValueSelect()(*it_overflow));
1636 }
1637 }
1638
1639 return nullptr;
1640 }
1641
1642 template <class K>
1643 size_type count_impl(const K &key, std::size_t my_hash,
1644 const hopscotch_bucket *bucket_for_hash) const
1645 {
1646 if (find_in_buckets(key, my_hash, bucket_for_hash) != nullptr) {
1647 return 1;
1648 }
1649 else if (bucket_for_hash->has_overflow() &&
1650 find_in_overflow(key) != m_overflow_elements.cend()) {
1651 return 1;
1652 }
1653 else {
1654 return 0;
1655 }
1656 }
1657
1658 template <class K>
1659 iterator find_impl(const K &key, std::size_t my_hash, hopscotch_bucket *bucket_for_hash)
1660 {
1661 hopscotch_bucket *bucket_found = find_in_buckets(key, my_hash, bucket_for_hash);
1662 if (bucket_found != nullptr) {
1663 return iterator(m_buckets_data.begin() +
1664 std::distance(m_buckets_data.data(), bucket_found),
1665 m_buckets_data.end(), m_overflow_elements.begin());
1666 }
1667
1668 if (!bucket_for_hash->has_overflow()) {
1669 return end();
1670 }
1671
1672 return iterator(m_buckets_data.end(), m_buckets_data.end(), find_in_overflow(key));
1673 }
1674
1675 template <class K>
1676 const_iterator find_impl(const K &key, std::size_t my_hash,
1677 const hopscotch_bucket *bucket_for_hash) const
1678 {
1679 const hopscotch_bucket *bucket_found = find_in_buckets(key, my_hash, bucket_for_hash);
1680 if (bucket_found != nullptr) {
1681 return const_iterator(m_buckets_data.cbegin() +
1682 std::distance(m_buckets_data.data(), bucket_found),
1683 m_buckets_data.cend(), m_overflow_elements.cbegin());
1684 }
1685
1686 if (!bucket_for_hash->has_overflow()) {
1687 return cend();
1688 }
1689
1690 return const_iterator(m_buckets_data.cend(), m_buckets_data.cend(), find_in_overflow(key));
1691 }
1692
1693 template <class K>
1694 hopscotch_bucket *find_in_buckets(const K &key, std::size_t my_hash,
1696 {
1697 const hopscotch_bucket *bucket_found =
1698 static_cast<const hopscotch_hash *>(this)->find_in_buckets(key, my_hash,
1700 return const_cast<hopscotch_bucket *>(bucket_found);
1701 }
1702
1703 /**
1704 * Return a pointer to the bucket which has the value, nullptr otherwise.
1705 */
1706 template <class K>
1707 const hopscotch_bucket *find_in_buckets(const K &key, std::size_t my_hash,
1708 const hopscotch_bucket *bucket_for_hash) const
1709 {
1710 (void)my_hash; // Avoid warning of unused variable when StoreHash is false;
1711
1712 // TODO Try to optimize the function.
1713 // I tried to use ffs and __builtin_ffs functions but I could not reduce
1714 // the time the function takes with -march=native
1715
1717 while (neighborhood_infos != 0) {
1718 if ((neighborhood_infos & 1) == 1) {
1719 // Check StoreHash before calling bucket_hash_equal. Functionally it
1720 // doesn't change anythin. If StoreHash is false, bucket_hash_equal is a
1721 // no-op. Avoiding the call is there to help GCC optimizes `my_hash`
1722 // parameter away, it seems to not be able to do without this hint.
1723 if ((!StoreHash || bucket_for_hash->bucket_hash_equal(my_hash)) &&
1724 compare_keys(KeySelect()(bucket_for_hash->value()), key)) {
1725 return bucket_for_hash;
1726 }
1727 }
1728
1730 neighborhood_infos = neighborhood_bitmap(neighborhood_infos >> 1);
1731 }
1732
1733 return nullptr;
1734 }
1735
1736 template <class K, class U = OverflowContainer,
1737 typename std::enable_if<!has_key_compare<U>::value>::type * = nullptr>
1739 {
1740 return std::find_if(
1742 [&](const value_type &value) { return compare_keys(key, KeySelect()(value)); });
1743 }
1744
1745 template <class K, class U = OverflowContainer,
1746 typename std::enable_if<!has_key_compare<U>::value>::type * = nullptr>
1748 {
1749 return std::find_if(
1751 [&](const value_type &value) { return compare_keys(key, KeySelect()(value)); });
1752 }
1753
1754 template <class K, class U = OverflowContainer,
1755 typename std::enable_if<has_key_compare<U>::value>::type * = nullptr>
1757 {
1758 return m_overflow_elements.find(key);
1759 }
1760
1761 template <class K, class U = OverflowContainer,
1762 typename std::enable_if<has_key_compare<U>::value>::type * = nullptr>
1764 {
1765 return m_overflow_elements.find(key);
1766 }
1767
1768 template <class U = OverflowContainer,
1769 typename std::enable_if<!has_key_compare<U>::value>::type * = nullptr>
1771 {
1772 return hopscotch_hash(bucket_count, static_cast<Hash &>(*this),
1773 static_cast<KeyEqual &>(*this), get_allocator(), m_max_load_factor);
1774 }
1775
1776 template <class U = OverflowContainer,
1777 typename std::enable_if<has_key_compare<U>::value>::type * = nullptr>
1779 {
1780 return hopscotch_hash(bucket_count, static_cast<Hash &>(*this),
1781 static_cast<KeyEqual &>(*this), get_allocator(), m_max_load_factor,
1782 m_overflow_elements.key_comp());
1783 }
1784
1785 public:
1787 static constexpr float DEFAULT_MAX_LOAD_FACTOR = (NeighborhoodSize <= 30) ? 0.8f : 0.9f;
1788
1789 private:
1790 static const std::size_t MAX_PROBES_FOR_EMPTY_BUCKET = 12 * NeighborhoodSize;
1791 static constexpr float MIN_LOAD_FACTOR_FOR_REHASH = 0.1f;
1792
1793 /**
1794 * We can only use the hash on rehash if the size of the hash type is the same
1795 * as the stored one or if we use a power of two modulo. In the case of the
1796 * power of two modulo, we just mask the least significant bytes, we just have
1797 * to check that the truncated_hash_type didn't truncated too much bytes.
1798 */
1799 template <
1800 class T = size_type,
1801 typename std::enable_if<std::is_same<T, truncated_hash_type>::value>::type * = nullptr>
1802 static bool USE_STORED_HASH_ON_REHASH(size_type /*bucket_count*/)
1803 {
1804 return StoreHash;
1805 }
1806
1807 template <
1808 class T = size_type,
1809 typename std::enable_if<!std::is_same<T, truncated_hash_type>::value>::type * = nullptr>
1811 {
1812 (void)bucket_count;
1815 return (bucket_count - 1) <= std::numeric_limits<truncated_hash_type>::max();
1816 }
1817 else {
1818 return false;
1819 }
1820 }
1821
1822 /**
1823 * Return an always valid pointer to an static empty hopscotch_bucket.
1824 */
1826 {
1827 static hopscotch_bucket empty_bucket;
1828 return &empty_bucket;
1829 }
1830
1831 private:
1834
1835 /**
1836 * Points to m_buckets_data.data() if !m_buckets_data.empty() otherwise points
1837 * to static_empty_bucket_ptr. This variable is useful to avoid the cost of
1838 * checking if m_buckets_data is empty when trying to find an element.
1839 *
1840 * TODO Remove m_buckets_data and only use a pointer+size instead of a
1841 * pointer+vector to save some space in the hopscotch_hash object.
1842 */
1844
1846
1847 /**
1848 * Min size of the hash table before a rehash can occurs automatically (except
1849 * if m_max_load_threshold_rehash os reached). If the neighborhood of a bucket
1850 * is full before the min is reached, the elements are put into
1851 * m_overflow_elements.
1852 */
1854
1855 /**
1856 * Max size of the hash table before a rehash occurs automatically to grow the
1857 * table.
1858 */
1860
1862 };
1863
1864 } // end namespace detail_hopscotch_hash
1865
1866} // end namespace tsl
1867// NOLINTEND
1868
1869#endif
void set_hash(truncated_hash_type my_hash) noexcept
Definition hopscotch_hash.h:202
bool bucket_hash_equal(std::size_t my_hash) const noexcept
Definition hopscotch_hash.h:192
truncated_hash_type m_hash
Definition hopscotch_hash.h:205
truncated_hash_type truncated_bucket_hash() const noexcept
Definition hopscotch_hash.h:197
void copy_hash(const hopscotch_bucket_hash &bucket) noexcept
Definition hopscotch_hash.h:200
truncated_hash_type truncated_bucket_hash() const noexcept
Definition hopscotch_hash.h:181
void set_hash(truncated_hash_type) noexcept
Definition hopscotch_hash.h:186
bool bucket_hash_equal(std::size_t) const noexcept
Definition hopscotch_hash.h:179
void copy_hash(const hopscotch_bucket_hash &) noexcept
Definition hopscotch_hash.h:184
Definition hopscotch_hash.h:210
const value_type & value() const noexcept
Definition hopscotch_hash.h:333
static truncated_hash_type truncate_hash(std::size_t my_hash) noexcept
Definition hopscotch_hash.h:382
value_type & value() noexcept
Definition hopscotch_hash.h:327
hopscotch_bucket(const hopscotch_bucket &bucket) noexcept(std::is_nothrow_copy_constructible< value_type >::value)
Definition hopscotch_hash.h:242
void clear() noexcept
Definition hopscotch_hash.h:372
~hopscotch_bucket() noexcept
Definition hopscotch_hash.h:284
neighborhood_bitmap m_neighborhood_infos
Definition hopscotch_hash.h:407
hopscotch_bucket(hopscotch_bucket &&bucket) noexcept(std::is_nothrow_move_constructible< value_type >::value)
Definition hopscotch_hash.h:253
void destroy_value() noexcept
Definition hopscotch_hash.h:398
static const std::size_t MAX_NEIGHBORHOOD_SIZE
Definition hopscotch_hash.h:213
typename std::aligned_storage< sizeof(value_type), alignof(value_type)>::type storage
Definition hopscotch_hash.h:405
bool check_neighbor_presence(std::size_t ineighbor) const noexcept
Definition hopscotch_hash.h:317
ValueType value_type
Definition hopscotch_hash.h:232
typename smallest_type_for_min_bits< NeighborhoodSize+ NB_RESERVED_BITS_IN_NEIGHBORHOOD >::type neighborhood_bitmap
Definition hopscotch_hash.h:233
storage m_value
Definition hopscotch_hash.h:408
static const std::size_t MIN_NEIGHBORHOOD_SIZE
Definition hopscotch_hash.h:212
void set_value_of_empty_bucket(truncated_hash_type my_hash, Args &&...value_type_args)
Definition hopscotch_hash.h:340
void swap_value_into_empty_bucket(hopscotch_bucket &empty_bucket)
Definition hopscotch_hash.h:350
void toggle_neighbor_presence(std::size_t ineighbor) noexcept
Definition hopscotch_hash.h:310
hopscotch_bucket() noexcept
Definition hopscotch_hash.h:237
hopscotch_bucket & operator=(const hopscotch_bucket &bucket) noexcept(std::is_nothrow_copy_constructible< value_type >::value)
Definition hopscotch_hash.h:265
bool empty() const noexcept
Definition hopscotch_hash.h:308
void set_empty(bool is_empty) noexcept
Definition hopscotch_hash.h:388
neighborhood_bitmap neighborhood_infos() const noexcept
Definition hopscotch_hash.h:291
bool has_overflow() const noexcept
Definition hopscotch_hash.h:306
void remove_value() noexcept
Definition hopscotch_hash.h:364
hopscotch_bucket & operator=(hopscotch_bucket &&)=delete
void set_overflow(bool has_overflow) noexcept
Definition hopscotch_hash.h:296
hopscotch_iterator(const hopscotch_iterator<!TIsConst > &other) noexcept
Definition hopscotch_hash.h:520
const hopscotch_hash::key_type & key() const
Definition hopscotch_hash.h:532
typename std::conditional< IsConst, typename hopscotch_hash::const_iterator_overflow, typename hopscotch_hash::iterator_overflow >::type iterator_overflow
Definition hopscotch_hash.h:498
typename std::conditional< IsConst, typename hopscotch_hash::const_iterator_buckets, typename hopscotch_hash::iterator_buckets >::type iterator_bucket
Definition hopscotch_hash.h:495
hopscotch_iterator() noexcept
Definition hopscotch_hash.h:516
value_type & reference
Definition hopscotch_hash.h:513
hopscotch_iterator(iterator_bucket buckets_iterator, iterator_bucket buckets_end_iterator, iterator_overflow overflow_iterator) noexcept
Definition hopscotch_hash.h:502
std::conditional< IsConst, consttypenameU::value_type &, typenameU::value_type & >::type value() const
Definition hopscotch_hash.h:545
iterator_bucket m_buckets_iterator
Definition hopscotch_hash.h:606
friend bool operator!=(const hopscotch_iterator &lhs, const hopscotch_iterator &rhs)
Definition hopscotch_hash.h:600
hopscotch_iterator operator++(int)
Definition hopscotch_hash.h:586
hopscotch_iterator & operator=(const hopscotch_iterator &other)=default
hopscotch_iterator(const hopscotch_iterator &other)=default
std::forward_iterator_tag iterator_category
Definition hopscotch_hash.h:510
std::ptrdiff_t difference_type
Definition hopscotch_hash.h:512
reference operator*() const
Definition hopscotch_hash.h:554
pointer operator->() const
Definition hopscotch_hash.h:563
iterator_overflow m_overflow_iterator
Definition hopscotch_hash.h:608
value_type * pointer
Definition hopscotch_hash.h:514
hopscotch_iterator & operator++()
Definition hopscotch_hash.h:572
hopscotch_iterator & operator=(hopscotch_iterator &&other)=default
const typename hopscotch_hash::value_type value_type
Definition hopscotch_hash.h:511
iterator_bucket m_buckets_end_iterator
Definition hopscotch_hash.h:607
friend bool operator==(const hopscotch_iterator &lhs, const hopscotch_iterator &rhs)
Definition hopscotch_hash.h:594
Definition hopscotch_hash.h:431
const_iterator begin() const noexcept
Definition hopscotch_hash.h:753
size_type erase(const K &key)
Definition hopscotch_hash.h:968
iterator_buckets insert_in_bucket(std::size_t ibucket_empty, std::size_t ibucket_for_hash, std::size_t my_hash, Args &&...value_type_args)
Definition hopscotch_hash.h:1521
const_iterator cend() const noexcept
Definition hopscotch_hash.h:772
std::pair< iterator, bool > emplace(Args &&...args)
Definition hopscotch_hash.h:890
const_iterator find_impl(const K &key, std::size_t my_hash, const hopscotch_bucket *bucket_for_hash) const
Definition hopscotch_hash.h:1676
typename overflow_container_type::const_iterator const_iterator_overflow
Definition hopscotch_hash.h:480
hopscotch_bucket * static_empty_bucket_ptr()
Definition hopscotch_hash.h:1825
void swap(hopscotch_hash &other)
Definition hopscotch_hash.h:994
buckets_container_type m_buckets_data
Definition hopscotch_hash.h:1832
size_type m_min_load_threshold_rehash
Definition hopscotch_hash.h:1853
std::size_t size_type
Definition hopscotch_hash.h:446
Hash hasher
Definition hopscotch_hash.h:448
std::pair< iterator, bool > insert_impl(P &&value)
Definition hopscotch_hash.h:1415
iterator_overflow mutable_overflow_iterator(const_iterator_overflow it)
Definition hopscotch_hash.h:1342
static const std::size_t MAX_PROBES_FOR_EMPTY_BUCKET
Definition hopscotch_hash.h:1790
std::pair< iterator, bool > insert(P &&value)
Definition hopscotch_hash.h:804
const U::value_type * find_value_impl(const K &key, std::size_t my_hash, const hopscotch_bucket *bucket_for_hash) const
Definition hopscotch_hash.h:1624
std::vector< hopscotch_bucket, buckets_allocator > buckets_container_type
Definition hopscotch_hash.h:465
typename std::allocator_traits< allocator_type >::template rebind_alloc< hopscotch_bucket > buckets_allocator
Definition hopscotch_hash.h:463
iterator insert_or_assign(const_iterator hint, const key_type &k, M &&obj)
Definition hopscotch_hash.h:866
size_type count_impl(const K &key, std::size_t my_hash, const hopscotch_bucket *bucket_for_hash) const
Definition hopscotch_hash.h:1643
hopscotch_bucket * m_buckets
Definition hopscotch_hash.h:1843
const_iterator find(const K &key) const
Definition hopscotch_hash.h:1084
value_type & reference
Definition hopscotch_hash.h:451
bool contains(const K &key) const
Definition hopscotch_hash.h:1094
const_iterator cbegin() const noexcept
Definition hopscotch_hash.h:755
size_type max_size() const noexcept
Definition hopscotch_hash.h:785
OverflowContainer overflow_container_type
Definition hopscotch_hash.h:467
hopscotch_iterator< true > const_iterator
Definition hopscotch_hash.h:456
float max_load_factor() const
Definition hopscotch_hash.h:1162
value_type * pointer
Definition hopscotch_hash.h:453
size_type count(const K &key) const
Definition hopscotch_hash.h:1070
U::key_compare key_comp() const
Definition hopscotch_hash.h:1211
const_iterator end() const noexcept
Definition hopscotch_hash.h:770
overflow_container_type m_overflow_elements
Definition hopscotch_hash.h:1833
std::pair< iterator, bool > insert(const value_type &value)
Definition hopscotch_hash.h:800
typename buckets_container_type::iterator iterator_buckets
Definition hopscotch_hash.h:476
key_equal key_eq() const
Definition hopscotch_hash.h:1187
std::ptrdiff_t difference_type
Definition hopscotch_hash.h:447
static const size_type DEFAULT_INIT_BUCKETS_SIZE
Definition hopscotch_hash.h:1786
size_type count(const K &key, std::size_t my_hash) const
Definition hopscotch_hash.h:1072
iterator_overflow erase_from_overflow(const_iterator_overflow pos, std::size_t ibucket_for_hash)
Definition hopscotch_hash.h:1349
static bool USE_STORED_HASH_ON_REHASH(size_type bucket_count)
Definition hopscotch_hash.h:1810
hopscotch_hash(const hopscotch_hash &other)
Definition hopscotch_hash.h:673
hopscotch_hash(hopscotch_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 &&std::is_nothrow_move_constructible< overflow_container_type >::value)
Definition hopscotch_hash.h:684
typename hopscotch_bucket::neighborhood_bitmap neighborhood_bitmap
Definition hopscotch_hash.h:461
std::pair< iterator, bool > try_emplace_impl(P &&key, Args &&...args_value)
Definition hopscotch_hash.h:1399
U::value_type & at(const K &key, std::size_t my_hash)
Definition hopscotch_hash.h:1022
size_type erase(const K &key, std::size_t my_hash)
Definition hopscotch_hash.h:970
float load_factor() const
Definition hopscotch_hash.h:1153
static constexpr float MIN_LOAD_FACTOR_FOR_REHASH
Definition hopscotch_hash.h:1791
typename buckets_container_type::const_iterator const_iterator_buckets
Definition hopscotch_hash.h:477
hopscotch_bucket * find_in_buckets(const K &key, std::size_t my_hash, hopscotch_bucket *bucket_for_hash)
Definition hopscotch_hash.h:1694
allocator_type get_allocator() const
Definition hopscotch_hash.h:738
bool empty() const noexcept
Definition hopscotch_hash.h:781
KeyEqual key_equal
Definition hopscotch_hash.h:449
iterator insert(const_iterator hint, const value_type &value)
Definition hopscotch_hash.h:811
typename overflow_container_type::iterator iterator_overflow
Definition hopscotch_hash.h:479
iterator erase(iterator pos)
Definition hopscotch_hash.h:935
typename std::integral_constant< bool, !std::is_same< U, void >::value > has_mapped_type
Definition hopscotch_hash.h:434
static constexpr float DEFAULT_MAX_LOAD_FACTOR
Definition hopscotch_hash.h:1787
U::value_type & at(const K &key)
Definition hopscotch_hash.h:1015
iterator begin() noexcept
Definition hopscotch_hash.h:743
hopscotch_hash new_hopscotch_hash(size_type bucket_count)
Definition hopscotch_hash.h:1770
const value_type * const_pointer
Definition hopscotch_hash.h:454
float m_max_load_factor
Definition hopscotch_hash.h:1861
bool compare_keys(const K1 &key1, const K2 &key2) const
Definition hopscotch_hash.h:1219
std::pair< iterator, bool > try_emplace(key_type &&k, Args &&...args)
Definition hopscotch_hash.h:906
std::pair< iterator, iterator > equal_range(const K &key)
Definition hopscotch_hash.h:1101
Allocator allocator_type
Definition hopscotch_hash.h:450
U::value_type * find_value_impl(const K &key, std::size_t my_hash, hopscotch_bucket *bucket_for_hash)
Definition hopscotch_hash.h:1607
iterator find(const K &key, std::size_t my_hash)
Definition hopscotch_hash.h:1079
size_type bucket_count() const
Definition hopscotch_hash.h:1128
std::pair< iterator, bool > insert_or_assign(const key_type &k, M &&obj)
Definition hopscotch_hash.h:856
size_type max_bucket_count() const
Definition hopscotch_hash.h:1143
std::pair< const_iterator, const_iterator > equal_range(const K &key, std::size_t my_hash) const
Definition hopscotch_hash.h:1119
void clear() noexcept
Definition hopscotch_hash.h:790
std::size_t bucket_for_hash(std::size_t my_hash) const
Definition hopscotch_hash.h:1224
const_iterator find(const K &key, std::size_t my_hash) const
Definition hopscotch_hash.h:1089
std::pair< iterator, bool > insert_value(std::size_t ibucket_for_hash, std::size_t my_hash, Args &&...value_type_args)
Definition hopscotch_hash.h:1430
iterator erase(const_iterator first, const_iterator last)
Definition hopscotch_hash.h:954
std::size_t find_empty_bucket(std::size_t ibucket_start) const
Definition hopscotch_hash.h:1501
std::pair< iterator, bool > try_emplace(const key_type &k, Args &&...args)
Definition hopscotch_hash.h:901
hasher hash_function() const
Definition hopscotch_hash.h:1185
size_type size() const noexcept
Definition hopscotch_hash.h:783
iterator_overflow find_in_overflow(const K &key)
Definition hopscotch_hash.h:1738
hopscotch_hash(size_type bucket_count, const Hash &my_hash, const KeyEqual &equal, const Allocator &alloc, float max_load_factor)
Definition hopscotch_hash.h:614
hopscotch_iterator< false > iterator
Definition hopscotch_hash.h:455
std::pair< iterator, bool > insert_or_assign_impl(K &&key, M &&obj)
Definition hopscotch_hash.h:1388
U::value_type & operator[](K &&key)
Definition hopscotch_hash.h:1052
void insert(InputIt first, InputIt last)
Definition hopscotch_hash.h:836
iterator emplace_hint(const_iterator hint, Args &&...args)
Definition hopscotch_hash.h:895
iterator_overflow insert_in_overflow(std::size_t ibucket_for_hash, Args &&...value_type_args)
Definition hopscotch_hash.h:1538
iterator insert_or_assign(const_iterator hint, key_type &&k, M &&obj)
Definition hopscotch_hash.h:878
iterator insert(const_iterator hint, value_type &&value)
Definition hopscotch_hash.h:827
iterator insert(const_iterator hint, P &&value)
Definition hopscotch_hash.h:822
typename KeySelect::key_type key_type
Definition hopscotch_hash.h:444
const_iterator_overflow find_in_overflow(const K &key) const
Definition hopscotch_hash.h:1747
void erase_from_bucket(hopscotch_bucket &bucket_for_value, std::size_t ibucket_for_hash) noexcept
Definition hopscotch_hash.h:1376
std::pair< iterator, bool > insert_or_assign(key_type &&k, M &&obj)
Definition hopscotch_hash.h:861
iterator try_emplace(const_iterator hint, const key_type &k, Args &&...args)
Definition hopscotch_hash.h:912
void rehash(size_type count_)
Definition hopscotch_hash.h:1171
iterator erase(const_iterator pos)
Definition hopscotch_hash.h:937
hopscotch_hash & operator=(const hopscotch_hash &other)
Definition hopscotch_hash.h:710
bool will_neighborhood_change_on_rehash(size_t ibucket_neighborhood_check) const
Definition hopscotch_hash.h:1474
iterator try_emplace(const_iterator hint, key_type &&k, Args &&...args)
Definition hopscotch_hash.h:922
hopscotch_hash & operator=(hopscotch_hash &&other)
Definition hopscotch_hash.h:730
size_type m_max_load_threshold_rehash
Definition hopscotch_hash.h:1859
void max_load_factor(float ml)
Definition hopscotch_hash.h:1164
void rehash_impl(size_type count_)
Definition hopscotch_hash.h:1235
bool swap_empty_bucket_closer(std::size_t &ibucket_empty_in_out)
Definition hopscotch_hash.h:1568
static bool USE_STORED_HASH_ON_REHASH(size_type)
Definition hopscotch_hash.h:1802
std::pair< const_iterator, const_iterator > equal_range(const K &key) const
Definition hopscotch_hash.h:1113
size_type m_nb_elements
Definition hopscotch_hash.h:1845
const value_type & const_reference
Definition hopscotch_hash.h:452
std::pair< iterator, iterator > equal_range(const K &key, std::size_t my_hash)
Definition hopscotch_hash.h:1107
hopscotch_hash(size_type bucket_count, const Hash &p_hash, const KeyEqual &equal, const Allocator &alloc, float max_load_factor, const typename OC::key_compare &comp)
Definition hopscotch_hash.h:644
iterator find_impl(const K &key, std::size_t my_hash, hopscotch_bucket *bucket_for_hash)
Definition hopscotch_hash.h:1659
iterator end() noexcept
Definition hopscotch_hash.h:765
bool contains(const K &key, std::size_t my_hash) const
Definition hopscotch_hash.h:1096
iterator mutable_iterator(const_iterator pos)
Definition hopscotch_hash.h:1192
const hopscotch_bucket * find_in_buckets(const K &key, std::size_t my_hash, const hopscotch_bucket *bucket_for_hash) const
Definition hopscotch_hash.h:1707
std::size_t hash_key(const K &key) const
Definition hopscotch_hash.h:1217
const U::value_type & at(const K &key) const
Definition hopscotch_hash.h:1030
void reserve(size_type count_)
Definition hopscotch_hash.h:1177
const U::value_type & at(const K &key, std::size_t my_hash) const
Definition hopscotch_hash.h:1037
iterator find(const K &key)
Definition hopscotch_hash.h:1077
std::pair< iterator, bool > insert(value_type &&value)
Definition hopscotch_hash.h:809
size_type overflow_size() const noexcept
Definition hopscotch_hash.h:1207
ValueType value_type
Definition hopscotch_hash.h:445
Definition hopscotch_growth_policy.h:82
#define TSL_HH_THROW_OR_TERMINATE(ex, msg)
Definition hopscotch_growth_policy.h:64
#define tsl_hh_assert(expr)
Definition hopscotch_growth_policy.h:47
std::uint_least32_t truncated_hash_type
Definition hopscotch_hash.h:170
static const std::size_t SMALLEST_TYPE_MAX_BITS_SUPPORTED
Definition hopscotch_hash.h:109
static const std::size_t NB_RESERVED_BITS_IN_NEIGHBORHOOD
Definition hopscotch_hash.h:168
T numeric_cast(U value, const char *error_message="numeric_cast() failed.")
Definition hopscotch_hash.h:89
Definition bhopscotch_map.h:38
Definition hopscotch_hash.h:70
Definition hopscotch_hash.h:55
void type
Definition hopscotch_hash.h:56