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