sparse-map
sparse_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_SPARSE_HASH_H
25 #define TSL_SPARSE_HASH_H
26 
27 
28 #include <algorithm>
29 #include <cassert>
30 #include <climits>
31 #include <cmath>
32 #include <cstdint>
33 #include <cstddef>
34 #include <iterator>
35 #include <limits>
36 #include <memory>
37 #include <stdexcept>
38 #include <tuple>
39 #include <type_traits>
40 #include <utility>
41 #include <vector>
43 
44 #ifdef __INTEL_COMPILER
45 # include <immintrin.h> // For _popcnt32 and _popcnt64
46 #endif
47 
48 #ifdef _MSC_VER
49 # include <intrin.h> // For __cpuid, __popcnt and __popcnt64
50 #endif
51 
52 
53 #ifdef TSL_DEBUG
54 # define tsl_sh_assert(expr) assert(expr)
55 #else
56 # define tsl_sh_assert(expr) (static_cast<void>(0))
57 #endif
58 
59 
60 namespace tsl {
61 
62 namespace sh {
63  enum class probing {
64  linear,
65  quadratic
66  };
67 
68  enum class exception_safety {
69  basic,
70  strong
71  };
72 
73  enum class sparsity {
74  high,
75  medium,
76  low
77  };
78 }
79 
80 
81 namespace detail_popcount {
82 /**
83  * Define the popcount(ll) methods and pick-up the best depending on the compiler.
84  */
85 
86 // From Wikipedia: https://en.wikipedia.org/wiki/Hamming_weight
87 inline int fallback_popcountll(unsigned long long int x) {
88  static_assert(sizeof(unsigned long long int) == sizeof(std::uint64_t),
89  "sizeof(unsigned long long int) must be equal to sizeof(std::uint64_t). "
90  "Open a feature request if you need support for a platform where it isn't the case.");
91 
92  const std::uint64_t m1 = 0x5555555555555555ull;
93  const std::uint64_t m2 = 0x3333333333333333ull;
94  const std::uint64_t m4 = 0x0f0f0f0f0f0f0f0full;
95  const std::uint64_t h01 = 0x0101010101010101ull;
96 
97  x -= (x >> 1ull) & m1;
98  x = (x & m2) + ((x >> 2ull) & m2);
99  x = (x + (x >> 4ull)) & m4;
100  return static_cast<int>((x * h01) >> (64ull - 8ull));
101 }
102 
103 inline int fallback_popcount(unsigned int x) {
104  static_assert(sizeof(unsigned int) == sizeof(std::uint32_t) || sizeof(unsigned int) == sizeof(std::uint64_t),
105  "sizeof(unsigned int) must be equal to sizeof(std::uint32_t) or sizeof(std::uint64_t). "
106  "Open a feature request if you need support for a platform where it isn't the case.");
107 
108  if (sizeof(unsigned int) == sizeof(std::uint32_t)) {
109  const std::uint32_t m1 = 0x55555555;
110  const std::uint32_t m2 = 0x33333333;
111  const std::uint32_t m4 = 0x0f0f0f0f;
112  const std::uint32_t h01 = 0x01010101;
113 
114  x -= (x >> 1) & m1;
115  x = (x & m2) + ((x >> 2) & m2);
116  x = (x + (x >> 4)) & m4;
117  return static_cast<int>((x * h01) >> (32 - 8));
118  }
119  else {
120  return fallback_popcountll(x);
121  }
122 }
123 
124 
125 #if defined(__clang__) || defined(__GNUC__)
126 inline int popcountll(unsigned long long int value) {
127  return __builtin_popcountll(value);
128 }
129 
130 inline int popcount(unsigned int value) {
131  return __builtin_popcount(value);
132 }
133 
134 
135 #elif defined(_MSC_VER)
136 /**
137 * We need to check for popcount support at runtime on Windows with __cpuid
138 * See https://msdn.microsoft.com/en-us/library/bb385231.aspx
139 */
140 inline bool has_popcount_support() {
141  int cpu_infos[4];
142  __cpuid(cpu_infos, 1);
143  return (cpu_infos[2] & (1 << 23)) != 0;
144 }
145 
146 inline int popcountll(unsigned long long int value) {
147 #ifdef _WIN64
148  static_assert(sizeof(unsigned long long int) == sizeof(std::int64_t),
149  "sizeof(unsigned long long int) must be equal to sizeof(std::int64_t). ");
150 
151  static const bool has_popcount = has_popcount_support();
152  return has_popcount?static_cast<int>(__popcnt64(static_cast<std::int64_t>(value))):fallback_popcountll(value);
153 #else
154  return fallback_popcountll(value);
155 #endif
156 }
157 
158 inline int popcount(unsigned int value) {
159  static_assert(sizeof(unsigned int) == sizeof(std::int32_t),
160  "sizeof(unsigned int) must be equal to sizeof(std::int32_t). ");
161 
162  static const bool has_popcount = has_popcount_support();
163  return has_popcount?static_cast<int>(__popcnt(static_cast<std::int32_t>(value))):fallback_popcount(value);
164 }
165 
166 
167 #elif defined(__INTEL_COMPILER)
168 inline int popcountll(unsigned long long int value) {
169  static_assert(sizeof(unsigned long long int) == sizeof(__int64), "");
170  return _popcnt64(static_cast<__int64>(value));
171 }
172 
173 inline int popcount(unsigned int value) {
174  return _popcnt32(static_cast<int>(value));
175 }
176 
177 
178 #else
179 inline int popcountll(unsigned long long int x) {
180  return fallback_popcountll(x);
181 }
182 
183 inline int popcount(unsigned int x) {
184  return fallback_popcount(x);
185 }
186 
187 
188 #endif
189 }
190 
191 
192 
194 
195 template<typename T>
196 struct make_void {
197  using type = void;
198 };
199 
200 template<typename T, typename = void>
201 struct has_is_transparent: std::false_type {
202 };
203 
204 template<typename T>
205 struct has_is_transparent<T, typename make_void<typename T::is_transparent>::type>: std::true_type {
206 };
207 
208 template<typename U>
209 struct is_power_of_two_policy: std::false_type {
210 };
211 
212 template<std::size_t GrowthFactor>
213 struct is_power_of_two_policy<tsl::sh::power_of_two_growth_policy<GrowthFactor>>: std::true_type {
214 };
215 
216 
217 inline constexpr bool is_power_of_two(std::size_t value) {
218  return value != 0 && (value & (value - 1)) == 0;
219 }
220 
221 inline std::size_t round_up_to_power_of_two(std::size_t value) {
222  if(is_power_of_two(value)) {
223  return value;
224  }
225 
226  if(value == 0) {
227  return 1;
228  }
229 
230  --value;
231  for(std::size_t i = 1; i < sizeof(std::size_t) * CHAR_BIT; i *= 2) {
232  value |= value >> i;
233  }
234 
235  return value + 1;
236 }
237 
238 template<typename T, typename U>
239 static T numeric_cast(U value, const char* error_message = "numeric_cast() failed.") {
240  T ret = static_cast<T>(value);
241  if(static_cast<U>(ret) != value) {
242  throw std::runtime_error(error_message);
243  }
244 
245  const bool is_same_signedness = (std::is_unsigned<T>::value && std::is_unsigned<U>::value) ||
246  (std::is_signed<T>::value && std::is_signed<U>::value);
247  if(is_same_signedness && (ret < T{}) != (value < U{})) {
248  throw std::runtime_error(error_message);
249  }
250 
251  return ret;
252 }
253 
254 
255 /**
256  * Fixed size type used to represent size_type values on serialization. Need to be big enough
257  * to represent a std::size_t on 32 and 64 bits platforms, and must be the same size on both platforms.
258  */
259 using slz_size_type = std::uint64_t;
260 static_assert(std::numeric_limits<slz_size_type>::max() >= std::numeric_limits<std::size_t>::max(),
261  "slz_size_type must be >= std::size_t");
262 
263 template<class T, class Deserializer>
264 static T deserialize_value(Deserializer& deserializer) {
265  // MSVC < 2017 is not conformant, circumvent the problem by removing the template keyword
266 #if defined (_MSC_VER) && _MSC_VER < 1910
267  return deserializer.Deserializer::operator()<T>();
268 #else
269  return deserializer.Deserializer::template operator()<T>();
270 #endif
271 }
272 
273 /**
274  * WARNING: the sparse_array class doesn't free the ressources allocated through the allocator passed in parameter
275  * in each method. You have to manually call `clear(Allocator&)` when you don't need a sparse_array object anymore.
276  *
277  * The reason is that the sparse_array doesn't store the allocator to avoid wasting space in each sparse_array when
278  * the allocator has a size > 0. It only allocates/deallocates objects with the allocator that is passed in parameter.
279  *
280  *
281  *
282  * Index denotes a value between [0, BITMAP_NB_BITS), it is an index similar to std::vector.
283  * Offset denotes the real position in `m_values` corresponding to an index.
284  *
285  * We are using raw pointers instead of std::vector to avoid loosing 2*sizeof(size_t) bytes to store the capacity
286  * and size of the vector in each sparse_array. We know we can only store up to BITMAP_NB_BITS elements in the array,
287  * we don't need such big types.
288  *
289  *
290  * T must be nothrow move contructible and/or copy constructible.
291  * Behaviour is undefined if the destructor of T throws an exception.
292  *
293  * See https://smerity.com/articles/2015/google_sparsehash.html for details on the idea behinds the implementation.
294  *
295  * TODO Check to use std::realloc and std::memmove when possible
296  */
297 template<typename T, typename Allocator, tsl::sh::sparsity Sparsity>
298 class sparse_array {
299 public:
300  using value_type = T;
301  using size_type = std::uint_least8_t;
302  using allocator_type = Allocator;
303  using iterator = value_type*;
304  using const_iterator = const value_type*;
305 
306 private:
307  static const size_type CAPACITY_GROWTH_STEP = (Sparsity == tsl::sh::sparsity::high)?2:
308  (Sparsity == tsl::sh::sparsity::medium)?4:
309  8; // (Sparsity == tsl::sh::sparsity::low)
310 
311  /**
312  * Bitmap size configuration.
313  * Use 32 bits for the bitmap on 32-bits or less environnements as popcount on 64 bits numbers is slow on
314  * these environnements. Use 64 bits bitmap otherwise.
315  */
316 #if SIZE_MAX <= UINT32_MAX
317  using bitmap_type = std::uint_least32_t;
318  static const std::size_t BITMAP_NB_BITS = 32;
319  static const std::size_t BUCKET_SHIFT = 5;
320 #else
321  using bitmap_type = std::uint_least64_t;
322  static const std::size_t BITMAP_NB_BITS = 64;
323  static const std::size_t BUCKET_SHIFT = 6;
324 #endif
325 
326  static const std::size_t BUCKET_MASK = BITMAP_NB_BITS - 1;
327 
328  static_assert(is_power_of_two(BITMAP_NB_BITS), "BITMAP_NB_BITS must be a power of two.");
329  static_assert(std::numeric_limits<bitmap_type>::digits >= BITMAP_NB_BITS,
330  "bitmap_type must be able to hold at least BITMAP_NB_BITS.");
331  static_assert((std::size_t(1) << BUCKET_SHIFT) == BITMAP_NB_BITS,
332  "(1 << BUCKET_SHIFT) must be equal to BITMAP_NB_BITS.");
333  static_assert(std::numeric_limits<size_type>::max() >= BITMAP_NB_BITS,
334  "size_type must be big enough to hold BITMAP_NB_BITS.");
335  static_assert(std::is_unsigned<bitmap_type>::value, "bitmap_type must be unsigned.");
336  static_assert((std::numeric_limits<bitmap_type>::max() & BUCKET_MASK) == BITMAP_NB_BITS - 1, "");
337 
338 
339 public:
340  static const std::size_t DEFAULT_INIT_BUCKETS_SIZE = BITMAP_NB_BITS;
341 
342  /**
343  * Map an ibucket [0, bucket_count) in the hash table to a sparse_ibucket
344  * (a sparse_array holds multiple buckets, so there is less sparse_array than bucket_count).
345  *
346  * The bucket ibucket is in m_sparse_buckets[sparse_ibucket(ibucket)][index_in_sparse_bucket(ibucket)]
347  * instead of something like m_buckets[ibucket] in a classical hash table.
348  */
349  static std::size_t sparse_ibucket(std::size_t ibucket) {
350  return ibucket >> BUCKET_SHIFT;
351  }
352 
353  /**
354  * Map an ibucket [0, bucket_count) in the hash table to an index in the sparse_array
355  * which corresponds to the bucket.
356  *
357  * The bucket ibucket is in m_sparse_buckets[sparse_ibucket(ibucket)][index_in_sparse_bucket(ibucket)]
358  * instead of something like m_buckets[ibucket] in a classical hash table.
359  */
360  static typename sparse_array::size_type index_in_sparse_bucket(std::size_t ibucket) {
361  return static_cast<typename sparse_array::size_type>(ibucket & sparse_array::BUCKET_MASK);
362  }
363 
364 
365 public:
366  sparse_array() noexcept: m_values(nullptr), m_bitmap_vals(0), m_bitmap_deleted_vals(0),
367  m_nb_elements(0), m_capacity(0), m_last_array(false)
368  {
369  }
370 
371  explicit sparse_array(bool last_bucket) noexcept: m_values(nullptr), m_bitmap_vals(0), m_bitmap_deleted_vals(0),
372  m_nb_elements(0), m_capacity(0), m_last_array(last_bucket)
373  {
374  }
375 
376  sparse_array(size_type capacity, Allocator& alloc): m_values(nullptr), m_bitmap_vals(0), m_bitmap_deleted_vals(0),
377  m_nb_elements(0), m_capacity(capacity), m_last_array(false)
378  {
379  if(m_capacity > 0) {
380  m_values = alloc.allocate(m_capacity);
381  tsl_sh_assert(m_values != nullptr); // allocate should throw if there is a failure
382  }
383  }
384 
385  sparse_array(const sparse_array& other, Allocator& alloc):
386  m_values(nullptr), m_bitmap_vals(other.m_bitmap_vals),
387  m_bitmap_deleted_vals(other.m_bitmap_deleted_vals),
388  m_nb_elements(0), m_capacity(other.m_capacity),
389  m_last_array(other.m_last_array)
390  {
391  tsl_sh_assert(other.m_capacity >= other.m_nb_elements);
392  if(m_capacity == 0) {
393  return;
394  }
395 
396  m_values = alloc.allocate(m_capacity);
397  tsl_sh_assert(m_values != nullptr); // allocate should throw if there is a failure
398  try {
399  for(size_type i = 0; i < other.m_nb_elements; i++) {
400  construct_value(alloc, m_values + i, other.m_values[i]);
401  m_nb_elements++;
402  }
403  }
404  catch(...) {
405  clear(alloc);
406  throw;
407  }
408  }
409 
410  sparse_array(sparse_array&& other) noexcept: m_values(other.m_values), m_bitmap_vals(other.m_bitmap_vals),
411  m_bitmap_deleted_vals(other.m_bitmap_deleted_vals),
412  m_nb_elements(other.m_nb_elements), m_capacity(other.m_capacity),
413  m_last_array(other.m_last_array)
414  {
415  other.m_values = nullptr;
416  other.m_bitmap_vals = 0;
417  other.m_bitmap_deleted_vals = 0;
418  other.m_nb_elements = 0;
419  other.m_capacity = 0;
420  }
421 
422  sparse_array(sparse_array&& other, Allocator& alloc):
423  m_values(nullptr), m_bitmap_vals(other.m_bitmap_vals),
424  m_bitmap_deleted_vals(other.m_bitmap_deleted_vals),
425  m_nb_elements(0), m_capacity(other.m_capacity),
426  m_last_array(other.m_last_array)
427  {
428  tsl_sh_assert(other.m_capacity >= other.m_nb_elements);
429  if(m_capacity == 0) {
430  return;
431  }
432 
433  m_values = alloc.allocate(m_capacity);
434  tsl_sh_assert(m_values != nullptr); // allocate should throw if there is a failure
435  try {
436  for(size_type i = 0; i < other.m_nb_elements; i++) {
437  construct_value(alloc, m_values + i, std::move(other.m_values[i]));
438  m_nb_elements++;
439  }
440  }
441  catch(...) {
442  clear(alloc);
443  throw;
444  }
445  }
446 
447  sparse_array& operator=(const sparse_array& ) = delete;
448  sparse_array& operator=(sparse_array&& ) = delete;
449 
450  ~sparse_array() noexcept {
451  // The code that manages the sparse_array must have called clear before destruction.
452  // See documentation of sparse_array for more details.
453  tsl_sh_assert(m_capacity == 0 && m_nb_elements == 0 && m_values == nullptr);
454  }
455 
456  iterator begin() noexcept { return m_values; }
457  iterator end() noexcept { return m_values + m_nb_elements; }
458  const_iterator begin() const noexcept { return cbegin(); }
459  const_iterator end() const noexcept { return cend(); }
460  const_iterator cbegin() const noexcept { return m_values; }
461  const_iterator cend() const noexcept { return m_values + m_nb_elements; }
462 
463  bool empty() const noexcept {
464  return m_nb_elements == 0;
465  }
466 
467  size_type size() const noexcept {
468  return m_nb_elements;
469  }
470 
471  void clear(allocator_type& alloc) noexcept {
472  destroy_and_deallocate_values(alloc, m_values, m_nb_elements, m_capacity);
473 
474  m_values = nullptr;
475  m_bitmap_vals = 0;
476  m_bitmap_deleted_vals = 0;
477  m_nb_elements = 0;
478  m_capacity = 0;
479  }
480 
481  bool last() const noexcept {
482  return m_last_array;
483  }
484 
485  void set_as_last() noexcept {
486  m_last_array = true;
487  }
488 
489  bool has_value(size_type index) const noexcept {
490  tsl_sh_assert(index < BITMAP_NB_BITS);
491  return (m_bitmap_vals & (bitmap_type(1) << index)) != 0;
492  }
493 
494  bool has_deleted_value(size_type index) const noexcept {
495  tsl_sh_assert(index < BITMAP_NB_BITS);
496  return (m_bitmap_deleted_vals & (bitmap_type(1) << index)) != 0;
497  }
498 
499  iterator value(size_type index) noexcept {
500  tsl_sh_assert(has_value(index));
501  return m_values + index_to_offset(index);
502  }
503 
504  const_iterator value(size_type index) const noexcept {
505  tsl_sh_assert(has_value(index));
506  return m_values + index_to_offset(index);
507  }
508 
509  /**
510  * Return iterator to set value.
511  */
512  template<typename... Args>
513  iterator set(allocator_type& alloc, size_type index, Args&&... value_args) {
514  tsl_sh_assert(!has_value(index));
515 
516  const size_type offset = index_to_offset(index);
517  insert_at_offset(alloc, offset, std::forward<Args>(value_args)...);
518 
519  m_bitmap_vals = (m_bitmap_vals | (bitmap_type(1) << index));
520  m_bitmap_deleted_vals = (m_bitmap_deleted_vals & ~(bitmap_type(1) << index));
521 
522  m_nb_elements++;
523 
524  tsl_sh_assert(has_value(index));
525  tsl_sh_assert(!has_deleted_value(index));
526 
527  return m_values + offset;
528  }
529 
530  iterator erase(allocator_type& alloc, iterator position) {
531  const size_type offset = static_cast<size_type>(std::distance(begin(), position));
532  return erase(alloc, position, offset_to_index(offset));
533  }
534 
535  // Return the next value or end if no next value
536  iterator erase(allocator_type& alloc, iterator position, size_type index) {
537  tsl_sh_assert(has_value(index));
538  tsl_sh_assert(!has_deleted_value(index));
539 
540  const size_type offset = static_cast<size_type>(std::distance(begin(), position));
541  erase_at_offset(alloc, offset);
542 
543  m_bitmap_vals = (m_bitmap_vals & ~(bitmap_type(1) << index));
544  m_bitmap_deleted_vals = (m_bitmap_deleted_vals | (bitmap_type(1) << index));
545 
546  m_nb_elements--;
547 
548  tsl_sh_assert(!has_value(index));
549  tsl_sh_assert(has_deleted_value(index));
550 
551  return m_values + offset;
552  }
553 
554  void swap(sparse_array& other) {
555  using std::swap;
556 
557  swap(m_values, other.m_values);
558  swap(m_bitmap_vals, other.m_bitmap_vals);
559  swap(m_bitmap_deleted_vals, other.m_bitmap_deleted_vals);
560  swap(m_nb_elements, other.m_nb_elements);
561  swap(m_capacity, other.m_capacity);
562  swap(m_last_array, other.m_last_array);
563  }
564 
565  static iterator mutable_iterator(const_iterator pos) {
566  return const_cast<iterator>(pos);
567  }
568 
569  template<class Serializer>
570  void serialize(Serializer& serializer) const {
571  const slz_size_type sparse_bucket_size = m_nb_elements;
572  serializer(sparse_bucket_size);
573 
574  const slz_size_type bitmap_vals = m_bitmap_vals;
575  serializer(bitmap_vals);
576 
577  const slz_size_type bitmap_deleted_vals = m_bitmap_deleted_vals;
578  serializer(bitmap_deleted_vals);
579 
580  for(const value_type& value: *this) {
581  serializer(value);
582  }
583  }
584 
585  template<class Deserializer>
586  static sparse_array deserialize_hash_compatible(Deserializer& deserializer, Allocator& alloc) {
587  const slz_size_type sparse_bucket_size = deserialize_value<slz_size_type>(deserializer);
588  const slz_size_type bitmap_vals = deserialize_value<slz_size_type>(deserializer);
589  const slz_size_type bitmap_deleted_vals = deserialize_value<slz_size_type>(deserializer);
590 
591  if(sparse_bucket_size > BITMAP_NB_BITS) {
592  throw std::runtime_error("Deserialized sparse_bucket_size is too big for the platform. Maximum should be BITMAP_NB_BITS.");
593  }
594 
595 
596  sparse_array sarray;
597  if(sparse_bucket_size == 0) {
598  return sarray;
599  }
600 
601  sarray.m_bitmap_vals = numeric_cast<bitmap_type>(bitmap_vals, "Deserialized bitmap_vals is too big.");
602  sarray.m_bitmap_deleted_vals = numeric_cast<bitmap_type>(bitmap_deleted_vals, "Deserialized bitmap_deleted_vals is too big.");
603 
604  sarray.m_capacity = numeric_cast<size_type>(sparse_bucket_size, "Deserialized sparse_bucket_size is too big.");
605  sarray.m_values = alloc.allocate(sarray.m_capacity);
606 
607  try {
608  for(size_type ivalue = 0; ivalue < sarray.m_capacity; ivalue++) {
609  construct_value(alloc, sarray.m_values + ivalue, deserialize_value<value_type>(deserializer));
610  sarray.m_nb_elements++;
611  }
612  }
613  catch(...) {
614  sarray.clear(alloc);
615  throw;
616  }
617 
618  return sarray;
619  }
620 
621  /**
622  * Deserialize the values of the bucket and insert them all in sparse_hash through sparse_hash.insert(...).
623  */
624  template<class Deserializer, class SparseHash>
625  static void deserialize_values_into_sparse_hash(Deserializer& deserializer, SparseHash& sparse_hash) {
626  const slz_size_type sparse_bucket_size = deserialize_value<slz_size_type>(deserializer);
627 
628  const slz_size_type bitmap_vals = deserialize_value<slz_size_type>(deserializer);
629  static_cast<void>(bitmap_vals); // Ignore, not needed
630 
631  const slz_size_type bitmap_deleted_vals = deserialize_value<slz_size_type>(deserializer);
632  static_cast<void>(bitmap_deleted_vals); // Ignore, not needed
633 
634 
635  for(slz_size_type ivalue = 0; ivalue < sparse_bucket_size; ivalue++) {
636  sparse_hash.insert(deserialize_value<value_type>(deserializer));
637  }
638  }
639 
640 private:
641  template<typename... Args>
642  static void construct_value(allocator_type& alloc, value_type* value, Args&&... value_args) {
643  std::allocator_traits<allocator_type>::construct(alloc, value, std::forward<Args>(value_args)...);
644  }
645 
646  static void destroy_value(allocator_type& alloc, value_type* value) noexcept {
647  std::allocator_traits<allocator_type>::destroy(alloc, value);
648  }
649 
650  static void destroy_and_deallocate_values(allocator_type& alloc, value_type* values,
651  size_type nb_values, size_type capacity_values) noexcept
652  {
653  for(size_type i = 0; i < nb_values; i++) {
654  destroy_value(alloc, values + i);
655  }
656 
657  alloc.deallocate(values, capacity_values);
658  }
659 
660  static size_type popcount(bitmap_type val) noexcept {
661  if(sizeof(bitmap_type) <= sizeof(unsigned int)) {
662  return static_cast<size_type>(tsl::detail_popcount::popcount(static_cast<unsigned int>(val)));
663  }
664  else {
665  return static_cast<size_type>(tsl::detail_popcount::popcountll(val));
666  }
667  }
668 
669  size_type index_to_offset(size_type index) const noexcept {
670  tsl_sh_assert(index < BITMAP_NB_BITS);
671  return popcount(m_bitmap_vals & ((bitmap_type(1) << index) - bitmap_type(1)));
672  }
673 
674  //TODO optimize
675  size_type offset_to_index(size_type offset) const noexcept {
676  tsl_sh_assert(offset < m_nb_elements);
677 
678  bitmap_type bitmap_vals = m_bitmap_vals;
679  size_type index = 0;
680  size_type nb_ones = 0;
681 
682  while(bitmap_vals != 0) {
683  if((bitmap_vals & 0x1) == 1) {
684  if(nb_ones == offset) {
685  break;
686  }
687 
688  nb_ones++;
689  }
690 
691  index++;
692  bitmap_vals = bitmap_vals >> 1;
693  }
694 
695  return index;
696  }
697 
698  size_type next_capacity() const noexcept {
699  return static_cast<size_type>(m_capacity + CAPACITY_GROWTH_STEP);
700  }
701 
702 
703 
704 
705 
706 
707 
708  /**
709  * Insertion
710  *
711  * Two situations:
712  * - Either we are in a situation where std::is_nothrow_move_constructible<value_type>::value is true.
713  * In this case, on insertion we just reallocate m_values when we reach
714  * its capacity (i.e. m_nb_elements == m_capacity), otherwise we just put the new value at its
715  * appropriate place. We can easly keep the strong exception guarantee as moving the values around is safe.
716  * - Otherwise we are in a situation where std::is_nothrow_move_constructible<value_type>::value is false.
717  * In this case on EACH insertion we allocate a new area of m_nb_elements + 1 where we copy the values
718  * of m_values into it and put the new value there. On success, we set m_values to this new area.
719  * Even if slower, it's the only way to preserve to strong exception guarantee.
720  */
721  template<typename... Args, typename U = value_type,
722  typename std::enable_if<std::is_nothrow_move_constructible<U>::value>::type* = nullptr>
723  void insert_at_offset(allocator_type& alloc, size_type offset, Args&&... value_args) {
724  if(m_nb_elements < m_capacity) {
725  insert_at_offset_no_realloc(alloc, offset, std::forward<Args>(value_args)...);
726  }
727  else {
728  insert_at_offset_realloc(alloc, offset, next_capacity(), std::forward<Args>(value_args)...);
729  }
730  }
731 
732  template<typename... Args, typename U = value_type,
733  typename std::enable_if<!std::is_nothrow_move_constructible<U>::value>::type* = nullptr>
734  void insert_at_offset(allocator_type& alloc, size_type offset, Args&&... value_args) {
735  insert_at_offset_realloc(alloc, offset, m_nb_elements + 1, std::forward<Args>(value_args)...);
736  }
737 
738  template<typename... Args, typename U = value_type,
739  typename std::enable_if<std::is_nothrow_move_constructible<U>::value>::type* = nullptr>
740  void insert_at_offset_no_realloc(allocator_type& alloc, size_type offset, Args&&... value_args) {
741  tsl_sh_assert(offset <= m_nb_elements);
742  tsl_sh_assert(m_nb_elements < m_capacity);
743 
744  for(size_type i = m_nb_elements; i > offset; i--) {
745  construct_value(alloc, m_values + i, std::move(m_values[i - 1]));
746  destroy_value(alloc, m_values + i - 1);
747  }
748 
749  try {
750  construct_value(alloc, m_values + offset, std::forward<Args>(value_args)...);
751  }
752  catch(...) {
753  for(size_type i = offset; i < m_nb_elements; i++) {
754  construct_value(alloc, m_values + i, std::move(m_values[i + 1]));
755  destroy_value(alloc, m_values + i + 1);
756  }
757  throw;
758  }
759  }
760 
761  template<typename... Args, typename U = value_type,
762  typename std::enable_if<std::is_nothrow_move_constructible<U>::value>::type* = nullptr>
763  void insert_at_offset_realloc(allocator_type& alloc, size_type offset, size_type new_capacity, Args&&... value_args) {
764  tsl_sh_assert(new_capacity > m_nb_elements);
765 
766  value_type* new_values = alloc.allocate(new_capacity);
767  tsl_sh_assert(new_values != nullptr); // allocate should throw if there is a failure
768 
769  try {
770  construct_value(alloc, new_values + offset, std::forward<Args>(value_args)...);
771  }
772  catch(...) {
773  alloc.deallocate(new_values, new_capacity);
774  throw;
775  }
776 
777  // Should not throw from here
778  for(size_type i = 0; i < offset; i++) {
779  construct_value(alloc, new_values + i, std::move(m_values[i]));
780  }
781 
782  for(size_type i = offset; i < m_nb_elements; i++) {
783  construct_value(alloc, new_values + i + 1, std::move(m_values[i]));
784  }
785 
786  destroy_and_deallocate_values(alloc, m_values, m_nb_elements, m_capacity);
787 
788  m_values = new_values;
789  m_capacity = new_capacity;
790  }
791 
792  template<typename... Args, typename U = value_type,
793  typename std::enable_if<!std::is_nothrow_move_constructible<U>::value>::type* = nullptr>
794  void insert_at_offset_realloc(allocator_type& alloc, size_type offset, size_type new_capacity, Args&&... value_args) {
795  tsl_sh_assert(new_capacity > m_nb_elements);
796 
797  value_type* new_values = alloc.allocate(new_capacity);
798  tsl_sh_assert(new_values != nullptr); // allocate should throw if there is a failure
799 
800  size_type nb_new_values = 0;
801  try {
802  for(size_type i = 0; i < offset; i++) {
803  construct_value(alloc, new_values + i, m_values[i]);
804  nb_new_values++;
805  }
806 
807  construct_value(alloc, new_values + offset, std::forward<Args>(value_args)...);
808  nb_new_values++;
809 
810  for(size_type i = offset; i < m_nb_elements; i++) {
811  construct_value(alloc, new_values + i + 1, m_values[i]);
812  nb_new_values++;
813  }
814  }
815  catch(...) {
816  destroy_and_deallocate_values(alloc, new_values, nb_new_values, new_capacity);
817  throw;
818  }
819 
820  tsl_sh_assert(nb_new_values == m_nb_elements + 1);
821 
822  destroy_and_deallocate_values(alloc, m_values, m_nb_elements, m_capacity);
823 
824  m_values = new_values;
825  m_capacity = new_capacity;
826  }
827 
828 
829 
830  /**
831  * Erasure
832  *
833  * Two situations:
834  * - Either we are in a situation where std::is_nothrow_move_constructible<value_type>::value is true.
835  * Simply destroy the value and left-shift move the value on the right of offset.
836  * - Otherwise we are in a situation where std::is_nothrow_move_constructible<value_type>::value is false.
837  * Copy all the values except the one at offset into a new heap area. On success, we set m_values
838  * to this new area. Even if slower, it's the only way to preserve to strong exception guarantee.
839  */
840  template<typename... Args, typename U = value_type,
841  typename std::enable_if<std::is_nothrow_move_constructible<U>::value>::type* = nullptr>
842  void erase_at_offset(allocator_type& alloc, size_type offset) noexcept {
843  tsl_sh_assert(offset < m_nb_elements);
844 
845  destroy_value(alloc, m_values + offset);
846 
847  for(size_type i = offset + 1; i < m_nb_elements; i++) {
848  construct_value(alloc, m_values + i - 1, std::move(m_values[i]));
849  destroy_value(alloc, m_values + i);
850  }
851  }
852 
853  template<typename... Args, typename U = value_type,
854  typename std::enable_if<!std::is_nothrow_move_constructible<U>::value>::type* = nullptr>
855  void erase_at_offset(allocator_type& alloc, size_type offset) {
856  tsl_sh_assert(offset < m_nb_elements);
857 
858  // Erasing the last element, don't need to reallocate. We keep the capacity.
859  if(offset + 1 == m_nb_elements) {
860  destroy_value(alloc, m_values + offset);
861  return;
862  }
863 
864  tsl_sh_assert(m_nb_elements > 1);
865  const size_type new_capacity = m_nb_elements - 1;
866 
867  value_type* new_values = alloc.allocate(new_capacity);
868  tsl_sh_assert(new_values != nullptr); // allocate should throw if there is a failure
869 
870  size_type nb_new_values = 0;
871  try {
872  for(size_type i = 0; i < m_nb_elements; i++) {
873  if(i != offset) {
874  construct_value(alloc, new_values + nb_new_values, m_values[i]);
875  nb_new_values++;
876  }
877  }
878  }
879  catch(...) {
880  destroy_and_deallocate_values(alloc, new_values, nb_new_values, new_capacity);
881  throw;
882  }
883 
884  tsl_sh_assert(nb_new_values == m_nb_elements - 1);
885 
886  destroy_and_deallocate_values(alloc, m_values, m_nb_elements, m_capacity);
887 
888  m_values = new_values;
889  m_capacity = new_capacity;
890  }
891 
892 private:
893  value_type* m_values;
894 
895  bitmap_type m_bitmap_vals;
896  bitmap_type m_bitmap_deleted_vals;
897 
898  size_type m_nb_elements;
899  size_type m_capacity;
900  bool m_last_array;
901 };
902 
903 
904 /**
905  * Internal common class used by `sparse_map` and `sparse_set`.
906  *
907  * `ValueType` is what will be stored by `sparse_hash` (usually `std::pair<Key, T>` for map and `Key` for set).
908  *
909  * `KeySelect` should be a `FunctionObject` which takes a `ValueType` in parameter and returns a
910  * reference to the key.
911  *
912  * `ValueSelect` should be a `FunctionObject` which takes a `ValueType` in parameter and returns a
913  * reference to the value. `ValueSelect` should be void if there is no value (in a set for example).
914  *
915  * The strong exception guarantee only holds if `ExceptionSafety` is set to `tsl::sh::exception_safety::strong`.
916  *
917  * `ValueType` must be nothrow move contructible and/or copy constructible.
918  * Behaviour is undefined if the destructor of `ValueType` throws.
919  *
920  *
921  * The class holds its buckets in a 2-dimensional fashion. Instead of having a linear `std::vector<bucket>`
922  * for [0, bucket_count) where each bucket stores one value, we have a `std::vector<sparse_array>` (m_sparse_buckets)
923  * where each `sparse_array` stores multiple values (up to `sparse_array::BITMAP_NB_BITS`). To convert a one dimensional
924  * `ibucket` position to a position in `std::vector<sparse_array>` and a position in `sparse_array`, use respectively
925  * the methods `sparse_array::sparse_ibucket(ibucket)` and `sparse_array::index_in_sparse_bucket(ibucket)`.
926  */
927 template<class ValueType,
928  class KeySelect,
929  class ValueSelect,
930  class Hash,
931  class KeyEqual,
932  class Allocator,
933  class GrowthPolicy,
934  tsl::sh::exception_safety ExceptionSafety,
935  tsl::sh::sparsity Sparsity,
936  tsl::sh::probing Probing>
937 class sparse_hash: private Allocator, private Hash, private KeyEqual, private GrowthPolicy {
938 private:
939  template<typename U>
940  using has_mapped_type = typename std::integral_constant<bool, !std::is_same<U, void>::value>;
941 
942 
943 
944  static_assert(noexcept(std::declval<GrowthPolicy>().bucket_for_hash(std::size_t(0))), "GrowthPolicy::bucket_for_hash must be noexcept.");
945  static_assert(noexcept(std::declval<GrowthPolicy>().clear()), "GrowthPolicy::clear must be noexcept.");
946 
947 public:
948  template<bool IsConst>
950 
951  using key_type = typename KeySelect::key_type;
952  using value_type = ValueType;
953  using size_type = std::size_t;
954  using difference_type = std::ptrdiff_t;
955  using hasher = Hash;
956  using key_equal = KeyEqual;
957  using allocator_type = Allocator;
958  using reference = value_type&;
959  using const_reference = const value_type&;
960  using pointer = value_type*;
961  using const_pointer = const value_type*;
962  using iterator = sparse_iterator<false>;
963  using const_iterator = sparse_iterator<true>;
964 
965 
966 private:
967  using sparse_array = tsl::detail_sparse_hash::sparse_array<ValueType, Allocator, Sparsity>;
968 
969  using sparse_buckets_allocator =
970  typename std::allocator_traits<allocator_type>::template rebind_alloc<sparse_array>;
971  using sparse_buckets_container = std::vector<sparse_array, sparse_buckets_allocator>;
972 
973 public:
974  /**
975  * The `operator*()` and `operator->()` methods return a const reference and const pointer respectively to the
976  * stored value type (`Key` for a set, `std::pair<Key, T>` for a map).
977  *
978  * In case of a map, to get a mutable reference to the value `T` associated to a key (the `.second` in the
979  * stored pair), you have to call `value()`.
980  */
981  template<bool IsConst>
982  class sparse_iterator {
983  friend class sparse_hash;
984 
985  private:
986  using sparse_bucket_iterator = typename std::conditional<IsConst,
987  typename sparse_buckets_container::const_iterator,
988  typename sparse_buckets_container::iterator>::type;
989 
990  using sparse_array_iterator = typename std::conditional<IsConst,
991  typename sparse_array::const_iterator,
992  typename sparse_array::iterator>::type;
993 
994  /**
995  * sparse_array_it should be nullptr if sparse_bucket_it == m_sparse_buckets.end(). (TODO better way?)
996  */
997  sparse_iterator(sparse_bucket_iterator sparse_bucket_it,
998  sparse_array_iterator sparse_array_it): m_sparse_buckets_it(sparse_bucket_it),
999  m_sparse_array_it(sparse_array_it)
1000  {
1001  }
1002 
1003  public:
1004  using iterator_category = std::forward_iterator_tag;
1005  using value_type = const typename sparse_hash::value_type;
1006  using difference_type = std::ptrdiff_t;
1007  using reference = value_type&;
1008  using pointer = value_type*;
1009 
1010 
1011  sparse_iterator() noexcept {
1012  }
1013 
1014  sparse_iterator(const sparse_iterator<false>& other) noexcept: m_sparse_buckets_it(other.m_sparse_buckets_it),
1015  m_sparse_array_it(other.m_sparse_array_it)
1016  {
1017  }
1018 
1019  const typename sparse_hash::key_type& key() const {
1020  return KeySelect()(*m_sparse_array_it);
1021  }
1022 
1023  template<class U = ValueSelect, typename std::enable_if<has_mapped_type<U>::value && IsConst>::type* = nullptr>
1024  const typename U::value_type& value() const {
1025  return U()(*m_sparse_array_it);
1026  }
1027 
1028  template<class U = ValueSelect, typename std::enable_if<has_mapped_type<U>::value && !IsConst>::type* = nullptr>
1029  typename U::value_type& value() {
1030  return U()(*m_sparse_array_it);
1031  }
1032 
1033  reference operator*() const {
1034  return *m_sparse_array_it;
1035  }
1036 
1037  pointer operator->() const {
1038  return std::addressof(*m_sparse_array_it);
1039  }
1040 
1042  tsl_sh_assert(m_sparse_array_it != nullptr);
1043  ++m_sparse_array_it;
1044 
1045  if(m_sparse_array_it == m_sparse_buckets_it->end()) {
1046  do {
1047  if(m_sparse_buckets_it->last()) {
1048  ++m_sparse_buckets_it;
1049  m_sparse_array_it = nullptr;
1050  return *this;
1051  }
1052 
1053  ++m_sparse_buckets_it;
1054  } while(m_sparse_buckets_it->empty());
1055 
1056  m_sparse_array_it = m_sparse_buckets_it->begin();
1057  }
1058 
1059  return *this;
1060  }
1061 
1063  sparse_iterator tmp(*this);
1064  ++*this;
1065 
1066  return tmp;
1067  }
1068 
1069  friend bool operator==(const sparse_iterator& lhs, const sparse_iterator& rhs) {
1070  return lhs.m_sparse_buckets_it == rhs.m_sparse_buckets_it &&
1071  lhs.m_sparse_array_it == rhs.m_sparse_array_it;
1072  }
1073 
1074  friend bool operator!=(const sparse_iterator& lhs, const sparse_iterator& rhs) {
1075  return !(lhs == rhs);
1076  }
1077 
1078  private:
1079  sparse_bucket_iterator m_sparse_buckets_it;
1080  sparse_array_iterator m_sparse_array_it;
1081  };
1082 
1083 
1084 public:
1085  sparse_hash(size_type bucket_count,
1086  const Hash& hash,
1087  const KeyEqual& equal,
1088  const Allocator& alloc,
1089  float max_load_factor): Allocator(alloc),
1090  Hash(hash),
1091  KeyEqual(equal),
1092  GrowthPolicy(bucket_count),
1093  m_sparse_buckets(alloc),
1094  m_first_or_empty_sparse_bucket(static_empty_sparse_bucket_ptr()),
1095  m_bucket_count(bucket_count),
1096  m_nb_elements(0),
1097  m_nb_deleted_buckets(0)
1098  {
1099  if(m_bucket_count > max_bucket_count()) {
1100  throw std::length_error("The map exceeds its maxmimum size.");
1101  }
1102 
1103  if(m_bucket_count > 0) {
1104  /*
1105  * We can't use the `vector(size_type count, const Allocator& alloc)` constructor
1106  * as it's only available in C++14 and we need to support C++11. We thus must resize after using
1107  * the `vector(const Allocator& alloc)` constructor.
1108  *
1109  * We can't use `vector(size_type count, const T& value, const Allocator& alloc)` as it requires the
1110  * value T to be copyable.
1111  */
1112  const size_type nb_sparse_buckets =
1113  std::max(size_type(1),
1114  sparse_array::sparse_ibucket(tsl::detail_sparse_hash::round_up_to_power_of_two(bucket_count)));
1115 
1116  m_sparse_buckets.resize(nb_sparse_buckets);
1117  m_first_or_empty_sparse_bucket = m_sparse_buckets.data();
1118 
1119  tsl_sh_assert(!m_sparse_buckets.empty());
1120  m_sparse_buckets.back().set_as_last();
1121  }
1122 
1123 
1124  this->max_load_factor(max_load_factor);
1125 
1126 
1127  // Check in the constructor instead of outside of a function to avoi compilation issues
1128  // when value_type is not complete.
1129  static_assert(std::is_nothrow_move_constructible<value_type>::value ||
1130  std::is_copy_constructible<value_type>::value,
1131  "Key, and T if present, must be nothrow move constructible and/or copy constructible.");
1132  }
1133 
1135  clear();
1136  }
1137 
1138  sparse_hash(const sparse_hash& other):
1139  Allocator(std::allocator_traits<Allocator>::select_on_container_copy_construction(other)),
1140  Hash(other),
1141  KeyEqual(other),
1142  GrowthPolicy(other),
1143  m_sparse_buckets(std::allocator_traits<Allocator>::select_on_container_copy_construction(other)),
1144  m_bucket_count(other.m_bucket_count),
1145  m_nb_elements(other.m_nb_elements),
1146  m_nb_deleted_buckets(other.m_nb_deleted_buckets),
1147  m_load_threshold_rehash(other.m_load_threshold_rehash),
1148  m_load_threshold_clear_deleted(other.m_load_threshold_clear_deleted),
1149  m_max_load_factor(other.m_max_load_factor)
1150  {
1151  copy_buckets_from(other),
1152  m_first_or_empty_sparse_bucket = m_sparse_buckets.data();
1153  }
1154 
1160  : Allocator(std::move(other)),
1161  Hash(std::move(other)),
1162  KeyEqual(std::move(other)),
1163  GrowthPolicy(std::move(other)),
1164  m_sparse_buckets(std::move(other.m_sparse_buckets)),
1165  m_first_or_empty_sparse_bucket(m_sparse_buckets.empty()?static_empty_sparse_bucket_ptr():
1166  m_sparse_buckets.data()),
1167  m_bucket_count(other.m_bucket_count),
1168  m_nb_elements(other.m_nb_elements),
1169  m_nb_deleted_buckets(other.m_nb_deleted_buckets),
1170  m_load_threshold_rehash(other.m_load_threshold_rehash),
1171  m_load_threshold_clear_deleted(other.m_load_threshold_clear_deleted),
1172  m_max_load_factor(other.m_max_load_factor)
1173  {
1174  other.GrowthPolicy::clear();
1175  other.m_sparse_buckets.clear();
1176  other.m_first_or_empty_sparse_bucket = static_empty_sparse_bucket_ptr();
1177  other.m_bucket_count = 0;
1178  other.m_nb_elements = 0;
1179  other.m_nb_deleted_buckets = 0;
1180  other.m_load_threshold_rehash = 0;
1181  other.m_load_threshold_clear_deleted = 0;
1182  }
1183 
1185  if(this != &other) {
1186  clear();
1187 
1188  if(std::allocator_traits<Allocator>::propagate_on_container_copy_assignment::value) {
1189  Allocator::operator=(other);
1190  }
1191 
1192  Hash::operator=(other);
1193  KeyEqual::operator=(other);
1194  GrowthPolicy::operator=(other);
1195 
1196  if(std::allocator_traits<Allocator>::propagate_on_container_copy_assignment::value) {
1197  m_sparse_buckets = sparse_buckets_container(static_cast<const Allocator&>(other));
1198  }
1199  else {
1200  if(m_sparse_buckets.size() != other.m_sparse_buckets.size()) {
1201  m_sparse_buckets = sparse_buckets_container(static_cast<const Allocator&>(*this));
1202  }
1203  else {
1204  m_sparse_buckets.clear();
1205  }
1206  }
1207 
1208  copy_buckets_from(other);
1209  m_first_or_empty_sparse_bucket = m_sparse_buckets.empty()?static_empty_sparse_bucket_ptr():
1210  m_sparse_buckets.data();
1211 
1212 
1213  m_bucket_count = other.m_bucket_count;
1214  m_nb_elements = other.m_nb_elements;
1215  m_nb_deleted_buckets = other.m_nb_deleted_buckets;
1216  m_load_threshold_rehash = other.m_load_threshold_rehash;
1217  m_load_threshold_clear_deleted = other.m_load_threshold_clear_deleted;
1218  m_max_load_factor = other.m_max_load_factor;
1219  }
1220 
1221  return *this;
1222  }
1223 
1225  clear();
1226 
1227  if(std::allocator_traits<Allocator>::propagate_on_container_move_assignment::value) {
1228  static_cast<Allocator&>(*this) = std::move(static_cast<Allocator&>(other));
1229  m_sparse_buckets = std::move(other.m_sparse_buckets);
1230  }
1231  else if(static_cast<Allocator&>(*this) != static_cast<Allocator&>(other)) {
1232  move_buckets_from(std::move(other));
1233  }
1234  else {
1235  static_cast<Allocator&>(*this) = std::move(static_cast<Allocator&>(other));
1236  m_sparse_buckets = std::move(other.m_sparse_buckets);
1237  }
1238 
1239 
1240  m_first_or_empty_sparse_bucket = m_sparse_buckets.empty()?static_empty_sparse_bucket_ptr():
1241  m_sparse_buckets.data();
1242 
1243  static_cast<Hash&>(*this) = std::move(static_cast<Hash&>(other));
1244  static_cast<KeyEqual&>(*this) = std::move(static_cast<KeyEqual&>(other));
1245  static_cast<GrowthPolicy&>(*this) = std::move(static_cast<GrowthPolicy&>(other));
1246  m_bucket_count = other.m_bucket_count;
1247  m_nb_elements = other.m_nb_elements;
1248  m_nb_deleted_buckets = other.m_nb_deleted_buckets;
1249  m_load_threshold_rehash = other.m_load_threshold_rehash;
1250  m_load_threshold_clear_deleted = other.m_load_threshold_clear_deleted;
1251  m_max_load_factor = other.m_max_load_factor;
1252 
1253  other.GrowthPolicy::clear();
1254  other.m_sparse_buckets.clear();
1255  other.m_first_or_empty_sparse_bucket = static_empty_sparse_bucket_ptr();
1256  other.m_bucket_count = 0;
1257  other.m_nb_elements = 0;
1258  other.m_nb_deleted_buckets = 0;
1259  other.m_load_threshold_rehash = 0;
1260  other.m_load_threshold_clear_deleted = 0;
1261 
1262  return *this;
1263  }
1264 
1265  allocator_type get_allocator() const {
1266  return static_cast<const Allocator&>(*this);
1267  }
1268 
1269 
1270  /*
1271  * Iterators
1272  */
1273  iterator begin() noexcept {
1274  auto begin = m_sparse_buckets.begin();
1275  while(begin != m_sparse_buckets.end() && begin->empty()) {
1276  ++begin;
1277  }
1278 
1279  return iterator(begin, (begin != m_sparse_buckets.end())?begin->begin():nullptr);
1280  }
1281 
1282  const_iterator begin() const noexcept {
1283  return cbegin();
1284  }
1285 
1286  const_iterator cbegin() const noexcept {
1287  auto begin = m_sparse_buckets.cbegin();
1288  while(begin != m_sparse_buckets.cend() && begin->empty()) {
1289  ++begin;
1290  }
1291 
1292  return const_iterator(begin, (begin != m_sparse_buckets.cend())?begin->cbegin():nullptr);
1293  }
1294 
1295  iterator end() noexcept {
1296  return iterator(m_sparse_buckets.end(), nullptr);
1297  }
1298 
1299  const_iterator end() const noexcept {
1300  return cend();
1301  }
1302 
1303  const_iterator cend() const noexcept {
1304  return const_iterator(m_sparse_buckets.cend(), nullptr);
1305  }
1306 
1307 
1308  /*
1309  * Capacity
1310  */
1311  bool empty() const noexcept {
1312  return m_nb_elements == 0;
1313  }
1314 
1315  size_type size() const noexcept {
1316  return m_nb_elements;
1317  }
1318 
1319  size_type max_size() const noexcept {
1320  return std::min(std::allocator_traits<Allocator>::max_size(), m_sparse_buckets.max_size());
1321  }
1322 
1323  /*
1324  * Modifiers
1325  */
1326  void clear() noexcept {
1327  for(auto& bucket: m_sparse_buckets) {
1328  bucket.clear(*this);
1329  }
1330 
1331  m_nb_elements = 0;
1332  m_nb_deleted_buckets = 0;
1333  }
1334 
1335 
1336 
1337  template<typename P>
1338  std::pair<iterator, bool> insert(P&& value) {
1339  return insert_impl(KeySelect()(value), std::forward<P>(value));
1340  }
1341 
1342  template<typename P>
1343  iterator insert(const_iterator hint, P&& value) {
1344  if(hint != cend() && compare_keys(KeySelect()(*hint), KeySelect()(value))) {
1345  return mutable_iterator(hint);
1346  }
1347 
1348  return insert(std::forward<P>(value)).first;
1349  }
1350 
1351  template<class InputIt>
1352  void insert(InputIt first, InputIt last) {
1353  if(std::is_base_of<std::forward_iterator_tag,
1354  typename std::iterator_traits<InputIt>::iterator_category>::value)
1355  {
1356  const auto nb_elements_insert = std::distance(first, last);
1357  const size_type nb_free_buckets = m_load_threshold_rehash - size();
1358  tsl_sh_assert(m_load_threshold_rehash >= size());
1359 
1360  if(nb_elements_insert > 0 && nb_free_buckets < size_type(nb_elements_insert)) {
1361  reserve(size() + size_type(nb_elements_insert));
1362  }
1363  }
1364 
1365  for(; first != last; ++first) {
1366  insert(*first);
1367  }
1368  }
1369 
1370 
1371 
1372  template<class K, class M>
1373  std::pair<iterator, bool> insert_or_assign(K&& key, M&& obj) {
1374  auto it = try_emplace(std::forward<K>(key), std::forward<M>(obj));
1375  if(!it.second) {
1376  it.first.value() = std::forward<M>(obj);
1377  }
1378 
1379  return it;
1380  }
1381 
1382  template<class K, class M>
1383  iterator insert_or_assign(const_iterator hint, K&& key, M&& obj) {
1384  if(hint != cend() && compare_keys(KeySelect()(*hint), key)) {
1385  auto it = mutable_iterator(hint);
1386  it.value() = std::forward<M>(obj);
1387 
1388  return it;
1389  }
1390 
1391  return insert_or_assign(std::forward<K>(key), std::forward<M>(obj)).first;
1392  }
1393 
1394 
1395  template<class... Args>
1396  std::pair<iterator, bool> emplace(Args&&... args) {
1397  return insert(value_type(std::forward<Args>(args)...));
1398  }
1399 
1400  template<class... Args>
1401  iterator emplace_hint(const_iterator hint, Args&&... args) {
1402  return insert(hint, value_type(std::forward<Args>(args)...));
1403  }
1404 
1405 
1406 
1407  template<class K, class... Args>
1408  std::pair<iterator, bool> try_emplace(K&& key, Args&&... args) {
1409  return insert_impl(key, std::piecewise_construct,
1410  std::forward_as_tuple(std::forward<K>(key)),
1411  std::forward_as_tuple(std::forward<Args>(args)...));
1412  }
1413 
1414  template<class K, class... Args>
1415  iterator try_emplace(const_iterator hint, K&& key, Args&&... args) {
1416  if(hint != cend() && compare_keys(KeySelect()(*hint), key)) {
1417  return mutable_iterator(hint);
1418  }
1419 
1420  return try_emplace(std::forward<K>(key), std::forward<Args>(args)...).first;
1421  }
1422 
1423  /**
1424  * Here to avoid `template<class K> size_type erase(const K& key)` being used when
1425  * we use an iterator instead of a const_iterator.
1426  */
1427  iterator erase(iterator pos) {
1428  tsl_sh_assert(pos != end() && m_nb_elements > 0);
1429  auto it_sparse_array_next = pos.m_sparse_buckets_it->erase(*this, pos.m_sparse_array_it);
1430  m_nb_elements--;
1431  m_nb_deleted_buckets++;
1432 
1433  if(it_sparse_array_next == pos.m_sparse_buckets_it->end()) {
1434  auto it_sparse_buckets_next = pos.m_sparse_buckets_it;
1435  do {
1436  ++it_sparse_buckets_next;
1437  } while(it_sparse_buckets_next != m_sparse_buckets.end() && it_sparse_buckets_next->empty());
1438 
1439  if(it_sparse_buckets_next == m_sparse_buckets.end()) {
1440  return end();
1441  }
1442  else {
1443  return iterator(it_sparse_buckets_next, it_sparse_buckets_next->begin());
1444  }
1445  }
1446  else {
1447  return iterator(pos.m_sparse_buckets_it, it_sparse_array_next);
1448  }
1449  }
1450 
1451  iterator erase(const_iterator pos) {
1452  return erase(mutable_iterator(pos));
1453  }
1454 
1455  iterator erase(const_iterator first, const_iterator last) {
1456  if(first == last) {
1457  return mutable_iterator(first);
1458  }
1459 
1460  // TODO Optimize, could avoid the call to std::distance.
1461  const size_type nb_elements_to_erase = static_cast<size_type>(std::distance(first, last));
1462  auto to_delete = mutable_iterator(first);
1463  for(size_type i = 0; i < nb_elements_to_erase; i++) {
1464  to_delete = erase(to_delete);
1465  }
1466 
1467  return to_delete;
1468  }
1469 
1470 
1471  template<class K>
1472  size_type erase(const K& key) {
1473  return erase(key, hash_key(key));
1474  }
1475 
1476  template<class K>
1477  size_type erase(const K& key, std::size_t hash) {
1478  return erase_impl(key, hash);
1479  }
1480 
1481 
1482 
1483  void swap(sparse_hash& other) {
1484  using std::swap;
1485 
1486  if(std::allocator_traits<Allocator>::propagate_on_container_swap::value) {
1487  swap(static_cast<Allocator&>(*this), static_cast<Allocator&>(other));
1488  }
1489  else {
1490  tsl_sh_assert(static_cast<Allocator&>(*this) == static_cast<Allocator&>(other));
1491  }
1492 
1493  swap(static_cast<Hash&>(*this), static_cast<Hash&>(other));
1494  swap(static_cast<KeyEqual&>(*this), static_cast<KeyEqual&>(other));
1495  swap(static_cast<GrowthPolicy&>(*this), static_cast<GrowthPolicy&>(other));
1496  swap(m_sparse_buckets, other.m_sparse_buckets);
1497  swap(m_first_or_empty_sparse_bucket, other.m_first_or_empty_sparse_bucket);
1498  swap(m_bucket_count, other.m_bucket_count);
1499  swap(m_nb_elements, other.m_nb_elements);
1500  swap(m_nb_deleted_buckets, other.m_nb_deleted_buckets);
1501  swap(m_load_threshold_rehash, other.m_load_threshold_rehash);
1502  swap(m_load_threshold_clear_deleted, other.m_load_threshold_clear_deleted);
1503  swap(m_max_load_factor, other.m_max_load_factor);
1504  }
1505 
1506 
1507  /*
1508  * Lookup
1509  */
1510  template<class K, class U = ValueSelect, typename std::enable_if<has_mapped_type<U>::value>::type* = nullptr>
1511  typename U::value_type& at(const K& key) {
1512  return at(key, hash_key(key));
1513  }
1514 
1515  template<class K, class U = ValueSelect, typename std::enable_if<has_mapped_type<U>::value>::type* = nullptr>
1516  typename U::value_type& at(const K& key, std::size_t hash) {
1517  return const_cast<typename U::value_type&>(static_cast<const sparse_hash*>(this)->at(key, hash));
1518  }
1519 
1520 
1521  template<class K, class U = ValueSelect, typename std::enable_if<has_mapped_type<U>::value>::type* = nullptr>
1522  const typename U::value_type& at(const K& key) const {
1523  return at(key, hash_key(key));
1524  }
1525 
1526  template<class K, class U = ValueSelect, typename std::enable_if<has_mapped_type<U>::value>::type* = nullptr>
1527  const typename U::value_type& at(const K& key, std::size_t hash) const {
1528  auto it = find(key, hash);
1529  if(it != cend()) {
1530  return it.value();
1531  }
1532  else {
1533  throw std::out_of_range("Couldn't find key.");
1534  }
1535  }
1536 
1537  template<class K, class U = ValueSelect, typename std::enable_if<has_mapped_type<U>::value>::type* = nullptr>
1538  typename U::value_type& operator[](K&& key) {
1539  return try_emplace(std::forward<K>(key)).first.value();
1540  }
1541 
1542 
1543  template<class K>
1544  size_type count(const K& key) const {
1545  return count(key, hash_key(key));
1546  }
1547 
1548  template<class K>
1549  size_type count(const K& key, std::size_t hash) const {
1550  if(find(key, hash) != cend()) {
1551  return 1;
1552  }
1553  else {
1554  return 0;
1555  }
1556  }
1557 
1558 
1559  template<class K>
1560  iterator find(const K& key) {
1561  return find_impl(key, hash_key(key));
1562  }
1563 
1564  template<class K>
1565  iterator find(const K& key, std::size_t hash) {
1566  return find_impl(key, hash);
1567  }
1568 
1569 
1570  template<class K>
1571  const_iterator find(const K& key) const {
1572  return find_impl(key, hash_key(key));
1573  }
1574 
1575  template<class K>
1576  const_iterator find(const K& key, std::size_t hash) const {
1577  return find_impl(key, hash);
1578  }
1579 
1580 
1581  template<class K>
1582  std::pair<iterator, iterator> equal_range(const K& key) {
1583  return equal_range(key, hash_key(key));
1584  }
1585 
1586  template<class K>
1587  std::pair<iterator, iterator> equal_range(const K& key, std::size_t hash) {
1588  iterator it = find(key, hash);
1589  return std::make_pair(it, (it == end())?it:std::next(it));
1590  }
1591 
1592 
1593  template<class K>
1594  std::pair<const_iterator, const_iterator> equal_range(const K& key) const {
1595  return equal_range(key, hash_key(key));
1596  }
1597 
1598  template<class K>
1599  std::pair<const_iterator, const_iterator> equal_range(const K& key, std::size_t hash) const {
1600  const_iterator it = find(key, hash);
1601  return std::make_pair(it, (it == cend())?it:std::next(it));
1602  }
1603 
1604  /*
1605  * Bucket interface
1606  */
1607  size_type bucket_count() const {
1608  return m_bucket_count;
1609  }
1610 
1611  size_type max_bucket_count() const {
1612  return m_sparse_buckets.max_size();
1613  }
1614 
1615  /*
1616  * Hash policy
1617  */
1618  float load_factor() const {
1619  if(bucket_count() == 0) {
1620  return 0;
1621  }
1622 
1623  return float(m_nb_elements)/float(bucket_count());
1624  }
1625 
1626  float max_load_factor() const {
1627  return m_max_load_factor;
1628  }
1629 
1630  void max_load_factor(float ml) {
1631  m_max_load_factor = std::max(0.1f, std::min(ml, 0.8f));
1632  m_load_threshold_rehash = size_type(float(bucket_count())*m_max_load_factor);
1633 
1634  const float max_load_factor_with_deleted_buckets = m_max_load_factor + 0.5f*(1.0f - m_max_load_factor);
1635  tsl_sh_assert(max_load_factor_with_deleted_buckets > 0.0f && max_load_factor_with_deleted_buckets <= 1.0f);
1636  m_load_threshold_clear_deleted = size_type(float(bucket_count())*max_load_factor_with_deleted_buckets);
1637  }
1638 
1639  void rehash(size_type count) {
1640  count = std::max(count, size_type(std::ceil(float(size())/max_load_factor())));
1641  rehash_impl(count);
1642  }
1643 
1644  void reserve(size_type count) {
1645  rehash(size_type(std::ceil(float(count)/max_load_factor())));
1646  }
1647 
1648  /*
1649  * Observers
1650  */
1651  hasher hash_function() const {
1652  return static_cast<const Hash&>(*this);
1653  }
1654 
1655  key_equal key_eq() const {
1656  return static_cast<const KeyEqual&>(*this);
1657  }
1658 
1659 
1660  /*
1661  * Other
1662  */
1663  iterator mutable_iterator(const_iterator pos) {
1664  auto it_sparse_buckets = m_sparse_buckets.begin() +
1665  std::distance(m_sparse_buckets.cbegin(), pos.m_sparse_buckets_it);
1666 
1667  return iterator(it_sparse_buckets, sparse_array::mutable_iterator(pos.m_sparse_array_it));
1668  }
1669 
1670  template<class Serializer>
1671  void serialize(Serializer& serializer) const {
1672  serialize_impl(serializer);
1673  }
1674 
1675  template<class Deserializer>
1676  void deserialize(Deserializer& deserializer, bool hash_compatible) {
1677  deserialize_impl(deserializer, hash_compatible);
1678  }
1679 
1680 private:
1681  template<class K>
1682  std::size_t hash_key(const K& key) const {
1683  return Hash::operator()(key);
1684  }
1685 
1686  template<class K1, class K2>
1687  bool compare_keys(const K1& key1, const K2& key2) const {
1688  return KeyEqual::operator()(key1, key2);
1689  }
1690 
1691  size_type bucket_for_hash(std::size_t hash) const {
1692  const std::size_t bucket = GrowthPolicy::bucket_for_hash(hash);
1693  tsl_sh_assert(sparse_array::sparse_ibucket(bucket) < m_sparse_buckets.size() ||
1694  (bucket == 0 && m_sparse_buckets.empty()));
1695 
1696  return bucket;
1697  }
1698 
1699  template<class U = GrowthPolicy, typename std::enable_if<is_power_of_two_policy<U>::value>::type* = nullptr>
1700  size_type next_bucket(size_type ibucket, size_type iprobe) const {
1701  (void) iprobe;
1702  if(Probing == tsl::sh::probing::linear) {
1703  return (ibucket + 1) & this->m_mask;
1704  }
1705  else {
1706  tsl_sh_assert(Probing == tsl::sh::probing::quadratic);
1707  return (ibucket + iprobe) & this->m_mask;
1708  }
1709  }
1710 
1711  template<class U = GrowthPolicy, typename std::enable_if<!is_power_of_two_policy<U>::value>::type* = nullptr>
1712  size_type next_bucket(size_type ibucket, size_type iprobe) const {
1713  (void) iprobe;
1714  if(Probing == tsl::sh::probing::linear) {
1715  ibucket++;
1716  return (ibucket != bucket_count())?ibucket:0;
1717  }
1718  else {
1719  tsl_sh_assert(Probing == tsl::sh::probing::quadratic);
1720  ibucket += iprobe;
1721  return (ibucket < bucket_count())?ibucket:ibucket % bucket_count();
1722  }
1723  }
1724 
1725 
1726  // TODO encapsulate m_sparse_buckets to avoid the managing the allocator
1727  void copy_buckets_from(const sparse_hash& other) {
1728  m_sparse_buckets.reserve(other.m_sparse_buckets.size());
1729 
1730  try {
1731  for(const auto& bucket: other.m_sparse_buckets) {
1732  m_sparse_buckets.emplace_back(bucket, static_cast<Allocator&>(*this));
1733  }
1734  }
1735  catch(...) {
1736  clear();
1737  throw;
1738  }
1739 
1740  tsl_sh_assert(m_sparse_buckets.empty() || m_sparse_buckets.back().last());
1741  }
1742 
1743  void move_buckets_from(sparse_hash&& other) {
1744  m_sparse_buckets.reserve(other.m_sparse_buckets.size());
1745 
1746  try {
1747  for(auto&& bucket: other.m_sparse_buckets) {
1748  m_sparse_buckets.emplace_back(std::move(bucket), static_cast<Allocator&>(*this));
1749  }
1750  }
1751  catch(...) {
1752  clear();
1753  throw;
1754  }
1755 
1756  tsl_sh_assert(m_sparse_buckets.empty() || m_sparse_buckets.back().last());
1757  }
1758 
1759 
1760  template<class K, class... Args>
1761  std::pair<iterator, bool> insert_impl(const K& key, Args&&... value_type_args) {
1762  if(size() >= m_load_threshold_rehash) {
1763  rehash_impl(GrowthPolicy::next_bucket_count());
1764  }
1765  else if(size() + m_nb_deleted_buckets >= m_load_threshold_clear_deleted) {
1766  clear_deleted_buckets();
1767  }
1768  tsl_sh_assert(!m_sparse_buckets.empty());
1769 
1770  /**
1771  * We must insert the value in the first empty or deleted bucket we find. If we first find a
1772  * deleted bucket, we still have to continue the search until we find an empty bucket or until we have
1773  * searched all the buckets to be sure that the value is not in the hash table.
1774  * We thus remember the position, if any, of the first deleted bucket we have encountered
1775  * so we can insert it there if needed.
1776  */
1777  bool found_first_deleted_bucket = false;
1778  std::size_t sparse_ibucket_first_deleted = 0;
1779  typename sparse_array::size_type index_in_sparse_bucket_first_deleted = 0;
1780 
1781 
1782 
1783  const std::size_t hash = hash_key(key);
1784  std::size_t ibucket = bucket_for_hash(hash);
1785 
1786  std::size_t probe = 0;
1787  while(true) {
1788  std::size_t sparse_ibucket = sparse_array::sparse_ibucket(ibucket);
1789  auto index_in_sparse_bucket = sparse_array::index_in_sparse_bucket(ibucket);
1790 
1791  if(m_sparse_buckets[sparse_ibucket].has_value(index_in_sparse_bucket)) {
1792  auto value_it = m_sparse_buckets[sparse_ibucket].value(index_in_sparse_bucket);
1793  if(compare_keys(key, KeySelect()(*value_it))) {
1794  return std::make_pair(iterator(m_sparse_buckets.begin() + sparse_ibucket, value_it), false);
1795  }
1796  }
1797  else if(m_sparse_buckets[sparse_ibucket].has_deleted_value(index_in_sparse_bucket) &&
1798  probe < m_bucket_count)
1799  {
1800  if(!found_first_deleted_bucket) {
1801  found_first_deleted_bucket = true;
1802  sparse_ibucket_first_deleted = sparse_ibucket;
1803  index_in_sparse_bucket_first_deleted = index_in_sparse_bucket;
1804  }
1805  }
1806  else if(found_first_deleted_bucket) {
1807  auto it = insert_in_bucket(sparse_ibucket_first_deleted, index_in_sparse_bucket_first_deleted,
1808  std::forward<Args>(value_type_args)...);
1809  m_nb_deleted_buckets--;
1810 
1811  return it;
1812  }
1813  else {
1814  return insert_in_bucket(sparse_ibucket, index_in_sparse_bucket,
1815  std::forward<Args>(value_type_args)...);
1816  }
1817 
1818  probe++;
1819  ibucket = next_bucket(ibucket, probe);
1820  }
1821  }
1822 
1823  template<class... Args>
1824  std::pair<iterator, bool> insert_in_bucket(std::size_t sparse_ibucket,
1825  typename sparse_array::size_type index_in_sparse_bucket,
1826  Args&&... value_type_args)
1827  {
1828  auto value_it = m_sparse_buckets[sparse_ibucket].set(*this, index_in_sparse_bucket,
1829  std::forward<Args>(value_type_args)...);
1830  m_nb_elements++;
1831 
1832  return std::make_pair(iterator(m_sparse_buckets.begin() + sparse_ibucket, value_it), true);
1833  }
1834 
1835 
1836 
1837 
1838 
1839  template<class K>
1840  size_type erase_impl(const K& key, std::size_t hash) {
1841  std::size_t ibucket = bucket_for_hash(hash);
1842 
1843  std::size_t probe = 0;
1844  while(true) {
1845  const std::size_t sparse_ibucket = sparse_array::sparse_ibucket(ibucket);
1846  const auto index_in_sparse_bucket = sparse_array::index_in_sparse_bucket(ibucket);
1847 
1848  if((m_first_or_empty_sparse_bucket + sparse_ibucket)->has_value(index_in_sparse_bucket)) {
1849  auto value_it = (m_first_or_empty_sparse_bucket + sparse_ibucket)->value(index_in_sparse_bucket);
1850  if(compare_keys(key, KeySelect()(*value_it))) {
1851  (m_first_or_empty_sparse_bucket + sparse_ibucket)->erase(*this, value_it, index_in_sparse_bucket);
1852  m_nb_elements--;
1853  m_nb_deleted_buckets++;
1854 
1855  return 1;
1856  }
1857  }
1858  else if(!(m_first_or_empty_sparse_bucket + sparse_ibucket)->has_deleted_value(index_in_sparse_bucket) || probe >= m_bucket_count) {
1859  return 0;
1860  }
1861 
1862  probe++;
1863  ibucket = next_bucket(ibucket, probe);
1864  }
1865  }
1866 
1867 
1868 
1869  template<class K>
1870  iterator find_impl(const K& key, std::size_t hash) {
1871  return mutable_iterator(static_cast<const sparse_hash*>(this)->find(key, hash));
1872  }
1873 
1874  template<class K>
1875  const_iterator find_impl(const K& key, std::size_t hash) const {
1876  std::size_t ibucket = bucket_for_hash(hash);
1877 
1878  std::size_t probe = 0;
1879  while(true) {
1880  const std::size_t sparse_ibucket = sparse_array::sparse_ibucket(ibucket);
1881  const auto index_in_sparse_bucket = sparse_array::index_in_sparse_bucket(ibucket);
1882 
1883  if((m_first_or_empty_sparse_bucket + sparse_ibucket)->has_value(index_in_sparse_bucket)) {
1884  auto value_it = (m_first_or_empty_sparse_bucket + sparse_ibucket)->value(index_in_sparse_bucket);
1885  if(compare_keys(key, KeySelect()(*value_it))) {
1886  return const_iterator(m_sparse_buckets.cbegin() + sparse_ibucket, value_it);
1887  }
1888  }
1889  else if(!(m_first_or_empty_sparse_bucket + sparse_ibucket)->has_deleted_value(index_in_sparse_bucket) || probe >= m_bucket_count) {
1890  return cend();
1891  }
1892 
1893  probe++;
1894  ibucket = next_bucket(ibucket, probe);
1895  }
1896  }
1897 
1898  void clear_deleted_buckets() {
1899  // TODO could be optimized, we could do it in-place instead of allocating a new bucket array.
1900  rehash_impl(m_bucket_count);
1901  tsl_sh_assert(m_nb_deleted_buckets == 0);
1902  }
1903 
1904  template<tsl::sh::exception_safety U = ExceptionSafety,
1905  typename std::enable_if<U == tsl::sh::exception_safety::basic>::type* = nullptr>
1906  void rehash_impl(size_type count) {
1907  sparse_hash new_table(count, static_cast<Hash&>(*this), static_cast<KeyEqual&>(*this),
1908  static_cast<Allocator&>(*this), m_max_load_factor);
1909 
1910  for(auto& bucket: m_sparse_buckets) {
1911  for(auto& val: bucket) {
1912  new_table.insert_on_rehash(std::move(val));
1913  }
1914 
1915  // TODO try to reuse some of the memory
1916  bucket.clear(*this);
1917  }
1918 
1919  new_table.swap(*this);
1920  }
1921 
1922  /**
1923  * TODO: For now we copy each element into the new map. We could move
1924  * them if they are nothrow_move_constructible without triggering
1925  * any exception if we reserve enough space in the sparse arrays beforehand.
1926  */
1927  template<tsl::sh::exception_safety U = ExceptionSafety,
1928  typename std::enable_if<U == tsl::sh::exception_safety::strong>::type* = nullptr>
1929  void rehash_impl(size_type count) {
1930  sparse_hash new_table(count, static_cast<Hash&>(*this), static_cast<KeyEqual&>(*this),
1931  static_cast<Allocator&>(*this), m_max_load_factor);
1932 
1933  for(const auto& bucket: m_sparse_buckets) {
1934  for(const auto& val: bucket) {
1935  new_table.insert_on_rehash(val);
1936  }
1937  }
1938 
1939  new_table.swap(*this);
1940  }
1941 
1942 
1943  template<typename K>
1944  void insert_on_rehash(K&& key_value) {
1945  const key_type& key = KeySelect()(key_value);
1946 
1947  const std::size_t hash = hash_key(key);
1948  std::size_t ibucket = bucket_for_hash(hash);
1949 
1950  std::size_t probe = 0;
1951  while(true) {
1952  std::size_t sparse_ibucket = sparse_array::sparse_ibucket(ibucket);
1953  auto index_in_sparse_bucket = sparse_array::index_in_sparse_bucket(ibucket);
1954 
1955  if(!(m_first_or_empty_sparse_bucket + sparse_ibucket)->has_value(index_in_sparse_bucket)) {
1956  (m_first_or_empty_sparse_bucket + sparse_ibucket)->set(*this, index_in_sparse_bucket,
1957  std::forward<K>(key_value));
1958  m_nb_elements++;
1959 
1960  return;
1961  }
1962  else {
1963  tsl_sh_assert(!compare_keys(key,
1964  KeySelect()(*(m_first_or_empty_sparse_bucket + sparse_ibucket)->value(index_in_sparse_bucket))));
1965  }
1966 
1967  probe++;
1968  ibucket = next_bucket(ibucket, probe);
1969  }
1970  }
1971 
1972  template<class Serializer>
1973  void serialize_impl(Serializer& serializer) const {
1974  const slz_size_type version = SERIALIZATION_PROTOCOL_VERSION;
1975  serializer(version);
1976 
1977  const slz_size_type bucket_count = m_bucket_count;
1978  serializer(bucket_count);
1979 
1980  const slz_size_type nb_sparse_buckets = m_sparse_buckets.size();
1981  serializer(nb_sparse_buckets);
1982 
1983  const slz_size_type nb_elements = m_nb_elements;
1984  serializer(nb_elements);
1985 
1986  const slz_size_type nb_deleted_buckets = m_nb_deleted_buckets;
1987  serializer(nb_deleted_buckets);
1988 
1989  const float max_load_factor = m_max_load_factor;
1990  serializer(max_load_factor);
1991 
1992 
1993  for(const auto& bucket: m_sparse_buckets) {
1994  bucket.serialize(serializer);
1995  }
1996  }
1997 
1998  template<class Deserializer>
1999  void deserialize_impl(Deserializer& deserializer, bool hash_compatible) {
2000  tsl_sh_assert(m_bucket_count == 0 && m_sparse_buckets.empty()); // Current hash table must be empty
2001 
2002  const slz_size_type version = deserialize_value<slz_size_type>(deserializer);
2003  // For now we only have one version of the serialization protocol.
2004  // If it doesn't match there is a problem with the file.
2005  if(version != SERIALIZATION_PROTOCOL_VERSION) {
2006  throw std::runtime_error("Can't deserialize the sparse_map/set. The protocol version header is invalid.");
2007  }
2008 
2009  const slz_size_type bucket_count_ds = deserialize_value<slz_size_type>(deserializer);
2010  const slz_size_type nb_sparse_buckets = deserialize_value<slz_size_type>(deserializer);
2011  const slz_size_type nb_elements = deserialize_value<slz_size_type>(deserializer);
2012  const slz_size_type nb_deleted_buckets = deserialize_value<slz_size_type>(deserializer);
2013  const float max_load_factor = deserialize_value<float>(deserializer);
2014 
2015 
2016  this->max_load_factor(max_load_factor);
2017 
2018  if(bucket_count_ds == 0) {
2019  tsl_sh_assert(nb_elements == 0 && nb_sparse_buckets == 0);
2020  return;
2021  }
2022 
2023 
2024  if(!hash_compatible) {
2025  reserve(numeric_cast<size_type>(nb_elements, "Deserialized nb_elements is too big."));
2026  for(slz_size_type ibucket = 0; ibucket < nb_sparse_buckets; ibucket++) {
2027  sparse_array::deserialize_values_into_sparse_hash(deserializer, *this);
2028  }
2029  }
2030  else {
2031  m_bucket_count = numeric_cast<size_type>(bucket_count_ds, "Deserialized bucket_count is too big.");
2032 
2033  GrowthPolicy::operator=(GrowthPolicy(m_bucket_count));
2034  // GrowthPolicy should not modify the bucket count we got from deserialization
2035  if(m_bucket_count != bucket_count_ds) {
2036  throw std::runtime_error("The GrowthPolicy is not the same even though hash_compatible is true.");
2037  }
2038 
2039  if(nb_sparse_buckets != sparse_array::sparse_ibucket(tsl::detail_sparse_hash::round_up_to_power_of_two(m_bucket_count))) {
2040  throw std::runtime_error("Deserialized nb_sparse_buckets is invalid.");
2041  }
2042 
2043 
2044  m_nb_elements = numeric_cast<size_type>(nb_elements, "Deserialized nb_elements is too big.");
2045  m_nb_deleted_buckets = numeric_cast<size_type>(nb_deleted_buckets, "Deserialized nb_deleted_buckets is too big.");
2046 
2047  tsl_sh_assert(nb_sparse_buckets > 0);
2048  m_sparse_buckets.reserve(numeric_cast<size_type>(nb_sparse_buckets, "Deserialized nb_sparse_buckets is too big."));
2049 
2050  for(slz_size_type ibucket = 0; ibucket < nb_sparse_buckets; ibucket++) {
2051  m_sparse_buckets.emplace_back(sparse_array::deserialize_hash_compatible(deserializer, static_cast<Allocator&>(*this)));
2052  }
2053 
2054  m_sparse_buckets.back().set_as_last();
2055  m_first_or_empty_sparse_bucket = m_sparse_buckets.data();
2056 
2057 
2058  if(load_factor() > this->max_load_factor()) {
2059  throw std::runtime_error("Invalid max_load_factor. Check that the serializer and deserializer supports "
2060  "floats correctly as they can be converted implicitely to ints.");
2061  }
2062  }
2063  }
2064 
2065 public:
2066  static const size_type DEFAULT_INIT_BUCKETS_SIZE = sparse_array::DEFAULT_INIT_BUCKETS_SIZE;
2067  static constexpr float DEFAULT_MAX_LOAD_FACTOR = 0.5f;
2068 
2069  /**
2070  * Protocol version currenlty used for serialization.
2071  */
2072  static const slz_size_type SERIALIZATION_PROTOCOL_VERSION = 1;
2073 
2074  /**
2075  * Return an always valid pointer to an static empty bucket_entry with last_bucket() == true.
2076  */
2078  static sparse_array empty_sparse_bucket(true);
2079  return &empty_sparse_bucket;
2080  }
2081 
2082 private:
2083  sparse_buckets_container m_sparse_buckets;
2084 
2085  /**
2086  * Points to m_sparse_buckets.data() if !m_sparse_buckets.empty() otherwise points to
2087  * static_empty_sparse_bucket_ptr. This variable is useful to avoid the cost of checking
2088  * if m_sparse_buckets is empty when trying to find an element.
2089  *
2090  * TODO Remove m_buckets and only use a pointer instead of a pointer+vector to save some space in the sparse_hash object.
2091  */
2092  sparse_array* m_first_or_empty_sparse_bucket;
2093 
2094  size_type m_bucket_count;
2095  size_type m_nb_elements;
2096  size_type m_nb_deleted_buckets;
2097 
2098  /**
2099  * Maximum that m_nb_elements can reach before a rehash occurs automatically to grow the hash table.
2100  */
2101  size_type m_load_threshold_rehash;
2102 
2103  /**
2104  * Maximum that m_nb_elements + m_nb_deleted_buckets can reach before cleaning up the buckets marked as deleted.
2105  */
2106  size_type m_load_threshold_clear_deleted;
2107  float m_max_load_factor;
2108 };
2109 
2110 }
2111 }
2112 
2113 #endif
const_iterator find(const K &key, std::size_t hash) const
Definition: sparse_hash.h:1576
~sparse_hash()
Definition: sparse_hash.h:1134
void deserialize(Deserializer &deserializer, bool hash_compatible)
Definition: sparse_hash.h:1676
std::pair< iterator, iterator > equal_range(const K &key)
Definition: sparse_hash.h:1582
sparse_iterator operator++(int)
Definition: sparse_hash.h:1062
key_equal key_eq() const
Definition: sparse_hash.h:1655
iterator erase(const_iterator pos)
Definition: sparse_hash.h:1451
std::pair< iterator, bool > insert(P &&value)
Definition: sparse_hash.h:1338
sparse_iterator() noexcept
Definition: sparse_hash.h:1011
float max_load_factor() const
Definition: sparse_hash.h:1626
iterator find(const K &key)
Definition: sparse_hash.h:1560
sparse_hash(sparse_hash &&other) noexcept(std::is_nothrow_move_constructible< Allocator >::value &&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< sparse_buckets_container >::value)
Definition: sparse_hash.h:1155
const_iterator find(const K &key) const
Definition: sparse_hash.h:1571
const U::value_type & at(const K &key, std::size_t hash) const
Definition: sparse_hash.h:1527
Definition: sparse_growth_policy.h:39
void rehash(size_type count)
Definition: sparse_hash.h:1639
iterator end() noexcept
Definition: sparse_hash.h:1295
static const size_type DEFAULT_INIT_BUCKETS_SIZE
Definition: sparse_hash.h:2066
std::pair< const_iterator, const_iterator > equal_range(const K &key, std::size_t hash) const
Definition: sparse_hash.h:1599
std::pair< iterator, bool > emplace(Args &&... args)
Definition: sparse_hash.h:1396
U::value_type & value()
Definition: sparse_hash.h:1029
iterator mutable_iterator(const_iterator pos)
Definition: sparse_hash.h:1663
std::size_t round_up_to_power_of_two(std::size_t value)
Definition: sparse_hash.h:221
sparse_array * static_empty_sparse_bucket_ptr()
Definition: sparse_hash.h:2077
pointer operator->() const
Definition: sparse_hash.h:1037
probing
Definition: sparse_hash.h:63
sparse_iterator(const sparse_iterator< false > &other) noexcept
Definition: sparse_hash.h:1014
U::value_type & at(const K &key)
Definition: sparse_hash.h:1511
size_type bucket_count() const
Definition: sparse_hash.h:1607
sparse_hash(size_type bucket_count, const Hash &hash, const KeyEqual &equal, const Allocator &alloc, float max_load_factor)
Definition: sparse_hash.h:1085
iterator try_emplace(const_iterator hint, K &&key, Args &&... args)
Definition: sparse_hash.h:1415
iterator find(const K &key, std::size_t hash)
Definition: sparse_hash.h:1565
bool empty() const noexcept
Definition: sparse_hash.h:1311
iterator insert(const_iterator hint, P &&value)
Definition: sparse_hash.h:1343
std::pair< const_iterator, const_iterator > equal_range(const K &key) const
Definition: sparse_hash.h:1594
sparse_iterator & operator++()
Definition: sparse_hash.h:1041
size_type count(const K &key) const
Definition: sparse_hash.h:1544
sparse_hash & operator=(sparse_hash &&other)
Definition: sparse_hash.h:1224
sparse_hash & operator=(const sparse_hash &other)
Definition: sparse_hash.h:1184
float load_factor() const
Definition: sparse_hash.h:1618
reference operator*() const
Definition: sparse_hash.h:1033
sparsity
Definition: sparse_hash.h:73
static const slz_size_type SERIALIZATION_PROTOCOL_VERSION
Definition: sparse_hash.h:2072
iterator begin() noexcept
Definition: sparse_hash.h:1273
exception_safety
Definition: sparse_hash.h:68
size_type size() const noexcept
Definition: sparse_hash.h:1315
friend class sparse_hash
Definition: sparse_hash.h:983
const sparse_hash::key_type & key() const
Definition: sparse_hash.h:1019
const_iterator cend() const noexcept
Definition: sparse_hash.h:1303
U::value_type & operator[](K &&key)
Definition: sparse_hash.h:1538
U::value_type & at(const K &key, std::size_t hash)
Definition: sparse_hash.h:1516
void max_load_factor(float ml)
Definition: sparse_hash.h:1630
iterator emplace_hint(const_iterator hint, Args &&... args)
Definition: sparse_hash.h:1401
#define tsl_sh_assert(expr)
Definition: sparse_hash.h:56
friend bool operator!=(const sparse_iterator &lhs, const sparse_iterator &rhs)
Definition: sparse_hash.h:1074
allocator_type get_allocator() const
Definition: sparse_hash.h:1265
Definition: sparse_growth_policy.h:40
friend bool operator==(const sparse_iterator &lhs, const sparse_iterator &rhs)
Definition: sparse_hash.h:1069
static constexpr float DEFAULT_MAX_LOAD_FACTOR
Definition: sparse_hash.h:2067
iterator insert_or_assign(const_iterator hint, K &&key, M &&obj)
Definition: sparse_hash.h:1383
size_type erase(const K &key)
Definition: sparse_hash.h:1472
size_type max_size() const noexcept
Definition: sparse_hash.h:1319
size_type erase(const K &key, std::size_t hash)
Definition: sparse_hash.h:1477
const_iterator begin() const noexcept
Definition: sparse_hash.h:1282
iterator erase(const_iterator first, const_iterator last)
Definition: sparse_hash.h:1455
std::pair< iterator, bool > insert_or_assign(K &&key, M &&obj)
Definition: sparse_hash.h:1373
void clear() noexcept
Definition: sparse_hash.h:1326
void reserve(size_type count)
Definition: sparse_hash.h:1644
const U::value_type & at(const K &key) const
Definition: sparse_hash.h:1522
constexpr bool is_power_of_two(std::size_t value)
Definition: sparse_hash.h:217
int fallback_popcountll(unsigned long long int x)
Definition: sparse_hash.h:87
const_iterator cbegin() const noexcept
Definition: sparse_hash.h:1286
const U::value_type & value() const
Definition: sparse_hash.h:1024
int fallback_popcount(unsigned int x)
Definition: sparse_hash.h:103
size_type count(const K &key, std::size_t hash) const
Definition: sparse_hash.h:1549
const_iterator end() const noexcept
Definition: sparse_hash.h:1299
Definition: sparse_hash.h:193
size_type max_bucket_count() const
Definition: sparse_hash.h:1611
void insert(InputIt first, InputIt last)
Definition: sparse_hash.h:1352
iterator erase(iterator pos)
Definition: sparse_hash.h:1427
sparse_hash(const sparse_hash &other)
Definition: sparse_hash.h:1138
int popcount(unsigned int x)
Definition: sparse_hash.h:183
void serialize(Serializer &serializer) const
Definition: sparse_hash.h:1671
void swap(sparse_hash &other)
Definition: sparse_hash.h:1483
hasher hash_function() const
Definition: sparse_hash.h:1651
Definition: sparse_growth_policy.h:49
std::pair< iterator, iterator > equal_range(const K &key, std::size_t hash)
Definition: sparse_hash.h:1587
Definition: sparse_hash.h:937
std::pair< iterator, bool > try_emplace(K &&key, Args &&... args)
Definition: sparse_hash.h:1408