1 | /** |
2 | * MIT License |
3 | * |
4 | * Copyright (c) 2017 Tessil |
5 | * |
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy |
7 | * of this software and associated documentation files (the "Software"), to deal |
8 | * in the Software without restriction, including without limitation the rights |
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
10 | * copies of the Software, and to permit persons to whom the Software is |
11 | * furnished to do so, subject to the following conditions: |
12 | * |
13 | * The above copyright notice and this permission notice shall be included in all |
14 | * copies or substantial portions of the Software. |
15 | * |
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
22 | * SOFTWARE. |
23 | */ |
24 | #ifndef TSL_HOPSCOTCH_MAP_H |
25 | #define TSL_HOPSCOTCH_MAP_H |
26 | |
27 | |
28 | #include <algorithm> |
29 | #include <cstddef> |
30 | #include <functional> |
31 | #include <initializer_list> |
32 | #include <list> |
33 | #include <memory> |
34 | #include <type_traits> |
35 | #include <utility> |
36 | #include "hopscotch_hash.h" |
37 | |
38 | |
39 | namespace tsl { |
40 | |
41 | /** |
42 | * Implementation of a hash map using the hopscotch hashing algorithm. |
43 | * |
44 | * The Key and the value T must be either nothrow move-constructible, copy-constuctible or both. |
45 | * |
46 | * The size of the neighborhood (NeighborhoodSize) must be > 0 and <= 62 if StoreHash is false. |
47 | * When StoreHash is true, 32-bits of the hash will be stored alongside the neighborhood limiting |
48 | * the NeighborhoodSize to <= 30. There is no memory usage difference between |
49 | * 'NeighborhoodSize 62; StoreHash false' and 'NeighborhoodSize 30; StoreHash true'. |
50 | * |
51 | * Storing the hash may improve performance on insert during the rehash process if the hash takes time |
52 | * to compute. It may also improve read performance if the KeyEqual function takes time (or incurs a cache-miss). |
53 | * If used with simple Hash and KeyEqual it may slow things down. |
54 | * |
55 | * StoreHash can only be set if the GrowthPolicy is set to tsl::power_of_two_growth_policy. |
56 | * |
57 | * GrowthPolicy defines how the map grows and consequently how a hash value is mapped to a bucket. |
58 | * By default the map uses tsl::power_of_two_growth_policy. This policy keeps the number of buckets |
59 | * to a power of two and uses a mask to map the hash to a bucket instead of the slow modulo. |
60 | * You may define your own growth policy, check tsl::power_of_two_growth_policy for the interface. |
61 | * |
62 | * If the destructors of Key or T throw an exception, behaviour of the class is undefined. |
63 | * |
64 | * Iterators invalidation: |
65 | * - clear, operator=, reserve, rehash: always invalidate the iterators. |
66 | * - insert, emplace, emplace_hint, operator[]: if there is an effective insert, invalidate the iterators |
67 | * if a displacement is needed to resolve a collision (which mean that most of the time, |
68 | * insert will invalidate the iterators). Or if there is a rehash. |
69 | * - erase: iterator on the erased element is the only one which become invalid. |
70 | */ |
71 | template<class Key, |
72 | class T, |
73 | class Hash = std::hash<Key>, |
74 | class KeyEqual = std::equal_to<Key>, |
75 | class Allocator = std::allocator<std::pair<Key, T>>, |
76 | unsigned int NeighborhoodSize = 62, |
77 | bool StoreHash = false, |
78 | class GrowthPolicy = tsl::power_of_two_growth_policy> |
79 | class hopscotch_map { |
80 | private: |
81 | template<typename U> |
82 | using has_is_transparent = tsl::detail_hopscotch_hash::has_is_transparent<U>; |
83 | |
84 | class KeySelect { |
85 | public: |
86 | using key_type = Key; |
87 | |
88 | const key_type& operator()(const std::pair<Key, T>& key_value) const { |
89 | return key_value.first; |
90 | } |
91 | |
92 | key_type& operator()(std::pair<Key, T>& key_value) { |
93 | return key_value.first; |
94 | } |
95 | }; |
96 | |
97 | class ValueSelect { |
98 | public: |
99 | using value_type = T; |
100 | |
101 | const value_type& operator()(const std::pair<Key, T>& key_value) const { |
102 | return key_value.second; |
103 | } |
104 | |
105 | value_type& operator()(std::pair<Key, T>& key_value) { |
106 | return key_value.second; |
107 | } |
108 | }; |
109 | |
110 | |
111 | using overflow_container_type = std::list<std::pair<Key, T>, Allocator>; |
112 | using ht = detail_hopscotch_hash::hopscotch_hash<std::pair<Key, T>, KeySelect, ValueSelect, |
113 | Hash, KeyEqual, |
114 | Allocator, NeighborhoodSize, |
115 | StoreHash, GrowthPolicy, |
116 | overflow_container_type>; |
117 | |
118 | public: |
119 | using key_type = typename ht::key_type; |
120 | using mapped_type = T; |
121 | using value_type = typename ht::value_type; |
122 | using size_type = typename ht::size_type; |
123 | using difference_type = typename ht::difference_type; |
124 | using hasher = typename ht::hasher; |
125 | using key_equal = typename ht::key_equal; |
126 | using allocator_type = typename ht::allocator_type; |
127 | using reference = typename ht::reference; |
128 | using const_reference = typename ht::const_reference; |
129 | using pointer = typename ht::pointer; |
130 | using const_pointer = typename ht::const_pointer; |
131 | using iterator = typename ht::iterator; |
132 | using const_iterator = typename ht::const_iterator; |
133 | |
134 | |
135 | |
136 | /* |
137 | * Constructors |
138 | */ |
139 | hopscotch_map() : hopscotch_map(ht::DEFAULT_INIT_BUCKETS_SIZE) { |
140 | } |
141 | |
142 | explicit hopscotch_map(size_type bucket_count, |
143 | const Hash& hash = Hash(), |
144 | const KeyEqual& equal = KeyEqual(), |
145 | const Allocator& alloc = Allocator()) : |
146 | m_ht(bucket_count, hash, equal, alloc, ht::DEFAULT_MAX_LOAD_FACTOR) |
147 | { |
148 | } |
149 | |
150 | hopscotch_map(size_type bucket_count, |
151 | const Allocator& alloc) : hopscotch_map(bucket_count, Hash(), KeyEqual(), alloc) |
152 | { |
153 | } |
154 | |
155 | hopscotch_map(size_type bucket_count, |
156 | const Hash& hash, |
157 | const Allocator& alloc) : hopscotch_map(bucket_count, hash, KeyEqual(), alloc) |
158 | { |
159 | } |
160 | |
161 | explicit hopscotch_map(const Allocator& alloc) : hopscotch_map(ht::DEFAULT_INIT_BUCKETS_SIZE, alloc) { |
162 | } |
163 | |
164 | template<class InputIt> |
165 | hopscotch_map(InputIt first, InputIt last, |
166 | size_type bucket_count = ht::DEFAULT_INIT_BUCKETS_SIZE, |
167 | const Hash& hash = Hash(), |
168 | const KeyEqual& equal = KeyEqual(), |
169 | const Allocator& alloc = Allocator()) : hopscotch_map(bucket_count, hash, equal, alloc) |
170 | { |
171 | insert(first, last); |
172 | } |
173 | |
174 | template<class InputIt> |
175 | hopscotch_map(InputIt first, InputIt last, |
176 | size_type bucket_count, |
177 | const Allocator& alloc) : hopscotch_map(first, last, bucket_count, Hash(), KeyEqual(), alloc) |
178 | { |
179 | } |
180 | |
181 | template<class InputIt> |
182 | hopscotch_map(InputIt first, InputIt last, |
183 | size_type bucket_count, |
184 | const Hash& hash, |
185 | const Allocator& alloc) : hopscotch_map(first, last, bucket_count, hash, KeyEqual(), alloc) |
186 | { |
187 | } |
188 | |
189 | hopscotch_map(std::initializer_list<value_type> init, |
190 | size_type bucket_count = ht::DEFAULT_INIT_BUCKETS_SIZE, |
191 | const Hash& hash = Hash(), |
192 | const KeyEqual& equal = KeyEqual(), |
193 | const Allocator& alloc = Allocator()) : |
194 | hopscotch_map(init.begin(), init.end(), bucket_count, hash, equal, alloc) |
195 | { |
196 | } |
197 | |
198 | hopscotch_map(std::initializer_list<value_type> init, |
199 | size_type bucket_count, |
200 | const Allocator& alloc) : |
201 | hopscotch_map(init.begin(), init.end(), bucket_count, Hash(), KeyEqual(), alloc) |
202 | { |
203 | } |
204 | |
205 | hopscotch_map(std::initializer_list<value_type> init, |
206 | size_type bucket_count, |
207 | const Hash& hash, |
208 | const Allocator& alloc) : |
209 | hopscotch_map(init.begin(), init.end(), bucket_count, hash, KeyEqual(), alloc) |
210 | { |
211 | } |
212 | |
213 | |
214 | hopscotch_map& operator=(std::initializer_list<value_type> ilist) { |
215 | m_ht.clear(); |
216 | |
217 | m_ht.reserve(ilist.size()); |
218 | m_ht.insert(ilist.begin(), ilist.end()); |
219 | |
220 | return *this; |
221 | } |
222 | |
223 | allocator_type get_allocator() const { return m_ht.get_allocator(); } |
224 | |
225 | |
226 | /* |
227 | * Iterators |
228 | */ |
229 | iterator begin() noexcept { return m_ht.begin(); } |
230 | const_iterator begin() const noexcept { return m_ht.begin(); } |
231 | const_iterator cbegin() const noexcept { return m_ht.cbegin(); } |
232 | |
233 | iterator end() noexcept { return m_ht.end(); } |
234 | const_iterator end() const noexcept { return m_ht.end(); } |
235 | const_iterator cend() const noexcept { return m_ht.cend(); } |
236 | |
237 | |
238 | /* |
239 | * Capacity |
240 | */ |
241 | bool empty() const noexcept { return m_ht.empty(); } |
242 | size_type size() const noexcept { return m_ht.size(); } |
243 | size_type max_size() const noexcept { return m_ht.max_size(); } |
244 | |
245 | /* |
246 | * Modifiers |
247 | */ |
248 | void clear() noexcept { m_ht.clear(); } |
249 | |
250 | |
251 | |
252 | |
253 | std::pair<iterator, bool> insert(const value_type& value) { |
254 | return m_ht.insert(value); |
255 | } |
256 | |
257 | template<class P, typename std::enable_if<std::is_constructible<value_type, P&&>::value>::type* = nullptr> |
258 | std::pair<iterator, bool> insert(P&& value) { |
259 | return m_ht.insert(std::forward<P>(value)); |
260 | } |
261 | |
262 | std::pair<iterator, bool> insert(value_type&& value) { |
263 | return m_ht.insert(std::move(value)); |
264 | } |
265 | |
266 | |
267 | iterator insert(const_iterator hint, const value_type& value) { |
268 | return m_ht.insert(hint, value); |
269 | } |
270 | |
271 | template<class P, typename std::enable_if<std::is_constructible<value_type, P&&>::value>::type* = nullptr> |
272 | iterator insert(const_iterator hint, P&& value) { |
273 | return m_ht.insert(hint, std::forward<P>(value)); |
274 | } |
275 | |
276 | iterator insert(const_iterator hint, value_type&& value) { |
277 | return m_ht.insert(hint, std::move(value)); |
278 | } |
279 | |
280 | |
281 | template<class InputIt> |
282 | void insert(InputIt first, InputIt last) { |
283 | m_ht.insert(first, last); |
284 | } |
285 | |
286 | void insert(std::initializer_list<value_type> ilist) { |
287 | m_ht.insert(ilist.begin(), ilist.end()); |
288 | } |
289 | |
290 | |
291 | |
292 | |
293 | template<class M> |
294 | std::pair<iterator, bool> insert_or_assign(const key_type& k, M&& obj) { |
295 | return m_ht.insert_or_assign(k, std::forward<M>(obj)); |
296 | } |
297 | |
298 | template<class M> |
299 | std::pair<iterator, bool> insert_or_assign(key_type&& k, M&& obj) { |
300 | return m_ht.insert_or_assign(std::move(k), std::forward<M>(obj)); |
301 | } |
302 | |
303 | template<class M> |
304 | iterator insert_or_assign(const_iterator hint, const key_type& k, M&& obj) { |
305 | return m_ht.insert_or_assign(hint, k, std::forward<M>(obj)); |
306 | } |
307 | |
308 | template<class M> |
309 | iterator insert_or_assign(const_iterator hint, key_type&& k, M&& obj) { |
310 | return m_ht.insert_or_assign(hint, std::move(k), std::forward<M>(obj)); |
311 | } |
312 | |
313 | |
314 | |
315 | |
316 | /** |
317 | * Due to the way elements are stored, emplace will need to move or copy the key-value once. |
318 | * The method is equivalent to insert(value_type(std::forward<Args>(args)...)); |
319 | * |
320 | * Mainly here for compatibility with the std::unordered_map interface. |
321 | */ |
322 | template<class... Args> |
323 | std::pair<iterator, bool> emplace(Args&&... args) { |
324 | return m_ht.emplace(std::forward<Args>(args)...); |
325 | } |
326 | |
327 | |
328 | |
329 | |
330 | /** |
331 | * Due to the way elements are stored, emplace_hint will need to move or copy the key-value once. |
332 | * The method is equivalent to insert(hint, value_type(std::forward<Args>(args)...)); |
333 | * |
334 | * Mainly here for compatibility with the std::unordered_map interface. |
335 | */ |
336 | template<class... Args> |
337 | iterator emplace_hint(const_iterator hint, Args&&... args) { |
338 | return m_ht.emplace_hint(hint, std::forward<Args>(args)...); |
339 | } |
340 | |
341 | |
342 | |
343 | |
344 | template<class... Args> |
345 | std::pair<iterator, bool> try_emplace(const key_type& k, Args&&... args) { |
346 | return m_ht.try_emplace(k, std::forward<Args>(args)...); |
347 | } |
348 | |
349 | template<class... Args> |
350 | std::pair<iterator, bool> try_emplace(key_type&& k, Args&&... args) { |
351 | return m_ht.try_emplace(std::move(k), std::forward<Args>(args)...); |
352 | } |
353 | |
354 | template<class... Args> |
355 | iterator try_emplace(const_iterator hint, const key_type& k, Args&&... args) { |
356 | return m_ht.try_emplace(hint, k, std::forward<Args>(args)...); |
357 | } |
358 | |
359 | template<class... Args> |
360 | iterator try_emplace(const_iterator hint, key_type&& k, Args&&... args) { |
361 | return m_ht.try_emplace(hint, std::move(k), std::forward<Args>(args)...); |
362 | } |
363 | |
364 | |
365 | |
366 | |
367 | iterator erase(iterator pos) { return m_ht.erase(pos); } |
368 | iterator erase(const_iterator pos) { return m_ht.erase(pos); } |
369 | iterator erase(const_iterator first, const_iterator last) { return m_ht.erase(first, last); } |
370 | size_type erase(const key_type& key) { return m_ht.erase(key); } |
371 | |
372 | /** |
373 | * Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same |
374 | * as hash_function()(key). Usefull to speed-up the lookup to the value if you already have the hash. |
375 | */ |
376 | size_type erase(const key_type& key, std::size_t precalculated_hash) { |
377 | return m_ht.erase(key, precalculated_hash); |
378 | } |
379 | |
380 | /** |
381 | * This overload only participates in the overload resolution if the typedef KeyEqual::is_transparent exists. |
382 | * If so, K must be hashable and comparable to Key. |
383 | */ |
384 | template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr> |
385 | size_type erase(const K& key) { return m_ht.erase(key); } |
386 | |
387 | /** |
388 | * @copydoc erase(const K& key) |
389 | * |
390 | * Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same |
391 | * as hash_function()(key). Usefull to speed-up the lookup to the value if you already have the hash. |
392 | */ |
393 | template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr> |
394 | size_type erase(const K& key, std::size_t precalculated_hash) { |
395 | return m_ht.erase(key, precalculated_hash); |
396 | } |
397 | |
398 | |
399 | |
400 | |
401 | void swap(hopscotch_map& other) { other.m_ht.swap(m_ht); } |
402 | |
403 | /* |
404 | * Lookup |
405 | */ |
406 | T& at(const Key& key) { return m_ht.at(key); } |
407 | |
408 | /** |
409 | * Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same |
410 | * as hash_function()(key). Usefull to speed-up the lookup if you already have the hash. |
411 | */ |
412 | T& at(const Key& key, std::size_t precalculated_hash) { return m_ht.at(key, precalculated_hash); } |
413 | |
414 | |
415 | const T& at(const Key& key) const { return m_ht.at(key); } |
416 | |
417 | /** |
418 | * @copydoc at(const Key& key, std::size_t precalculated_hash) |
419 | */ |
420 | const T& at(const Key& key, std::size_t precalculated_hash) const { return m_ht.at(key, precalculated_hash); } |
421 | |
422 | |
423 | /** |
424 | * This overload only participates in the overload resolution if the typedef KeyEqual::is_transparent exists. |
425 | * If so, K must be hashable and comparable to Key. |
426 | */ |
427 | template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr> |
428 | T& at(const K& key) { return m_ht.at(key); } |
429 | |
430 | /** |
431 | * @copydoc at(const K& key) |
432 | * |
433 | * Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same |
434 | * as hash_function()(key). Usefull to speed-up the lookup if you already have the hash. |
435 | */ |
436 | template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr> |
437 | T& at(const K& key, std::size_t precalculated_hash) { return m_ht.at(key, precalculated_hash); } |
438 | |
439 | |
440 | /** |
441 | * @copydoc at(const K& key) |
442 | */ |
443 | template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr> |
444 | const T& at(const K& key) const { return m_ht.at(key); } |
445 | |
446 | /** |
447 | * @copydoc at(const K& key, std::size_t precalculated_hash) |
448 | */ |
449 | template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr> |
450 | const T& at(const K& key, std::size_t precalculated_hash) const { return m_ht.at(key, precalculated_hash); } |
451 | |
452 | |
453 | |
454 | |
455 | T& operator[](const Key& key) { return m_ht[key]; } |
456 | T& operator[](Key&& key) { return m_ht[std::move(key)]; } |
457 | |
458 | |
459 | |
460 | |
461 | size_type count(const Key& key) const { return m_ht.count(key); } |
462 | |
463 | /** |
464 | * Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same |
465 | * as hash_function()(key). Usefull to speed-up the lookup if you already have the hash. |
466 | */ |
467 | size_type count(const Key& key, std::size_t precalculated_hash) const { |
468 | return m_ht.count(key, precalculated_hash); |
469 | } |
470 | |
471 | /** |
472 | * This overload only participates in the overload resolution if the typedef KeyEqual::is_transparent exists. |
473 | * If so, K must be hashable and comparable to Key. |
474 | */ |
475 | template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr> |
476 | size_type count(const K& key) const { return m_ht.count(key); } |
477 | |
478 | /** |
479 | * @copydoc count(const K& key) const |
480 | * |
481 | * Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same |
482 | * as hash_function()(key). Usefull to speed-up the lookup if you already have the hash. |
483 | */ |
484 | template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr> |
485 | size_type count(const K& key, std::size_t precalculated_hash) const { return m_ht.count(key, precalculated_hash); } |
486 | |
487 | |
488 | |
489 | |
490 | iterator find(const Key& key) { return m_ht.find(key); } |
491 | |
492 | /** |
493 | * Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same |
494 | * as hash_function()(key). Usefull to speed-up the lookup if you already have the hash. |
495 | */ |
496 | iterator find(const Key& key, std::size_t precalculated_hash) { return m_ht.find(key, precalculated_hash); } |
497 | |
498 | const_iterator find(const Key& key) const { return m_ht.find(key); } |
499 | |
500 | /** |
501 | * @copydoc find(const Key& key, std::size_t precalculated_hash) |
502 | */ |
503 | const_iterator find(const Key& key, std::size_t precalculated_hash) const { |
504 | return m_ht.find(key, precalculated_hash); |
505 | } |
506 | |
507 | /** |
508 | * This overload only participates in the overload resolution if the typedef KeyEqual::is_transparent exists. |
509 | * If so, K must be hashable and comparable to Key. |
510 | */ |
511 | template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr> |
512 | iterator find(const K& key) { return m_ht.find(key); } |
513 | |
514 | /** |
515 | * @copydoc find(const K& key) |
516 | * |
517 | * Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same |
518 | * as hash_function()(key). Usefull to speed-up the lookup if you already have the hash. |
519 | */ |
520 | template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr> |
521 | iterator find(const K& key, std::size_t precalculated_hash) { return m_ht.find(key, precalculated_hash); } |
522 | |
523 | /** |
524 | * @copydoc find(const K& key) |
525 | */ |
526 | template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr> |
527 | const_iterator find(const K& key) const { return m_ht.find(key); } |
528 | |
529 | /** |
530 | * @copydoc find(const K& key) |
531 | * |
532 | * Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same |
533 | * as hash_function()(key). Usefull to speed-up the lookup if you already have the hash. |
534 | */ |
535 | template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr> |
536 | const_iterator find(const K& key, std::size_t precalculated_hash) const { |
537 | return m_ht.find(key, precalculated_hash); |
538 | } |
539 | |
540 | |
541 | |
542 | |
543 | std::pair<iterator, iterator> equal_range(const Key& key) { return m_ht.equal_range(key); } |
544 | |
545 | /** |
546 | * Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same |
547 | * as hash_function()(key). Usefull to speed-up the lookup if you already have the hash. |
548 | */ |
549 | std::pair<iterator, iterator> equal_range(const Key& key, std::size_t precalculated_hash) { |
550 | return m_ht.equal_range(key, precalculated_hash); |
551 | } |
552 | |
553 | std::pair<const_iterator, const_iterator> equal_range(const Key& key) const { return m_ht.equal_range(key); } |
554 | |
555 | /** |
556 | * @copydoc equal_range(const Key& key, std::size_t precalculated_hash) |
557 | */ |
558 | std::pair<const_iterator, const_iterator> equal_range(const Key& key, std::size_t precalculated_hash) const { |
559 | return m_ht.equal_range(key, precalculated_hash); |
560 | } |
561 | |
562 | /** |
563 | * This overload only participates in the overload resolution if the typedef KeyEqual::is_transparent exists. |
564 | * If so, K must be hashable and comparable to Key. |
565 | */ |
566 | template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr> |
567 | std::pair<iterator, iterator> equal_range(const K& key) { return m_ht.equal_range(key); } |
568 | |
569 | |
570 | /** |
571 | * @copydoc equal_range(const K& key) |
572 | * |
573 | * Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same |
574 | * as hash_function()(key). Usefull to speed-up the lookup if you already have the hash. |
575 | */ |
576 | template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr> |
577 | std::pair<iterator, iterator> equal_range(const K& key, std::size_t precalculated_hash) { |
578 | return m_ht.equal_range(key, precalculated_hash); |
579 | } |
580 | |
581 | /** |
582 | * @copydoc equal_range(const K& key) |
583 | */ |
584 | template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr> |
585 | std::pair<const_iterator, const_iterator> equal_range(const K& key) const { return m_ht.equal_range(key); } |
586 | |
587 | /** |
588 | * @copydoc equal_range(const K& key, std::size_t precalculated_hash) |
589 | */ |
590 | template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr> |
591 | std::pair<const_iterator, const_iterator> equal_range(const K& key, std::size_t precalculated_hash) const { |
592 | return m_ht.equal_range(key, precalculated_hash); |
593 | } |
594 | |
595 | |
596 | |
597 | |
598 | /* |
599 | * Bucket interface |
600 | */ |
601 | size_type bucket_count() const { return m_ht.bucket_count(); } |
602 | size_type max_bucket_count() const { return m_ht.max_bucket_count(); } |
603 | |
604 | |
605 | /* |
606 | * Hash policy |
607 | */ |
608 | float load_factor() const { return m_ht.load_factor(); } |
609 | float max_load_factor() const { return m_ht.max_load_factor(); } |
610 | void max_load_factor(float ml) { m_ht.max_load_factor(ml); } |
611 | |
612 | void rehash(size_type count) { m_ht.rehash(count); } |
613 | void reserve(size_type count) { m_ht.reserve(count); } |
614 | |
615 | |
616 | /* |
617 | * Observers |
618 | */ |
619 | hasher hash_function() const { return m_ht.hash_function(); } |
620 | key_equal key_eq() const { return m_ht.key_eq(); } |
621 | |
622 | /* |
623 | * Other |
624 | */ |
625 | |
626 | /** |
627 | * Convert a const_iterator to an iterator. |
628 | */ |
629 | iterator mutable_iterator(const_iterator pos) { |
630 | return m_ht.mutable_iterator(pos); |
631 | } |
632 | |
633 | size_type overflow_size() const noexcept { return m_ht.overflow_size(); } |
634 | |
635 | friend bool operator==(const hopscotch_map& lhs, const hopscotch_map& rhs) { |
636 | if(lhs.size() != rhs.size()) { |
637 | return false; |
638 | } |
639 | |
640 | for(const auto& element_lhs : lhs) { |
641 | const auto it_element_rhs = rhs.find(element_lhs.first); |
642 | if(it_element_rhs == rhs.cend() || element_lhs.second != it_element_rhs->second) { |
643 | return false; |
644 | } |
645 | } |
646 | |
647 | return true; |
648 | } |
649 | |
650 | friend bool operator!=(const hopscotch_map& lhs, const hopscotch_map& rhs) { |
651 | return !operator==(lhs, rhs); |
652 | } |
653 | |
654 | friend void swap(hopscotch_map& lhs, hopscotch_map& rhs) { |
655 | lhs.swap(rhs); |
656 | } |
657 | |
658 | |
659 | |
660 | private: |
661 | ht m_ht; |
662 | }; |
663 | |
664 | } // end namespace tsl |
665 | |
666 | #endif |
667 | |