isotree 0.2.2 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (151) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +8 -1
  3. data/LICENSE.txt +2 -2
  4. data/README.md +32 -14
  5. data/ext/isotree/ext.cpp +144 -31
  6. data/ext/isotree/extconf.rb +7 -7
  7. data/lib/isotree/isolation_forest.rb +110 -30
  8. data/lib/isotree/version.rb +1 -1
  9. data/vendor/isotree/LICENSE +1 -1
  10. data/vendor/isotree/README.md +165 -27
  11. data/vendor/isotree/include/isotree.hpp +2111 -0
  12. data/vendor/isotree/include/isotree_oop.hpp +394 -0
  13. data/vendor/isotree/inst/COPYRIGHTS +62 -0
  14. data/vendor/isotree/src/RcppExports.cpp +525 -52
  15. data/vendor/isotree/src/Rwrapper.cpp +1931 -268
  16. data/vendor/isotree/src/c_interface.cpp +953 -0
  17. data/vendor/isotree/src/crit.hpp +4232 -0
  18. data/vendor/isotree/src/dist.hpp +1886 -0
  19. data/vendor/isotree/src/exp_depth_table.hpp +134 -0
  20. data/vendor/isotree/src/extended.hpp +1444 -0
  21. data/vendor/isotree/src/external_facing_generic.hpp +399 -0
  22. data/vendor/isotree/src/fit_model.hpp +2401 -0
  23. data/vendor/isotree/src/{dealloc.cpp → headers_joined.hpp} +38 -22
  24. data/vendor/isotree/src/helpers_iforest.hpp +813 -0
  25. data/vendor/isotree/src/{impute.cpp → impute.hpp} +353 -122
  26. data/vendor/isotree/src/indexer.cpp +515 -0
  27. data/vendor/isotree/src/instantiate_template_headers.cpp +118 -0
  28. data/vendor/isotree/src/instantiate_template_headers.hpp +240 -0
  29. data/vendor/isotree/src/isoforest.hpp +1659 -0
  30. data/vendor/isotree/src/isotree.hpp +1804 -392
  31. data/vendor/isotree/src/isotree_exportable.hpp +99 -0
  32. data/vendor/isotree/src/merge_models.cpp +159 -16
  33. data/vendor/isotree/src/mult.hpp +1321 -0
  34. data/vendor/isotree/src/oop_interface.cpp +842 -0
  35. data/vendor/isotree/src/oop_interface.hpp +278 -0
  36. data/vendor/isotree/src/other_helpers.hpp +219 -0
  37. data/vendor/isotree/src/predict.hpp +1932 -0
  38. data/vendor/isotree/src/python_helpers.hpp +134 -0
  39. data/vendor/isotree/src/ref_indexer.hpp +154 -0
  40. data/vendor/isotree/src/robinmap/LICENSE +21 -0
  41. data/vendor/isotree/src/robinmap/README.md +483 -0
  42. data/vendor/isotree/src/robinmap/include/tsl/robin_growth_policy.h +406 -0
  43. data/vendor/isotree/src/robinmap/include/tsl/robin_hash.h +1620 -0
  44. data/vendor/isotree/src/robinmap/include/tsl/robin_map.h +807 -0
  45. data/vendor/isotree/src/robinmap/include/tsl/robin_set.h +660 -0
  46. data/vendor/isotree/src/serialize.cpp +4300 -139
  47. data/vendor/isotree/src/sql.cpp +141 -59
  48. data/vendor/isotree/src/subset_models.cpp +174 -0
  49. data/vendor/isotree/src/utils.hpp +3808 -0
  50. data/vendor/isotree/src/xoshiro.hpp +467 -0
  51. data/vendor/isotree/src/ziggurat.hpp +405 -0
  52. metadata +38 -104
  53. data/vendor/cereal/LICENSE +0 -24
  54. data/vendor/cereal/README.md +0 -85
  55. data/vendor/cereal/include/cereal/access.hpp +0 -351
  56. data/vendor/cereal/include/cereal/archives/adapters.hpp +0 -163
  57. data/vendor/cereal/include/cereal/archives/binary.hpp +0 -169
  58. data/vendor/cereal/include/cereal/archives/json.hpp +0 -1019
  59. data/vendor/cereal/include/cereal/archives/portable_binary.hpp +0 -334
  60. data/vendor/cereal/include/cereal/archives/xml.hpp +0 -956
  61. data/vendor/cereal/include/cereal/cereal.hpp +0 -1089
  62. data/vendor/cereal/include/cereal/details/helpers.hpp +0 -422
  63. data/vendor/cereal/include/cereal/details/polymorphic_impl.hpp +0 -796
  64. data/vendor/cereal/include/cereal/details/polymorphic_impl_fwd.hpp +0 -65
  65. data/vendor/cereal/include/cereal/details/static_object.hpp +0 -127
  66. data/vendor/cereal/include/cereal/details/traits.hpp +0 -1411
  67. data/vendor/cereal/include/cereal/details/util.hpp +0 -84
  68. data/vendor/cereal/include/cereal/external/base64.hpp +0 -134
  69. data/vendor/cereal/include/cereal/external/rapidjson/allocators.h +0 -284
  70. data/vendor/cereal/include/cereal/external/rapidjson/cursorstreamwrapper.h +0 -78
  71. data/vendor/cereal/include/cereal/external/rapidjson/document.h +0 -2652
  72. data/vendor/cereal/include/cereal/external/rapidjson/encodedstream.h +0 -299
  73. data/vendor/cereal/include/cereal/external/rapidjson/encodings.h +0 -716
  74. data/vendor/cereal/include/cereal/external/rapidjson/error/en.h +0 -74
  75. data/vendor/cereal/include/cereal/external/rapidjson/error/error.h +0 -161
  76. data/vendor/cereal/include/cereal/external/rapidjson/filereadstream.h +0 -99
  77. data/vendor/cereal/include/cereal/external/rapidjson/filewritestream.h +0 -104
  78. data/vendor/cereal/include/cereal/external/rapidjson/fwd.h +0 -151
  79. data/vendor/cereal/include/cereal/external/rapidjson/internal/biginteger.h +0 -290
  80. data/vendor/cereal/include/cereal/external/rapidjson/internal/diyfp.h +0 -271
  81. data/vendor/cereal/include/cereal/external/rapidjson/internal/dtoa.h +0 -245
  82. data/vendor/cereal/include/cereal/external/rapidjson/internal/ieee754.h +0 -78
  83. data/vendor/cereal/include/cereal/external/rapidjson/internal/itoa.h +0 -308
  84. data/vendor/cereal/include/cereal/external/rapidjson/internal/meta.h +0 -186
  85. data/vendor/cereal/include/cereal/external/rapidjson/internal/pow10.h +0 -55
  86. data/vendor/cereal/include/cereal/external/rapidjson/internal/regex.h +0 -740
  87. data/vendor/cereal/include/cereal/external/rapidjson/internal/stack.h +0 -232
  88. data/vendor/cereal/include/cereal/external/rapidjson/internal/strfunc.h +0 -69
  89. data/vendor/cereal/include/cereal/external/rapidjson/internal/strtod.h +0 -290
  90. data/vendor/cereal/include/cereal/external/rapidjson/internal/swap.h +0 -46
  91. data/vendor/cereal/include/cereal/external/rapidjson/istreamwrapper.h +0 -128
  92. data/vendor/cereal/include/cereal/external/rapidjson/memorybuffer.h +0 -70
  93. data/vendor/cereal/include/cereal/external/rapidjson/memorystream.h +0 -71
  94. data/vendor/cereal/include/cereal/external/rapidjson/msinttypes/inttypes.h +0 -316
  95. data/vendor/cereal/include/cereal/external/rapidjson/msinttypes/stdint.h +0 -300
  96. data/vendor/cereal/include/cereal/external/rapidjson/ostreamwrapper.h +0 -81
  97. data/vendor/cereal/include/cereal/external/rapidjson/pointer.h +0 -1414
  98. data/vendor/cereal/include/cereal/external/rapidjson/prettywriter.h +0 -277
  99. data/vendor/cereal/include/cereal/external/rapidjson/rapidjson.h +0 -656
  100. data/vendor/cereal/include/cereal/external/rapidjson/reader.h +0 -2230
  101. data/vendor/cereal/include/cereal/external/rapidjson/schema.h +0 -2497
  102. data/vendor/cereal/include/cereal/external/rapidjson/stream.h +0 -223
  103. data/vendor/cereal/include/cereal/external/rapidjson/stringbuffer.h +0 -121
  104. data/vendor/cereal/include/cereal/external/rapidjson/writer.h +0 -709
  105. data/vendor/cereal/include/cereal/external/rapidxml/license.txt +0 -52
  106. data/vendor/cereal/include/cereal/external/rapidxml/manual.html +0 -406
  107. data/vendor/cereal/include/cereal/external/rapidxml/rapidxml.hpp +0 -2624
  108. data/vendor/cereal/include/cereal/external/rapidxml/rapidxml_iterators.hpp +0 -175
  109. data/vendor/cereal/include/cereal/external/rapidxml/rapidxml_print.hpp +0 -428
  110. data/vendor/cereal/include/cereal/external/rapidxml/rapidxml_utils.hpp +0 -123
  111. data/vendor/cereal/include/cereal/macros.hpp +0 -154
  112. data/vendor/cereal/include/cereal/specialize.hpp +0 -139
  113. data/vendor/cereal/include/cereal/types/array.hpp +0 -79
  114. data/vendor/cereal/include/cereal/types/atomic.hpp +0 -55
  115. data/vendor/cereal/include/cereal/types/base_class.hpp +0 -203
  116. data/vendor/cereal/include/cereal/types/bitset.hpp +0 -176
  117. data/vendor/cereal/include/cereal/types/boost_variant.hpp +0 -164
  118. data/vendor/cereal/include/cereal/types/chrono.hpp +0 -72
  119. data/vendor/cereal/include/cereal/types/common.hpp +0 -129
  120. data/vendor/cereal/include/cereal/types/complex.hpp +0 -56
  121. data/vendor/cereal/include/cereal/types/concepts/pair_associative_container.hpp +0 -73
  122. data/vendor/cereal/include/cereal/types/deque.hpp +0 -62
  123. data/vendor/cereal/include/cereal/types/forward_list.hpp +0 -68
  124. data/vendor/cereal/include/cereal/types/functional.hpp +0 -43
  125. data/vendor/cereal/include/cereal/types/list.hpp +0 -62
  126. data/vendor/cereal/include/cereal/types/map.hpp +0 -36
  127. data/vendor/cereal/include/cereal/types/memory.hpp +0 -425
  128. data/vendor/cereal/include/cereal/types/optional.hpp +0 -66
  129. data/vendor/cereal/include/cereal/types/polymorphic.hpp +0 -483
  130. data/vendor/cereal/include/cereal/types/queue.hpp +0 -132
  131. data/vendor/cereal/include/cereal/types/set.hpp +0 -103
  132. data/vendor/cereal/include/cereal/types/stack.hpp +0 -76
  133. data/vendor/cereal/include/cereal/types/string.hpp +0 -61
  134. data/vendor/cereal/include/cereal/types/tuple.hpp +0 -123
  135. data/vendor/cereal/include/cereal/types/unordered_map.hpp +0 -36
  136. data/vendor/cereal/include/cereal/types/unordered_set.hpp +0 -99
  137. data/vendor/cereal/include/cereal/types/utility.hpp +0 -47
  138. data/vendor/cereal/include/cereal/types/valarray.hpp +0 -89
  139. data/vendor/cereal/include/cereal/types/variant.hpp +0 -109
  140. data/vendor/cereal/include/cereal/types/vector.hpp +0 -112
  141. data/vendor/cereal/include/cereal/version.hpp +0 -52
  142. data/vendor/isotree/src/Makevars +0 -4
  143. data/vendor/isotree/src/crit.cpp +0 -912
  144. data/vendor/isotree/src/dist.cpp +0 -749
  145. data/vendor/isotree/src/extended.cpp +0 -790
  146. data/vendor/isotree/src/fit_model.cpp +0 -1090
  147. data/vendor/isotree/src/helpers_iforest.cpp +0 -324
  148. data/vendor/isotree/src/isoforest.cpp +0 -771
  149. data/vendor/isotree/src/mult.cpp +0 -607
  150. data/vendor/isotree/src/predict.cpp +0 -853
  151. data/vendor/isotree/src/utils.cpp +0 -1566
@@ -0,0 +1,1620 @@
1
+ /**
2
+ * MIT License
3
+ *
4
+ * Copyright (c) 2017 Thibaut Goetghebuer-Planchon <tessil@gmx.com>
5
+ *
6
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
7
+ * of this software and associated documentation files (the "Software"), to deal
8
+ * in the Software without restriction, including without limitation the rights
9
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
+ * copies of the Software, and to permit persons to whom the Software is
11
+ * furnished to do so, subject to the following conditions:
12
+ *
13
+ * The above copyright notice and this permission notice shall be included in
14
+ * all copies or substantial portions of the Software.
15
+ *
16
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22
+ * SOFTWARE.
23
+ */
24
+ #ifndef TSL_ROBIN_HASH_H
25
+ #define TSL_ROBIN_HASH_H
26
+
27
+ #include <algorithm>
28
+ #include <cassert>
29
+ #include <cmath>
30
+ #include <cstddef>
31
+ #include <cstdint>
32
+ #include <exception>
33
+ #include <iterator>
34
+ #include <limits>
35
+ #include <memory>
36
+ #include <stdexcept>
37
+ #include <tuple>
38
+ #include <type_traits>
39
+ #include <utility>
40
+ #include <vector>
41
+
42
+ #include "robin_growth_policy.h"
43
+
44
+ namespace tsl {
45
+
46
+ namespace detail_robin_hash {
47
+
48
+ template <typename T>
49
+ struct make_void {
50
+ using type = void;
51
+ };
52
+
53
+ template <typename T, typename = void>
54
+ struct has_is_transparent : std::false_type {};
55
+
56
+ template <typename T>
57
+ struct has_is_transparent<T,
58
+ typename make_void<typename T::is_transparent>::type>
59
+ : std::true_type {};
60
+
61
+ template <typename U>
62
+ struct is_power_of_two_policy : std::false_type {};
63
+
64
+ template <std::size_t GrowthFactor>
65
+ struct is_power_of_two_policy<tsl::rh::power_of_two_growth_policy<GrowthFactor>>
66
+ : std::true_type {};
67
+
68
+ // Only available in C++17, we need to be compatible with C++11
69
+ template <class T>
70
+ const T& clamp(const T& v, const T& lo, const T& hi) {
71
+ return std::min(hi, std::max(lo, v));
72
+ }
73
+
74
+ template <typename T, typename U>
75
+ static T numeric_cast(U value,
76
+ const char* error_message = "numeric_cast() failed.") {
77
+ T ret = static_cast<T>(value);
78
+ if (static_cast<U>(ret) != value) {
79
+ TSL_RH_THROW_OR_TERMINATE(std::runtime_error, error_message);
80
+ }
81
+
82
+ const bool is_same_signedness =
83
+ (std::is_unsigned<T>::value && std::is_unsigned<U>::value) ||
84
+ (std::is_signed<T>::value && std::is_signed<U>::value);
85
+ if (!is_same_signedness && (ret < T{}) != (value < U{})) {
86
+ TSL_RH_THROW_OR_TERMINATE(std::runtime_error, error_message);
87
+ }
88
+
89
+ return ret;
90
+ }
91
+
92
+ template <class T, class Deserializer>
93
+ static T deserialize_value(Deserializer& deserializer) {
94
+ // MSVC < 2017 is not conformant, circumvent the problem by removing the
95
+ // template keyword
96
+ #if defined(_MSC_VER) && _MSC_VER < 1910
97
+ return deserializer.Deserializer::operator()<T>();
98
+ #else
99
+ return deserializer.Deserializer::template operator()<T>();
100
+ #endif
101
+ }
102
+
103
+ /**
104
+ * Fixed size type used to represent size_type values on serialization. Need to
105
+ * be big enough to represent a std::size_t on 32 and 64 bits platforms, and
106
+ * must be the same size on both platforms.
107
+ */
108
+ using slz_size_type = std::uint64_t;
109
+ static_assert(std::numeric_limits<slz_size_type>::max() >=
110
+ std::numeric_limits<std::size_t>::max(),
111
+ "slz_size_type must be >= std::size_t");
112
+
113
+ using truncated_hash_type = std::uint32_t;
114
+
115
+ /**
116
+ * Helper class that stores a truncated hash if StoreHash is true and nothing
117
+ * otherwise.
118
+ */
119
+ template <bool StoreHash>
120
+ class bucket_entry_hash {
121
+ public:
122
+ bool bucket_hash_equal(std::size_t /*hash*/) const noexcept { return true; }
123
+
124
+ truncated_hash_type truncated_hash() const noexcept { return 0; }
125
+
126
+ protected:
127
+ void set_hash(truncated_hash_type /*hash*/) noexcept {}
128
+ };
129
+
130
+ template <>
131
+ class bucket_entry_hash<true> {
132
+ public:
133
+ bool bucket_hash_equal(std::size_t hash) const noexcept {
134
+ return m_hash == truncated_hash_type(hash);
135
+ }
136
+
137
+ truncated_hash_type truncated_hash() const noexcept { return m_hash; }
138
+
139
+ protected:
140
+ void set_hash(truncated_hash_type hash) noexcept {
141
+ m_hash = truncated_hash_type(hash);
142
+ }
143
+
144
+ private:
145
+ truncated_hash_type m_hash;
146
+ };
147
+
148
+ /**
149
+ * Each bucket entry has:
150
+ * - A value of type `ValueType`.
151
+ * - An integer to store how far the value of the bucket, if any, is from its
152
+ * ideal bucket (ex: if the current bucket 5 has the value 'foo' and
153
+ * `hash('foo') % nb_buckets` == 3, `dist_from_ideal_bucket()` will return 2 as
154
+ * the current value of the bucket is two buckets away from its ideal bucket) If
155
+ * there is no value in the bucket (i.e. `empty()` is true)
156
+ * `dist_from_ideal_bucket()` will be < 0.
157
+ * - A marker which tells us if the bucket is the last bucket of the bucket
158
+ * array (useful for the iterator of the hash table).
159
+ * - If `StoreHash` is true, 32 bits of the hash of the value, if any, are also
160
+ * stored in the bucket. If the size of the hash is more than 32 bits, it is
161
+ * truncated. We don't store the full hash as storing the hash is a potential
162
+ * opportunity to use the unused space due to the alignment of the bucket_entry
163
+ * structure. We can thus potentially store the hash without any extra space
164
+ * (which would not be possible with 64 bits of the hash).
165
+ */
166
+ template <typename ValueType, bool StoreHash>
167
+ class bucket_entry : public bucket_entry_hash<StoreHash> {
168
+ using bucket_hash = bucket_entry_hash<StoreHash>;
169
+
170
+ public:
171
+ using value_type = ValueType;
172
+ using distance_type = std::int16_t;
173
+
174
+ bucket_entry() noexcept
175
+ : bucket_hash(),
176
+ m_dist_from_ideal_bucket(EMPTY_MARKER_DIST_FROM_IDEAL_BUCKET),
177
+ m_last_bucket(false) {
178
+ tsl_rh_assert(empty());
179
+ }
180
+
181
+ bucket_entry(bool last_bucket) noexcept
182
+ : bucket_hash(),
183
+ m_dist_from_ideal_bucket(EMPTY_MARKER_DIST_FROM_IDEAL_BUCKET),
184
+ m_last_bucket(last_bucket) {
185
+ tsl_rh_assert(empty());
186
+ }
187
+
188
+ bucket_entry(const bucket_entry& other) noexcept(
189
+ std::is_nothrow_copy_constructible<value_type>::value)
190
+ : bucket_hash(other),
191
+ m_dist_from_ideal_bucket(EMPTY_MARKER_DIST_FROM_IDEAL_BUCKET),
192
+ m_last_bucket(other.m_last_bucket) {
193
+ if (!other.empty()) {
194
+ ::new (static_cast<void*>(std::addressof(m_value)))
195
+ value_type(other.value());
196
+ m_dist_from_ideal_bucket = other.m_dist_from_ideal_bucket;
197
+ }
198
+ }
199
+
200
+ /**
201
+ * Never really used, but still necessary as we must call resize on an empty
202
+ * `std::vector<bucket_entry>`. and we need to support move-only types. See
203
+ * robin_hash constructor for details.
204
+ */
205
+ bucket_entry(bucket_entry&& other) noexcept(
206
+ std::is_nothrow_move_constructible<value_type>::value)
207
+ : bucket_hash(std::move(other)),
208
+ m_dist_from_ideal_bucket(EMPTY_MARKER_DIST_FROM_IDEAL_BUCKET),
209
+ m_last_bucket(other.m_last_bucket) {
210
+ if (!other.empty()) {
211
+ ::new (static_cast<void*>(std::addressof(m_value)))
212
+ value_type(std::move(other.value()));
213
+ m_dist_from_ideal_bucket = other.m_dist_from_ideal_bucket;
214
+ }
215
+ }
216
+
217
+ bucket_entry& operator=(const bucket_entry& other) noexcept(
218
+ std::is_nothrow_copy_constructible<value_type>::value) {
219
+ if (this != &other) {
220
+ clear();
221
+
222
+ bucket_hash::operator=(other);
223
+ if (!other.empty()) {
224
+ ::new (static_cast<void*>(std::addressof(m_value)))
225
+ value_type(other.value());
226
+ }
227
+
228
+ m_dist_from_ideal_bucket = other.m_dist_from_ideal_bucket;
229
+ m_last_bucket = other.m_last_bucket;
230
+ }
231
+
232
+ return *this;
233
+ }
234
+
235
+ bucket_entry& operator=(bucket_entry&&) = delete;
236
+
237
+ ~bucket_entry() noexcept { clear(); }
238
+
239
+ void clear() noexcept {
240
+ if (!empty()) {
241
+ destroy_value();
242
+ m_dist_from_ideal_bucket = EMPTY_MARKER_DIST_FROM_IDEAL_BUCKET;
243
+ }
244
+ }
245
+
246
+ bool empty() const noexcept {
247
+ return m_dist_from_ideal_bucket == EMPTY_MARKER_DIST_FROM_IDEAL_BUCKET;
248
+ }
249
+
250
+ value_type& value() noexcept {
251
+ tsl_rh_assert(!empty());
252
+ return *reinterpret_cast<value_type*>(std::addressof(m_value));
253
+ }
254
+
255
+ const value_type& value() const noexcept {
256
+ tsl_rh_assert(!empty());
257
+ return *reinterpret_cast<const value_type*>(std::addressof(m_value));
258
+ }
259
+
260
+ distance_type dist_from_ideal_bucket() const noexcept {
261
+ return m_dist_from_ideal_bucket;
262
+ }
263
+
264
+ bool last_bucket() const noexcept { return m_last_bucket; }
265
+
266
+ void set_as_last_bucket() noexcept { m_last_bucket = true; }
267
+
268
+ template <typename... Args>
269
+ void set_value_of_empty_bucket(distance_type dist_from_ideal_bucket,
270
+ truncated_hash_type hash,
271
+ Args&&... value_type_args) {
272
+ tsl_rh_assert(dist_from_ideal_bucket >= 0);
273
+ tsl_rh_assert(empty());
274
+
275
+ ::new (static_cast<void*>(std::addressof(m_value)))
276
+ value_type(std::forward<Args>(value_type_args)...);
277
+ this->set_hash(hash);
278
+ m_dist_from_ideal_bucket = dist_from_ideal_bucket;
279
+
280
+ tsl_rh_assert(!empty());
281
+ }
282
+
283
+ void swap_with_value_in_bucket(distance_type& dist_from_ideal_bucket,
284
+ truncated_hash_type& hash, value_type& value) {
285
+ tsl_rh_assert(!empty());
286
+
287
+ using std::swap;
288
+ swap(value, this->value());
289
+ swap(dist_from_ideal_bucket, m_dist_from_ideal_bucket);
290
+
291
+ if (StoreHash) {
292
+ const truncated_hash_type tmp_hash = this->truncated_hash();
293
+ this->set_hash(hash);
294
+ hash = tmp_hash;
295
+ } else {
296
+ // Avoid warning of unused variable if StoreHash is false
297
+ TSL_RH_UNUSED(hash);
298
+ }
299
+ }
300
+
301
+ static truncated_hash_type truncate_hash(std::size_t hash) noexcept {
302
+ return truncated_hash_type(hash);
303
+ }
304
+
305
+ private:
306
+ void destroy_value() noexcept {
307
+ tsl_rh_assert(!empty());
308
+ value().~value_type();
309
+ }
310
+
311
+ public:
312
+ static const distance_type EMPTY_MARKER_DIST_FROM_IDEAL_BUCKET = -1;
313
+ static const distance_type DIST_FROM_IDEAL_BUCKET_LIMIT = 4096;
314
+ static_assert(DIST_FROM_IDEAL_BUCKET_LIMIT <=
315
+ std::numeric_limits<distance_type>::max() - 1,
316
+ "DIST_FROM_IDEAL_BUCKET_LIMIT must be <= "
317
+ "std::numeric_limits<distance_type>::max() - 1.");
318
+
319
+ private:
320
+ using storage = typename std::aligned_storage<sizeof(value_type),
321
+ alignof(value_type)>::type;
322
+
323
+ distance_type m_dist_from_ideal_bucket;
324
+ bool m_last_bucket;
325
+ storage m_value;
326
+ };
327
+
328
+ /**
329
+ * Internal common class used by `robin_map` and `robin_set`.
330
+ *
331
+ * ValueType is what will be stored by `robin_hash` (usually `std::pair<Key, T>`
332
+ * for map and `Key` for set).
333
+ *
334
+ * `KeySelect` should be a `FunctionObject` which takes a `ValueType` in
335
+ * parameter and returns a reference to the key.
336
+ *
337
+ * `ValueSelect` should be a `FunctionObject` which takes a `ValueType` in
338
+ * parameter and returns a reference to the value. `ValueSelect` should be void
339
+ * if there is no value (in a set for example).
340
+ *
341
+ * The strong exception guarantee only holds if the expression
342
+ * `std::is_nothrow_swappable<ValueType>::value &&
343
+ * std::is_nothrow_move_constructible<ValueType>::value` is true.
344
+ *
345
+ * Behaviour is undefined if the destructor of `ValueType` throws.
346
+ */
347
+ template <class ValueType, class KeySelect, class ValueSelect, class Hash,
348
+ class KeyEqual, class Allocator, bool StoreHash, class GrowthPolicy>
349
+ class robin_hash : private Hash, private KeyEqual, private GrowthPolicy {
350
+ private:
351
+ template <typename U>
352
+ using has_mapped_type =
353
+ typename std::integral_constant<bool, !std::is_same<U, void>::value>;
354
+
355
+ static_assert(
356
+ noexcept(std::declval<GrowthPolicy>().bucket_for_hash(std::size_t(0))),
357
+ "GrowthPolicy::bucket_for_hash must be noexcept.");
358
+ static_assert(noexcept(std::declval<GrowthPolicy>().clear()),
359
+ "GrowthPolicy::clear must be noexcept.");
360
+
361
+ public:
362
+ template <bool IsConst>
363
+ class robin_iterator;
364
+
365
+ using key_type = typename KeySelect::key_type;
366
+ using value_type = ValueType;
367
+ using size_type = std::size_t;
368
+ using difference_type = std::ptrdiff_t;
369
+ using hasher = Hash;
370
+ using key_equal = KeyEqual;
371
+ using allocator_type = Allocator;
372
+ using reference = value_type&;
373
+ using const_reference = const value_type&;
374
+ using pointer = value_type*;
375
+ using const_pointer = const value_type*;
376
+ using iterator = robin_iterator<false>;
377
+ using const_iterator = robin_iterator<true>;
378
+
379
+ private:
380
+ /**
381
+ * Either store the hash because we are asked by the `StoreHash` template
382
+ * parameter or store the hash because it doesn't cost us anything in size and
383
+ * can be used to speed up rehash.
384
+ */
385
+ static constexpr bool STORE_HASH =
386
+ StoreHash ||
387
+ ((sizeof(tsl::detail_robin_hash::bucket_entry<value_type, true>) ==
388
+ sizeof(tsl::detail_robin_hash::bucket_entry<value_type, false>)) &&
389
+ (sizeof(std::size_t) == sizeof(truncated_hash_type) ||
390
+ is_power_of_two_policy<GrowthPolicy>::value) &&
391
+ // Don't store the hash for primitive types with default hash.
392
+ (!std::is_arithmetic<key_type>::value ||
393
+ !std::is_same<Hash, std::hash<key_type>>::value));
394
+
395
+ /**
396
+ * Only use the stored hash on lookup if we are explicitly asked. We are not
397
+ * sure how slow the KeyEqual operation is. An extra comparison may slow
398
+ * things down with a fast KeyEqual.
399
+ */
400
+ static constexpr bool USE_STORED_HASH_ON_LOOKUP = StoreHash;
401
+
402
+ /**
403
+ * We can only use the hash on rehash if the size of the hash type is the same
404
+ * as the stored one or if we use a power of two modulo. In the case of the
405
+ * power of two modulo, we just mask the least significant bytes, we just have
406
+ * to check that the truncated_hash_type didn't truncated more bytes.
407
+ */
408
+ static bool USE_STORED_HASH_ON_REHASH(size_type bucket_count) {
409
+ if (STORE_HASH && sizeof(std::size_t) == sizeof(truncated_hash_type)) {
410
+ TSL_RH_UNUSED(bucket_count);
411
+ return true;
412
+ } else if (STORE_HASH && is_power_of_two_policy<GrowthPolicy>::value) {
413
+ return bucket_count == 0 ||
414
+ (bucket_count - 1) <=
415
+ std::numeric_limits<truncated_hash_type>::max();
416
+ } else {
417
+ TSL_RH_UNUSED(bucket_count);
418
+ return false;
419
+ }
420
+ }
421
+
422
+ using bucket_entry =
423
+ tsl::detail_robin_hash::bucket_entry<value_type, STORE_HASH>;
424
+ using distance_type = typename bucket_entry::distance_type;
425
+
426
+ using buckets_allocator = typename std::allocator_traits<
427
+ allocator_type>::template rebind_alloc<bucket_entry>;
428
+ using buckets_container_type = std::vector<bucket_entry, buckets_allocator>;
429
+
430
+ public:
431
+ /**
432
+ * The 'operator*()' and 'operator->()' methods return a const reference and
433
+ * const pointer respectively to the stored value type.
434
+ *
435
+ * In case of a map, to get a mutable reference to the value associated to a
436
+ * key (the '.second' in the stored pair), you have to call 'value()'.
437
+ *
438
+ * The main reason for this is that if we returned a `std::pair<Key, T>&`
439
+ * instead of a `const std::pair<Key, T>&`, the user may modify the key which
440
+ * will put the map in a undefined state.
441
+ */
442
+ template <bool IsConst>
443
+ class robin_iterator {
444
+ friend class robin_hash;
445
+
446
+ private:
447
+ using bucket_entry_ptr =
448
+ typename std::conditional<IsConst, const bucket_entry*,
449
+ bucket_entry*>::type;
450
+
451
+ robin_iterator(bucket_entry_ptr bucket) noexcept : m_bucket(bucket) {}
452
+
453
+ public:
454
+ using iterator_category = std::forward_iterator_tag;
455
+ using value_type = const typename robin_hash::value_type;
456
+ using difference_type = std::ptrdiff_t;
457
+ using reference = value_type&;
458
+ using pointer = value_type*;
459
+
460
+ robin_iterator() noexcept {}
461
+
462
+ // Copy constructor from iterator to const_iterator.
463
+ template <bool TIsConst = IsConst,
464
+ typename std::enable_if<TIsConst>::type* = nullptr>
465
+ robin_iterator(const robin_iterator<!TIsConst>& other) noexcept
466
+ : m_bucket(other.m_bucket) {}
467
+
468
+ robin_iterator(const robin_iterator& other) = default;
469
+ robin_iterator(robin_iterator&& other) = default;
470
+ robin_iterator& operator=(const robin_iterator& other) = default;
471
+ robin_iterator& operator=(robin_iterator&& other) = default;
472
+
473
+ const typename robin_hash::key_type& key() const {
474
+ return KeySelect()(m_bucket->value());
475
+ }
476
+
477
+ template <class U = ValueSelect,
478
+ typename std::enable_if<has_mapped_type<U>::value &&
479
+ IsConst>::type* = nullptr>
480
+ const typename U::value_type& value() const {
481
+ return U()(m_bucket->value());
482
+ }
483
+
484
+ template <class U = ValueSelect,
485
+ typename std::enable_if<has_mapped_type<U>::value &&
486
+ !IsConst>::type* = nullptr>
487
+ typename U::value_type& value() const {
488
+ return U()(m_bucket->value());
489
+ }
490
+
491
+ reference operator*() const { return m_bucket->value(); }
492
+
493
+ pointer operator->() const { return std::addressof(m_bucket->value()); }
494
+
495
+ robin_iterator& operator++() {
496
+ while (true) {
497
+ if (m_bucket->last_bucket()) {
498
+ ++m_bucket;
499
+ return *this;
500
+ }
501
+
502
+ ++m_bucket;
503
+ if (!m_bucket->empty()) {
504
+ return *this;
505
+ }
506
+ }
507
+ }
508
+
509
+ robin_iterator operator++(int) {
510
+ robin_iterator tmp(*this);
511
+ ++*this;
512
+
513
+ return tmp;
514
+ }
515
+
516
+ friend bool operator==(const robin_iterator& lhs,
517
+ const robin_iterator& rhs) {
518
+ return lhs.m_bucket == rhs.m_bucket;
519
+ }
520
+
521
+ friend bool operator!=(const robin_iterator& lhs,
522
+ const robin_iterator& rhs) {
523
+ return !(lhs == rhs);
524
+ }
525
+
526
+ private:
527
+ bucket_entry_ptr m_bucket;
528
+ };
529
+
530
+ public:
531
+ #if defined(__cplusplus) && __cplusplus >= 201402L
532
+ robin_hash(size_type bucket_count, const Hash& hash, const KeyEqual& equal,
533
+ const Allocator& alloc,
534
+ float min_load_factor = DEFAULT_MIN_LOAD_FACTOR,
535
+ float max_load_factor = DEFAULT_MAX_LOAD_FACTOR)
536
+ : Hash(hash),
537
+ KeyEqual(equal),
538
+ GrowthPolicy(bucket_count),
539
+ m_buckets_data(bucket_count, alloc),
540
+ m_buckets(m_buckets_data.empty() ? static_empty_bucket_ptr()
541
+ : m_buckets_data.data()),
542
+ m_bucket_count(bucket_count),
543
+ m_nb_elements(0),
544
+ m_grow_on_next_insert(false),
545
+ m_try_shrink_on_next_insert(false) {
546
+ if (bucket_count > max_bucket_count()) {
547
+ TSL_RH_THROW_OR_TERMINATE(std::length_error,
548
+ "The map exceeds its maximum bucket count.");
549
+ }
550
+
551
+ if (m_bucket_count > 0) {
552
+ tsl_rh_assert(!m_buckets_data.empty());
553
+ m_buckets_data.back().set_as_last_bucket();
554
+ }
555
+
556
+ this->min_load_factor(min_load_factor);
557
+ this->max_load_factor(max_load_factor);
558
+ }
559
+ #else
560
+ /**
561
+ * C++11 doesn't support the creation of a std::vector with a custom allocator
562
+ * and 'count' default-inserted elements. The needed contructor `explicit
563
+ * vector(size_type count, const Allocator& alloc = Allocator());` is only
564
+ * available in C++14 and later. We thus must resize after using the
565
+ * `vector(const Allocator& alloc)` constructor.
566
+ *
567
+ * We can't use `vector(size_type count, const T& value, const Allocator&
568
+ * alloc)` as it requires the value T to be copyable.
569
+ */
570
+ robin_hash(size_type bucket_count, const Hash& hash, const KeyEqual& equal,
571
+ const Allocator& alloc,
572
+ float min_load_factor = DEFAULT_MIN_LOAD_FACTOR,
573
+ float max_load_factor = DEFAULT_MAX_LOAD_FACTOR)
574
+ : Hash(hash),
575
+ KeyEqual(equal),
576
+ GrowthPolicy(bucket_count),
577
+ m_buckets_data(alloc),
578
+ m_buckets(static_empty_bucket_ptr()),
579
+ m_bucket_count(bucket_count),
580
+ m_nb_elements(0),
581
+ m_grow_on_next_insert(false),
582
+ m_try_shrink_on_next_insert(false) {
583
+ if (bucket_count > max_bucket_count()) {
584
+ TSL_RH_THROW_OR_TERMINATE(std::length_error,
585
+ "The map exceeds its maximum bucket count.");
586
+ }
587
+
588
+ if (m_bucket_count > 0) {
589
+ m_buckets_data.resize(m_bucket_count);
590
+ m_buckets = m_buckets_data.data();
591
+
592
+ tsl_rh_assert(!m_buckets_data.empty());
593
+ m_buckets_data.back().set_as_last_bucket();
594
+ }
595
+
596
+ this->min_load_factor(min_load_factor);
597
+ this->max_load_factor(max_load_factor);
598
+ }
599
+ #endif
600
+
601
+ robin_hash(const robin_hash& other)
602
+ : Hash(other),
603
+ KeyEqual(other),
604
+ GrowthPolicy(other),
605
+ m_buckets_data(other.m_buckets_data),
606
+ m_buckets(m_buckets_data.empty() ? static_empty_bucket_ptr()
607
+ : m_buckets_data.data()),
608
+ m_bucket_count(other.m_bucket_count),
609
+ m_nb_elements(other.m_nb_elements),
610
+ m_load_threshold(other.m_load_threshold),
611
+ m_min_load_factor(other.m_min_load_factor),
612
+ m_max_load_factor(other.m_max_load_factor),
613
+ m_grow_on_next_insert(other.m_grow_on_next_insert),
614
+ m_try_shrink_on_next_insert(other.m_try_shrink_on_next_insert) {}
615
+
616
+ robin_hash(robin_hash&& other) noexcept(
617
+ std::is_nothrow_move_constructible<
618
+ Hash>::value&& std::is_nothrow_move_constructible<KeyEqual>::value&&
619
+ std::is_nothrow_move_constructible<GrowthPolicy>::value&&
620
+ std::is_nothrow_move_constructible<buckets_container_type>::value)
621
+ : Hash(std::move(static_cast<Hash&>(other))),
622
+ KeyEqual(std::move(static_cast<KeyEqual&>(other))),
623
+ GrowthPolicy(std::move(static_cast<GrowthPolicy&>(other))),
624
+ m_buckets_data(std::move(other.m_buckets_data)),
625
+ m_buckets(m_buckets_data.empty() ? static_empty_bucket_ptr()
626
+ : m_buckets_data.data()),
627
+ m_bucket_count(other.m_bucket_count),
628
+ m_nb_elements(other.m_nb_elements),
629
+ m_load_threshold(other.m_load_threshold),
630
+ m_min_load_factor(other.m_min_load_factor),
631
+ m_max_load_factor(other.m_max_load_factor),
632
+ m_grow_on_next_insert(other.m_grow_on_next_insert),
633
+ m_try_shrink_on_next_insert(other.m_try_shrink_on_next_insert) {
634
+ other.clear_and_shrink();
635
+ }
636
+
637
+ robin_hash& operator=(const robin_hash& other) {
638
+ if (&other != this) {
639
+ Hash::operator=(other);
640
+ KeyEqual::operator=(other);
641
+ GrowthPolicy::operator=(other);
642
+
643
+ m_buckets_data = other.m_buckets_data;
644
+ m_buckets = m_buckets_data.empty() ? static_empty_bucket_ptr()
645
+ : m_buckets_data.data();
646
+ m_bucket_count = other.m_bucket_count;
647
+ m_nb_elements = other.m_nb_elements;
648
+
649
+ m_load_threshold = other.m_load_threshold;
650
+ m_min_load_factor = other.m_min_load_factor;
651
+ m_max_load_factor = other.m_max_load_factor;
652
+
653
+ m_grow_on_next_insert = other.m_grow_on_next_insert;
654
+ m_try_shrink_on_next_insert = other.m_try_shrink_on_next_insert;
655
+ }
656
+
657
+ return *this;
658
+ }
659
+
660
+ robin_hash& operator=(robin_hash&& other) {
661
+ other.swap(*this);
662
+ other.clear();
663
+
664
+ return *this;
665
+ }
666
+
667
+ allocator_type get_allocator() const {
668
+ return m_buckets_data.get_allocator();
669
+ }
670
+
671
+ /*
672
+ * Iterators
673
+ */
674
+ iterator begin() noexcept {
675
+ std::size_t i = 0;
676
+ while (i < m_bucket_count && m_buckets[i].empty()) {
677
+ i++;
678
+ }
679
+
680
+ return iterator(m_buckets + i);
681
+ }
682
+
683
+ const_iterator begin() const noexcept { return cbegin(); }
684
+
685
+ const_iterator cbegin() const noexcept {
686
+ std::size_t i = 0;
687
+ while (i < m_bucket_count && m_buckets[i].empty()) {
688
+ i++;
689
+ }
690
+
691
+ return const_iterator(m_buckets + i);
692
+ }
693
+
694
+ iterator end() noexcept { return iterator(m_buckets + m_bucket_count); }
695
+
696
+ const_iterator end() const noexcept { return cend(); }
697
+
698
+ const_iterator cend() const noexcept {
699
+ return const_iterator(m_buckets + m_bucket_count);
700
+ }
701
+
702
+ /*
703
+ * Capacity
704
+ */
705
+ bool empty() const noexcept { return m_nb_elements == 0; }
706
+
707
+ size_type size() const noexcept { return m_nb_elements; }
708
+
709
+ size_type max_size() const noexcept { return m_buckets_data.max_size(); }
710
+
711
+ /*
712
+ * Modifiers
713
+ */
714
+ void clear() noexcept {
715
+ if (m_min_load_factor > 0.0f) {
716
+ clear_and_shrink();
717
+ } else {
718
+ for (auto& bucket : m_buckets_data) {
719
+ bucket.clear();
720
+ }
721
+
722
+ m_nb_elements = 0;
723
+ m_grow_on_next_insert = false;
724
+ }
725
+ }
726
+
727
+ template <typename P>
728
+ std::pair<iterator, bool> insert(P&& value) {
729
+ return insert_impl(KeySelect()(value), std::forward<P>(value));
730
+ }
731
+
732
+ template <typename P>
733
+ iterator insert_hint(const_iterator hint, P&& value) {
734
+ if (hint != cend() &&
735
+ compare_keys(KeySelect()(*hint), KeySelect()(value))) {
736
+ return mutable_iterator(hint);
737
+ }
738
+
739
+ return insert(std::forward<P>(value)).first;
740
+ }
741
+
742
+ template <class InputIt>
743
+ void insert(InputIt first, InputIt last) {
744
+ if (std::is_base_of<
745
+ std::forward_iterator_tag,
746
+ typename std::iterator_traits<InputIt>::iterator_category>::value) {
747
+ const auto nb_elements_insert = std::distance(first, last);
748
+ const size_type nb_free_buckets = m_load_threshold - size();
749
+ tsl_rh_assert(m_load_threshold >= size());
750
+
751
+ if (nb_elements_insert > 0 &&
752
+ nb_free_buckets < size_type(nb_elements_insert)) {
753
+ reserve(size() + size_type(nb_elements_insert));
754
+ }
755
+ }
756
+
757
+ for (; first != last; ++first) {
758
+ insert(*first);
759
+ }
760
+ }
761
+
762
+ template <class K, class M>
763
+ std::pair<iterator, bool> insert_or_assign(K&& key, M&& obj) {
764
+ auto it = try_emplace(std::forward<K>(key), std::forward<M>(obj));
765
+ if (!it.second) {
766
+ it.first.value() = std::forward<M>(obj);
767
+ }
768
+
769
+ return it;
770
+ }
771
+
772
+ template <class K, class M>
773
+ iterator insert_or_assign(const_iterator hint, K&& key, M&& obj) {
774
+ if (hint != cend() && compare_keys(KeySelect()(*hint), key)) {
775
+ auto it = mutable_iterator(hint);
776
+ it.value() = std::forward<M>(obj);
777
+
778
+ return it;
779
+ }
780
+
781
+ return insert_or_assign(std::forward<K>(key), std::forward<M>(obj)).first;
782
+ }
783
+
784
+ template <class... Args>
785
+ std::pair<iterator, bool> emplace(Args&&... args) {
786
+ return insert(value_type(std::forward<Args>(args)...));
787
+ }
788
+
789
+ template <class... Args>
790
+ iterator emplace_hint(const_iterator hint, Args&&... args) {
791
+ return insert_hint(hint, value_type(std::forward<Args>(args)...));
792
+ }
793
+
794
+ template <class K, class... Args>
795
+ std::pair<iterator, bool> try_emplace(K&& key, Args&&... args) {
796
+ return insert_impl(key, std::piecewise_construct,
797
+ std::forward_as_tuple(std::forward<K>(key)),
798
+ std::forward_as_tuple(std::forward<Args>(args)...));
799
+ }
800
+
801
+ template <class K, class... Args>
802
+ iterator try_emplace_hint(const_iterator hint, K&& key, Args&&... args) {
803
+ if (hint != cend() && compare_keys(KeySelect()(*hint), key)) {
804
+ return mutable_iterator(hint);
805
+ }
806
+
807
+ return try_emplace(std::forward<K>(key), std::forward<Args>(args)...).first;
808
+ }
809
+
810
+ /**
811
+ * Here to avoid `template<class K> size_type erase(const K& key)` being used
812
+ * when we use an `iterator` instead of a `const_iterator`.
813
+ */
814
+ iterator erase(iterator pos) {
815
+ erase_from_bucket(pos);
816
+
817
+ /**
818
+ * Erase bucket used a backward shift after clearing the bucket.
819
+ * Check if there is a new value in the bucket, if not get the next
820
+ * non-empty.
821
+ */
822
+ if (pos.m_bucket->empty()) {
823
+ ++pos;
824
+ }
825
+
826
+ m_try_shrink_on_next_insert = true;
827
+
828
+ return pos;
829
+ }
830
+
831
+ iterator erase(const_iterator pos) { return erase(mutable_iterator(pos)); }
832
+
833
+ iterator erase(const_iterator first, const_iterator last) {
834
+ if (first == last) {
835
+ return mutable_iterator(first);
836
+ }
837
+
838
+ auto first_mutable = mutable_iterator(first);
839
+ auto last_mutable = mutable_iterator(last);
840
+ for (auto it = first_mutable.m_bucket; it != last_mutable.m_bucket; ++it) {
841
+ if (!it->empty()) {
842
+ it->clear();
843
+ m_nb_elements--;
844
+ }
845
+ }
846
+
847
+ if (last_mutable == end()) {
848
+ m_try_shrink_on_next_insert = true;
849
+ return end();
850
+ }
851
+
852
+ /*
853
+ * Backward shift on the values which come after the deleted values.
854
+ * We try to move the values closer to their ideal bucket.
855
+ */
856
+ std::size_t icloser_bucket =
857
+ static_cast<std::size_t>(first_mutable.m_bucket - m_buckets);
858
+ std::size_t ito_move_closer_value =
859
+ static_cast<std::size_t>(last_mutable.m_bucket - m_buckets);
860
+ tsl_rh_assert(ito_move_closer_value > icloser_bucket);
861
+
862
+ const std::size_t ireturn_bucket =
863
+ ito_move_closer_value -
864
+ std::min(
865
+ ito_move_closer_value - icloser_bucket,
866
+ std::size_t(
867
+ m_buckets[ito_move_closer_value].dist_from_ideal_bucket()));
868
+
869
+ while (ito_move_closer_value < m_bucket_count &&
870
+ m_buckets[ito_move_closer_value].dist_from_ideal_bucket() > 0) {
871
+ icloser_bucket =
872
+ ito_move_closer_value -
873
+ std::min(
874
+ ito_move_closer_value - icloser_bucket,
875
+ std::size_t(
876
+ m_buckets[ito_move_closer_value].dist_from_ideal_bucket()));
877
+
878
+ tsl_rh_assert(m_buckets[icloser_bucket].empty());
879
+ const distance_type new_distance = distance_type(
880
+ m_buckets[ito_move_closer_value].dist_from_ideal_bucket() -
881
+ (ito_move_closer_value - icloser_bucket));
882
+ m_buckets[icloser_bucket].set_value_of_empty_bucket(
883
+ new_distance, m_buckets[ito_move_closer_value].truncated_hash(),
884
+ std::move(m_buckets[ito_move_closer_value].value()));
885
+ m_buckets[ito_move_closer_value].clear();
886
+
887
+ ++icloser_bucket;
888
+ ++ito_move_closer_value;
889
+ }
890
+
891
+ m_try_shrink_on_next_insert = true;
892
+
893
+ return iterator(m_buckets + ireturn_bucket);
894
+ }
895
+
896
+ template <class K>
897
+ size_type erase(const K& key) {
898
+ return erase(key, hash_key(key));
899
+ }
900
+
901
+ template <class K>
902
+ size_type erase(const K& key, std::size_t hash) {
903
+ auto it = find(key, hash);
904
+ if (it != end()) {
905
+ erase_from_bucket(it);
906
+ m_try_shrink_on_next_insert = true;
907
+
908
+ return 1;
909
+ } else {
910
+ return 0;
911
+ }
912
+ }
913
+
914
+ void swap(robin_hash& other) {
915
+ using std::swap;
916
+
917
+ swap(static_cast<Hash&>(*this), static_cast<Hash&>(other));
918
+ swap(static_cast<KeyEqual&>(*this), static_cast<KeyEqual&>(other));
919
+ swap(static_cast<GrowthPolicy&>(*this), static_cast<GrowthPolicy&>(other));
920
+ swap(m_buckets_data, other.m_buckets_data);
921
+ swap(m_buckets, other.m_buckets);
922
+ swap(m_bucket_count, other.m_bucket_count);
923
+ swap(m_nb_elements, other.m_nb_elements);
924
+ swap(m_load_threshold, other.m_load_threshold);
925
+ swap(m_min_load_factor, other.m_min_load_factor);
926
+ swap(m_max_load_factor, other.m_max_load_factor);
927
+ swap(m_grow_on_next_insert, other.m_grow_on_next_insert);
928
+ swap(m_try_shrink_on_next_insert, other.m_try_shrink_on_next_insert);
929
+ }
930
+
931
+ /*
932
+ * Lookup
933
+ */
934
+ template <class K, class U = ValueSelect,
935
+ typename std::enable_if<has_mapped_type<U>::value>::type* = nullptr>
936
+ typename U::value_type& at(const K& key) {
937
+ return at(key, hash_key(key));
938
+ }
939
+
940
+ template <class K, class U = ValueSelect,
941
+ typename std::enable_if<has_mapped_type<U>::value>::type* = nullptr>
942
+ typename U::value_type& at(const K& key, std::size_t hash) {
943
+ return const_cast<typename U::value_type&>(
944
+ static_cast<const robin_hash*>(this)->at(key, hash));
945
+ }
946
+
947
+ template <class K, class U = ValueSelect,
948
+ typename std::enable_if<has_mapped_type<U>::value>::type* = nullptr>
949
+ const typename U::value_type& at(const K& key) const {
950
+ return at(key, hash_key(key));
951
+ }
952
+
953
+ template <class K, class U = ValueSelect,
954
+ typename std::enable_if<has_mapped_type<U>::value>::type* = nullptr>
955
+ const typename U::value_type& at(const K& key, std::size_t hash) const {
956
+ auto it = find(key, hash);
957
+ if (it != cend()) {
958
+ return it.value();
959
+ } else {
960
+ TSL_RH_THROW_OR_TERMINATE(std::out_of_range, "Couldn't find key.");
961
+ }
962
+ }
963
+
964
+ template <class K, class U = ValueSelect,
965
+ typename std::enable_if<has_mapped_type<U>::value>::type* = nullptr>
966
+ typename U::value_type& operator[](K&& key) {
967
+ return try_emplace(std::forward<K>(key)).first.value();
968
+ }
969
+
970
+ template <class K>
971
+ size_type count(const K& key) const {
972
+ return count(key, hash_key(key));
973
+ }
974
+
975
+ template <class K>
976
+ size_type count(const K& key, std::size_t hash) const {
977
+ if (find(key, hash) != cend()) {
978
+ return 1;
979
+ } else {
980
+ return 0;
981
+ }
982
+ }
983
+
984
+ template <class K>
985
+ iterator find(const K& key) {
986
+ return find_impl(key, hash_key(key));
987
+ }
988
+
989
+ template <class K>
990
+ iterator find(const K& key, std::size_t hash) {
991
+ return find_impl(key, hash);
992
+ }
993
+
994
+ template <class K>
995
+ const_iterator find(const K& key) const {
996
+ return find_impl(key, hash_key(key));
997
+ }
998
+
999
+ template <class K>
1000
+ const_iterator find(const K& key, std::size_t hash) const {
1001
+ return find_impl(key, hash);
1002
+ }
1003
+
1004
+ template <class K>
1005
+ bool contains(const K& key) const {
1006
+ return contains(key, hash_key(key));
1007
+ }
1008
+
1009
+ template <class K>
1010
+ bool contains(const K& key, std::size_t hash) const {
1011
+ return count(key, hash) != 0;
1012
+ }
1013
+
1014
+ template <class K>
1015
+ std::pair<iterator, iterator> equal_range(const K& key) {
1016
+ return equal_range(key, hash_key(key));
1017
+ }
1018
+
1019
+ template <class K>
1020
+ std::pair<iterator, iterator> equal_range(const K& key, std::size_t hash) {
1021
+ iterator it = find(key, hash);
1022
+ return std::make_pair(it, (it == end()) ? it : std::next(it));
1023
+ }
1024
+
1025
+ template <class K>
1026
+ std::pair<const_iterator, const_iterator> equal_range(const K& key) const {
1027
+ return equal_range(key, hash_key(key));
1028
+ }
1029
+
1030
+ template <class K>
1031
+ std::pair<const_iterator, const_iterator> equal_range(
1032
+ const K& key, std::size_t hash) const {
1033
+ const_iterator it = find(key, hash);
1034
+ return std::make_pair(it, (it == cend()) ? it : std::next(it));
1035
+ }
1036
+
1037
+ /*
1038
+ * Bucket interface
1039
+ */
1040
+ size_type bucket_count() const { return m_bucket_count; }
1041
+
1042
+ size_type max_bucket_count() const {
1043
+ return std::min(GrowthPolicy::max_bucket_count(),
1044
+ m_buckets_data.max_size());
1045
+ }
1046
+
1047
+ /*
1048
+ * Hash policy
1049
+ */
1050
+ float load_factor() const {
1051
+ if (bucket_count() == 0) {
1052
+ return 0;
1053
+ }
1054
+
1055
+ return float(m_nb_elements) / float(bucket_count());
1056
+ }
1057
+
1058
+ float min_load_factor() const { return m_min_load_factor; }
1059
+
1060
+ float max_load_factor() const { return m_max_load_factor; }
1061
+
1062
+ void min_load_factor(float ml) {
1063
+ m_min_load_factor = clamp(ml, float(MINIMUM_MIN_LOAD_FACTOR),
1064
+ float(MAXIMUM_MIN_LOAD_FACTOR));
1065
+ }
1066
+
1067
+ void max_load_factor(float ml) {
1068
+ m_max_load_factor = clamp(ml, float(MINIMUM_MAX_LOAD_FACTOR),
1069
+ float(MAXIMUM_MAX_LOAD_FACTOR));
1070
+ m_load_threshold = size_type(float(bucket_count()) * m_max_load_factor);
1071
+ }
1072
+
1073
+ void rehash(size_type count_) {
1074
+ count_ = std::max(count_,
1075
+ size_type(std::ceil(float(size()) / max_load_factor())));
1076
+ rehash_impl(count_);
1077
+ }
1078
+
1079
+ void reserve(size_type count_) {
1080
+ rehash(size_type(std::ceil(float(count_) / max_load_factor())));
1081
+ }
1082
+
1083
+ /*
1084
+ * Observers
1085
+ */
1086
+ hasher hash_function() const { return static_cast<const Hash&>(*this); }
1087
+
1088
+ key_equal key_eq() const { return static_cast<const KeyEqual&>(*this); }
1089
+
1090
+ /*
1091
+ * Other
1092
+ */
1093
+ iterator mutable_iterator(const_iterator pos) {
1094
+ return iterator(const_cast<bucket_entry*>(pos.m_bucket));
1095
+ }
1096
+
1097
+ template <class Serializer>
1098
+ void serialize(Serializer& serializer) const {
1099
+ serialize_impl(serializer);
1100
+ }
1101
+
1102
+ template <class Deserializer>
1103
+ void deserialize(Deserializer& deserializer, bool hash_compatible) {
1104
+ deserialize_impl(deserializer, hash_compatible);
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
+ std::size_t bucket_for_hash(std::size_t hash) const {
1119
+ const std::size_t bucket = GrowthPolicy::bucket_for_hash(hash);
1120
+ tsl_rh_assert(bucket < m_bucket_count ||
1121
+ (bucket == 0 && m_bucket_count == 0));
1122
+
1123
+ return bucket;
1124
+ }
1125
+
1126
+ template <class U = GrowthPolicy,
1127
+ typename std::enable_if<is_power_of_two_policy<U>::value>::type* =
1128
+ nullptr>
1129
+ std::size_t next_bucket(std::size_t index) const noexcept {
1130
+ tsl_rh_assert(index < bucket_count());
1131
+
1132
+ return (index + 1) & this->m_mask;
1133
+ }
1134
+
1135
+ template <class U = GrowthPolicy,
1136
+ typename std::enable_if<!is_power_of_two_policy<U>::value>::type* =
1137
+ nullptr>
1138
+ std::size_t next_bucket(std::size_t index) const noexcept {
1139
+ tsl_rh_assert(index < bucket_count());
1140
+
1141
+ index++;
1142
+ return (index != bucket_count()) ? index : 0;
1143
+ }
1144
+
1145
+ template <class K>
1146
+ iterator find_impl(const K& key, std::size_t hash) {
1147
+ return mutable_iterator(
1148
+ static_cast<const robin_hash*>(this)->find(key, hash));
1149
+ }
1150
+
1151
+ template <class K>
1152
+ const_iterator find_impl(const K& key, std::size_t hash) const {
1153
+ std::size_t ibucket = bucket_for_hash(hash);
1154
+ distance_type dist_from_ideal_bucket = 0;
1155
+
1156
+ while (dist_from_ideal_bucket <=
1157
+ m_buckets[ibucket].dist_from_ideal_bucket()) {
1158
+ if (TSL_RH_LIKELY(
1159
+ (!USE_STORED_HASH_ON_LOOKUP ||
1160
+ m_buckets[ibucket].bucket_hash_equal(hash)) &&
1161
+ compare_keys(KeySelect()(m_buckets[ibucket].value()), key))) {
1162
+ return const_iterator(m_buckets + ibucket);
1163
+ }
1164
+
1165
+ ibucket = next_bucket(ibucket);
1166
+ dist_from_ideal_bucket++;
1167
+ }
1168
+
1169
+ return cend();
1170
+ }
1171
+
1172
+ void erase_from_bucket(iterator pos) {
1173
+ pos.m_bucket->clear();
1174
+ m_nb_elements--;
1175
+
1176
+ /**
1177
+ * Backward shift, swap the empty bucket, previous_ibucket, with the values
1178
+ * on its right, ibucket, until we cross another empty bucket or if the
1179
+ * other bucket has a distance_from_ideal_bucket == 0.
1180
+ *
1181
+ * We try to move the values closer to their ideal bucket.
1182
+ */
1183
+ std::size_t previous_ibucket =
1184
+ static_cast<std::size_t>(pos.m_bucket - m_buckets);
1185
+ std::size_t ibucket = next_bucket(previous_ibucket);
1186
+
1187
+ while (m_buckets[ibucket].dist_from_ideal_bucket() > 0) {
1188
+ tsl_rh_assert(m_buckets[previous_ibucket].empty());
1189
+
1190
+ const distance_type new_distance =
1191
+ distance_type(m_buckets[ibucket].dist_from_ideal_bucket() - 1);
1192
+ m_buckets[previous_ibucket].set_value_of_empty_bucket(
1193
+ new_distance, m_buckets[ibucket].truncated_hash(),
1194
+ std::move(m_buckets[ibucket].value()));
1195
+ m_buckets[ibucket].clear();
1196
+
1197
+ previous_ibucket = ibucket;
1198
+ ibucket = next_bucket(ibucket);
1199
+ }
1200
+ }
1201
+
1202
+ template <class K, class... Args>
1203
+ std::pair<iterator, bool> insert_impl(const K& key,
1204
+ Args&&... value_type_args) {
1205
+ const std::size_t hash = hash_key(key);
1206
+
1207
+ std::size_t ibucket = bucket_for_hash(hash);
1208
+ distance_type dist_from_ideal_bucket = 0;
1209
+
1210
+ while (dist_from_ideal_bucket <=
1211
+ m_buckets[ibucket].dist_from_ideal_bucket()) {
1212
+ if ((!USE_STORED_HASH_ON_LOOKUP ||
1213
+ m_buckets[ibucket].bucket_hash_equal(hash)) &&
1214
+ compare_keys(KeySelect()(m_buckets[ibucket].value()), key)) {
1215
+ return std::make_pair(iterator(m_buckets + ibucket), false);
1216
+ }
1217
+
1218
+ ibucket = next_bucket(ibucket);
1219
+ dist_from_ideal_bucket++;
1220
+ }
1221
+
1222
+ if (rehash_on_extreme_load()) {
1223
+ ibucket = bucket_for_hash(hash);
1224
+ dist_from_ideal_bucket = 0;
1225
+
1226
+ while (dist_from_ideal_bucket <=
1227
+ m_buckets[ibucket].dist_from_ideal_bucket()) {
1228
+ ibucket = next_bucket(ibucket);
1229
+ dist_from_ideal_bucket++;
1230
+ }
1231
+ }
1232
+
1233
+ if (m_buckets[ibucket].empty()) {
1234
+ m_buckets[ibucket].set_value_of_empty_bucket(
1235
+ dist_from_ideal_bucket, bucket_entry::truncate_hash(hash),
1236
+ std::forward<Args>(value_type_args)...);
1237
+ } else {
1238
+ insert_value(ibucket, dist_from_ideal_bucket,
1239
+ bucket_entry::truncate_hash(hash),
1240
+ std::forward<Args>(value_type_args)...);
1241
+ }
1242
+
1243
+ m_nb_elements++;
1244
+ /*
1245
+ * The value will be inserted in ibucket in any case, either because it was
1246
+ * empty or by stealing the bucket (robin hood).
1247
+ */
1248
+ return std::make_pair(iterator(m_buckets + ibucket), true);
1249
+ }
1250
+
1251
+ template <class... Args>
1252
+ void insert_value(std::size_t ibucket, distance_type dist_from_ideal_bucket,
1253
+ truncated_hash_type hash, Args&&... value_type_args) {
1254
+ value_type value(std::forward<Args>(value_type_args)...);
1255
+ insert_value_impl(ibucket, dist_from_ideal_bucket, hash, value);
1256
+ }
1257
+
1258
+ void insert_value(std::size_t ibucket, distance_type dist_from_ideal_bucket,
1259
+ truncated_hash_type hash, value_type&& value) {
1260
+ insert_value_impl(ibucket, dist_from_ideal_bucket, hash, value);
1261
+ }
1262
+
1263
+ /*
1264
+ * We don't use `value_type&& value` as last argument due to a bug in MSVC
1265
+ * when `value_type` is a pointer, The compiler is not able to see the
1266
+ * difference between `std::string*` and `std::string*&&` resulting in a
1267
+ * compilation error.
1268
+ *
1269
+ * The `value` will be in a moved state at the end of the function.
1270
+ */
1271
+ void insert_value_impl(std::size_t ibucket,
1272
+ distance_type dist_from_ideal_bucket,
1273
+ truncated_hash_type hash, value_type& value) {
1274
+ m_buckets[ibucket].swap_with_value_in_bucket(dist_from_ideal_bucket, hash,
1275
+ value);
1276
+ ibucket = next_bucket(ibucket);
1277
+ dist_from_ideal_bucket++;
1278
+
1279
+ while (!m_buckets[ibucket].empty()) {
1280
+ if (dist_from_ideal_bucket >
1281
+ m_buckets[ibucket].dist_from_ideal_bucket()) {
1282
+ if (dist_from_ideal_bucket >=
1283
+ bucket_entry::DIST_FROM_IDEAL_BUCKET_LIMIT) {
1284
+ /**
1285
+ * The number of probes is really high, rehash the map on the next
1286
+ * insert. Difficult to do now as rehash may throw an exception.
1287
+ */
1288
+ m_grow_on_next_insert = true;
1289
+ }
1290
+
1291
+ m_buckets[ibucket].swap_with_value_in_bucket(dist_from_ideal_bucket,
1292
+ hash, value);
1293
+ }
1294
+
1295
+ ibucket = next_bucket(ibucket);
1296
+ dist_from_ideal_bucket++;
1297
+ }
1298
+
1299
+ m_buckets[ibucket].set_value_of_empty_bucket(dist_from_ideal_bucket, hash,
1300
+ std::move(value));
1301
+ }
1302
+
1303
+ void rehash_impl(size_type count_) {
1304
+ robin_hash new_table(count_, static_cast<Hash&>(*this),
1305
+ static_cast<KeyEqual&>(*this), get_allocator(),
1306
+ m_min_load_factor, m_max_load_factor);
1307
+
1308
+ const bool use_stored_hash =
1309
+ USE_STORED_HASH_ON_REHASH(new_table.bucket_count());
1310
+ for (auto& bucket : m_buckets_data) {
1311
+ if (bucket.empty()) {
1312
+ continue;
1313
+ }
1314
+
1315
+ const std::size_t hash =
1316
+ use_stored_hash ? bucket.truncated_hash()
1317
+ : new_table.hash_key(KeySelect()(bucket.value()));
1318
+
1319
+ new_table.insert_value_on_rehash(new_table.bucket_for_hash(hash), 0,
1320
+ bucket_entry::truncate_hash(hash),
1321
+ std::move(bucket.value()));
1322
+ }
1323
+
1324
+ new_table.m_nb_elements = m_nb_elements;
1325
+ new_table.swap(*this);
1326
+ }
1327
+
1328
+ void clear_and_shrink() noexcept {
1329
+ GrowthPolicy::clear();
1330
+ m_buckets_data.clear();
1331
+ m_buckets = static_empty_bucket_ptr();
1332
+ m_bucket_count = 0;
1333
+ m_nb_elements = 0;
1334
+ m_load_threshold = 0;
1335
+ m_grow_on_next_insert = false;
1336
+ m_try_shrink_on_next_insert = false;
1337
+ }
1338
+
1339
+ void insert_value_on_rehash(std::size_t ibucket,
1340
+ distance_type dist_from_ideal_bucket,
1341
+ truncated_hash_type hash, value_type&& value) {
1342
+ while (true) {
1343
+ if (dist_from_ideal_bucket >
1344
+ m_buckets[ibucket].dist_from_ideal_bucket()) {
1345
+ if (m_buckets[ibucket].empty()) {
1346
+ m_buckets[ibucket].set_value_of_empty_bucket(dist_from_ideal_bucket,
1347
+ hash, std::move(value));
1348
+ return;
1349
+ } else {
1350
+ m_buckets[ibucket].swap_with_value_in_bucket(dist_from_ideal_bucket,
1351
+ hash, value);
1352
+ }
1353
+ }
1354
+
1355
+ dist_from_ideal_bucket++;
1356
+ ibucket = next_bucket(ibucket);
1357
+ }
1358
+ }
1359
+
1360
+ /**
1361
+ * Grow the table if m_grow_on_next_insert is true or we reached the
1362
+ * max_load_factor. Shrink the table if m_try_shrink_on_next_insert is true
1363
+ * (an erase occurred) and we're below the min_load_factor.
1364
+ *
1365
+ * Return true if the table has been rehashed.
1366
+ */
1367
+ bool rehash_on_extreme_load() {
1368
+ if (m_grow_on_next_insert || size() >= m_load_threshold) {
1369
+ rehash_impl(GrowthPolicy::next_bucket_count());
1370
+ m_grow_on_next_insert = false;
1371
+
1372
+ return true;
1373
+ }
1374
+
1375
+ if (m_try_shrink_on_next_insert) {
1376
+ m_try_shrink_on_next_insert = false;
1377
+ if (m_min_load_factor != 0.0f && load_factor() < m_min_load_factor) {
1378
+ reserve(size() + 1);
1379
+
1380
+ return true;
1381
+ }
1382
+ }
1383
+
1384
+ return false;
1385
+ }
1386
+
1387
+ template <class Serializer>
1388
+ void serialize_impl(Serializer& serializer) const {
1389
+ const slz_size_type version = SERIALIZATION_PROTOCOL_VERSION;
1390
+ serializer(version);
1391
+
1392
+ // Indicate if the truncated hash of each bucket is stored. Use a
1393
+ // std::int16_t instead of a bool to avoid the need for the serializer to
1394
+ // support an extra 'bool' type.
1395
+ const std::int16_t hash_stored_for_bucket =
1396
+ static_cast<std::int16_t>(STORE_HASH);
1397
+ serializer(hash_stored_for_bucket);
1398
+
1399
+ const slz_size_type nb_elements = m_nb_elements;
1400
+ serializer(nb_elements);
1401
+
1402
+ const slz_size_type bucket_count = m_buckets_data.size();
1403
+ serializer(bucket_count);
1404
+
1405
+ const float min_load_factor = m_min_load_factor;
1406
+ serializer(min_load_factor);
1407
+
1408
+ const float max_load_factor = m_max_load_factor;
1409
+ serializer(max_load_factor);
1410
+
1411
+ for (const bucket_entry& bucket : m_buckets_data) {
1412
+ if (bucket.empty()) {
1413
+ const std::int16_t empty_bucket =
1414
+ bucket_entry::EMPTY_MARKER_DIST_FROM_IDEAL_BUCKET;
1415
+ serializer(empty_bucket);
1416
+ } else {
1417
+ const std::int16_t dist_from_ideal_bucket =
1418
+ bucket.dist_from_ideal_bucket();
1419
+ serializer(dist_from_ideal_bucket);
1420
+ if (STORE_HASH) {
1421
+ const std::uint32_t truncated_hash = bucket.truncated_hash();
1422
+ serializer(truncated_hash);
1423
+ }
1424
+ serializer(bucket.value());
1425
+ }
1426
+ }
1427
+ }
1428
+
1429
+ template <class Deserializer>
1430
+ void deserialize_impl(Deserializer& deserializer, bool hash_compatible) {
1431
+ tsl_rh_assert(m_buckets_data.empty()); // Current hash table must be empty
1432
+
1433
+ const slz_size_type version =
1434
+ deserialize_value<slz_size_type>(deserializer);
1435
+ // For now we only have one version of the serialization protocol.
1436
+ // If it doesn't match there is a problem with the file.
1437
+ if (version != SERIALIZATION_PROTOCOL_VERSION) {
1438
+ TSL_RH_THROW_OR_TERMINATE(std::runtime_error,
1439
+ "Can't deserialize the ordered_map/set. "
1440
+ "The protocol version header is invalid.");
1441
+ }
1442
+
1443
+ const bool hash_stored_for_bucket =
1444
+ deserialize_value<std::int16_t>(deserializer) ? true : false;
1445
+ if (hash_compatible && STORE_HASH != hash_stored_for_bucket) {
1446
+ TSL_RH_THROW_OR_TERMINATE(
1447
+ std::runtime_error,
1448
+ "Can't deserialize a map with a different StoreHash "
1449
+ "than the one used during the serialization when "
1450
+ "hash compatibility is used");
1451
+ }
1452
+
1453
+ const slz_size_type nb_elements =
1454
+ deserialize_value<slz_size_type>(deserializer);
1455
+ const slz_size_type bucket_count_ds =
1456
+ deserialize_value<slz_size_type>(deserializer);
1457
+ const float min_load_factor = deserialize_value<float>(deserializer);
1458
+ const float max_load_factor = deserialize_value<float>(deserializer);
1459
+
1460
+ if (min_load_factor < MINIMUM_MIN_LOAD_FACTOR ||
1461
+ min_load_factor > MAXIMUM_MIN_LOAD_FACTOR) {
1462
+ TSL_RH_THROW_OR_TERMINATE(
1463
+ std::runtime_error,
1464
+ "Invalid min_load_factor. Check that the serializer "
1465
+ "and deserializer support floats correctly as they "
1466
+ "can be converted implicitly to ints.");
1467
+ }
1468
+
1469
+ if (max_load_factor < MINIMUM_MAX_LOAD_FACTOR ||
1470
+ max_load_factor > MAXIMUM_MAX_LOAD_FACTOR) {
1471
+ TSL_RH_THROW_OR_TERMINATE(
1472
+ std::runtime_error,
1473
+ "Invalid max_load_factor. Check that the serializer "
1474
+ "and deserializer support floats correctly as they "
1475
+ "can be converted implicitly to ints.");
1476
+ }
1477
+
1478
+ this->min_load_factor(min_load_factor);
1479
+ this->max_load_factor(max_load_factor);
1480
+
1481
+ if (bucket_count_ds == 0) {
1482
+ tsl_rh_assert(nb_elements == 0);
1483
+ return;
1484
+ }
1485
+
1486
+ if (!hash_compatible) {
1487
+ reserve(numeric_cast<size_type>(nb_elements,
1488
+ "Deserialized nb_elements is too big."));
1489
+ for (slz_size_type ibucket = 0; ibucket < bucket_count_ds; ibucket++) {
1490
+ const distance_type dist_from_ideal_bucket =
1491
+ deserialize_value<std::int16_t>(deserializer);
1492
+ if (dist_from_ideal_bucket !=
1493
+ bucket_entry::EMPTY_MARKER_DIST_FROM_IDEAL_BUCKET) {
1494
+ if (hash_stored_for_bucket) {
1495
+ TSL_RH_UNUSED(deserialize_value<std::uint32_t>(deserializer));
1496
+ }
1497
+
1498
+ insert(deserialize_value<value_type>(deserializer));
1499
+ }
1500
+ }
1501
+
1502
+ tsl_rh_assert(nb_elements == size());
1503
+ } else {
1504
+ m_bucket_count = numeric_cast<size_type>(
1505
+ bucket_count_ds, "Deserialized bucket_count is too big.");
1506
+
1507
+ GrowthPolicy::operator=(GrowthPolicy(m_bucket_count));
1508
+ // GrowthPolicy should not modify the bucket count we got from
1509
+ // deserialization
1510
+ if (m_bucket_count != bucket_count_ds) {
1511
+ TSL_RH_THROW_OR_TERMINATE(std::runtime_error,
1512
+ "The GrowthPolicy is not the same even "
1513
+ "though hash_compatible is true.");
1514
+ }
1515
+
1516
+ m_nb_elements = numeric_cast<size_type>(
1517
+ nb_elements, "Deserialized nb_elements is too big.");
1518
+ m_buckets_data.resize(m_bucket_count);
1519
+ m_buckets = m_buckets_data.data();
1520
+
1521
+ for (bucket_entry& bucket : m_buckets_data) {
1522
+ const distance_type dist_from_ideal_bucket =
1523
+ deserialize_value<std::int16_t>(deserializer);
1524
+ if (dist_from_ideal_bucket !=
1525
+ bucket_entry::EMPTY_MARKER_DIST_FROM_IDEAL_BUCKET) {
1526
+ truncated_hash_type truncated_hash = 0;
1527
+ if (hash_stored_for_bucket) {
1528
+ tsl_rh_assert(hash_stored_for_bucket);
1529
+ truncated_hash = deserialize_value<std::uint32_t>(deserializer);
1530
+ }
1531
+
1532
+ bucket.set_value_of_empty_bucket(
1533
+ dist_from_ideal_bucket, truncated_hash,
1534
+ deserialize_value<value_type>(deserializer));
1535
+ }
1536
+ }
1537
+
1538
+ if (!m_buckets_data.empty()) {
1539
+ m_buckets_data.back().set_as_last_bucket();
1540
+ }
1541
+ }
1542
+ }
1543
+
1544
+ public:
1545
+ static const size_type DEFAULT_INIT_BUCKETS_SIZE = 0;
1546
+
1547
+ static constexpr float DEFAULT_MAX_LOAD_FACTOR = 0.5f;
1548
+ static constexpr float MINIMUM_MAX_LOAD_FACTOR = 0.2f;
1549
+ static constexpr float MAXIMUM_MAX_LOAD_FACTOR = 0.95f;
1550
+
1551
+ static constexpr float DEFAULT_MIN_LOAD_FACTOR = 0.0f;
1552
+ static constexpr float MINIMUM_MIN_LOAD_FACTOR = 0.0f;
1553
+ static constexpr float MAXIMUM_MIN_LOAD_FACTOR = 0.15f;
1554
+
1555
+ static_assert(MINIMUM_MAX_LOAD_FACTOR < MAXIMUM_MAX_LOAD_FACTOR,
1556
+ "MINIMUM_MAX_LOAD_FACTOR should be < MAXIMUM_MAX_LOAD_FACTOR");
1557
+ static_assert(MINIMUM_MIN_LOAD_FACTOR < MAXIMUM_MIN_LOAD_FACTOR,
1558
+ "MINIMUM_MIN_LOAD_FACTOR should be < MAXIMUM_MIN_LOAD_FACTOR");
1559
+ static_assert(MAXIMUM_MIN_LOAD_FACTOR < MINIMUM_MAX_LOAD_FACTOR,
1560
+ "MAXIMUM_MIN_LOAD_FACTOR should be < MINIMUM_MAX_LOAD_FACTOR");
1561
+
1562
+ private:
1563
+ /**
1564
+ * Protocol version currenlty used for serialization.
1565
+ */
1566
+ static const slz_size_type SERIALIZATION_PROTOCOL_VERSION = 1;
1567
+
1568
+ /**
1569
+ * Return an always valid pointer to an static empty bucket_entry with
1570
+ * last_bucket() == true.
1571
+ */
1572
+ bucket_entry* static_empty_bucket_ptr() noexcept {
1573
+ static bucket_entry empty_bucket(true);
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
1582
+ * to static_empty_bucket_ptr. This variable is useful to avoid the cost of
1583
+ * checking if m_buckets_data is empty when trying to find an element.
1584
+ *
1585
+ * TODO Remove m_buckets_data and only use a pointer instead of a
1586
+ * pointer+vector to save some space in the robin_hash object. Manage the
1587
+ * Allocator manually.
1588
+ */
1589
+ bucket_entry* m_buckets;
1590
+
1591
+ /**
1592
+ * Used a lot in find, avoid the call to m_buckets_data.size() which is a bit
1593
+ * slower.
1594
+ */
1595
+ size_type m_bucket_count;
1596
+
1597
+ size_type m_nb_elements;
1598
+
1599
+ size_type m_load_threshold;
1600
+
1601
+ float m_min_load_factor;
1602
+ float m_max_load_factor;
1603
+
1604
+ bool m_grow_on_next_insert;
1605
+
1606
+ /**
1607
+ * We can't shrink down the map on erase operations as the erase methods need
1608
+ * to return the next iterator. Shrinking the map would invalidate all the
1609
+ * iterators and we could not return the next iterator in a meaningful way, On
1610
+ * erase, we thus just indicate on erase that we should try to shrink the hash
1611
+ * table on the next insert if we go below the min_load_factor.
1612
+ */
1613
+ bool m_try_shrink_on_next_insert;
1614
+ };
1615
+
1616
+ } // namespace detail_robin_hash
1617
+
1618
+ } // namespace tsl
1619
+
1620
+ #endif