ordered-map
ordered_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_ORDERED_HASH_H
25 #define TSL_ORDERED_HASH_H
26 
27 
28 #include <algorithm>
29 #include <cassert>
30 #include <climits>
31 #include <cmath>
32 #include <cstddef>
33 #include <cstdint>
34 #include <exception>
35 #include <functional>
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>
44 
45 
46 /**
47  * Macros for compatibility with GCC 4.8
48  */
49 #if (defined(__GNUC__) && (__GNUC__ == 4) && (__GNUC_MINOR__ < 9))
50 # define TSL_OH_NO_CONTAINER_ERASE_CONST_ITERATOR
51 # define TSL_OH_NO_CONTAINER_EMPLACE_CONST_ITERATOR
52 #endif
53 
54 /**
55  * Only activate tsl_oh_assert if TSL_DEBUG is defined.
56  * This way we avoid the performance hit when NDEBUG is not defined with assert as tsl_oh_assert is used a lot
57  * (people usually compile with "-O3" and not "-O3 -DNDEBUG").
58  */
59 #ifdef TSL_DEBUG
60 # define tsl_oh_assert(expr) assert(expr)
61 #else
62 # define tsl_oh_assert(expr) (static_cast<void>(0))
63 #endif
64 
65 /**
66  * If exceptions are enabled, throw the exception passed in parameter, otherwise call std::terminate.
67  */
68 #if (defined(__cpp_exceptions) || defined(__EXCEPTIONS) || (defined (_MSC_VER) && defined (_CPPUNWIND))) && !defined(TSL_NO_EXCEPTIONS)
69 # define TSL_OH_THROW_OR_TERMINATE(ex, msg) throw ex(msg)
70 #else
71 # ifdef NDEBUG
72 # define TSL_OH_THROW_OR_TERMINATE(ex, msg) std::terminate()
73 # else
74 # include <cstdio>
75 # define TSL_OH_THROW_OR_TERMINATE(ex, msg) do { std::fprintf(stderr, msg); std::terminate(); } while(0)
76 # endif
77 #endif
78 
79 
80 namespace tsl {
81 
83 
84 template<typename T>
85 struct make_void {
86  using type = void;
87 };
88 
89 template<typename T, typename = void>
90 struct has_is_transparent: std::false_type {
91 };
92 
93 template<typename T>
94 struct has_is_transparent<T, typename make_void<typename T::is_transparent>::type>: std::true_type {
95 };
96 
97 
98 template<typename T, typename = void>
99 struct is_vector: std::false_type {
100 };
101 
102 template<typename T>
103 struct is_vector<T, typename std::enable_if<
104  std::is_same<T, std::vector<typename T::value_type, typename T::allocator_type>>::value
105  >::type>: std::true_type {
106 };
107 
108 template<typename T, typename U>
109 static T numeric_cast(U value, const char* error_message = "numeric_cast() failed.") {
110  T ret = static_cast<T>(value);
111  if(static_cast<U>(ret) != value) {
112  TSL_OH_THROW_OR_TERMINATE(std::runtime_error, error_message);
113  }
114 
115  const bool is_same_signedness = (std::is_unsigned<T>::value && std::is_unsigned<U>::value) ||
116  (std::is_signed<T>::value && std::is_signed<U>::value);
117  if(!is_same_signedness && (ret < T{}) != (value < U{})) {
118  TSL_OH_THROW_OR_TERMINATE(std::runtime_error, error_message);
119  }
120 
121  return ret;
122 }
123 
124 
125 /**
126  * Fixed size type used to represent size_type values on serialization. Need to be big enough
127  * to represent a std::size_t on 32 and 64 bits platforms, and must be the same size on both platforms.
128  */
129 using slz_size_type = std::uint64_t;
130 static_assert(std::numeric_limits<slz_size_type>::max() >= std::numeric_limits<std::size_t>::max(),
131  "slz_size_type must be >= std::size_t");
132 
133 template<class T, class Deserializer>
134 static T deserialize_value(Deserializer& deserializer) {
135  // MSVC < 2017 is not conformant, circumvent the problem by removing the template keyword
136 #if defined (_MSC_VER) && _MSC_VER < 1910
137  return deserializer.Deserializer::operator()<T>();
138 #else
139  return deserializer.Deserializer::template operator()<T>();
140 #endif
141 }
142 
143 
144 /**
145  * Each bucket entry stores an index which is the index in m_values corresponding to the bucket's value
146  * and a hash (which may be truncated to 32 bits depending on IndexType) corresponding to the hash of the value.
147  *
148  * The size of IndexType limits the size of the hash table to std::numeric_limits<IndexType>::max() - 1 elements (-1 due to
149  * a reserved value used to mark a bucket as empty).
150  */
151 template<class IndexType>
152 class bucket_entry {
153  static_assert(std::is_unsigned<IndexType>::value, "IndexType must be an unsigned value.");
154  static_assert(std::numeric_limits<IndexType>::max() <= std::numeric_limits<std::size_t>::max(),
155  "std::numeric_limits<IndexType>::max() must be <= std::numeric_limits<std::size_t>::max().");
156 
157 public:
158  using index_type = IndexType;
159  using truncated_hash_type = typename std::conditional<std::numeric_limits<IndexType>::max() <=
160  std::numeric_limits<std::uint_least32_t>::max(),
161  std::uint_least32_t,
162  std::size_t>::type;
163 
164  bucket_entry() noexcept: m_index(EMPTY_MARKER_INDEX), m_hash(0) {
165  }
166 
167  bool empty() const noexcept {
168  return m_index == EMPTY_MARKER_INDEX;
169  }
170 
171  void clear() noexcept {
172  m_index = EMPTY_MARKER_INDEX;
173  }
174 
175  index_type index() const noexcept {
176  tsl_oh_assert(!empty());
177  return m_index;
178  }
179 
180  index_type& index_ref() noexcept {
181  tsl_oh_assert(!empty());
182  return m_index;
183  }
184 
185  void set_index(index_type index) noexcept {
186  tsl_oh_assert(index <= max_size());
187 
188  m_index = index;
189  }
190 
191  truncated_hash_type truncated_hash() const noexcept {
192  tsl_oh_assert(!empty());
193  return m_hash;
194  }
195 
196  truncated_hash_type& truncated_hash_ref() noexcept {
197  tsl_oh_assert(!empty());
198  return m_hash;
199  }
200 
201  void set_hash(std::size_t hash) noexcept {
202  m_hash = truncate_hash(hash);
203  }
204 
205  template<class Serializer>
206  void serialize(Serializer& serializer) const {
207  const slz_size_type index = m_index;
208  serializer(index);
209 
210  const slz_size_type hash = m_hash;
211  serializer(hash);
212  }
213 
214  template<class Deserializer>
215  static bucket_entry deserialize(Deserializer& deserializer) {
216  const slz_size_type index = deserialize_value<slz_size_type>(deserializer);
217  const slz_size_type hash = deserialize_value<slz_size_type>(deserializer);
218 
219  bucket_entry bentry;
220  bentry.m_index = numeric_cast<index_type>(index, "Deserialized index is too big.");
221  bentry.m_hash = numeric_cast<truncated_hash_type>(hash, "Deserialized hash is too big.");
222 
223  return bentry;
224  }
225 
226 
227 
228  static truncated_hash_type truncate_hash(std::size_t hash) noexcept {
229  return truncated_hash_type(hash);
230  }
231 
232  static std::size_t max_size() noexcept {
233  return static_cast<std::size_t>(std::numeric_limits<index_type>::max()) - NB_RESERVED_INDEXES;
234  }
235 
236 private:
237  static const index_type EMPTY_MARKER_INDEX = std::numeric_limits<index_type>::max();
238  static const std::size_t NB_RESERVED_INDEXES = 1;
239 
240  index_type m_index;
241  truncated_hash_type m_hash;
242 };
243 
244 
245 
246 /**
247  * Internal common class used by ordered_map and ordered_set.
248  *
249  * ValueType is what will be stored by ordered_hash (usually std::pair<Key, T> for map and Key for set).
250  *
251  * KeySelect should be a FunctionObject which takes a ValueType in parameter and return a reference to the key.
252  *
253  * ValueSelect should be a FunctionObject which takes a ValueType in parameter and return a reference to the value.
254  * ValueSelect should be void if there is no value (in set for example).
255  *
256  * ValueTypeContainer is the container which will be used to store ValueType values.
257  * Usually a std::deque<ValueType, Allocator> or std::vector<ValueType, Allocator>.
258  *
259  *
260  *
261  * The orderd_hash structure is a hash table which preserves the order of insertion of the elements.
262  * To do so, it stores the values in the ValueTypeContainer (m_values) using emplace_back at each
263  * insertion of a new element. Another structure (m_buckets of type std::vector<bucket_entry>) will
264  * serve as buckets array for the hash table part. Each bucket stores an index which corresponds to
265  * the index in m_values where the bucket's value is and the (truncated) hash of this value. An index
266  * is used instead of a pointer to the value to reduce the size of each bucket entry.
267  *
268  * To resolve collisions in the buckets array, the structures use robin hood linear probing with
269  * backward shift deletion.
270  */
271 template<class ValueType,
272  class KeySelect,
273  class ValueSelect,
274  class Hash,
275  class KeyEqual,
276  class Allocator,
277  class ValueTypeContainer,
278  class IndexType>
279 class ordered_hash: private Hash, private KeyEqual {
280 private:
281  template<typename U>
282  using has_mapped_type = typename std::integral_constant<bool, !std::is_same<U, void>::value>;
283 
284  static_assert(std::is_same<typename ValueTypeContainer::value_type, ValueType>::value,
285  "ValueTypeContainer::value_type != ValueType. "
286  "Check that the ValueTypeContainer has 'Key' as type for a set or 'std::pair<Key, T>' as type for a map.");
287 
288  static_assert(std::is_same<typename ValueTypeContainer::allocator_type, Allocator>::value,
289  "ValueTypeContainer::allocator_type != Allocator. "
290  "Check that the allocator for ValueTypeContainer is the same as Allocator.");
291 
292  static_assert(std::is_same<typename Allocator::value_type, ValueType>::value,
293  "Allocator::value_type != ValueType. "
294  "Check that the allocator has 'Key' as type for a set or 'std::pair<Key, T>' as type for a map.");
295 
296 
297 public:
298  template<bool IsConst>
300 
301  using key_type = typename KeySelect::key_type;
302  using value_type = ValueType;
303  using size_type = std::size_t;
304  using difference_type = std::ptrdiff_t;
305  using hasher = Hash;
306  using key_equal = KeyEqual;
307  using allocator_type = Allocator;
308  using reference = value_type&;
309  using const_reference = const value_type&;
310  using pointer = value_type*;
311  using const_pointer = const value_type*;
312  using iterator = ordered_iterator<false>;
313  using const_iterator = ordered_iterator<true>;
314  using reverse_iterator = std::reverse_iterator<iterator>;
315  using const_reverse_iterator = std::reverse_iterator<const_iterator>;
316 
317  using values_container_type = ValueTypeContainer;
318 
319 public:
320  template<bool IsConst>
321  class ordered_iterator {
322  friend class ordered_hash;
323 
324  private:
325  using iterator = typename std::conditional<IsConst,
326  typename values_container_type::const_iterator,
327  typename values_container_type::iterator>::type;
328 
329 
330  ordered_iterator(iterator it) noexcept: m_iterator(it) {
331  }
332 
333  public:
334  using iterator_category = std::random_access_iterator_tag;
335  using value_type = const typename ordered_hash::value_type;
336  using difference_type = typename iterator::difference_type;
337  using reference = value_type&;
338  using pointer = value_type*;
339 
340 
341  ordered_iterator() noexcept {
342  }
343 
344  // Copy constructor from iterator to const_iterator.
345  template<bool TIsConst = IsConst, typename std::enable_if<TIsConst>::type* = nullptr>
346  ordered_iterator(const ordered_iterator<!TIsConst>& other) noexcept: m_iterator(other.m_iterator) {
347  }
348 
349  ordered_iterator(const ordered_iterator& other) = default;
350  ordered_iterator(ordered_iterator&& other) = default;
351  ordered_iterator& operator=(const ordered_iterator& other) = default;
352  ordered_iterator& operator=(ordered_iterator&& other) = default;
353 
354  const typename ordered_hash::key_type& key() const {
355  return KeySelect()(*m_iterator);
356  }
357 
358  template<class U = ValueSelect, typename std::enable_if<has_mapped_type<U>::value && IsConst>::type* = nullptr>
359  const typename U::value_type& value() const {
360  return U()(*m_iterator);
361  }
362 
363  template<class U = ValueSelect, typename std::enable_if<has_mapped_type<U>::value && !IsConst>::type* = nullptr>
364  typename U::value_type& value() {
365  return U()(*m_iterator);
366  }
367 
368  reference operator*() const { return *m_iterator; }
369  pointer operator->() const { return m_iterator.operator->(); }
370 
371  ordered_iterator& operator++() { ++m_iterator; return *this; }
372  ordered_iterator& operator--() { --m_iterator; return *this; }
373 
374  ordered_iterator operator++(int) { ordered_iterator tmp(*this); ++(*this); return tmp; }
375  ordered_iterator operator--(int) { ordered_iterator tmp(*this); --(*this); return tmp; }
376 
377  reference operator[](difference_type n) const { return m_iterator[n]; }
378 
379  ordered_iterator& operator+=(difference_type n) { m_iterator += n; return *this; }
380  ordered_iterator& operator-=(difference_type n) { m_iterator -= n; return *this; }
381 
382  ordered_iterator operator+(difference_type n) { ordered_iterator tmp(*this); tmp += n; return tmp; }
383  ordered_iterator operator-(difference_type n) { ordered_iterator tmp(*this); tmp -= n; return tmp; }
384 
385  friend bool operator==(const ordered_iterator& lhs, const ordered_iterator& rhs) {
386  return lhs.m_iterator == rhs.m_iterator;
387  }
388 
389  friend bool operator!=(const ordered_iterator& lhs, const ordered_iterator& rhs) {
390  return lhs.m_iterator != rhs.m_iterator;
391  }
392 
393  friend bool operator<(const ordered_iterator& lhs, const ordered_iterator& rhs) {
394  return lhs.m_iterator < rhs.m_iterator;
395  }
396 
397  friend bool operator>(const ordered_iterator& lhs, const ordered_iterator& rhs) {
398  return lhs.m_iterator > rhs.m_iterator;
399  }
400 
401  friend bool operator<=(const ordered_iterator& lhs, const ordered_iterator& rhs) {
402  return lhs.m_iterator <= rhs.m_iterator;
403  }
404 
405  friend bool operator>=(const ordered_iterator& lhs, const ordered_iterator& rhs) {
406  return lhs.m_iterator >= rhs.m_iterator;
407  }
408 
409  friend ordered_iterator operator+(difference_type n, const ordered_iterator& it) {
410  return n + it.m_iterator;
411  }
412 
413  friend difference_type operator-(const ordered_iterator& lhs, const ordered_iterator& rhs) {
414  return lhs.m_iterator - rhs.m_iterator;
415  }
416 
417  private:
418  iterator m_iterator;
419  };
420 
421 
422 private:
423  using bucket_entry = tsl::detail_ordered_hash::bucket_entry<IndexType>;
424 
425  using buckets_container_allocator = typename
426  std::allocator_traits<allocator_type>::template rebind_alloc<bucket_entry>;
427 
428  using buckets_container_type = std::vector<bucket_entry, buckets_container_allocator>;
429 
430 
431  using truncated_hash_type = typename bucket_entry::truncated_hash_type;
432  using index_type = typename bucket_entry::index_type;
433 
434 public:
435  ordered_hash(size_type bucket_count,
436  const Hash& hash,
437  const KeyEqual& equal,
438  const Allocator& alloc,
439  float max_load_factor): Hash(hash),
440  KeyEqual(equal),
441  m_buckets_data(alloc),
442  m_buckets(static_empty_bucket_ptr()),
443  m_mask(0),
444  m_values(alloc),
445  m_grow_on_next_insert(false)
446  {
447  if(bucket_count > max_bucket_count()) {
448  TSL_OH_THROW_OR_TERMINATE(std::length_error, "The map exceeds its maxmimum size.");
449  }
450 
451  if(bucket_count > 0) {
452  bucket_count = round_up_to_power_of_two(bucket_count);
453 
454  m_buckets_data.resize(bucket_count);
455  m_buckets = m_buckets_data.data(),
456  m_mask = bucket_count - 1;
457  }
458 
459  this->max_load_factor(max_load_factor);
460  }
461 
462  ordered_hash(const ordered_hash& other): Hash(other),
463  KeyEqual(other),
464  m_buckets_data(other.m_buckets_data),
465  m_buckets(m_buckets_data.empty()?static_empty_bucket_ptr():
466  m_buckets_data.data()),
467  m_mask(other.m_mask),
468  m_values(other.m_values),
469  m_grow_on_next_insert(other.m_grow_on_next_insert),
470  m_max_load_factor(other.m_max_load_factor),
471  m_load_threshold(other.m_load_threshold)
472  {
473  }
474 
479  : Hash(std::move(static_cast<Hash&>(other))),
480  KeyEqual(std::move(static_cast<KeyEqual&>(other))),
481  m_buckets_data(std::move(other.m_buckets_data)),
482  m_buckets(m_buckets_data.empty()?static_empty_bucket_ptr():
483  m_buckets_data.data()),
484  m_mask(other.m_mask),
485  m_values(std::move(other.m_values)),
486  m_grow_on_next_insert(other.m_grow_on_next_insert),
487  m_max_load_factor(other.m_max_load_factor),
488  m_load_threshold(other.m_load_threshold)
489  {
490  other.m_buckets_data.clear();
491  other.m_buckets = static_empty_bucket_ptr();
492  other.m_mask = 0;
493  other.m_values.clear();
494  other.m_grow_on_next_insert = false;
495  other.m_load_threshold = 0;
496  }
497 
499  if(&other != this) {
500  Hash::operator=(other);
501  KeyEqual::operator=(other);
502 
503  m_buckets_data = other.m_buckets_data;
504  m_buckets = m_buckets_data.empty()?static_empty_bucket_ptr():
505  m_buckets_data.data();
506 
507  m_mask = other.m_mask;
508  m_values = other.m_values;
509  m_grow_on_next_insert = other.m_grow_on_next_insert;
510  m_max_load_factor = other.m_max_load_factor;
511  m_load_threshold = other.m_load_threshold;
512  }
513 
514  return *this;
515  }
516 
518  other.swap(*this);
519  other.clear();
520 
521  return *this;
522  }
523 
524  allocator_type get_allocator() const {
525  return m_values.get_allocator();
526  }
527 
528 
529  /*
530  * Iterators
531  */
532  iterator begin() noexcept {
533  return iterator(m_values.begin());
534  }
535 
536  const_iterator begin() const noexcept {
537  return cbegin();
538  }
539 
540  const_iterator cbegin() const noexcept {
541  return const_iterator(m_values.cbegin());
542  }
543 
544  iterator end() noexcept {
545  return iterator(m_values.end());
546  }
547 
548  const_iterator end() const noexcept {
549  return cend();
550  }
551 
552  const_iterator cend() const noexcept {
553  return const_iterator(m_values.cend());
554  }
555 
556 
557  reverse_iterator rbegin() noexcept {
558  return reverse_iterator(m_values.end());
559  }
560 
561  const_reverse_iterator rbegin() const noexcept {
562  return rcbegin();
563  }
564 
565  const_reverse_iterator rcbegin() const noexcept {
566  return const_reverse_iterator(m_values.cend());
567  }
568 
569  reverse_iterator rend() noexcept {
570  return reverse_iterator(m_values.begin());
571  }
572 
573  const_reverse_iterator rend() const noexcept {
574  return rcend();
575  }
576 
577  const_reverse_iterator rcend() const noexcept {
578  return const_reverse_iterator(m_values.cbegin());
579  }
580 
581 
582  /*
583  * Capacity
584  */
585  bool empty() const noexcept {
586  return m_values.empty();
587  }
588 
589  size_type size() const noexcept {
590  return m_values.size();
591  }
592 
593  size_type max_size() const noexcept {
594  return std::min(bucket_entry::max_size(), m_values.max_size());
595  }
596 
597 
598  /*
599  * Modifiers
600  */
601  void clear() noexcept {
602  for(auto& bucket: m_buckets_data) {
603  bucket.clear();
604  }
605 
606  m_values.clear();
607  m_grow_on_next_insert = false;
608  }
609 
610  template<typename P>
611  std::pair<iterator, bool> insert(P&& value) {
612  return insert_impl(KeySelect()(value), std::forward<P>(value));
613  }
614 
615  template<typename P>
616  iterator insert_hint(const_iterator hint, P&& value) {
617  if(hint != cend() && compare_keys(KeySelect()(*hint), KeySelect()(value))) {
618  return mutable_iterator(hint);
619  }
620 
621  return insert(std::forward<P>(value)).first;
622  }
623 
624  template<class InputIt>
625  void insert(InputIt first, InputIt last) {
626  if(std::is_base_of<std::forward_iterator_tag,
627  typename std::iterator_traits<InputIt>::iterator_category>::value)
628  {
629  const auto nb_elements_insert = std::distance(first, last);
630  const size_type nb_free_buckets = m_load_threshold - size();
631  tsl_oh_assert(m_load_threshold >= size());
632 
633  if(nb_elements_insert > 0 && nb_free_buckets < size_type(nb_elements_insert)) {
634  reserve(size() + size_type(nb_elements_insert));
635  }
636  }
637 
638  for(; first != last; ++first) {
639  insert(*first);
640  }
641  }
642 
643 
644 
645  template<class K, class M>
646  std::pair<iterator, bool> insert_or_assign(K&& key, M&& value) {
647  auto it = try_emplace(std::forward<K>(key), std::forward<M>(value));
648  if(!it.second) {
649  it.first.value() = std::forward<M>(value);
650  }
651 
652  return it;
653  }
654 
655  template<class K, class M>
656  iterator insert_or_assign(const_iterator hint, K&& key, M&& obj) {
657  if(hint != cend() && compare_keys(KeySelect()(*hint), key)) {
658  auto it = mutable_iterator(hint);
659  it.value() = std::forward<M>(obj);
660 
661  return it;
662  }
663 
664  return insert_or_assign(std::forward<K>(key), std::forward<M>(obj)).first;
665  }
666 
667 
668 
669  template<class... Args>
670  std::pair<iterator, bool> emplace(Args&&... args) {
671  return insert(value_type(std::forward<Args>(args)...));
672  }
673 
674  template<class... Args>
675  iterator emplace_hint(const_iterator hint, Args&&... args) {
676  return insert_hint(hint, value_type(std::forward<Args>(args)...));
677  }
678 
679 
680 
681  template<class K, class... Args>
682  std::pair<iterator, bool> try_emplace(K&& key, Args&&... value_args) {
683  return insert_impl(key, std::piecewise_construct,
684  std::forward_as_tuple(std::forward<K>(key)),
685  std::forward_as_tuple(std::forward<Args>(value_args)...));
686  }
687 
688  template<class K, class... Args>
689  iterator try_emplace_hint(const_iterator hint, K&& key, Args&&... args) {
690  if(hint != cend() && compare_keys(KeySelect()(*hint), key)) {
691  return mutable_iterator(hint);
692  }
693 
694  return try_emplace(std::forward<K>(key), std::forward<Args>(args)...).first;
695  }
696 
697 
698 
699  /**
700  * Here to avoid `template<class K> size_type erase(const K& key)` being used when
701  * we use an `iterator` instead of a `const_iterator`.
702  */
703  iterator erase(iterator pos) {
704  return erase(const_iterator(pos));
705  }
706 
707  iterator erase(const_iterator pos) {
708  tsl_oh_assert(pos != cend());
709 
710  const std::size_t index_erase = iterator_to_index(pos);
711 
712  auto it_bucket = find_key(pos.key(), hash_key(pos.key()));
713  tsl_oh_assert(it_bucket != m_buckets_data.end());
714 
715  erase_value_from_bucket(it_bucket);
716 
717  /*
718  * One element was removed from m_values, due to the left shift the next element
719  * is now at the position of the previous element (or end if none).
720  */
721  return begin() + index_erase;
722  }
723 
724  iterator erase(const_iterator first, const_iterator last) {
725  if(first == last) {
726  return mutable_iterator(first);
727  }
728 
729  tsl_oh_assert(std::distance(first, last) > 0);
730  const std::size_t start_index = iterator_to_index(first);
731  const std::size_t nb_values = std::size_t(std::distance(first, last));
732  const std::size_t end_index = start_index + nb_values;
733 
734  // Delete all values
735 #ifdef TSL_OH_NO_CONTAINER_ERASE_CONST_ITERATOR
736  auto next_it = m_values.erase(mutable_iterator(first).m_iterator, mutable_iterator(last).m_iterator);
737 #else
738  auto next_it = m_values.erase(first.m_iterator, last.m_iterator);
739 #endif
740 
741  /*
742  * Mark the buckets corresponding to the values as empty and do a backward shift.
743  *
744  * Also, the erase operation on m_values has shifted all the values on the right of last.m_iterator.
745  * Adapt the indexes for these values.
746  */
747  std::size_t ibucket = 0;
748  while(ibucket < m_buckets_data.size()) {
749  if(m_buckets[ibucket].empty()) {
750  ibucket++;
751  }
752  else if(m_buckets[ibucket].index() >= start_index && m_buckets[ibucket].index() < end_index) {
753  m_buckets[ibucket].clear();
754  backward_shift(ibucket);
755  // Don't increment ibucket, backward_shift may have replaced current bucket.
756  }
757  else if(m_buckets[ibucket].index() >= end_index) {
758  m_buckets[ibucket].set_index(index_type(m_buckets[ibucket].index() - nb_values));
759  ibucket++;
760  }
761  else {
762  ibucket++;
763  }
764  }
765 
766  return iterator(next_it);
767  }
768 
769 
770  template<class K>
771  size_type erase(const K& key) {
772  return erase(key, hash_key(key));
773  }
774 
775  template<class K>
776  size_type erase(const K& key, std::size_t hash) {
777  return erase_impl(key, hash);
778  }
779 
780  void swap(ordered_hash& other) {
781  using std::swap;
782 
783  swap(static_cast<Hash&>(*this), static_cast<Hash&>(other));
784  swap(static_cast<KeyEqual&>(*this), static_cast<KeyEqual&>(other));
785  swap(m_buckets_data, other.m_buckets_data);
786  swap(m_buckets, other.m_buckets);
787  swap(m_mask, other.m_mask);
788  swap(m_values, other.m_values);
789  swap(m_grow_on_next_insert, other.m_grow_on_next_insert);
790  swap(m_max_load_factor, other.m_max_load_factor);
791  swap(m_load_threshold, other.m_load_threshold);
792  }
793 
794 
795 
796 
797  /*
798  * Lookup
799  */
800  template<class K, class U = ValueSelect, typename std::enable_if<has_mapped_type<U>::value>::type* = nullptr>
801  typename U::value_type& at(const K& key) {
802  return at(key, hash_key(key));
803  }
804 
805  template<class K, class U = ValueSelect, typename std::enable_if<has_mapped_type<U>::value>::type* = nullptr>
806  typename U::value_type& at(const K& key, std::size_t hash) {
807  return const_cast<typename U::value_type&>(static_cast<const ordered_hash*>(this)->at(key, hash));
808  }
809 
810  template<class K, class U = ValueSelect, typename std::enable_if<has_mapped_type<U>::value>::type* = nullptr>
811  const typename U::value_type& at(const K& key) const {
812  return at(key, hash_key(key));
813  }
814 
815  template<class K, class U = ValueSelect, typename std::enable_if<has_mapped_type<U>::value>::type* = nullptr>
816  const typename U::value_type& at(const K& key, std::size_t hash) const {
817  auto it = find(key, hash);
818  if(it != end()) {
819  return it.value();
820  }
821  else {
822  TSL_OH_THROW_OR_TERMINATE(std::out_of_range, "Couldn't find the key.");
823  }
824  }
825 
826 
827  template<class K, class U = ValueSelect, typename std::enable_if<has_mapped_type<U>::value>::type* = nullptr>
828  typename U::value_type& operator[](K&& key) {
829  return try_emplace(std::forward<K>(key)).first.value();
830  }
831 
832 
833  template<class K>
834  size_type count(const K& key) const {
835  return count(key, hash_key(key));
836  }
837 
838  template<class K>
839  size_type count(const K& key, std::size_t hash) const {
840  if(find(key, hash) == cend()) {
841  return 0;
842  }
843  else {
844  return 1;
845  }
846  }
847 
848  template<class K>
849  iterator find(const K& key) {
850  return find(key, hash_key(key));
851  }
852 
853  template<class K>
854  iterator find(const K& key, std::size_t hash) {
855  auto it_bucket = find_key(key, hash);
856  return (it_bucket != m_buckets_data.end())?iterator(m_values.begin() + it_bucket->index()):end();
857  }
858 
859  template<class K>
860  const_iterator find(const K& key) const {
861  return find(key, hash_key(key));
862  }
863 
864  template<class K>
865  const_iterator find(const K& key, std::size_t hash) const {
866  auto it_bucket = find_key(key, hash);
867  return (it_bucket != m_buckets_data.cend())?const_iterator(m_values.begin() + it_bucket->index()):end();
868  }
869 
870 
871  template<class K>
872  std::pair<iterator, iterator> equal_range(const K& key) {
873  return equal_range(key, hash_key(key));
874  }
875 
876  template<class K>
877  std::pair<iterator, iterator> equal_range(const K& key, std::size_t hash) {
878  iterator it = find(key, hash);
879  return std::make_pair(it, (it == end())?it:std::next(it));
880  }
881 
882  template<class K>
883  std::pair<const_iterator, const_iterator> equal_range(const K& key) const {
884  return equal_range(key, hash_key(key));
885  }
886 
887  template<class K>
888  std::pair<const_iterator, const_iterator> equal_range(const K& key, std::size_t hash) const {
889  const_iterator it = find(key, hash);
890  return std::make_pair(it, (it == cend())?it:std::next(it));
891  }
892 
893 
894  /*
895  * Bucket interface
896  */
897  size_type bucket_count() const {
898  return m_buckets_data.size();
899  }
900 
901  size_type max_bucket_count() const {
902  return m_buckets_data.max_size();
903  }
904 
905  /*
906  * Hash policy
907  */
908  float load_factor() const {
909  if(bucket_count() == 0) {
910  return 0;
911  }
912 
913  return float(size())/float(bucket_count());
914  }
915 
916  float max_load_factor() const {
917  return m_max_load_factor;
918  }
919 
920  void max_load_factor(float ml) {
921  m_max_load_factor = std::max(0.1f, std::min(ml, 0.95f));
922  m_load_threshold = size_type(float(bucket_count())*m_max_load_factor);
923  }
924 
925  void rehash(size_type count) {
926  count = std::max(count, size_type(std::ceil(float(size())/max_load_factor())));
927  rehash_impl(count);
928  }
929 
930  void reserve(size_type count) {
931  reserve_space_for_values(count);
932 
933  count = size_type(std::ceil(float(count)/max_load_factor()));
934  rehash(count);
935  }
936 
937 
938  /*
939  * Observers
940  */
941  hasher hash_function() const {
942  return static_cast<const Hash&>(*this);
943  }
944 
945  key_equal key_eq() const {
946  return static_cast<const KeyEqual&>(*this);
947  }
948 
949 
950  /*
951  * Other
952  */
953  iterator mutable_iterator(const_iterator pos) {
954  return iterator(m_values.begin() + iterator_to_index(pos));
955  }
956 
957  iterator nth(size_type index) {
958  tsl_oh_assert(index <= size());
959  return iterator(m_values.begin() + index);
960  }
961 
962  const_iterator nth(size_type index) const {
963  tsl_oh_assert(index <= size());
964  return const_iterator(m_values.cbegin() + index);
965  }
966 
967  const_reference front() const {
968  tsl_oh_assert(!empty());
969  return m_values.front();
970  }
971 
972  const_reference back() const {
973  tsl_oh_assert(!empty());
974  return m_values.back();
975  }
976 
977  const values_container_type& values_container() const noexcept {
978  return m_values;
979  }
980 
981  template<class U = values_container_type, typename std::enable_if<is_vector<U>::value>::type* = nullptr>
982  const typename values_container_type::value_type* data() const noexcept {
983  return m_values.data();
984  }
985 
986  template<class U = values_container_type, typename std::enable_if<is_vector<U>::value>::type* = nullptr>
987  size_type capacity() const noexcept {
988  return m_values.capacity();
989  }
990 
991  void shrink_to_fit() {
992  m_values.shrink_to_fit();
993  }
994 
995 
996  template<typename P>
997  std::pair<iterator, bool> insert_at_position(const_iterator pos, P&& value) {
998  return insert_at_position_impl(pos.m_iterator, KeySelect()(value), std::forward<P>(value));
999  }
1000 
1001  template<class... Args>
1002  std::pair<iterator, bool> emplace_at_position(const_iterator pos, Args&&... args) {
1003  return insert_at_position(pos, value_type(std::forward<Args>(args)...));
1004  }
1005 
1006  template<class K, class... Args>
1007  std::pair<iterator, bool> try_emplace_at_position(const_iterator pos, K&& key, Args&&... value_args) {
1008  return insert_at_position_impl(pos.m_iterator, key,
1009  std::piecewise_construct,
1010  std::forward_as_tuple(std::forward<K>(key)),
1011  std::forward_as_tuple(std::forward<Args>(value_args)...));
1012  }
1013 
1014 
1015  void pop_back() {
1016  tsl_oh_assert(!empty());
1017  erase(std::prev(end()));
1018  }
1019 
1020 
1021  /**
1022  * Here to avoid `template<class K> size_type unordered_erase(const K& key)` being used when
1023  * we use a iterator instead of a const_iterator.
1024  */
1025  iterator unordered_erase(iterator pos) {
1026  return unordered_erase(const_iterator(pos));
1027  }
1028 
1029  iterator unordered_erase(const_iterator pos) {
1030  const std::size_t index_erase = iterator_to_index(pos);
1031  unordered_erase(pos.key());
1032 
1033  /*
1034  * One element was deleted, index_erase now points to the next element as the elements after
1035  * the deleted value were shifted to the left in m_values (will be end() if we deleted the last element).
1036  */
1037  return begin() + index_erase;
1038  }
1039 
1040  template<class K>
1041  size_type unordered_erase(const K& key) {
1042  return unordered_erase(key, hash_key(key));
1043  }
1044 
1045  template<class K>
1046  size_type unordered_erase(const K& key, std::size_t hash) {
1047  auto it_bucket_key = find_key(key, hash);
1048  if(it_bucket_key == m_buckets_data.end()) {
1049  return 0;
1050  }
1051 
1052  /**
1053  * If we are not erasing the last element in m_values, we swap
1054  * the element we are erasing with the last element. We then would
1055  * just have to do a pop_back() in m_values.
1056  */
1057  if(!compare_keys(key, KeySelect()(back()))) {
1058  auto it_bucket_last_elem = find_key(KeySelect()(back()), hash_key(KeySelect()(back())));
1059  tsl_oh_assert(it_bucket_last_elem != m_buckets_data.end());
1060  tsl_oh_assert(it_bucket_last_elem->index() == m_values.size() - 1);
1061 
1062  using std::swap;
1063  swap(m_values[it_bucket_key->index()], m_values[it_bucket_last_elem->index()]);
1064  swap(it_bucket_key->index_ref(), it_bucket_last_elem->index_ref());
1065  }
1066 
1067  erase_value_from_bucket(it_bucket_key);
1068 
1069  return 1;
1070  }
1071 
1072  template<class Serializer>
1073  void serialize(Serializer& serializer) const {
1074  serialize_impl(serializer);
1075  }
1076 
1077  template<class Deserializer>
1078  void deserialize(Deserializer& deserializer, bool hash_compatible) {
1079  deserialize_impl(deserializer, hash_compatible);
1080  }
1081 
1082  friend bool operator==(const ordered_hash& lhs, const ordered_hash& rhs) {
1083  return lhs.m_values == rhs.m_values;
1084  }
1085 
1086  friend bool operator!=(const ordered_hash& lhs, const ordered_hash& rhs) {
1087  return lhs.m_values != rhs.m_values;
1088  }
1089 
1090  friend bool operator<(const ordered_hash& lhs, const ordered_hash& rhs) {
1091  return lhs.m_values < rhs.m_values;
1092  }
1093 
1094  friend bool operator<=(const ordered_hash& lhs, const ordered_hash& rhs) {
1095  return lhs.m_values <= rhs.m_values;
1096  }
1097 
1098  friend bool operator>(const ordered_hash& lhs, const ordered_hash& rhs) {
1099  return lhs.m_values > rhs.m_values;
1100  }
1101 
1102  friend bool operator>=(const ordered_hash& lhs, const ordered_hash& rhs) {
1103  return lhs.m_values >= rhs.m_values;
1104  }
1105 
1106 
1107 private:
1108  template<class K>
1109  std::size_t hash_key(const K& key) const {
1110  return Hash::operator()(key);
1111  }
1112 
1113  template<class K1, class K2>
1114  bool compare_keys(const K1& key1, const K2& key2) const {
1115  return KeyEqual::operator()(key1, key2);
1116  }
1117 
1118  template<class K>
1119  typename buckets_container_type::iterator find_key(const K& key, std::size_t hash) {
1120  auto it = static_cast<const ordered_hash*>(this)->find_key(key, hash);
1121  return m_buckets_data.begin() + std::distance(m_buckets_data.cbegin(), it);
1122  }
1123 
1124  /**
1125  * Return bucket which has the key 'key' or m_buckets_data.end() if none.
1126  *
1127  * From the bucket_for_hash, search for the value until we either find an empty bucket
1128  * or a bucket which has a value with a distance from its ideal bucket longer
1129  * than the probe length for the value we are looking for.
1130  */
1131  template<class K>
1132  typename buckets_container_type::const_iterator find_key(const K& key, std::size_t hash) const {
1133  for(std::size_t ibucket = bucket_for_hash(hash), dist_from_ideal_bucket = 0; ;
1134  ibucket = next_bucket(ibucket), dist_from_ideal_bucket++)
1135  {
1136  if(m_buckets[ibucket].empty()) {
1137  return m_buckets_data.end();
1138  }
1139  else if(m_buckets[ibucket].truncated_hash() == bucket_entry::truncate_hash(hash) &&
1140  compare_keys(key, KeySelect()(m_values[m_buckets[ibucket].index()])))
1141  {
1142  return m_buckets_data.begin() + ibucket;
1143  }
1144  else if(dist_from_ideal_bucket > distance_from_ideal_bucket(ibucket)) {
1145  return m_buckets_data.end();
1146  }
1147  }
1148  }
1149 
1150  void rehash_impl(size_type bucket_count) {
1151  tsl_oh_assert(bucket_count >= size_type(std::ceil(float(size())/max_load_factor())));
1152 
1153  if(bucket_count > max_bucket_count()) {
1154  TSL_OH_THROW_OR_TERMINATE(std::length_error, "The map exceeds its maxmimum size.");
1155  }
1156 
1157  if(bucket_count > 0) {
1158  bucket_count = round_up_to_power_of_two(bucket_count);
1159  }
1160 
1161  if(bucket_count == this->bucket_count()) {
1162  return;
1163  }
1164 
1165 
1166  buckets_container_type old_buckets(bucket_count);
1167  m_buckets_data.swap(old_buckets);
1168  m_buckets = m_buckets_data.empty()?static_empty_bucket_ptr():
1169  m_buckets_data.data();
1170  // Everything should be noexcept from here.
1171 
1172  m_mask = (bucket_count > 0)?(bucket_count - 1):0;
1173  this->max_load_factor(m_max_load_factor);
1174  m_grow_on_next_insert = false;
1175 
1176 
1177 
1178  for(const bucket_entry& old_bucket: old_buckets) {
1179  if(old_bucket.empty()) {
1180  continue;
1181  }
1182 
1183  truncated_hash_type insert_hash = old_bucket.truncated_hash();
1184  index_type insert_index = old_bucket.index();
1185 
1186  for(std::size_t ibucket = bucket_for_hash(insert_hash), dist_from_ideal_bucket = 0; ;
1187  ibucket = next_bucket(ibucket), dist_from_ideal_bucket++)
1188  {
1189  if(m_buckets[ibucket].empty()) {
1190  m_buckets[ibucket].set_index(insert_index);
1191  m_buckets[ibucket].set_hash(insert_hash);
1192  break;
1193  }
1194 
1195  const std::size_t distance = distance_from_ideal_bucket(ibucket);
1196  if(dist_from_ideal_bucket > distance) {
1197  std::swap(insert_index, m_buckets[ibucket].index_ref());
1198  std::swap(insert_hash, m_buckets[ibucket].truncated_hash_ref());
1199  dist_from_ideal_bucket = distance;
1200  }
1201  }
1202  }
1203  }
1204 
1205  template<class T = values_container_type, typename std::enable_if<is_vector<T>::value>::type* = nullptr>
1206  void reserve_space_for_values(size_type count) {
1207  m_values.reserve(count);
1208  }
1209 
1210  template<class T = values_container_type, typename std::enable_if<!is_vector<T>::value>::type* = nullptr>
1211  void reserve_space_for_values(size_type /*count*/) {
1212  }
1213 
1214  /**
1215  * Swap the empty bucket with the values on its right until we cross another empty bucket
1216  * or if the other bucket has a distance_from_ideal_bucket == 0.
1217  */
1218  void backward_shift(std::size_t empty_ibucket) noexcept {
1219  tsl_oh_assert(m_buckets[empty_ibucket].empty());
1220 
1221  std::size_t previous_ibucket = empty_ibucket;
1222  for(std::size_t current_ibucket = next_bucket(previous_ibucket);
1223  !m_buckets[current_ibucket].empty() && distance_from_ideal_bucket(current_ibucket) > 0;
1224  previous_ibucket = current_ibucket, current_ibucket = next_bucket(current_ibucket))
1225  {
1226  std::swap(m_buckets[current_ibucket], m_buckets[previous_ibucket]);
1227  }
1228  }
1229 
1230  void erase_value_from_bucket(typename buckets_container_type::iterator it_bucket) {
1231  tsl_oh_assert(it_bucket != m_buckets_data.end() && !it_bucket->empty());
1232 
1233  m_values.erase(m_values.begin() + it_bucket->index());
1234 
1235  /*
1236  * m_values.erase shifted all the values on the right of the erased value,
1237  * shift the indexes by -1 in the buckets array for these values.
1238  */
1239  if(it_bucket->index() != m_values.size()) {
1240  shift_indexes_in_buckets(it_bucket->index(), -1);
1241  }
1242 
1243  // Mark the bucket as empty and do a backward shift of the values on the right
1244  it_bucket->clear();
1245  backward_shift(std::size_t(std::distance(m_buckets_data.begin(), it_bucket)));
1246  }
1247 
1248  /**
1249  * Go through each value from [from_ivalue, m_values.size()) in m_values and for each
1250  * bucket corresponding to the value, shift the index by delta.
1251  *
1252  * delta must be equal to 1 or -1.
1253  */
1254  void shift_indexes_in_buckets(index_type from_ivalue, int delta) noexcept {
1255  tsl_oh_assert(delta == 1 || delta == -1);
1256 
1257  for(std::size_t ivalue = from_ivalue; ivalue < m_values.size(); ivalue++) {
1258  // All the values in m_values have been shifted by delta. Find the bucket corresponding
1259  // to the value m_values[ivalue]
1260  const index_type old_index = static_cast<index_type>(ivalue - delta);
1261 
1262  std::size_t ibucket = bucket_for_hash(hash_key(KeySelect()(m_values[ivalue])));
1263  while(m_buckets[ibucket].index() != old_index) {
1264  ibucket = next_bucket(ibucket);
1265  }
1266 
1267  m_buckets[ibucket].set_index(index_type(ivalue));
1268  }
1269  }
1270 
1271  template<class K>
1272  size_type erase_impl(const K& key, std::size_t hash) {
1273  auto it_bucket = find_key(key, hash);
1274  if(it_bucket != m_buckets_data.end()) {
1275  erase_value_from_bucket(it_bucket);
1276 
1277  return 1;
1278  }
1279  else {
1280  return 0;
1281  }
1282  }
1283 
1284  /**
1285  * Insert the element at the end.
1286  */
1287  template<class K, class... Args>
1288  std::pair<iterator, bool> insert_impl(const K& key, Args&&... value_type_args) {
1289  const std::size_t hash = hash_key(key);
1290 
1291  std::size_t ibucket = bucket_for_hash(hash);
1292  std::size_t dist_from_ideal_bucket = 0;
1293 
1294  while(!m_buckets[ibucket].empty() && dist_from_ideal_bucket <= distance_from_ideal_bucket(ibucket)) {
1295  if(m_buckets[ibucket].truncated_hash() == bucket_entry::truncate_hash(hash) &&
1296  compare_keys(key, KeySelect()(m_values[m_buckets[ibucket].index()])))
1297  {
1298  return std::make_pair(begin() + m_buckets[ibucket].index(), false);
1299  }
1300 
1301  ibucket = next_bucket(ibucket);
1302  dist_from_ideal_bucket++;
1303  }
1304 
1305  if(size() >= max_size()) {
1306  TSL_OH_THROW_OR_TERMINATE(std::length_error, "We reached the maximum size for the hash table.");
1307  }
1308 
1309 
1310  if(grow_on_high_load()) {
1311  ibucket = bucket_for_hash(hash);
1312  dist_from_ideal_bucket = 0;
1313  }
1314 
1315 
1316  m_values.emplace_back(std::forward<Args>(value_type_args)...);
1317  insert_index(ibucket, dist_from_ideal_bucket,
1318  index_type(m_values.size() - 1), bucket_entry::truncate_hash(hash));
1319 
1320 
1321  return std::make_pair(std::prev(end()), true);
1322  }
1323 
1324  /**
1325  * Insert the element before insert_position.
1326  */
1327  template<class K, class... Args>
1328  std::pair<iterator, bool> insert_at_position_impl(typename values_container_type::const_iterator insert_position,
1329  const K& key, Args&&... value_type_args)
1330  {
1331  const std::size_t hash = hash_key(key);
1332 
1333  std::size_t ibucket = bucket_for_hash(hash);
1334  std::size_t dist_from_ideal_bucket = 0;
1335 
1336  while(!m_buckets[ibucket].empty() && dist_from_ideal_bucket <= distance_from_ideal_bucket(ibucket)) {
1337  if(m_buckets[ibucket].truncated_hash() == bucket_entry::truncate_hash(hash) &&
1338  compare_keys(key, KeySelect()(m_values[m_buckets[ibucket].index()])))
1339  {
1340  return std::make_pair(begin() + m_buckets[ibucket].index(), false);
1341  }
1342 
1343  ibucket = next_bucket(ibucket);
1344  dist_from_ideal_bucket++;
1345  }
1346 
1347  if(size() >= max_size()) {
1348  TSL_OH_THROW_OR_TERMINATE(std::length_error, "We reached the maximum size for the hash table.");
1349  }
1350 
1351 
1352  if(grow_on_high_load()) {
1353  ibucket = bucket_for_hash(hash);
1354  dist_from_ideal_bucket = 0;
1355  }
1356 
1357 
1358  const index_type index_insert_position = index_type(std::distance(m_values.cbegin(), insert_position));
1359 
1360 #ifdef TSL_OH_NO_CONTAINER_EMPLACE_CONST_ITERATOR
1361  m_values.emplace(m_values.begin() + std::distance(m_values.cbegin(), insert_position), std::forward<Args>(value_type_args)...);
1362 #else
1363  m_values.emplace(insert_position, std::forward<Args>(value_type_args)...);
1364 #endif
1365 
1366  insert_index(ibucket, dist_from_ideal_bucket,
1367  index_insert_position, bucket_entry::truncate_hash(hash));
1368 
1369  /*
1370  * The insertion didn't happend at the end of the m_values container,
1371  * we need to shift the indexes in m_buckets_data.
1372  */
1373  if(index_insert_position != m_values.size() - 1) {
1374  shift_indexes_in_buckets(index_insert_position + 1, 1);
1375  }
1376 
1377  return std::make_pair(iterator(m_values.begin() + index_insert_position), true);
1378  }
1379 
1380  void insert_index(std::size_t ibucket, std::size_t dist_from_ideal_bucket,
1381  index_type index_insert, truncated_hash_type hash_insert) noexcept
1382  {
1383  while(!m_buckets[ibucket].empty()) {
1384  const std::size_t distance = distance_from_ideal_bucket(ibucket);
1385  if(dist_from_ideal_bucket > distance) {
1386  std::swap(index_insert, m_buckets[ibucket].index_ref());
1387  std::swap(hash_insert, m_buckets[ibucket].truncated_hash_ref());
1388 
1389  dist_from_ideal_bucket = distance;
1390  }
1391 
1392 
1393  ibucket = next_bucket(ibucket);
1394  dist_from_ideal_bucket++;
1395 
1396 
1397  if(dist_from_ideal_bucket > REHASH_ON_HIGH_NB_PROBES__NPROBES && !m_grow_on_next_insert &&
1398  load_factor() >= REHASH_ON_HIGH_NB_PROBES__MIN_LOAD_FACTOR)
1399  {
1400  // We don't want to grow the map now as we need this method to be noexcept.
1401  // Do it on next insert.
1402  m_grow_on_next_insert = true;
1403  }
1404  }
1405 
1406 
1407  m_buckets[ibucket].set_index(index_insert);
1408  m_buckets[ibucket].set_hash(hash_insert);
1409  }
1410 
1411  std::size_t distance_from_ideal_bucket(std::size_t ibucket) const noexcept {
1412  const std::size_t ideal_bucket = bucket_for_hash(m_buckets[ibucket].truncated_hash());
1413 
1414  if(ibucket >= ideal_bucket) {
1415  return ibucket - ideal_bucket;
1416  }
1417  // If the bucket is smaller than the ideal bucket for the value, there was a wrapping at the end of the
1418  // bucket array due to the modulo.
1419  else {
1420  return (bucket_count() + ibucket) - ideal_bucket;
1421  }
1422  }
1423 
1424  std::size_t next_bucket(std::size_t index) const noexcept {
1425  tsl_oh_assert(index < m_buckets_data.size());
1426 
1427  index++;
1428  return (index < m_buckets_data.size())?index:0;
1429  }
1430 
1431  std::size_t bucket_for_hash(std::size_t hash) const noexcept {
1432  return hash & m_mask;
1433  }
1434 
1435  std::size_t iterator_to_index(const_iterator it) const noexcept {
1436  const auto dist = std::distance(cbegin(), it);
1437  tsl_oh_assert(dist >= 0);
1438 
1439  return std::size_t(dist);
1440  }
1441 
1442  /**
1443  * Return true if the map has been rehashed.
1444  */
1445  bool grow_on_high_load() {
1446  if(m_grow_on_next_insert || size() >= m_load_threshold) {
1447  rehash_impl(std::max(size_type(1), bucket_count() * 2));
1448  m_grow_on_next_insert = false;
1449 
1450  return true;
1451  }
1452  else {
1453  return false;
1454  }
1455  }
1456 
1457  template<class Serializer>
1458  void serialize_impl(Serializer& serializer) const {
1459  const slz_size_type version = SERIALIZATION_PROTOCOL_VERSION;
1460  serializer(version);
1461 
1462  const slz_size_type nb_elements = m_values.size();
1463  serializer(nb_elements);
1464 
1465  const slz_size_type bucket_count = m_buckets_data.size();
1466  serializer(bucket_count);
1467 
1468  const float max_load_factor = m_max_load_factor;
1469  serializer(max_load_factor);
1470 
1471 
1472  for(const value_type& value: m_values) {
1473  serializer(value);
1474  }
1475 
1476  for(const bucket_entry& bucket: m_buckets_data) {
1477  bucket.serialize(serializer);
1478  }
1479  }
1480 
1481  template<class Deserializer>
1482  void deserialize_impl(Deserializer& deserializer, bool hash_compatible) {
1483  tsl_oh_assert(m_buckets_data.empty()); // Current hash table must be empty
1484 
1485  const slz_size_type version = deserialize_value<slz_size_type>(deserializer);
1486  // For now we only have one version of the serialization protocol.
1487  // If it doesn't match there is a problem with the file.
1488  if(version != SERIALIZATION_PROTOCOL_VERSION) {
1489  TSL_OH_THROW_OR_TERMINATE(std::runtime_error, "Can't deserialize the ordered_map/set. "
1490  "The protocol version header is invalid.");
1491  }
1492 
1493  const slz_size_type nb_elements = deserialize_value<slz_size_type>(deserializer);
1494  const slz_size_type bucket_count_ds = deserialize_value<slz_size_type>(deserializer);
1495  const float max_load_factor = deserialize_value<float>(deserializer);
1496 
1497 
1498  this->max_load_factor(max_load_factor);
1499 
1500  if(bucket_count_ds == 0) {
1501  tsl_oh_assert(nb_elements == 0);
1502  return;
1503  }
1504 
1505 
1506  if(!hash_compatible) {
1507  reserve(numeric_cast<size_type>(nb_elements, "Deserialized nb_elements is too big."));
1508  for(slz_size_type el = 0; el < nb_elements; el++) {
1509  insert(deserialize_value<value_type>(deserializer));
1510  }
1511  }
1512  else {
1513  m_buckets_data.reserve(numeric_cast<size_type>(bucket_count_ds, "Deserialized bucket_count is too big."));
1514  m_buckets = m_buckets_data.data(),
1515  m_mask = m_buckets_data.capacity() - 1;
1516 
1517  reserve_space_for_values(numeric_cast<size_type>(nb_elements, "Deserialized nb_elements is too big."));
1518  for(slz_size_type el = 0; el < nb_elements; el++) {
1519  m_values.push_back(deserialize_value<value_type>(deserializer));
1520  }
1521 
1522  for(slz_size_type b = 0; b < bucket_count_ds; b++) {
1523  m_buckets_data.push_back(bucket_entry::deserialize(deserializer));
1524  }
1525 
1526  if(load_factor() > this->max_load_factor()) {
1527  TSL_OH_THROW_OR_TERMINATE(std::runtime_error, "Invalid max_load_factor. Check that the serializer "
1528  "and deserializer supports floats correctly as they "
1529  "can be converted implicitely to ints.");
1530  }
1531  }
1532  }
1533 
1534  static std::size_t round_up_to_power_of_two(std::size_t value) {
1535  if(is_power_of_two(value)) {
1536  return value;
1537  }
1538 
1539  if(value == 0) {
1540  return 1;
1541  }
1542 
1543  --value;
1544  for(std::size_t i = 1; i < sizeof(std::size_t) * CHAR_BIT; i *= 2) {
1545  value |= value >> i;
1546  }
1547 
1548  return value + 1;
1549  }
1550 
1551  static constexpr bool is_power_of_two(std::size_t value) {
1552  return value != 0 && (value & (value - 1)) == 0;
1553  }
1554 
1555 
1556 public:
1557  static const size_type DEFAULT_INIT_BUCKETS_SIZE = 0;
1558  static constexpr float DEFAULT_MAX_LOAD_FACTOR = 0.75f;
1559 
1560 private:
1561  static const size_type REHASH_ON_HIGH_NB_PROBES__NPROBES = 128;
1562  static constexpr float REHASH_ON_HIGH_NB_PROBES__MIN_LOAD_FACTOR = 0.15f;
1563 
1564  /**
1565  * Protocol version currenlty used for serialization.
1566  */
1567  static const slz_size_type SERIALIZATION_PROTOCOL_VERSION = 1;
1568 
1569  /**
1570  * Return an always valid pointer to an static empty bucket_entry with last_bucket() == true.
1571  */
1572  bucket_entry* static_empty_bucket_ptr() {
1573  static bucket_entry empty_bucket;
1574  return &empty_bucket;
1575  }
1576 
1577 private:
1578  buckets_container_type m_buckets_data;
1579 
1580  /**
1581  * Points to m_buckets_data.data() if !m_buckets_data.empty() otherwise points to static_empty_bucket_ptr.
1582  * This variable is useful to avoid the cost of checking if m_buckets_data is empty when trying
1583  * to find an element.
1584  *
1585  * TODO Remove m_buckets_data and only use a pointer+size instead of a pointer+vector to save some space in the ordered_hash object.
1586  */
1587  bucket_entry* m_buckets;
1588 
1589  size_type m_mask;
1590 
1591  values_container_type m_values;
1592 
1593  bool m_grow_on_next_insert;
1594  float m_max_load_factor;
1595  size_type m_load_threshold;
1596 };
1597 
1598 
1599 } // end namespace detail_ordered_hash
1600 
1601 } // end namespace tsl
1602 
1603 #endif
size_type unordered_erase(const K &key, std::size_t hash)
Definition: ordered_hash.h:1046
U::value_type & at(const K &key, std::size_t hash)
Definition: ordered_hash.h:806
std::pair< iterator, bool > insert(P &&value)
Definition: ordered_hash.h:611
iterator emplace_hint(const_iterator hint, Args &&... args)
Definition: ordered_hash.h:675
iterator try_emplace_hint(const_iterator hint, K &&key, Args &&... args)
Definition: ordered_hash.h:689
static constexpr float DEFAULT_MAX_LOAD_FACTOR
Definition: ordered_hash.h:1558
Definition: ordered_hash.h:279
const_reverse_iterator rcbegin() const noexcept
Definition: ordered_hash.h:565
size_type bucket_count() const
Definition: ordered_hash.h:897
static const size_type DEFAULT_INIT_BUCKETS_SIZE
Definition: ordered_hash.h:1557
void pop_back()
Definition: ordered_hash.h:1015
ordered_iterator(const ordered_iterator<!TIsConst > &other) noexcept
Definition: ordered_hash.h:346
friend bool operator>(const ordered_hash &lhs, const ordered_hash &rhs)
Definition: ordered_hash.h:1098
size_type count(const K &key, std::size_t hash) const
Definition: ordered_hash.h:839
ordered_iterator() noexcept
Definition: ordered_hash.h:341
Definition: ordered_hash.h:82
iterator mutable_iterator(const_iterator pos)
Definition: ordered_hash.h:953
Definition: ordered_hash.h:80
const ordered_hash::key_type & key() const
Definition: ordered_hash.h:354
const_reverse_iterator rend() const noexcept
Definition: ordered_hash.h:573
std::pair< iterator, iterator > equal_range(const K &key)
Definition: ordered_hash.h:872
ordered_iterator operator-(difference_type n)
Definition: ordered_hash.h:383
friend bool operator!=(const ordered_iterator &lhs, const ordered_iterator &rhs)
Definition: ordered_hash.h:389
key_equal key_eq() const
Definition: ordered_hash.h:945
void deserialize(Deserializer &deserializer, bool hash_compatible)
Definition: ordered_hash.h:1078
friend bool operator<=(const ordered_hash &lhs, const ordered_hash &rhs)
Definition: ordered_hash.h:1094
std::pair< iterator, bool > try_emplace_at_position(const_iterator pos, K &&key, Args &&... value_args)
Definition: ordered_hash.h:1007
const_iterator end() const noexcept
Definition: ordered_hash.h:548
iterator erase(const_iterator pos)
Definition: ordered_hash.h:707
reference operator[](difference_type n) const
Definition: ordered_hash.h:377
const_iterator find(const K &key) const
Definition: ordered_hash.h:860
iterator unordered_erase(const_iterator pos)
Definition: ordered_hash.h:1029
const_iterator cend() const noexcept
Definition: ordered_hash.h:552
const_iterator begin() const noexcept
Definition: ordered_hash.h:536
friend bool operator>(const ordered_iterator &lhs, const ordered_iterator &rhs)
Definition: ordered_hash.h:397
void insert(InputIt first, InputIt last)
Definition: ordered_hash.h:625
iterator end() noexcept
Definition: ordered_hash.h:544
size_type capacity() const noexcept
Definition: ordered_hash.h:987
const U::value_type & at(const K &key, std::size_t hash) const
Definition: ordered_hash.h:816
reference operator*() const
Definition: ordered_hash.h:368
friend bool operator<(const ordered_iterator &lhs, const ordered_iterator &rhs)
Definition: ordered_hash.h:393
friend bool operator>=(const ordered_iterator &lhs, const ordered_iterator &rhs)
Definition: ordered_hash.h:405
size_type unordered_erase(const K &key)
Definition: ordered_hash.h:1041
iterator find(const K &key, std::size_t hash)
Definition: ordered_hash.h:854
friend bool operator==(const ordered_iterator &lhs, const ordered_iterator &rhs)
Definition: ordered_hash.h:385
std::pair< iterator, bool > try_emplace(K &&key, Args &&... value_args)
Definition: ordered_hash.h:682
ordered_iterator operator--(int)
Definition: ordered_hash.h:375
friend bool operator>=(const ordered_hash &lhs, const ordered_hash &rhs)
Definition: ordered_hash.h:1102
iterator insert_or_assign(const_iterator hint, K &&key, M &&obj)
Definition: ordered_hash.h:656
void reserve(size_type count)
Definition: ordered_hash.h:930
std::pair< iterator, bool > emplace_at_position(const_iterator pos, Args &&... args)
Definition: ordered_hash.h:1002
const values_container_type::value_type * data() const noexcept
Definition: ordered_hash.h:982
iterator begin() noexcept
Definition: ordered_hash.h:532
friend class ordered_hash
Definition: ordered_hash.h:322
void clear() noexcept
Definition: ordered_hash.h:601
iterator erase(iterator pos)
Definition: ordered_hash.h:703
size_type count(const K &key) const
Definition: ordered_hash.h:834
friend bool operator<=(const ordered_iterator &lhs, const ordered_iterator &rhs)
Definition: ordered_hash.h:401
allocator_type get_allocator() const
Definition: ordered_hash.h:524
size_type erase(const K &key, std::size_t hash)
Definition: ordered_hash.h:776
ordered_iterator(ordered_iterator &&other)=default
std::pair< iterator, bool > insert_or_assign(K &&key, M &&value)
Definition: ordered_hash.h:646
float max_load_factor() const
Definition: ordered_hash.h:916
ordered_iterator & operator-=(difference_type n)
Definition: ordered_hash.h:380
friend bool operator!=(const ordered_hash &lhs, const ordered_hash &rhs)
Definition: ordered_hash.h:1086
U::value_type & operator[](K &&key)
Definition: ordered_hash.h:828
const_iterator cbegin() const noexcept
Definition: ordered_hash.h:540
const_iterator nth(size_type index) const
Definition: ordered_hash.h:962
std::pair< const_iterator, const_iterator > equal_range(const K &key) const
Definition: ordered_hash.h:883
bool empty() const noexcept
Definition: ordered_hash.h:585
const_reverse_iterator rcend() const noexcept
Definition: ordered_hash.h:577
ordered_iterator & operator=(const ordered_iterator &other)=default
std::pair< iterator, bool > emplace(Args &&... args)
Definition: ordered_hash.h:670
const_iterator find(const K &key, std::size_t hash) const
Definition: ordered_hash.h:865
hasher hash_function() const
Definition: ordered_hash.h:941
void shrink_to_fit()
Definition: ordered_hash.h:991
pointer operator->() const
Definition: ordered_hash.h:369
ordered_hash(size_type bucket_count, const Hash &hash, const KeyEqual &equal, const Allocator &alloc, float max_load_factor)
Definition: ordered_hash.h:435
iterator find(const K &key)
Definition: ordered_hash.h:849
const U::value_type & value() const
Definition: ordered_hash.h:359
size_type max_bucket_count() const
Definition: ordered_hash.h:901
size_type size() const noexcept
Definition: ordered_hash.h:589
friend ordered_iterator operator+(difference_type n, const ordered_iterator &it)
Definition: ordered_hash.h:409
size_type max_size() const noexcept
Definition: ordered_hash.h:593
ordered_hash & operator=(const ordered_hash &other)
Definition: ordered_hash.h:498
iterator insert_hint(const_iterator hint, P &&value)
Definition: ordered_hash.h:616
iterator unordered_erase(iterator pos)
Definition: ordered_hash.h:1025
ordered_iterator operator++(int)
Definition: ordered_hash.h:374
ordered_iterator & operator=(ordered_iterator &&other)=default
iterator nth(size_type index)
Definition: ordered_hash.h:957
#define tsl_oh_assert(expr)
Definition: ordered_hash.h:62
ordered_iterator & operator++()
Definition: ordered_hash.h:371
const_reverse_iterator rbegin() const noexcept
Definition: ordered_hash.h:561
iterator erase(const_iterator first, const_iterator last)
Definition: ordered_hash.h:724
ordered_iterator & operator--()
Definition: ordered_hash.h:372
const_reference front() const
Definition: ordered_hash.h:967
ordered_iterator operator+(difference_type n)
Definition: ordered_hash.h:382
std::pair< iterator, iterator > equal_range(const K &key, std::size_t hash)
Definition: ordered_hash.h:877
float load_factor() const
Definition: ordered_hash.h:908
reverse_iterator rbegin() noexcept
Definition: ordered_hash.h:557
const U::value_type & at(const K &key) const
Definition: ordered_hash.h:811
void swap(ordered_hash &other)
Definition: ordered_hash.h:780
ordered_iterator & operator+=(difference_type n)
Definition: ordered_hash.h:379
friend bool operator==(const ordered_hash &lhs, const ordered_hash &rhs)
Definition: ordered_hash.h:1082
std::pair< const_iterator, const_iterator > equal_range(const K &key, std::size_t hash) const
Definition: ordered_hash.h:888
U::value_type & value()
Definition: ordered_hash.h:364
ordered_hash(ordered_hash &&other) noexcept(std::is_nothrow_move_constructible< Hash >::value &&std::is_nothrow_move_constructible< KeyEqual >::value &&std::is_nothrow_move_constructible< buckets_container_type >::value &&std::is_nothrow_move_constructible< values_container_type >::value)
Definition: ordered_hash.h:475
friend bool operator<(const ordered_hash &lhs, const ordered_hash &rhs)
Definition: ordered_hash.h:1090
#define TSL_OH_THROW_OR_TERMINATE(ex, msg)
Definition: ordered_hash.h:75
reverse_iterator rend() noexcept
Definition: ordered_hash.h:569
friend difference_type operator-(const ordered_iterator &lhs, const ordered_iterator &rhs)
Definition: ordered_hash.h:413
size_type erase(const K &key)
Definition: ordered_hash.h:771
const values_container_type & values_container() const noexcept
Definition: ordered_hash.h:977
const_reference back() const
Definition: ordered_hash.h:972
ordered_iterator(const ordered_iterator &other)=default
ordered_hash(const ordered_hash &other)
Definition: ordered_hash.h:462
U::value_type & at(const K &key)
Definition: ordered_hash.h:801
void max_load_factor(float ml)
Definition: ordered_hash.h:920
std::pair< iterator, bool > insert_at_position(const_iterator pos, P &&value)
Definition: ordered_hash.h:997
void serialize(Serializer &serializer) const
Definition: ordered_hash.h:1073
ordered_hash & operator=(ordered_hash &&other)
Definition: ordered_hash.h:517
void rehash(size_type count)
Definition: ordered_hash.h:925