Line data Source code
1 : // hashtable.h header -*- C++ -*-
2 :
3 : // Copyright (C) 2007-2021 Free Software Foundation, Inc.
4 : //
5 : // This file is part of the GNU ISO C++ Library. This library is free
6 : // software; you can redistribute it and/or modify it under the
7 : // terms of the GNU General Public License as published by the
8 : // Free Software Foundation; either version 3, or (at your option)
9 : // any later version.
10 :
11 : // This library is distributed in the hope that it will be useful,
12 : // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 : // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 : // GNU General Public License for more details.
15 :
16 : // Under Section 7 of GPL version 3, you are granted additional
17 : // permissions described in the GCC Runtime Library Exception, version
18 : // 3.1, as published by the Free Software Foundation.
19 :
20 : // You should have received a copy of the GNU General Public License and
21 : // a copy of the GCC Runtime Library Exception along with this program;
22 : // see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
23 : // <http://www.gnu.org/licenses/>.
24 :
25 : /** @file bits/hashtable.h
26 : * This is an internal header file, included by other library headers.
27 : * Do not attempt to use it directly. @headername{unordered_map, unordered_set}
28 : */
29 :
30 : #ifndef _HASHTABLE_H
31 : #define _HASHTABLE_H 1
32 :
33 : #pragma GCC system_header
34 :
35 : #include <bits/hashtable_policy.h>
36 : #include <bits/enable_special_members.h>
37 : #if __cplusplus > 201402L
38 : # include <bits/node_handle.h>
39 : #endif
40 :
41 : namespace std _GLIBCXX_VISIBILITY(default)
42 : {
43 : _GLIBCXX_BEGIN_NAMESPACE_VERSION
44 : /// @cond undocumented
45 :
46 : template<typename _Tp, typename _Hash>
47 : using __cache_default
48 : = __not_<__and_<// Do not cache for fast hasher.
49 : __is_fast_hash<_Hash>,
50 : // Mandatory to have erase not throwing.
51 : __is_nothrow_invocable<const _Hash&, const _Tp&>>>;
52 :
53 : // Helper to conditionally delete the default constructor.
54 : // The _Hash_node_base type is used to distinguish this specialization
55 : // from any other potentially-overlapping subobjects of the hashtable.
56 : template<typename _Equal, typename _Hash, typename _Allocator>
57 : using _Hashtable_enable_default_ctor
58 : = _Enable_default_constructor<__and_<is_default_constructible<_Equal>,
59 : is_default_constructible<_Hash>,
60 : is_default_constructible<_Allocator>>{},
61 : __detail::_Hash_node_base>;
62 :
63 : /**
64 : * Primary class template _Hashtable.
65 : *
66 : * @ingroup hashtable-detail
67 : *
68 : * @tparam _Value CopyConstructible type.
69 : *
70 : * @tparam _Key CopyConstructible type.
71 : *
72 : * @tparam _Alloc An allocator type
73 : * ([lib.allocator.requirements]) whose _Alloc::value_type is
74 : * _Value. As a conforming extension, we allow for
75 : * _Alloc::value_type != _Value.
76 : *
77 : * @tparam _ExtractKey Function object that takes an object of type
78 : * _Value and returns a value of type _Key.
79 : *
80 : * @tparam _Equal Function object that takes two objects of type k
81 : * and returns a bool-like value that is true if the two objects
82 : * are considered equal.
83 : *
84 : * @tparam _Hash The hash function. A unary function object with
85 : * argument type _Key and result type size_t. Return values should
86 : * be distributed over the entire range [0, numeric_limits<size_t>:::max()].
87 : *
88 : * @tparam _RangeHash The range-hashing function (in the terminology of
89 : * Tavori and Dreizin). A binary function object whose argument
90 : * types and result type are all size_t. Given arguments r and N,
91 : * the return value is in the range [0, N).
92 : *
93 : * @tparam _Unused Not used.
94 : *
95 : * @tparam _RehashPolicy Policy class with three members, all of
96 : * which govern the bucket count. _M_next_bkt(n) returns a bucket
97 : * count no smaller than n. _M_bkt_for_elements(n) returns a
98 : * bucket count appropriate for an element count of n.
99 : * _M_need_rehash(n_bkt, n_elt, n_ins) determines whether, if the
100 : * current bucket count is n_bkt and the current element count is
101 : * n_elt, we need to increase the bucket count for n_ins insertions.
102 : * If so, returns make_pair(true, n), where n is the new bucket count. If
103 : * not, returns make_pair(false, <anything>)
104 : *
105 : * @tparam _Traits Compile-time class with three boolean
106 : * std::integral_constant members: __cache_hash_code, __constant_iterators,
107 : * __unique_keys.
108 : *
109 : * Each _Hashtable data structure has:
110 : *
111 : * - _Bucket[] _M_buckets
112 : * - _Hash_node_base _M_before_begin
113 : * - size_type _M_bucket_count
114 : * - size_type _M_element_count
115 : *
116 : * with _Bucket being _Hash_node_base* and _Hash_node containing:
117 : *
118 : * - _Hash_node* _M_next
119 : * - Tp _M_value
120 : * - size_t _M_hash_code if cache_hash_code is true
121 : *
122 : * In terms of Standard containers the hashtable is like the aggregation of:
123 : *
124 : * - std::forward_list<_Node> containing the elements
125 : * - std::vector<std::forward_list<_Node>::iterator> representing the buckets
126 : *
127 : * The non-empty buckets contain the node before the first node in the
128 : * bucket. This design makes it possible to implement something like a
129 : * std::forward_list::insert_after on container insertion and
130 : * std::forward_list::erase_after on container erase
131 : * calls. _M_before_begin is equivalent to
132 : * std::forward_list::before_begin. Empty buckets contain
133 : * nullptr. Note that one of the non-empty buckets contains
134 : * &_M_before_begin which is not a dereferenceable node so the
135 : * node pointer in a bucket shall never be dereferenced, only its
136 : * next node can be.
137 : *
138 : * Walking through a bucket's nodes requires a check on the hash code to
139 : * see if each node is still in the bucket. Such a design assumes a
140 : * quite efficient hash functor and is one of the reasons it is
141 : * highly advisable to set __cache_hash_code to true.
142 : *
143 : * The container iterators are simply built from nodes. This way
144 : * incrementing the iterator is perfectly efficient independent of
145 : * how many empty buckets there are in the container.
146 : *
147 : * On insert we compute the element's hash code and use it to find the
148 : * bucket index. If the element must be inserted in an empty bucket
149 : * we add it at the beginning of the singly linked list and make the
150 : * bucket point to _M_before_begin. The bucket that used to point to
151 : * _M_before_begin, if any, is updated to point to its new before
152 : * begin node.
153 : *
154 : * On erase, the simple iterator design requires using the hash
155 : * functor to get the index of the bucket to update. For this
156 : * reason, when __cache_hash_code is set to false the hash functor must
157 : * not throw and this is enforced by a static assertion.
158 : *
159 : * Functionality is implemented by decomposition into base classes,
160 : * where the derived _Hashtable class is used in _Map_base,
161 : * _Insert, _Rehash_base, and _Equality base classes to access the
162 : * "this" pointer. _Hashtable_base is used in the base classes as a
163 : * non-recursive, fully-completed-type so that detailed nested type
164 : * information, such as iterator type and node type, can be
165 : * used. This is similar to the "Curiously Recurring Template
166 : * Pattern" (CRTP) technique, but uses a reconstructed, not
167 : * explicitly passed, template pattern.
168 : *
169 : * Base class templates are:
170 : * - __detail::_Hashtable_base
171 : * - __detail::_Map_base
172 : * - __detail::_Insert
173 : * - __detail::_Rehash_base
174 : * - __detail::_Equality
175 : */
176 : template<typename _Key, typename _Value, typename _Alloc,
177 : typename _ExtractKey, typename _Equal,
178 : typename _Hash, typename _RangeHash, typename _Unused,
179 : typename _RehashPolicy, typename _Traits>
180 : class _Hashtable
181 : : public __detail::_Hashtable_base<_Key, _Value, _ExtractKey, _Equal,
182 : _Hash, _RangeHash, _Unused, _Traits>,
183 : public __detail::_Map_base<_Key, _Value, _Alloc, _ExtractKey, _Equal,
184 : _Hash, _RangeHash, _Unused,
185 : _RehashPolicy, _Traits>,
186 : public __detail::_Insert<_Key, _Value, _Alloc, _ExtractKey, _Equal,
187 : _Hash, _RangeHash, _Unused,
188 : _RehashPolicy, _Traits>,
189 : public __detail::_Rehash_base<_Key, _Value, _Alloc, _ExtractKey, _Equal,
190 : _Hash, _RangeHash, _Unused,
191 : _RehashPolicy, _Traits>,
192 : public __detail::_Equality<_Key, _Value, _Alloc, _ExtractKey, _Equal,
193 : _Hash, _RangeHash, _Unused,
194 : _RehashPolicy, _Traits>,
195 : private __detail::_Hashtable_alloc<
196 : __alloc_rebind<_Alloc,
197 : __detail::_Hash_node<_Value,
198 : _Traits::__hash_cached::value>>>,
199 : private _Hashtable_enable_default_ctor<_Equal, _Hash, _Alloc>
200 : {
201 : static_assert(is_same<typename remove_cv<_Value>::type, _Value>::value,
202 : "unordered container must have a non-const, non-volatile value_type");
203 : #if __cplusplus > 201703L || defined __STRICT_ANSI__
204 : static_assert(is_same<typename _Alloc::value_type, _Value>{},
205 : "unordered container must have the same value_type as its allocator");
206 : #endif
207 :
208 : using __traits_type = _Traits;
209 : using __hash_cached = typename __traits_type::__hash_cached;
210 : using __constant_iterators = typename __traits_type::__constant_iterators;
211 : using __node_type = __detail::_Hash_node<_Value, __hash_cached::value>;
212 : using __node_alloc_type = __alloc_rebind<_Alloc, __node_type>;
213 :
214 : using __hashtable_alloc = __detail::_Hashtable_alloc<__node_alloc_type>;
215 :
216 : using __node_value_type =
217 : __detail::_Hash_node_value<_Value, __hash_cached::value>;
218 : using __node_ptr = typename __hashtable_alloc::__node_ptr;
219 : using __value_alloc_traits =
220 : typename __hashtable_alloc::__value_alloc_traits;
221 : using __node_alloc_traits =
222 : typename __hashtable_alloc::__node_alloc_traits;
223 : using __node_base = typename __hashtable_alloc::__node_base;
224 : using __node_base_ptr = typename __hashtable_alloc::__node_base_ptr;
225 : using __buckets_ptr = typename __hashtable_alloc::__buckets_ptr;
226 :
227 : using __insert_base = __detail::_Insert<_Key, _Value, _Alloc, _ExtractKey,
228 : _Equal, _Hash,
229 : _RangeHash, _Unused,
230 : _RehashPolicy, _Traits>;
231 : using __enable_default_ctor
232 : = _Hashtable_enable_default_ctor<_Equal, _Hash, _Alloc>;
233 :
234 : public:
235 : typedef _Key key_type;
236 : typedef _Value value_type;
237 : typedef _Alloc allocator_type;
238 : typedef _Equal key_equal;
239 :
240 : // mapped_type, if present, comes from _Map_base.
241 : // hasher, if present, comes from _Hash_code_base/_Hashtable_base.
242 : typedef typename __value_alloc_traits::pointer pointer;
243 : typedef typename __value_alloc_traits::const_pointer const_pointer;
244 : typedef value_type& reference;
245 : typedef const value_type& const_reference;
246 :
247 : using iterator = typename __insert_base::iterator;
248 :
249 : using const_iterator = typename __insert_base::const_iterator;
250 :
251 : using local_iterator = __detail::_Local_iterator<key_type, _Value,
252 : _ExtractKey, _Hash, _RangeHash, _Unused,
253 : __constant_iterators::value,
254 : __hash_cached::value>;
255 :
256 : using const_local_iterator = __detail::_Local_const_iterator<
257 : key_type, _Value,
258 : _ExtractKey, _Hash, _RangeHash, _Unused,
259 : __constant_iterators::value, __hash_cached::value>;
260 :
261 : private:
262 : using __rehash_type = _RehashPolicy;
263 : using __rehash_state = typename __rehash_type::_State;
264 :
265 : using __unique_keys = typename __traits_type::__unique_keys;
266 :
267 : using __hashtable_base = __detail::
268 : _Hashtable_base<_Key, _Value, _ExtractKey,
269 : _Equal, _Hash, _RangeHash, _Unused, _Traits>;
270 :
271 : using __hash_code_base = typename __hashtable_base::__hash_code_base;
272 : using __hash_code = typename __hashtable_base::__hash_code;
273 : using __ireturn_type = typename __insert_base::__ireturn_type;
274 :
275 : using __map_base = __detail::_Map_base<_Key, _Value, _Alloc, _ExtractKey,
276 : _Equal, _Hash, _RangeHash, _Unused,
277 : _RehashPolicy, _Traits>;
278 :
279 : using __rehash_base = __detail::_Rehash_base<_Key, _Value, _Alloc,
280 : _ExtractKey, _Equal,
281 : _Hash, _RangeHash, _Unused,
282 : _RehashPolicy, _Traits>;
283 :
284 : using __eq_base = __detail::_Equality<_Key, _Value, _Alloc, _ExtractKey,
285 : _Equal, _Hash, _RangeHash, _Unused,
286 : _RehashPolicy, _Traits>;
287 :
288 : using __reuse_or_alloc_node_gen_t =
289 : __detail::_ReuseOrAllocNode<__node_alloc_type>;
290 : using __alloc_node_gen_t =
291 : __detail::_AllocNode<__node_alloc_type>;
292 :
293 : // Simple RAII type for managing a node containing an element
294 : struct _Scoped_node
295 : {
296 : // Take ownership of a node with a constructed element.
297 : _Scoped_node(__node_ptr __n, __hashtable_alloc* __h)
298 : : _M_h(__h), _M_node(__n) { }
299 :
300 : // Allocate a node and construct an element within it.
301 : template<typename... _Args>
302 738 : _Scoped_node(__hashtable_alloc* __h, _Args&&... __args)
303 738 : : _M_h(__h),
304 738 : _M_node(__h->_M_allocate_node(std::forward<_Args>(__args)...))
305 738 : { }
306 :
307 : // Destroy element and deallocate node.
308 738 : ~_Scoped_node() { if (_M_node) _M_h->_M_deallocate_node(_M_node); };
309 :
310 : _Scoped_node(const _Scoped_node&) = delete;
311 : _Scoped_node& operator=(const _Scoped_node&) = delete;
312 :
313 : __hashtable_alloc* _M_h;
314 : __node_ptr _M_node;
315 : };
316 :
317 : template<typename _Ht>
318 : static constexpr
319 : typename conditional<std::is_lvalue_reference<_Ht>::value,
320 : const value_type&, value_type&&>::type
321 : __fwd_value_for(value_type& __val) noexcept
322 : { return std::move(__val); }
323 :
324 : // Compile-time diagnostics.
325 :
326 : // _Hash_code_base has everything protected, so use this derived type to
327 : // access it.
328 : struct __hash_code_base_access : __hash_code_base
329 : { using __hash_code_base::_M_bucket_index; };
330 :
331 : // Getting a bucket index from a node shall not throw because it is used
332 : // in methods (erase, swap...) that shall not throw.
333 : static_assert(noexcept(declval<const __hash_code_base_access&>()
334 : ._M_bucket_index(declval<const __node_value_type&>(),
335 : (std::size_t)0)),
336 : "Cache the hash code or qualify your functors involved"
337 : " in hash code and bucket index computation with noexcept");
338 :
339 : // To get bucket index we need _RangeHash not to throw.
340 : static_assert(is_nothrow_default_constructible<_RangeHash>::value,
341 : "Functor used to map hash code to bucket index"
342 : " must be nothrow default constructible");
343 : static_assert(noexcept(
344 : std::declval<const _RangeHash&>()((std::size_t)0, (std::size_t)0)),
345 : "Functor used to map hash code to bucket index must be"
346 : " noexcept");
347 :
348 : // To compute bucket index we also need _ExtratKey not to throw.
349 : static_assert(is_nothrow_default_constructible<_ExtractKey>::value,
350 : "_ExtractKey must be nothrow default constructible");
351 : static_assert(noexcept(
352 : std::declval<const _ExtractKey&>()(std::declval<_Value>())),
353 : "_ExtractKey functor must be noexcept invocable");
354 :
355 : template<typename _Keya, typename _Valuea, typename _Alloca,
356 : typename _ExtractKeya, typename _Equala,
357 : typename _Hasha, typename _RangeHasha, typename _Unuseda,
358 : typename _RehashPolicya, typename _Traitsa,
359 : bool _Unique_keysa>
360 : friend struct __detail::_Map_base;
361 :
362 : template<typename _Keya, typename _Valuea, typename _Alloca,
363 : typename _ExtractKeya, typename _Equala,
364 : typename _Hasha, typename _RangeHasha, typename _Unuseda,
365 : typename _RehashPolicya, typename _Traitsa>
366 : friend struct __detail::_Insert_base;
367 :
368 : template<typename _Keya, typename _Valuea, typename _Alloca,
369 : typename _ExtractKeya, typename _Equala,
370 : typename _Hasha, typename _RangeHasha, typename _Unuseda,
371 : typename _RehashPolicya, typename _Traitsa,
372 : bool _Constant_iteratorsa>
373 : friend struct __detail::_Insert;
374 :
375 : template<typename _Keya, typename _Valuea, typename _Alloca,
376 : typename _ExtractKeya, typename _Equala,
377 : typename _Hasha, typename _RangeHasha, typename _Unuseda,
378 : typename _RehashPolicya, typename _Traitsa,
379 : bool _Unique_keysa>
380 : friend struct __detail::_Equality;
381 :
382 : public:
383 : using size_type = typename __hashtable_base::size_type;
384 : using difference_type = typename __hashtable_base::difference_type;
385 :
386 : #if __cplusplus > 201402L
387 : using node_type = _Node_handle<_Key, _Value, __node_alloc_type>;
388 : using insert_return_type = _Node_insert_return<iterator, node_type>;
389 : #endif
390 :
391 : private:
392 : __buckets_ptr _M_buckets = &_M_single_bucket;
393 : size_type _M_bucket_count = 1;
394 : __node_base _M_before_begin;
395 : size_type _M_element_count = 0;
396 : _RehashPolicy _M_rehash_policy;
397 :
398 : // A single bucket used when only need for 1 bucket. Especially
399 : // interesting in move semantic to leave hashtable with only 1 bucket
400 : // which is not allocated so that we can have those operations noexcept
401 : // qualified.
402 : // Note that we can't leave hashtable with 0 bucket without adding
403 : // numerous checks in the code to avoid 0 modulus.
404 : __node_base_ptr _M_single_bucket = nullptr;
405 :
406 : void
407 : _M_update_bbegin()
408 : {
409 : if (_M_begin())
410 : _M_buckets[_M_bucket_index(*_M_begin())] = &_M_before_begin;
411 : }
412 :
413 : void
414 : _M_update_bbegin(__node_ptr __n)
415 : {
416 : _M_before_begin._M_nxt = __n;
417 : _M_update_bbegin();
418 : }
419 :
420 : bool
421 400 : _M_uses_single_bucket(__buckets_ptr __bkts) const
422 400 : { return __builtin_expect(__bkts == &_M_single_bucket, false); }
423 :
424 : bool
425 : _M_uses_single_bucket() const
426 : { return _M_uses_single_bucket(_M_buckets); }
427 :
428 : __hashtable_alloc&
429 : _M_base_alloc() { return *this; }
430 :
431 : __buckets_ptr
432 114 : _M_allocate_buckets(size_type __bkt_count)
433 : {
434 114 : if (__builtin_expect(__bkt_count == 1, false))
435 : {
436 0 : _M_single_bucket = nullptr;
437 0 : return &_M_single_bucket;
438 : }
439 :
440 114 : return __hashtable_alloc::_M_allocate_buckets(__bkt_count);
441 : }
442 :
443 : void
444 400 : _M_deallocate_buckets(__buckets_ptr __bkts, size_type __bkt_count)
445 : {
446 400 : if (_M_uses_single_bucket(__bkts))
447 286 : return;
448 :
449 114 : __hashtable_alloc::_M_deallocate_buckets(__bkts, __bkt_count);
450 : }
451 :
452 : void
453 400 : _M_deallocate_buckets()
454 400 : { _M_deallocate_buckets(_M_buckets, _M_bucket_count); }
455 :
456 : // Gets bucket begin, deals with the fact that non-empty buckets contain
457 : // their before begin node.
458 : __node_ptr
459 : _M_bucket_begin(size_type __bkt) const;
460 :
461 : __node_ptr
462 400 : _M_begin() const
463 400 : { return static_cast<__node_ptr>(_M_before_begin._M_nxt); }
464 :
465 : // Assign *this using another _Hashtable instance. Whether elements
466 : // are copied or moved depends on the _Ht reference.
467 : template<typename _Ht>
468 : void
469 : _M_assign_elements(_Ht&&);
470 :
471 : template<typename _Ht, typename _NodeGenerator>
472 : void
473 : _M_assign(_Ht&&, const _NodeGenerator&);
474 :
475 : void
476 : _M_move_assign(_Hashtable&&, true_type);
477 :
478 : void
479 : _M_move_assign(_Hashtable&&, false_type);
480 :
481 : void
482 : _M_reset() noexcept;
483 :
484 : _Hashtable(const _Hash& __h, const _Equal& __eq,
485 : const allocator_type& __a)
486 : : __hashtable_base(__h, __eq),
487 : __hashtable_alloc(__node_alloc_type(__a)),
488 : __enable_default_ctor(_Enable_default_constructor_tag{})
489 : { }
490 :
491 : template<bool _No_realloc = true>
492 : static constexpr bool
493 : _S_nothrow_move()
494 : {
495 : #if __cplusplus <= 201402L
496 : return __and_<__bool_constant<_No_realloc>,
497 : is_nothrow_copy_constructible<_Hash>,
498 : is_nothrow_copy_constructible<_Equal>>::value;
499 : #else
500 : if constexpr (_No_realloc)
501 : if constexpr (is_nothrow_copy_constructible<_Hash>())
502 : return is_nothrow_copy_constructible<_Equal>();
503 : return false;
504 : #endif
505 : }
506 :
507 : _Hashtable(_Hashtable&& __ht, __node_alloc_type&& __a,
508 : true_type /* alloc always equal */)
509 : noexcept(_S_nothrow_move());
510 :
511 : _Hashtable(_Hashtable&&, __node_alloc_type&&,
512 : false_type /* alloc always equal */);
513 :
514 : template<typename _InputIterator>
515 : _Hashtable(_InputIterator __first, _InputIterator __last,
516 : size_type __bkt_count_hint,
517 : const _Hash&, const _Equal&, const allocator_type&,
518 : true_type __uks);
519 :
520 : template<typename _InputIterator>
521 : _Hashtable(_InputIterator __first, _InputIterator __last,
522 : size_type __bkt_count_hint,
523 : const _Hash&, const _Equal&, const allocator_type&,
524 : false_type __uks);
525 :
526 : public:
527 : // Constructor, destructor, assignment, swap
528 286 : _Hashtable() = default;
529 :
530 : _Hashtable(const _Hashtable&);
531 :
532 : _Hashtable(const _Hashtable&, const allocator_type&);
533 :
534 : explicit
535 : _Hashtable(size_type __bkt_count_hint,
536 : const _Hash& __hf = _Hash(),
537 : const key_equal& __eql = key_equal(),
538 : const allocator_type& __a = allocator_type());
539 :
540 : // Use delegating constructors.
541 : _Hashtable(_Hashtable&& __ht)
542 : noexcept(_S_nothrow_move())
543 : : _Hashtable(std::move(__ht), std::move(__ht._M_node_allocator()),
544 : true_type{})
545 : { }
546 :
547 : _Hashtable(_Hashtable&& __ht, const allocator_type& __a)
548 : noexcept(_S_nothrow_move<__node_alloc_traits::_S_always_equal()>())
549 : : _Hashtable(std::move(__ht), __node_alloc_type(__a),
550 : typename __node_alloc_traits::is_always_equal{})
551 : { }
552 :
553 : explicit
554 : _Hashtable(const allocator_type& __a)
555 : : __hashtable_alloc(__node_alloc_type(__a)),
556 : __enable_default_ctor(_Enable_default_constructor_tag{})
557 : { }
558 :
559 : template<typename _InputIterator>
560 : _Hashtable(_InputIterator __f, _InputIterator __l,
561 : size_type __bkt_count_hint = 0,
562 : const _Hash& __hf = _Hash(),
563 : const key_equal& __eql = key_equal(),
564 : const allocator_type& __a = allocator_type())
565 : : _Hashtable(__f, __l, __bkt_count_hint, __hf, __eql, __a,
566 : __unique_keys{})
567 : { }
568 :
569 : _Hashtable(initializer_list<value_type> __l,
570 : size_type __bkt_count_hint = 0,
571 : const _Hash& __hf = _Hash(),
572 : const key_equal& __eql = key_equal(),
573 : const allocator_type& __a = allocator_type())
574 : : _Hashtable(__l.begin(), __l.end(), __bkt_count_hint,
575 : __hf, __eql, __a, __unique_keys{})
576 : { }
577 :
578 : _Hashtable&
579 : operator=(const _Hashtable& __ht);
580 :
581 : _Hashtable&
582 : operator=(_Hashtable&& __ht)
583 : noexcept(__node_alloc_traits::_S_nothrow_move()
584 : && is_nothrow_move_assignable<_Hash>::value
585 : && is_nothrow_move_assignable<_Equal>::value)
586 : {
587 : constexpr bool __move_storage =
588 : __node_alloc_traits::_S_propagate_on_move_assign()
589 : || __node_alloc_traits::_S_always_equal();
590 : _M_move_assign(std::move(__ht), __bool_constant<__move_storage>());
591 : return *this;
592 : }
593 :
594 : _Hashtable&
595 : operator=(initializer_list<value_type> __l)
596 : {
597 : __reuse_or_alloc_node_gen_t __roan(_M_begin(), *this);
598 : _M_before_begin._M_nxt = nullptr;
599 : clear();
600 :
601 : // We consider that all elements of __l are going to be inserted.
602 : auto __l_bkt_count = _M_rehash_policy._M_bkt_for_elements(__l.size());
603 :
604 : // Do not shrink to keep potential user reservation.
605 : if (_M_bucket_count < __l_bkt_count)
606 : rehash(__l_bkt_count);
607 :
608 : this->_M_insert_range(__l.begin(), __l.end(), __roan, __unique_keys{});
609 : return *this;
610 : }
611 :
612 : ~_Hashtable() noexcept;
613 :
614 : void
615 : swap(_Hashtable&)
616 : noexcept(__and_<__is_nothrow_swappable<_Hash>,
617 : __is_nothrow_swappable<_Equal>>::value);
618 :
619 : // Basic container operations
620 : iterator
621 : begin() noexcept
622 : { return iterator(_M_begin()); }
623 :
624 : const_iterator
625 : begin() const noexcept
626 : { return const_iterator(_M_begin()); }
627 :
628 : iterator
629 2 : end() noexcept
630 2 : { return iterator(nullptr); }
631 :
632 : const_iterator
633 : end() const noexcept
634 : { return const_iterator(nullptr); }
635 :
636 : const_iterator
637 : cbegin() const noexcept
638 : { return const_iterator(_M_begin()); }
639 :
640 : const_iterator
641 : cend() const noexcept
642 : { return const_iterator(nullptr); }
643 :
644 : size_type
645 : size() const noexcept
646 : { return _M_element_count; }
647 :
648 : _GLIBCXX_NODISCARD bool
649 : empty() const noexcept
650 : { return size() == 0; }
651 :
652 : allocator_type
653 : get_allocator() const noexcept
654 : { return allocator_type(this->_M_node_allocator()); }
655 :
656 : size_type
657 : max_size() const noexcept
658 : { return __node_alloc_traits::max_size(this->_M_node_allocator()); }
659 :
660 : // Observers
661 : key_equal
662 : key_eq() const
663 : { return this->_M_eq(); }
664 :
665 : // hash_function, if present, comes from _Hash_code_base.
666 :
667 : // Bucket operations
668 : size_type
669 : bucket_count() const noexcept
670 : { return _M_bucket_count; }
671 :
672 : size_type
673 : max_bucket_count() const noexcept
674 : { return max_size(); }
675 :
676 : size_type
677 : bucket_size(size_type __bkt) const
678 : { return std::distance(begin(__bkt), end(__bkt)); }
679 :
680 : size_type
681 : bucket(const key_type& __k) const
682 : { return _M_bucket_index(this->_M_hash_code(__k)); }
683 :
684 : local_iterator
685 : begin(size_type __bkt)
686 : {
687 : return local_iterator(*this, _M_bucket_begin(__bkt),
688 : __bkt, _M_bucket_count);
689 : }
690 :
691 : local_iterator
692 : end(size_type __bkt)
693 : { return local_iterator(*this, nullptr, __bkt, _M_bucket_count); }
694 :
695 : const_local_iterator
696 : begin(size_type __bkt) const
697 : {
698 : return const_local_iterator(*this, _M_bucket_begin(__bkt),
699 : __bkt, _M_bucket_count);
700 : }
701 :
702 : const_local_iterator
703 : end(size_type __bkt) const
704 : { return const_local_iterator(*this, nullptr, __bkt, _M_bucket_count); }
705 :
706 : // DR 691.
707 : const_local_iterator
708 : cbegin(size_type __bkt) const
709 : {
710 : return const_local_iterator(*this, _M_bucket_begin(__bkt),
711 : __bkt, _M_bucket_count);
712 : }
713 :
714 : const_local_iterator
715 : cend(size_type __bkt) const
716 : { return const_local_iterator(*this, nullptr, __bkt, _M_bucket_count); }
717 :
718 : float
719 : load_factor() const noexcept
720 : {
721 : return static_cast<float>(size()) / static_cast<float>(bucket_count());
722 : }
723 :
724 : // max_load_factor, if present, comes from _Rehash_base.
725 :
726 : // Generalization of max_load_factor. Extension, not found in
727 : // TR1. Only useful if _RehashPolicy is something other than
728 : // the default.
729 : const _RehashPolicy&
730 : __rehash_policy() const
731 : { return _M_rehash_policy; }
732 :
733 : void
734 : __rehash_policy(const _RehashPolicy& __pol)
735 : { _M_rehash_policy = __pol; }
736 :
737 : // Lookup.
738 : iterator
739 : find(const key_type& __k);
740 :
741 : const_iterator
742 : find(const key_type& __k) const;
743 :
744 : size_type
745 : count(const key_type& __k) const;
746 :
747 : std::pair<iterator, iterator>
748 : equal_range(const key_type& __k);
749 :
750 : std::pair<const_iterator, const_iterator>
751 : equal_range(const key_type& __k) const;
752 :
753 : #if __cplusplus >= 202002L
754 : #define __cpp_lib_generic_unordered_lookup 201811L
755 :
756 : template<typename _Kt,
757 : typename = __has_is_transparent_t<_Hash, _Kt>,
758 : typename = __has_is_transparent_t<_Equal, _Kt>>
759 : iterator
760 : _M_find_tr(const _Kt& __k);
761 :
762 : template<typename _Kt,
763 : typename = __has_is_transparent_t<_Hash, _Kt>,
764 : typename = __has_is_transparent_t<_Equal, _Kt>>
765 : const_iterator
766 : _M_find_tr(const _Kt& __k) const;
767 :
768 : template<typename _Kt,
769 : typename = __has_is_transparent_t<_Hash, _Kt>,
770 : typename = __has_is_transparent_t<_Equal, _Kt>>
771 : size_type
772 : _M_count_tr(const _Kt& __k) const;
773 :
774 : template<typename _Kt,
775 : typename = __has_is_transparent_t<_Hash, _Kt>,
776 : typename = __has_is_transparent_t<_Equal, _Kt>>
777 : pair<iterator, iterator>
778 : _M_equal_range_tr(const _Kt& __k);
779 :
780 : template<typename _Kt,
781 : typename = __has_is_transparent_t<_Hash, _Kt>,
782 : typename = __has_is_transparent_t<_Equal, _Kt>>
783 : pair<const_iterator, const_iterator>
784 : _M_equal_range_tr(const _Kt& __k) const;
785 : #endif // C++20
786 :
787 : private:
788 : // Bucket index computation helpers.
789 : size_type
790 370 : _M_bucket_index(const __node_value_type& __n) const noexcept
791 370 : { return __hash_code_base::_M_bucket_index(__n, _M_bucket_count); }
792 :
793 : size_type
794 854 : _M_bucket_index(__hash_code __c) const
795 854 : { return __hash_code_base::_M_bucket_index(__c, _M_bucket_count); }
796 :
797 : // Find and insert helper functions and types
798 : // Find the node before the one matching the criteria.
799 : __node_base_ptr
800 : _M_find_before_node(size_type, const key_type&, __hash_code) const;
801 :
802 : template<typename _Kt>
803 : __node_base_ptr
804 : _M_find_before_node_tr(size_type, const _Kt&, __hash_code) const;
805 :
806 : __node_ptr
807 740 : _M_find_node(size_type __bkt, const key_type& __key,
808 : __hash_code __c) const
809 : {
810 740 : __node_base_ptr __before_n = _M_find_before_node(__bkt, __key, __c);
811 740 : if (__before_n)
812 314 : return static_cast<__node_ptr>(__before_n->_M_nxt);
813 426 : return nullptr;
814 : }
815 :
816 : template<typename _Kt>
817 : __node_ptr
818 : _M_find_node_tr(size_type __bkt, const _Kt& __key,
819 : __hash_code __c) const
820 : {
821 : auto __before_n = _M_find_before_node_tr(__bkt, __key, __c);
822 : if (__before_n)
823 : return static_cast<__node_ptr>(__before_n->_M_nxt);
824 : return nullptr;
825 : }
826 :
827 : // Insert a node at the beginning of a bucket.
828 : void
829 : _M_insert_bucket_begin(size_type, __node_ptr);
830 :
831 : // Remove the bucket first node
832 : void
833 : _M_remove_bucket_begin(size_type __bkt, __node_ptr __next_n,
834 : size_type __next_bkt);
835 :
836 : // Get the node before __n in the bucket __bkt
837 : __node_base_ptr
838 : _M_get_previous_node(size_type __bkt, __node_ptr __n);
839 :
840 : // Insert node __n with hash code __code, in bucket __bkt if no
841 : // rehash (assumes no element with same key already present).
842 : // Takes ownership of __n if insertion succeeds, throws otherwise.
843 : iterator
844 : _M_insert_unique_node(size_type __bkt, __hash_code,
845 : __node_ptr __n, size_type __n_elt = 1);
846 :
847 : // Insert node __n with key __k and hash code __code.
848 : // Takes ownership of __n if insertion succeeds, throws otherwise.
849 : iterator
850 : _M_insert_multi_node(__node_ptr __hint,
851 : __hash_code __code, __node_ptr __n);
852 :
853 : template<typename... _Args>
854 : std::pair<iterator, bool>
855 : _M_emplace(true_type __uks, _Args&&... __args);
856 :
857 : template<typename... _Args>
858 : iterator
859 : _M_emplace(false_type __uks, _Args&&... __args)
860 : { return _M_emplace(cend(), __uks, std::forward<_Args>(__args)...); }
861 :
862 : // Emplace with hint, useless when keys are unique.
863 : template<typename... _Args>
864 : iterator
865 : _M_emplace(const_iterator, true_type __uks, _Args&&... __args)
866 : { return _M_emplace(__uks, std::forward<_Args>(__args)...).first; }
867 :
868 : template<typename... _Args>
869 : iterator
870 : _M_emplace(const_iterator, false_type __uks, _Args&&... __args);
871 :
872 : template<typename _Arg, typename _NodeGenerator>
873 : std::pair<iterator, bool>
874 : _M_insert(_Arg&&, const _NodeGenerator&, true_type __uks);
875 :
876 : template<typename _Arg, typename _NodeGenerator>
877 : iterator
878 : _M_insert(_Arg&& __arg, const _NodeGenerator& __node_gen,
879 : false_type __uks)
880 : {
881 : return _M_insert(cend(), std::forward<_Arg>(__arg), __node_gen,
882 : __uks);
883 : }
884 :
885 : // Insert with hint, not used when keys are unique.
886 : template<typename _Arg, typename _NodeGenerator>
887 : iterator
888 : _M_insert(const_iterator, _Arg&& __arg,
889 : const _NodeGenerator& __node_gen, true_type __uks)
890 : {
891 : return
892 : _M_insert(std::forward<_Arg>(__arg), __node_gen, __uks).first;
893 : }
894 :
895 : // Insert with hint when keys are not unique.
896 : template<typename _Arg, typename _NodeGenerator>
897 : iterator
898 : _M_insert(const_iterator, _Arg&&,
899 : const _NodeGenerator&, false_type __uks);
900 :
901 : size_type
902 : _M_erase(true_type __uks, const key_type&);
903 :
904 : size_type
905 : _M_erase(false_type __uks, const key_type&);
906 :
907 : iterator
908 : _M_erase(size_type __bkt, __node_base_ptr __prev_n, __node_ptr __n);
909 :
910 : public:
911 : // Emplace
912 : template<typename... _Args>
913 : __ireturn_type
914 738 : emplace(_Args&&... __args)
915 738 : { return _M_emplace(__unique_keys{}, std::forward<_Args>(__args)...); }
916 :
917 : template<typename... _Args>
918 : iterator
919 : emplace_hint(const_iterator __hint, _Args&&... __args)
920 : {
921 : return _M_emplace(__hint, __unique_keys{},
922 : std::forward<_Args>(__args)...);
923 : }
924 :
925 : // Insert member functions via inheritance.
926 :
927 : // Erase
928 : iterator
929 : erase(const_iterator);
930 :
931 : // LWG 2059.
932 : iterator
933 : erase(iterator __it)
934 : { return erase(const_iterator(__it)); }
935 :
936 : size_type
937 : erase(const key_type& __k)
938 : { return _M_erase(__unique_keys{}, __k); }
939 :
940 : iterator
941 : erase(const_iterator, const_iterator);
942 :
943 : void
944 : clear() noexcept;
945 :
946 : // Set number of buckets keeping it appropriate for container's number
947 : // of elements.
948 : void rehash(size_type __bkt_count);
949 :
950 : // DR 1189.
951 : // reserve, if present, comes from _Rehash_base.
952 :
953 : #if __cplusplus > 201402L
954 : /// Re-insert an extracted node into a container with unique keys.
955 : insert_return_type
956 : _M_reinsert_node(node_type&& __nh)
957 : {
958 : insert_return_type __ret;
959 : if (__nh.empty())
960 : __ret.position = end();
961 : else
962 : {
963 : __glibcxx_assert(get_allocator() == __nh.get_allocator());
964 :
965 : const key_type& __k = __nh._M_key();
966 : __hash_code __code = this->_M_hash_code(__k);
967 : size_type __bkt = _M_bucket_index(__code);
968 : if (__node_ptr __n = _M_find_node(__bkt, __k, __code))
969 : {
970 : __ret.node = std::move(__nh);
971 : __ret.position = iterator(__n);
972 : __ret.inserted = false;
973 : }
974 : else
975 : {
976 : __ret.position
977 : = _M_insert_unique_node(__bkt, __code, __nh._M_ptr);
978 : __nh._M_ptr = nullptr;
979 : __ret.inserted = true;
980 : }
981 : }
982 : return __ret;
983 : }
984 :
985 : /// Re-insert an extracted node into a container with equivalent keys.
986 : iterator
987 : _M_reinsert_node_multi(const_iterator __hint, node_type&& __nh)
988 : {
989 : if (__nh.empty())
990 : return end();
991 :
992 : __glibcxx_assert(get_allocator() == __nh.get_allocator());
993 :
994 : const key_type& __k = __nh._M_key();
995 : auto __code = this->_M_hash_code(__k);
996 : auto __ret
997 : = _M_insert_multi_node(__hint._M_cur, __code, __nh._M_ptr);
998 : __nh._M_ptr = nullptr;
999 : return __ret;
1000 : }
1001 :
1002 : private:
1003 : node_type
1004 : _M_extract_node(size_t __bkt, __node_base_ptr __prev_n)
1005 : {
1006 : __node_ptr __n = static_cast<__node_ptr>(__prev_n->_M_nxt);
1007 : if (__prev_n == _M_buckets[__bkt])
1008 : _M_remove_bucket_begin(__bkt, __n->_M_next(),
1009 : __n->_M_nxt ? _M_bucket_index(*__n->_M_next()) : 0);
1010 : else if (__n->_M_nxt)
1011 : {
1012 : size_type __next_bkt = _M_bucket_index(*__n->_M_next());
1013 : if (__next_bkt != __bkt)
1014 : _M_buckets[__next_bkt] = __prev_n;
1015 : }
1016 :
1017 : __prev_n->_M_nxt = __n->_M_nxt;
1018 : __n->_M_nxt = nullptr;
1019 : --_M_element_count;
1020 : return { __n, this->_M_node_allocator() };
1021 : }
1022 :
1023 : public:
1024 : // Extract a node.
1025 : node_type
1026 : extract(const_iterator __pos)
1027 : {
1028 : size_t __bkt = _M_bucket_index(*__pos._M_cur);
1029 : return _M_extract_node(__bkt,
1030 : _M_get_previous_node(__bkt, __pos._M_cur));
1031 : }
1032 :
1033 : /// Extract a node.
1034 : node_type
1035 : extract(const _Key& __k)
1036 : {
1037 : node_type __nh;
1038 : __hash_code __code = this->_M_hash_code(__k);
1039 : std::size_t __bkt = _M_bucket_index(__code);
1040 : if (__node_base_ptr __prev_node = _M_find_before_node(__bkt, __k, __code))
1041 : __nh = _M_extract_node(__bkt, __prev_node);
1042 : return __nh;
1043 : }
1044 :
1045 : /// Merge from a compatible container into one with unique keys.
1046 : template<typename _Compatible_Hashtable>
1047 : void
1048 : _M_merge_unique(_Compatible_Hashtable& __src) noexcept
1049 : {
1050 : static_assert(is_same_v<typename _Compatible_Hashtable::node_type,
1051 : node_type>, "Node types are compatible");
1052 : __glibcxx_assert(get_allocator() == __src.get_allocator());
1053 :
1054 : auto __n_elt = __src.size();
1055 : for (auto __i = __src.begin(), __end = __src.end(); __i != __end;)
1056 : {
1057 : auto __pos = __i++;
1058 : const key_type& __k = _ExtractKey{}(*__pos);
1059 : __hash_code __code = this->_M_hash_code(__k);
1060 : size_type __bkt = _M_bucket_index(__code);
1061 : if (_M_find_node(__bkt, __k, __code) == nullptr)
1062 : {
1063 : auto __nh = __src.extract(__pos);
1064 : _M_insert_unique_node(__bkt, __code, __nh._M_ptr, __n_elt);
1065 : __nh._M_ptr = nullptr;
1066 : __n_elt = 1;
1067 : }
1068 : else if (__n_elt != 1)
1069 : --__n_elt;
1070 : }
1071 : }
1072 :
1073 : /// Merge from a compatible container into one with equivalent keys.
1074 : template<typename _Compatible_Hashtable>
1075 : void
1076 : _M_merge_multi(_Compatible_Hashtable& __src) noexcept
1077 : {
1078 : static_assert(is_same_v<typename _Compatible_Hashtable::node_type,
1079 : node_type>, "Node types are compatible");
1080 : __glibcxx_assert(get_allocator() == __src.get_allocator());
1081 :
1082 : this->reserve(size() + __src.size());
1083 : for (auto __i = __src.begin(), __end = __src.end(); __i != __end;)
1084 : _M_reinsert_node_multi(cend(), __src.extract(__i++));
1085 : }
1086 : #endif // C++17
1087 :
1088 : private:
1089 : // Helper rehash method used when keys are unique.
1090 : void _M_rehash_aux(size_type __bkt_count, true_type __uks);
1091 :
1092 : // Helper rehash method used when keys can be non-unique.
1093 : void _M_rehash_aux(size_type __bkt_count, false_type __uks);
1094 :
1095 : // Unconditionally change size of bucket array to n, restore
1096 : // hash policy state to __state on exception.
1097 : void _M_rehash(size_type __bkt_count, const __rehash_state& __state);
1098 : };
1099 :
1100 :
1101 : // Definitions of class template _Hashtable's out-of-line member functions.
1102 : template<typename _Key, typename _Value, typename _Alloc,
1103 : typename _ExtractKey, typename _Equal,
1104 : typename _Hash, typename _RangeHash, typename _Unused,
1105 : typename _RehashPolicy, typename _Traits>
1106 : auto
1107 : _Hashtable<_Key, _Value, _Alloc, _ExtractKey, _Equal,
1108 : _Hash, _RangeHash, _Unused, _RehashPolicy, _Traits>::
1109 : _M_bucket_begin(size_type __bkt) const
1110 : -> __node_ptr
1111 : {
1112 : __node_base_ptr __n = _M_buckets[__bkt];
1113 : return __n ? static_cast<__node_ptr>(__n->_M_nxt) : nullptr;
1114 : }
1115 :
1116 : template<typename _Key, typename _Value, typename _Alloc,
1117 : typename _ExtractKey, typename _Equal,
1118 : typename _Hash, typename _RangeHash, typename _Unused,
1119 : typename _RehashPolicy, typename _Traits>
1120 : _Hashtable<_Key, _Value, _Alloc, _ExtractKey, _Equal,
1121 : _Hash, _RangeHash, _Unused, _RehashPolicy, _Traits>::
1122 : _Hashtable(size_type __bkt_count_hint,
1123 : const _Hash& __h, const _Equal& __eq, const allocator_type& __a)
1124 : : _Hashtable(__h, __eq, __a)
1125 : {
1126 : auto __bkt_count = _M_rehash_policy._M_next_bkt(__bkt_count_hint);
1127 : if (__bkt_count > _M_bucket_count)
1128 : {
1129 : _M_buckets = _M_allocate_buckets(__bkt_count);
1130 : _M_bucket_count = __bkt_count;
1131 : }
1132 : }
1133 :
1134 : template<typename _Key, typename _Value, typename _Alloc,
1135 : typename _ExtractKey, typename _Equal,
1136 : typename _Hash, typename _RangeHash, typename _Unused,
1137 : typename _RehashPolicy, typename _Traits>
1138 : template<typename _InputIterator>
1139 : _Hashtable<_Key, _Value, _Alloc, _ExtractKey, _Equal,
1140 : _Hash, _RangeHash, _Unused, _RehashPolicy, _Traits>::
1141 : _Hashtable(_InputIterator __f, _InputIterator __l,
1142 : size_type __bkt_count_hint,
1143 : const _Hash& __h, const _Equal& __eq,
1144 : const allocator_type& __a, true_type /* __uks */)
1145 : : _Hashtable(__bkt_count_hint, __h, __eq, __a)
1146 : {
1147 : for (; __f != __l; ++__f)
1148 : this->insert(*__f);
1149 : }
1150 :
1151 : template<typename _Key, typename _Value, typename _Alloc,
1152 : typename _ExtractKey, typename _Equal,
1153 : typename _Hash, typename _RangeHash, typename _Unused,
1154 : typename _RehashPolicy, typename _Traits>
1155 : template<typename _InputIterator>
1156 : _Hashtable<_Key, _Value, _Alloc, _ExtractKey, _Equal,
1157 : _Hash, _RangeHash, _Unused, _RehashPolicy, _Traits>::
1158 : _Hashtable(_InputIterator __f, _InputIterator __l,
1159 : size_type __bkt_count_hint,
1160 : const _Hash& __h, const _Equal& __eq,
1161 : const allocator_type& __a, false_type /* __uks */)
1162 : : _Hashtable(__h, __eq, __a)
1163 : {
1164 : auto __nb_elems = __detail::__distance_fw(__f, __l);
1165 : auto __bkt_count =
1166 : _M_rehash_policy._M_next_bkt(
1167 : std::max(_M_rehash_policy._M_bkt_for_elements(__nb_elems),
1168 : __bkt_count_hint));
1169 :
1170 : if (__bkt_count > _M_bucket_count)
1171 : {
1172 : _M_buckets = _M_allocate_buckets(__bkt_count);
1173 : _M_bucket_count = __bkt_count;
1174 : }
1175 :
1176 : for (; __f != __l; ++__f)
1177 : this->insert(*__f);
1178 : }
1179 :
1180 : template<typename _Key, typename _Value, typename _Alloc,
1181 : typename _ExtractKey, typename _Equal,
1182 : typename _Hash, typename _RangeHash, typename _Unused,
1183 : typename _RehashPolicy, typename _Traits>
1184 : auto
1185 : _Hashtable<_Key, _Value, _Alloc, _ExtractKey, _Equal,
1186 : _Hash, _RangeHash, _Unused, _RehashPolicy, _Traits>::
1187 : operator=(const _Hashtable& __ht)
1188 : -> _Hashtable&
1189 : {
1190 : if (&__ht == this)
1191 : return *this;
1192 :
1193 : if (__node_alloc_traits::_S_propagate_on_copy_assign())
1194 : {
1195 : auto& __this_alloc = this->_M_node_allocator();
1196 : auto& __that_alloc = __ht._M_node_allocator();
1197 : if (!__node_alloc_traits::_S_always_equal()
1198 : && __this_alloc != __that_alloc)
1199 : {
1200 : // Replacement allocator cannot free existing storage.
1201 : this->_M_deallocate_nodes(_M_begin());
1202 : _M_before_begin._M_nxt = nullptr;
1203 : _M_deallocate_buckets();
1204 : _M_buckets = nullptr;
1205 : std::__alloc_on_copy(__this_alloc, __that_alloc);
1206 : __hashtable_base::operator=(__ht);
1207 : _M_bucket_count = __ht._M_bucket_count;
1208 : _M_element_count = __ht._M_element_count;
1209 : _M_rehash_policy = __ht._M_rehash_policy;
1210 : __alloc_node_gen_t __alloc_node_gen(*this);
1211 : __try
1212 : {
1213 : _M_assign(__ht, __alloc_node_gen);
1214 : }
1215 : __catch(...)
1216 : {
1217 : // _M_assign took care of deallocating all memory. Now we
1218 : // must make sure this instance remains in a usable state.
1219 : _M_reset();
1220 : __throw_exception_again;
1221 : }
1222 : return *this;
1223 : }
1224 : std::__alloc_on_copy(__this_alloc, __that_alloc);
1225 : }
1226 :
1227 : // Reuse allocated buckets and nodes.
1228 : _M_assign_elements(__ht);
1229 : return *this;
1230 : }
1231 :
1232 : template<typename _Key, typename _Value, typename _Alloc,
1233 : typename _ExtractKey, typename _Equal,
1234 : typename _Hash, typename _RangeHash, typename _Unused,
1235 : typename _RehashPolicy, typename _Traits>
1236 : template<typename _Ht>
1237 : void
1238 : _Hashtable<_Key, _Value, _Alloc, _ExtractKey, _Equal,
1239 : _Hash, _RangeHash, _Unused, _RehashPolicy, _Traits>::
1240 : _M_assign_elements(_Ht&& __ht)
1241 : {
1242 : __buckets_ptr __former_buckets = nullptr;
1243 : std::size_t __former_bucket_count = _M_bucket_count;
1244 : const __rehash_state& __former_state = _M_rehash_policy._M_state();
1245 :
1246 : if (_M_bucket_count != __ht._M_bucket_count)
1247 : {
1248 : __former_buckets = _M_buckets;
1249 : _M_buckets = _M_allocate_buckets(__ht._M_bucket_count);
1250 : _M_bucket_count = __ht._M_bucket_count;
1251 : }
1252 : else
1253 : __builtin_memset(_M_buckets, 0,
1254 : _M_bucket_count * sizeof(__node_base_ptr));
1255 :
1256 : __try
1257 : {
1258 : __hashtable_base::operator=(std::forward<_Ht>(__ht));
1259 : _M_element_count = __ht._M_element_count;
1260 : _M_rehash_policy = __ht._M_rehash_policy;
1261 : __reuse_or_alloc_node_gen_t __roan(_M_begin(), *this);
1262 : _M_before_begin._M_nxt = nullptr;
1263 : _M_assign(std::forward<_Ht>(__ht), __roan);
1264 : if (__former_buckets)
1265 : _M_deallocate_buckets(__former_buckets, __former_bucket_count);
1266 : }
1267 : __catch(...)
1268 : {
1269 : if (__former_buckets)
1270 : {
1271 : // Restore previous buckets.
1272 : _M_deallocate_buckets();
1273 : _M_rehash_policy._M_reset(__former_state);
1274 : _M_buckets = __former_buckets;
1275 : _M_bucket_count = __former_bucket_count;
1276 : }
1277 : __builtin_memset(_M_buckets, 0,
1278 : _M_bucket_count * sizeof(__node_base_ptr));
1279 : __throw_exception_again;
1280 : }
1281 : }
1282 :
1283 : template<typename _Key, typename _Value, typename _Alloc,
1284 : typename _ExtractKey, typename _Equal,
1285 : typename _Hash, typename _RangeHash, typename _Unused,
1286 : typename _RehashPolicy, typename _Traits>
1287 : template<typename _Ht, typename _NodeGenerator>
1288 : void
1289 : _Hashtable<_Key, _Value, _Alloc, _ExtractKey, _Equal,
1290 : _Hash, _RangeHash, _Unused, _RehashPolicy, _Traits>::
1291 : _M_assign(_Ht&& __ht, const _NodeGenerator& __node_gen)
1292 : {
1293 : __buckets_ptr __buckets = nullptr;
1294 : if (!_M_buckets)
1295 : _M_buckets = __buckets = _M_allocate_buckets(_M_bucket_count);
1296 :
1297 : __try
1298 : {
1299 : if (!__ht._M_before_begin._M_nxt)
1300 : return;
1301 :
1302 : // First deal with the special first node pointed to by
1303 : // _M_before_begin.
1304 : __node_ptr __ht_n = __ht._M_begin();
1305 : __node_ptr __this_n
1306 : = __node_gen(__fwd_value_for<_Ht>(__ht_n->_M_v()));
1307 : this->_M_copy_code(*__this_n, *__ht_n);
1308 : _M_update_bbegin(__this_n);
1309 :
1310 : // Then deal with other nodes.
1311 : __node_ptr __prev_n = __this_n;
1312 : for (__ht_n = __ht_n->_M_next(); __ht_n; __ht_n = __ht_n->_M_next())
1313 : {
1314 : __this_n = __node_gen(__fwd_value_for<_Ht>(__ht_n->_M_v()));
1315 : __prev_n->_M_nxt = __this_n;
1316 : this->_M_copy_code(*__this_n, *__ht_n);
1317 : size_type __bkt = _M_bucket_index(*__this_n);
1318 : if (!_M_buckets[__bkt])
1319 : _M_buckets[__bkt] = __prev_n;
1320 : __prev_n = __this_n;
1321 : }
1322 : }
1323 : __catch(...)
1324 : {
1325 : clear();
1326 : if (__buckets)
1327 : _M_deallocate_buckets();
1328 : __throw_exception_again;
1329 : }
1330 : }
1331 :
1332 : template<typename _Key, typename _Value, typename _Alloc,
1333 : typename _ExtractKey, typename _Equal,
1334 : typename _Hash, typename _RangeHash, typename _Unused,
1335 : typename _RehashPolicy, typename _Traits>
1336 : void
1337 : _Hashtable<_Key, _Value, _Alloc, _ExtractKey, _Equal,
1338 : _Hash, _RangeHash, _Unused, _RehashPolicy, _Traits>::
1339 : _M_reset() noexcept
1340 : {
1341 : _M_rehash_policy._M_reset();
1342 : _M_bucket_count = 1;
1343 : _M_single_bucket = nullptr;
1344 : _M_buckets = &_M_single_bucket;
1345 : _M_before_begin._M_nxt = nullptr;
1346 : _M_element_count = 0;
1347 : }
1348 :
1349 : template<typename _Key, typename _Value, typename _Alloc,
1350 : typename _ExtractKey, typename _Equal,
1351 : typename _Hash, typename _RangeHash, typename _Unused,
1352 : typename _RehashPolicy, typename _Traits>
1353 : void
1354 : _Hashtable<_Key, _Value, _Alloc, _ExtractKey, _Equal,
1355 : _Hash, _RangeHash, _Unused, _RehashPolicy, _Traits>::
1356 : _M_move_assign(_Hashtable&& __ht, true_type)
1357 : {
1358 : if (__builtin_expect(std::__addressof(__ht) == this, false))
1359 : return;
1360 :
1361 : this->_M_deallocate_nodes(_M_begin());
1362 : _M_deallocate_buckets();
1363 : __hashtable_base::operator=(std::move(__ht));
1364 : _M_rehash_policy = __ht._M_rehash_policy;
1365 : if (!__ht._M_uses_single_bucket())
1366 : _M_buckets = __ht._M_buckets;
1367 : else
1368 : {
1369 : _M_buckets = &_M_single_bucket;
1370 : _M_single_bucket = __ht._M_single_bucket;
1371 : }
1372 :
1373 : _M_bucket_count = __ht._M_bucket_count;
1374 : _M_before_begin._M_nxt = __ht._M_before_begin._M_nxt;
1375 : _M_element_count = __ht._M_element_count;
1376 : std::__alloc_on_move(this->_M_node_allocator(), __ht._M_node_allocator());
1377 :
1378 : // Fix bucket containing the _M_before_begin pointer that can't be moved.
1379 : _M_update_bbegin();
1380 : __ht._M_reset();
1381 : }
1382 :
1383 : template<typename _Key, typename _Value, typename _Alloc,
1384 : typename _ExtractKey, typename _Equal,
1385 : typename _Hash, typename _RangeHash, typename _Unused,
1386 : typename _RehashPolicy, typename _Traits>
1387 : void
1388 : _Hashtable<_Key, _Value, _Alloc, _ExtractKey, _Equal,
1389 : _Hash, _RangeHash, _Unused, _RehashPolicy, _Traits>::
1390 : _M_move_assign(_Hashtable&& __ht, false_type)
1391 : {
1392 : if (__ht._M_node_allocator() == this->_M_node_allocator())
1393 : _M_move_assign(std::move(__ht), true_type{});
1394 : else
1395 : {
1396 : // Can't move memory, move elements then.
1397 : _M_assign_elements(std::move(__ht));
1398 : __ht.clear();
1399 : }
1400 : }
1401 :
1402 : template<typename _Key, typename _Value, typename _Alloc,
1403 : typename _ExtractKey, typename _Equal,
1404 : typename _Hash, typename _RangeHash, typename _Unused,
1405 : typename _RehashPolicy, typename _Traits>
1406 : _Hashtable<_Key, _Value, _Alloc, _ExtractKey, _Equal,
1407 : _Hash, _RangeHash, _Unused, _RehashPolicy, _Traits>::
1408 : _Hashtable(const _Hashtable& __ht)
1409 : : __hashtable_base(__ht),
1410 : __map_base(__ht),
1411 : __rehash_base(__ht),
1412 : __hashtable_alloc(
1413 : __node_alloc_traits::_S_select_on_copy(__ht._M_node_allocator())),
1414 : __enable_default_ctor(__ht),
1415 : _M_buckets(nullptr),
1416 : _M_bucket_count(__ht._M_bucket_count),
1417 : _M_element_count(__ht._M_element_count),
1418 : _M_rehash_policy(__ht._M_rehash_policy)
1419 : {
1420 : __alloc_node_gen_t __alloc_node_gen(*this);
1421 : _M_assign(__ht, __alloc_node_gen);
1422 : }
1423 :
1424 : template<typename _Key, typename _Value, typename _Alloc,
1425 : typename _ExtractKey, typename _Equal,
1426 : typename _Hash, typename _RangeHash, typename _Unused,
1427 : typename _RehashPolicy, typename _Traits>
1428 : _Hashtable<_Key, _Value, _Alloc, _ExtractKey, _Equal,
1429 : _Hash, _RangeHash, _Unused, _RehashPolicy, _Traits>::
1430 : _Hashtable(_Hashtable&& __ht, __node_alloc_type&& __a,
1431 : true_type /* alloc always equal */)
1432 : noexcept(_S_nothrow_move())
1433 : : __hashtable_base(__ht),
1434 : __map_base(__ht),
1435 : __rehash_base(__ht),
1436 : __hashtable_alloc(std::move(__a)),
1437 : __enable_default_ctor(__ht),
1438 : _M_buckets(__ht._M_buckets),
1439 : _M_bucket_count(__ht._M_bucket_count),
1440 : _M_before_begin(__ht._M_before_begin._M_nxt),
1441 : _M_element_count(__ht._M_element_count),
1442 : _M_rehash_policy(__ht._M_rehash_policy)
1443 : {
1444 : // Update buckets if __ht is using its single bucket.
1445 : if (__ht._M_uses_single_bucket())
1446 : {
1447 : _M_buckets = &_M_single_bucket;
1448 : _M_single_bucket = __ht._M_single_bucket;
1449 : }
1450 :
1451 : // Fix bucket containing the _M_before_begin pointer that can't be moved.
1452 : _M_update_bbegin();
1453 :
1454 : __ht._M_reset();
1455 : }
1456 :
1457 : template<typename _Key, typename _Value, typename _Alloc,
1458 : typename _ExtractKey, typename _Equal,
1459 : typename _Hash, typename _RangeHash, typename _Unused,
1460 : typename _RehashPolicy, typename _Traits>
1461 : _Hashtable<_Key, _Value, _Alloc, _ExtractKey, _Equal,
1462 : _Hash, _RangeHash, _Unused, _RehashPolicy, _Traits>::
1463 : _Hashtable(const _Hashtable& __ht, const allocator_type& __a)
1464 : : __hashtable_base(__ht),
1465 : __map_base(__ht),
1466 : __rehash_base(__ht),
1467 : __hashtable_alloc(__node_alloc_type(__a)),
1468 : __enable_default_ctor(__ht),
1469 : _M_buckets(),
1470 : _M_bucket_count(__ht._M_bucket_count),
1471 : _M_element_count(__ht._M_element_count),
1472 : _M_rehash_policy(__ht._M_rehash_policy)
1473 : {
1474 : __alloc_node_gen_t __alloc_node_gen(*this);
1475 : _M_assign(__ht, __alloc_node_gen);
1476 : }
1477 :
1478 : template<typename _Key, typename _Value, typename _Alloc,
1479 : typename _ExtractKey, typename _Equal,
1480 : typename _Hash, typename _RangeHash, typename _Unused,
1481 : typename _RehashPolicy, typename _Traits>
1482 : _Hashtable<_Key, _Value, _Alloc, _ExtractKey, _Equal,
1483 : _Hash, _RangeHash, _Unused, _RehashPolicy, _Traits>::
1484 : _Hashtable(_Hashtable&& __ht, __node_alloc_type&& __a,
1485 : false_type /* alloc always equal */)
1486 : : __hashtable_base(__ht),
1487 : __map_base(__ht),
1488 : __rehash_base(__ht),
1489 : __hashtable_alloc(std::move(__a)),
1490 : __enable_default_ctor(__ht),
1491 : _M_buckets(nullptr),
1492 : _M_bucket_count(__ht._M_bucket_count),
1493 : _M_element_count(__ht._M_element_count),
1494 : _M_rehash_policy(__ht._M_rehash_policy)
1495 : {
1496 : if (__ht._M_node_allocator() == this->_M_node_allocator())
1497 : {
1498 : if (__ht._M_uses_single_bucket())
1499 : {
1500 : _M_buckets = &_M_single_bucket;
1501 : _M_single_bucket = __ht._M_single_bucket;
1502 : }
1503 : else
1504 : _M_buckets = __ht._M_buckets;
1505 :
1506 : // Fix bucket containing the _M_before_begin pointer that can't be
1507 : // moved.
1508 : _M_update_bbegin(__ht._M_begin());
1509 :
1510 : __ht._M_reset();
1511 : }
1512 : else
1513 : {
1514 : __alloc_node_gen_t __alloc_gen(*this);
1515 :
1516 : using _Fwd_Ht = typename
1517 : conditional<__move_if_noexcept_cond<value_type>::value,
1518 : const _Hashtable&, _Hashtable&&>::type;
1519 : _M_assign(std::forward<_Fwd_Ht>(__ht), __alloc_gen);
1520 : __ht.clear();
1521 : }
1522 : }
1523 :
1524 : template<typename _Key, typename _Value, typename _Alloc,
1525 : typename _ExtractKey, typename _Equal,
1526 : typename _Hash, typename _RangeHash, typename _Unused,
1527 : typename _RehashPolicy, typename _Traits>
1528 286 : _Hashtable<_Key, _Value, _Alloc, _ExtractKey, _Equal,
1529 : _Hash, _RangeHash, _Unused, _RehashPolicy, _Traits>::
1530 : ~_Hashtable() noexcept
1531 : {
1532 286 : clear();
1533 286 : _M_deallocate_buckets();
1534 286 : }
1535 :
1536 : template<typename _Key, typename _Value, typename _Alloc,
1537 : typename _ExtractKey, typename _Equal,
1538 : typename _Hash, typename _RangeHash, typename _Unused,
1539 : typename _RehashPolicy, typename _Traits>
1540 : void
1541 : _Hashtable<_Key, _Value, _Alloc, _ExtractKey, _Equal,
1542 : _Hash, _RangeHash, _Unused, _RehashPolicy, _Traits>::
1543 : swap(_Hashtable& __x)
1544 : noexcept(__and_<__is_nothrow_swappable<_Hash>,
1545 : __is_nothrow_swappable<_Equal>>::value)
1546 : {
1547 : // The only base class with member variables is hash_code_base.
1548 : // We define _Hash_code_base::_M_swap because different
1549 : // specializations have different members.
1550 : this->_M_swap(__x);
1551 :
1552 : std::__alloc_on_swap(this->_M_node_allocator(), __x._M_node_allocator());
1553 : std::swap(_M_rehash_policy, __x._M_rehash_policy);
1554 :
1555 : // Deal properly with potentially moved instances.
1556 : if (this->_M_uses_single_bucket())
1557 : {
1558 : if (!__x._M_uses_single_bucket())
1559 : {
1560 : _M_buckets = __x._M_buckets;
1561 : __x._M_buckets = &__x._M_single_bucket;
1562 : }
1563 : }
1564 : else if (__x._M_uses_single_bucket())
1565 : {
1566 : __x._M_buckets = _M_buckets;
1567 : _M_buckets = &_M_single_bucket;
1568 : }
1569 : else
1570 : std::swap(_M_buckets, __x._M_buckets);
1571 :
1572 : std::swap(_M_bucket_count, __x._M_bucket_count);
1573 : std::swap(_M_before_begin._M_nxt, __x._M_before_begin._M_nxt);
1574 : std::swap(_M_element_count, __x._M_element_count);
1575 : std::swap(_M_single_bucket, __x._M_single_bucket);
1576 :
1577 : // Fix buckets containing the _M_before_begin pointers that can't be
1578 : // swapped.
1579 : _M_update_bbegin();
1580 : __x._M_update_bbegin();
1581 : }
1582 :
1583 : template<typename _Key, typename _Value, typename _Alloc,
1584 : typename _ExtractKey, typename _Equal,
1585 : typename _Hash, typename _RangeHash, typename _Unused,
1586 : typename _RehashPolicy, typename _Traits>
1587 : auto
1588 2 : _Hashtable<_Key, _Value, _Alloc, _ExtractKey, _Equal,
1589 : _Hash, _RangeHash, _Unused, _RehashPolicy, _Traits>::
1590 : find(const key_type& __k)
1591 : -> iterator
1592 : {
1593 2 : __hash_code __code = this->_M_hash_code(__k);
1594 2 : std::size_t __bkt = _M_bucket_index(__code);
1595 2 : return iterator(_M_find_node(__bkt, __k, __code));
1596 : }
1597 :
1598 : template<typename _Key, typename _Value, typename _Alloc,
1599 : typename _ExtractKey, typename _Equal,
1600 : typename _Hash, typename _RangeHash, typename _Unused,
1601 : typename _RehashPolicy, typename _Traits>
1602 : auto
1603 : _Hashtable<_Key, _Value, _Alloc, _ExtractKey, _Equal,
1604 : _Hash, _RangeHash, _Unused, _RehashPolicy, _Traits>::
1605 : find(const key_type& __k) const
1606 : -> const_iterator
1607 : {
1608 : __hash_code __code = this->_M_hash_code(__k);
1609 : std::size_t __bkt = _M_bucket_index(__code);
1610 : return const_iterator(_M_find_node(__bkt, __k, __code));
1611 : }
1612 :
1613 : #if __cplusplus > 201703L
1614 : template<typename _Key, typename _Value, typename _Alloc,
1615 : typename _ExtractKey, typename _Equal,
1616 : typename _Hash, typename _RangeHash, typename _Unused,
1617 : typename _RehashPolicy, typename _Traits>
1618 : template<typename _Kt, typename, typename>
1619 : auto
1620 : _Hashtable<_Key, _Value, _Alloc, _ExtractKey, _Equal,
1621 : _Hash, _RangeHash, _Unused, _RehashPolicy, _Traits>::
1622 : _M_find_tr(const _Kt& __k)
1623 : -> iterator
1624 : {
1625 : __hash_code __code = this->_M_hash_code_tr(__k);
1626 : std::size_t __bkt = _M_bucket_index(__code);
1627 : return iterator(_M_find_node_tr(__bkt, __k, __code));
1628 : }
1629 :
1630 : template<typename _Key, typename _Value, typename _Alloc,
1631 : typename _ExtractKey, typename _Equal,
1632 : typename _Hash, typename _RangeHash, typename _Unused,
1633 : typename _RehashPolicy, typename _Traits>
1634 : template<typename _Kt, typename, typename>
1635 : auto
1636 : _Hashtable<_Key, _Value, _Alloc, _ExtractKey, _Equal,
1637 : _Hash, _RangeHash, _Unused, _RehashPolicy, _Traits>::
1638 : _M_find_tr(const _Kt& __k) const
1639 : -> const_iterator
1640 : {
1641 : __hash_code __code = this->_M_hash_code_tr(__k);
1642 : std::size_t __bkt = _M_bucket_index(__code);
1643 : return const_iterator(_M_find_node_tr(__bkt, __k, __code));
1644 : }
1645 : #endif
1646 :
1647 : template<typename _Key, typename _Value, typename _Alloc,
1648 : typename _ExtractKey, typename _Equal,
1649 : typename _Hash, typename _RangeHash, typename _Unused,
1650 : typename _RehashPolicy, typename _Traits>
1651 : auto
1652 : _Hashtable<_Key, _Value, _Alloc, _ExtractKey, _Equal,
1653 : _Hash, _RangeHash, _Unused, _RehashPolicy, _Traits>::
1654 : count(const key_type& __k) const
1655 : -> size_type
1656 : {
1657 : auto __it = find(__k);
1658 : if (!__it._M_cur)
1659 : return 0;
1660 :
1661 : if (__unique_keys::value)
1662 : return 1;
1663 :
1664 : // All equivalent values are next to each other, if we find a
1665 : // non-equivalent value after an equivalent one it means that we won't
1666 : // find any new equivalent value.
1667 : size_type __result = 1;
1668 : for (auto __ref = __it++;
1669 : __it._M_cur && this->_M_node_equals(*__ref._M_cur, *__it._M_cur);
1670 : ++__it)
1671 : ++__result;
1672 :
1673 : return __result;
1674 : }
1675 :
1676 : #if __cplusplus > 201703L
1677 : template<typename _Key, typename _Value, typename _Alloc,
1678 : typename _ExtractKey, typename _Equal,
1679 : typename _Hash, typename _RangeHash, typename _Unused,
1680 : typename _RehashPolicy, typename _Traits>
1681 : template<typename _Kt, typename, typename>
1682 : auto
1683 : _Hashtable<_Key, _Value, _Alloc, _ExtractKey, _Equal,
1684 : _Hash, _RangeHash, _Unused, _RehashPolicy, _Traits>::
1685 : _M_count_tr(const _Kt& __k) const
1686 : -> size_type
1687 : {
1688 : __hash_code __code = this->_M_hash_code_tr(__k);
1689 : std::size_t __bkt = _M_bucket_index(__code);
1690 : auto __n = _M_find_node_tr(__bkt, __k, __code);
1691 : if (!__n)
1692 : return 0;
1693 :
1694 : // All equivalent values are next to each other, if we find a
1695 : // non-equivalent value after an equivalent one it means that we won't
1696 : // find any new equivalent value.
1697 : iterator __it(__n);
1698 : size_type __result = 1;
1699 : for (++__it;
1700 : __it._M_cur && this->_M_equals_tr(__k, __code, *__it._M_cur);
1701 : ++__it)
1702 : ++__result;
1703 :
1704 : return __result;
1705 : }
1706 : #endif
1707 :
1708 : template<typename _Key, typename _Value, typename _Alloc,
1709 : typename _ExtractKey, typename _Equal,
1710 : typename _Hash, typename _RangeHash, typename _Unused,
1711 : typename _RehashPolicy, typename _Traits>
1712 : auto
1713 : _Hashtable<_Key, _Value, _Alloc, _ExtractKey, _Equal,
1714 : _Hash, _RangeHash, _Unused, _RehashPolicy, _Traits>::
1715 : equal_range(const key_type& __k)
1716 : -> pair<iterator, iterator>
1717 : {
1718 : auto __ite = find(__k);
1719 : if (!__ite._M_cur)
1720 : return { __ite, __ite };
1721 :
1722 : auto __beg = __ite++;
1723 : if (__unique_keys::value)
1724 : return { __beg, __ite };
1725 :
1726 : // All equivalent values are next to each other, if we find a
1727 : // non-equivalent value after an equivalent one it means that we won't
1728 : // find any new equivalent value.
1729 : while (__ite._M_cur && this->_M_node_equals(*__beg._M_cur, *__ite._M_cur))
1730 : ++__ite;
1731 :
1732 : return { __beg, __ite };
1733 : }
1734 :
1735 : template<typename _Key, typename _Value, typename _Alloc,
1736 : typename _ExtractKey, typename _Equal,
1737 : typename _Hash, typename _RangeHash, typename _Unused,
1738 : typename _RehashPolicy, typename _Traits>
1739 : auto
1740 : _Hashtable<_Key, _Value, _Alloc, _ExtractKey, _Equal,
1741 : _Hash, _RangeHash, _Unused, _RehashPolicy, _Traits>::
1742 : equal_range(const key_type& __k) const
1743 : -> pair<const_iterator, const_iterator>
1744 : {
1745 : auto __ite = find(__k);
1746 : if (!__ite._M_cur)
1747 : return { __ite, __ite };
1748 :
1749 : auto __beg = __ite++;
1750 : if (__unique_keys::value)
1751 : return { __beg, __ite };
1752 :
1753 : // All equivalent values are next to each other, if we find a
1754 : // non-equivalent value after an equivalent one it means that we won't
1755 : // find any new equivalent value.
1756 : while (__ite._M_cur && this->_M_node_equals(*__beg._M_cur, *__ite._M_cur))
1757 : ++__ite;
1758 :
1759 : return { __beg, __ite };
1760 : }
1761 :
1762 : #if __cplusplus > 201703L
1763 : template<typename _Key, typename _Value, typename _Alloc,
1764 : typename _ExtractKey, typename _Equal,
1765 : typename _Hash, typename _RangeHash, typename _Unused,
1766 : typename _RehashPolicy, typename _Traits>
1767 : template<typename _Kt, typename, typename>
1768 : auto
1769 : _Hashtable<_Key, _Value, _Alloc, _ExtractKey, _Equal,
1770 : _Hash, _RangeHash, _Unused, _RehashPolicy, _Traits>::
1771 : _M_equal_range_tr(const _Kt& __k)
1772 : -> pair<iterator, iterator>
1773 : {
1774 : __hash_code __code = this->_M_hash_code_tr(__k);
1775 : std::size_t __bkt = _M_bucket_index(__code);
1776 : auto __n = _M_find_node_tr(__bkt, __k, __code);
1777 : iterator __ite(__n);
1778 : if (!__n)
1779 : return { __ite, __ite };
1780 :
1781 : // All equivalent values are next to each other, if we find a
1782 : // non-equivalent value after an equivalent one it means that we won't
1783 : // find any new equivalent value.
1784 : auto __beg = __ite++;
1785 : while (__ite._M_cur && this->_M_equals_tr(__k, __code, *__ite._M_cur))
1786 : ++__ite;
1787 :
1788 : return { __beg, __ite };
1789 : }
1790 :
1791 : template<typename _Key, typename _Value, typename _Alloc,
1792 : typename _ExtractKey, typename _Equal,
1793 : typename _Hash, typename _RangeHash, typename _Unused,
1794 : typename _RehashPolicy, typename _Traits>
1795 : template<typename _Kt, typename, typename>
1796 : auto
1797 : _Hashtable<_Key, _Value, _Alloc, _ExtractKey, _Equal,
1798 : _Hash, _RangeHash, _Unused, _RehashPolicy, _Traits>::
1799 : _M_equal_range_tr(const _Kt& __k) const
1800 : -> pair<const_iterator, const_iterator>
1801 : {
1802 : __hash_code __code = this->_M_hash_code_tr(__k);
1803 : std::size_t __bkt = _M_bucket_index(__code);
1804 : auto __n = _M_find_node_tr(__bkt, __k, __code);
1805 : const_iterator __ite(__n);
1806 : if (!__n)
1807 : return { __ite, __ite };
1808 :
1809 : // All equivalent values are next to each other, if we find a
1810 : // non-equivalent value after an equivalent one it means that we won't
1811 : // find any new equivalent value.
1812 : auto __beg = __ite++;
1813 : while (__ite._M_cur && this->_M_equals_tr(__k, __code, *__ite._M_cur))
1814 : ++__ite;
1815 :
1816 : return { __beg, __ite };
1817 : }
1818 : #endif
1819 :
1820 : // Find the node before the one whose key compares equal to k in the bucket
1821 : // bkt. Return nullptr if no node is found.
1822 : template<typename _Key, typename _Value, typename _Alloc,
1823 : typename _ExtractKey, typename _Equal,
1824 : typename _Hash, typename _RangeHash, typename _Unused,
1825 : typename _RehashPolicy, typename _Traits>
1826 : auto
1827 740 : _Hashtable<_Key, _Value, _Alloc, _ExtractKey, _Equal,
1828 : _Hash, _RangeHash, _Unused, _RehashPolicy, _Traits>::
1829 : _M_find_before_node(size_type __bkt, const key_type& __k,
1830 : __hash_code __code) const
1831 : -> __node_base_ptr
1832 : {
1833 740 : __node_base_ptr __prev_p = _M_buckets[__bkt];
1834 740 : if (!__prev_p)
1835 304 : return nullptr;
1836 :
1837 436 : for (__node_ptr __p = static_cast<__node_ptr>(__prev_p->_M_nxt);;
1838 74 : __p = __p->_M_next())
1839 : {
1840 510 : if (this->_M_equals(__k, __code, *__p))
1841 314 : return __prev_p;
1842 :
1843 196 : if (!__p->_M_nxt || _M_bucket_index(*__p->_M_next()) != __bkt)
1844 122 : break;
1845 74 : __prev_p = __p;
1846 : }
1847 :
1848 122 : return nullptr;
1849 : }
1850 :
1851 : template<typename _Key, typename _Value, typename _Alloc,
1852 : typename _ExtractKey, typename _Equal,
1853 : typename _Hash, typename _RangeHash, typename _Unused,
1854 : typename _RehashPolicy, typename _Traits>
1855 : template<typename _Kt>
1856 : auto
1857 : _Hashtable<_Key, _Value, _Alloc, _ExtractKey, _Equal,
1858 : _Hash, _RangeHash, _Unused, _RehashPolicy, _Traits>::
1859 : _M_find_before_node_tr(size_type __bkt, const _Kt& __k,
1860 : __hash_code __code) const
1861 : -> __node_base_ptr
1862 : {
1863 : __node_base_ptr __prev_p = _M_buckets[__bkt];
1864 : if (!__prev_p)
1865 : return nullptr;
1866 :
1867 : for (__node_ptr __p = static_cast<__node_ptr>(__prev_p->_M_nxt);;
1868 : __p = __p->_M_next())
1869 : {
1870 : if (this->_M_equals_tr(__k, __code, *__p))
1871 : return __prev_p;
1872 :
1873 : if (!__p->_M_nxt || _M_bucket_index(*__p->_M_next()) != __bkt)
1874 : break;
1875 : __prev_p = __p;
1876 : }
1877 :
1878 : return nullptr;
1879 : }
1880 :
1881 : template<typename _Key, typename _Value, typename _Alloc,
1882 : typename _ExtractKey, typename _Equal,
1883 : typename _Hash, typename _RangeHash, typename _Unused,
1884 : typename _RehashPolicy, typename _Traits>
1885 : void
1886 424 : _Hashtable<_Key, _Value, _Alloc, _ExtractKey, _Equal,
1887 : _Hash, _RangeHash, _Unused, _RehashPolicy, _Traits>::
1888 : _M_insert_bucket_begin(size_type __bkt, __node_ptr __node)
1889 : {
1890 424 : if (_M_buckets[__bkt])
1891 : {
1892 : // Bucket is not empty, we just need to insert the new node
1893 : // after the bucket before begin.
1894 119 : __node->_M_nxt = _M_buckets[__bkt]->_M_nxt;
1895 119 : _M_buckets[__bkt]->_M_nxt = __node;
1896 : }
1897 : else
1898 : {
1899 : // The bucket is empty, the new node is inserted at the
1900 : // beginning of the singly-linked list and the bucket will
1901 : // contain _M_before_begin pointer.
1902 305 : __node->_M_nxt = _M_before_begin._M_nxt;
1903 305 : _M_before_begin._M_nxt = __node;
1904 :
1905 305 : if (__node->_M_nxt)
1906 : // We must update former begin bucket that is pointing to
1907 : // _M_before_begin.
1908 203 : _M_buckets[_M_bucket_index(*__node->_M_next())] = __node;
1909 :
1910 305 : _M_buckets[__bkt] = &_M_before_begin;
1911 : }
1912 424 : }
1913 :
1914 : template<typename _Key, typename _Value, typename _Alloc,
1915 : typename _ExtractKey, typename _Equal,
1916 : typename _Hash, typename _RangeHash, typename _Unused,
1917 : typename _RehashPolicy, typename _Traits>
1918 : void
1919 : _Hashtable<_Key, _Value, _Alloc, _ExtractKey, _Equal,
1920 : _Hash, _RangeHash, _Unused, _RehashPolicy, _Traits>::
1921 : _M_remove_bucket_begin(size_type __bkt, __node_ptr __next,
1922 : size_type __next_bkt)
1923 : {
1924 : if (!__next || __next_bkt != __bkt)
1925 : {
1926 : // Bucket is now empty
1927 : // First update next bucket if any
1928 : if (__next)
1929 : _M_buckets[__next_bkt] = _M_buckets[__bkt];
1930 :
1931 : // Second update before begin node if necessary
1932 : if (&_M_before_begin == _M_buckets[__bkt])
1933 : _M_before_begin._M_nxt = __next;
1934 : _M_buckets[__bkt] = nullptr;
1935 : }
1936 : }
1937 :
1938 : template<typename _Key, typename _Value, typename _Alloc,
1939 : typename _ExtractKey, typename _Equal,
1940 : typename _Hash, typename _RangeHash, typename _Unused,
1941 : typename _RehashPolicy, typename _Traits>
1942 : auto
1943 : _Hashtable<_Key, _Value, _Alloc, _ExtractKey, _Equal,
1944 : _Hash, _RangeHash, _Unused, _RehashPolicy, _Traits>::
1945 : _M_get_previous_node(size_type __bkt, __node_ptr __n)
1946 : -> __node_base_ptr
1947 : {
1948 : __node_base_ptr __prev_n = _M_buckets[__bkt];
1949 : while (__prev_n->_M_nxt != __n)
1950 : __prev_n = __prev_n->_M_nxt;
1951 : return __prev_n;
1952 : }
1953 :
1954 : template<typename _Key, typename _Value, typename _Alloc,
1955 : typename _ExtractKey, typename _Equal,
1956 : typename _Hash, typename _RangeHash, typename _Unused,
1957 : typename _RehashPolicy, typename _Traits>
1958 : template<typename... _Args>
1959 : auto
1960 738 : _Hashtable<_Key, _Value, _Alloc, _ExtractKey, _Equal,
1961 : _Hash, _RangeHash, _Unused, _RehashPolicy, _Traits>::
1962 : _M_emplace(true_type /* __uks */, _Args&&... __args)
1963 : -> pair<iterator, bool>
1964 : {
1965 : // First build the node to get access to the hash code
1966 738 : _Scoped_node __node { this, std::forward<_Args>(__args)... };
1967 738 : const key_type& __k = _ExtractKey{}(__node._M_node->_M_v());
1968 738 : __hash_code __code = this->_M_hash_code(__k);
1969 738 : size_type __bkt = _M_bucket_index(__code);
1970 738 : if (__node_ptr __p = _M_find_node(__bkt, __k, __code))
1971 : // There is already an equivalent node, no insertion
1972 314 : return std::make_pair(iterator(__p), false);
1973 :
1974 : // Insert the node
1975 424 : auto __pos = _M_insert_unique_node(__bkt, __code, __node._M_node);
1976 424 : __node._M_node = nullptr;
1977 424 : return { __pos, true };
1978 738 : }
1979 :
1980 : template<typename _Key, typename _Value, typename _Alloc,
1981 : typename _ExtractKey, typename _Equal,
1982 : typename _Hash, typename _RangeHash, typename _Unused,
1983 : typename _RehashPolicy, typename _Traits>
1984 : template<typename... _Args>
1985 : auto
1986 : _Hashtable<_Key, _Value, _Alloc, _ExtractKey, _Equal,
1987 : _Hash, _RangeHash, _Unused, _RehashPolicy, _Traits>::
1988 : _M_emplace(const_iterator __hint, false_type /* __uks */,
1989 : _Args&&... __args)
1990 : -> iterator
1991 : {
1992 : // First build the node to get its hash code.
1993 : _Scoped_node __node { this, std::forward<_Args>(__args)... };
1994 : const key_type& __k = _ExtractKey{}(__node._M_node->_M_v());
1995 :
1996 : __hash_code __code = this->_M_hash_code(__k);
1997 : auto __pos
1998 : = _M_insert_multi_node(__hint._M_cur, __code, __node._M_node);
1999 : __node._M_node = nullptr;
2000 : return __pos;
2001 : }
2002 :
2003 : template<typename _Key, typename _Value, typename _Alloc,
2004 : typename _ExtractKey, typename _Equal,
2005 : typename _Hash, typename _RangeHash, typename _Unused,
2006 : typename _RehashPolicy, typename _Traits>
2007 : auto
2008 424 : _Hashtable<_Key, _Value, _Alloc, _ExtractKey, _Equal,
2009 : _Hash, _RangeHash, _Unused, _RehashPolicy, _Traits>::
2010 : _M_insert_unique_node(size_type __bkt, __hash_code __code,
2011 : __node_ptr __node, size_type __n_elt)
2012 : -> iterator
2013 : {
2014 424 : const __rehash_state& __saved_state = _M_rehash_policy._M_state();
2015 : std::pair<bool, std::size_t> __do_rehash
2016 424 : = _M_rehash_policy._M_need_rehash(_M_bucket_count, _M_element_count,
2017 : __n_elt);
2018 :
2019 424 : if (__do_rehash.first)
2020 : {
2021 114 : _M_rehash(__do_rehash.second, __saved_state);
2022 114 : __bkt = _M_bucket_index(__code);
2023 : }
2024 :
2025 424 : this->_M_store_code(*__node, __code);
2026 :
2027 : // Always insert at the beginning of the bucket.
2028 424 : _M_insert_bucket_begin(__bkt, __node);
2029 424 : ++_M_element_count;
2030 424 : return iterator(__node);
2031 : }
2032 :
2033 : template<typename _Key, typename _Value, typename _Alloc,
2034 : typename _ExtractKey, typename _Equal,
2035 : typename _Hash, typename _RangeHash, typename _Unused,
2036 : typename _RehashPolicy, typename _Traits>
2037 : auto
2038 : _Hashtable<_Key, _Value, _Alloc, _ExtractKey, _Equal,
2039 : _Hash, _RangeHash, _Unused, _RehashPolicy, _Traits>::
2040 : _M_insert_multi_node(__node_ptr __hint,
2041 : __hash_code __code, __node_ptr __node)
2042 : -> iterator
2043 : {
2044 : const __rehash_state& __saved_state = _M_rehash_policy._M_state();
2045 : std::pair<bool, std::size_t> __do_rehash
2046 : = _M_rehash_policy._M_need_rehash(_M_bucket_count, _M_element_count, 1);
2047 :
2048 : if (__do_rehash.first)
2049 : _M_rehash(__do_rehash.second, __saved_state);
2050 :
2051 : this->_M_store_code(*__node, __code);
2052 : const key_type& __k = _ExtractKey{}(__node->_M_v());
2053 : size_type __bkt = _M_bucket_index(__code);
2054 :
2055 : // Find the node before an equivalent one or use hint if it exists and
2056 : // if it is equivalent.
2057 : __node_base_ptr __prev
2058 : = __builtin_expect(__hint != nullptr, false)
2059 : && this->_M_equals(__k, __code, *__hint)
2060 : ? __hint
2061 : : _M_find_before_node(__bkt, __k, __code);
2062 :
2063 : if (__prev)
2064 : {
2065 : // Insert after the node before the equivalent one.
2066 : __node->_M_nxt = __prev->_M_nxt;
2067 : __prev->_M_nxt = __node;
2068 : if (__builtin_expect(__prev == __hint, false))
2069 : // hint might be the last bucket node, in this case we need to
2070 : // update next bucket.
2071 : if (__node->_M_nxt
2072 : && !this->_M_equals(__k, __code, *__node->_M_next()))
2073 : {
2074 : size_type __next_bkt = _M_bucket_index(*__node->_M_next());
2075 : if (__next_bkt != __bkt)
2076 : _M_buckets[__next_bkt] = __node;
2077 : }
2078 : }
2079 : else
2080 : // The inserted node has no equivalent in the hashtable. We must
2081 : // insert the new node at the beginning of the bucket to preserve
2082 : // equivalent elements' relative positions.
2083 : _M_insert_bucket_begin(__bkt, __node);
2084 : ++_M_element_count;
2085 : return iterator(__node);
2086 : }
2087 :
2088 : // Insert v if no element with its key is already present.
2089 : template<typename _Key, typename _Value, typename _Alloc,
2090 : typename _ExtractKey, typename _Equal,
2091 : typename _Hash, typename _RangeHash, typename _Unused,
2092 : typename _RehashPolicy, typename _Traits>
2093 : template<typename _Arg, typename _NodeGenerator>
2094 : auto
2095 : _Hashtable<_Key, _Value, _Alloc, _ExtractKey, _Equal,
2096 : _Hash, _RangeHash, _Unused, _RehashPolicy, _Traits>::
2097 : _M_insert(_Arg&& __v, const _NodeGenerator& __node_gen,
2098 : true_type /* __uks */)
2099 : -> pair<iterator, bool>
2100 : {
2101 : const key_type& __k = _ExtractKey{}(__v);
2102 : __hash_code __code = this->_M_hash_code(__k);
2103 : size_type __bkt = _M_bucket_index(__code);
2104 :
2105 : if (__node_ptr __node = _M_find_node(__bkt, __k, __code))
2106 : return { iterator(__node), false };
2107 :
2108 : _Scoped_node __node{ __node_gen(std::forward<_Arg>(__v)), this };
2109 : auto __pos
2110 : = _M_insert_unique_node(__bkt, __code, __node._M_node);
2111 : __node._M_node = nullptr;
2112 : return { __pos, true };
2113 : }
2114 :
2115 : // Insert v unconditionally.
2116 : template<typename _Key, typename _Value, typename _Alloc,
2117 : typename _ExtractKey, typename _Equal,
2118 : typename _Hash, typename _RangeHash, typename _Unused,
2119 : typename _RehashPolicy, typename _Traits>
2120 : template<typename _Arg, typename _NodeGenerator>
2121 : auto
2122 : _Hashtable<_Key, _Value, _Alloc, _ExtractKey, _Equal,
2123 : _Hash, _RangeHash, _Unused, _RehashPolicy, _Traits>::
2124 : _M_insert(const_iterator __hint, _Arg&& __v,
2125 : const _NodeGenerator& __node_gen,
2126 : false_type /* __uks */)
2127 : -> iterator
2128 : {
2129 : // First compute the hash code so that we don't do anything if it
2130 : // throws.
2131 : __hash_code __code = this->_M_hash_code(_ExtractKey{}(__v));
2132 :
2133 : // Second allocate new node so that we don't rehash if it throws.
2134 : _Scoped_node __node{ __node_gen(std::forward<_Arg>(__v)), this };
2135 : auto __pos
2136 : = _M_insert_multi_node(__hint._M_cur, __code, __node._M_node);
2137 : __node._M_node = nullptr;
2138 : return __pos;
2139 : }
2140 :
2141 : template<typename _Key, typename _Value, typename _Alloc,
2142 : typename _ExtractKey, typename _Equal,
2143 : typename _Hash, typename _RangeHash, typename _Unused,
2144 : typename _RehashPolicy, typename _Traits>
2145 : auto
2146 : _Hashtable<_Key, _Value, _Alloc, _ExtractKey, _Equal,
2147 : _Hash, _RangeHash, _Unused, _RehashPolicy, _Traits>::
2148 : erase(const_iterator __it)
2149 : -> iterator
2150 : {
2151 : __node_ptr __n = __it._M_cur;
2152 : std::size_t __bkt = _M_bucket_index(*__n);
2153 :
2154 : // Look for previous node to unlink it from the erased one, this
2155 : // is why we need buckets to contain the before begin to make
2156 : // this search fast.
2157 : __node_base_ptr __prev_n = _M_get_previous_node(__bkt, __n);
2158 : return _M_erase(__bkt, __prev_n, __n);
2159 : }
2160 :
2161 : template<typename _Key, typename _Value, typename _Alloc,
2162 : typename _ExtractKey, typename _Equal,
2163 : typename _Hash, typename _RangeHash, typename _Unused,
2164 : typename _RehashPolicy, typename _Traits>
2165 : auto
2166 : _Hashtable<_Key, _Value, _Alloc, _ExtractKey, _Equal,
2167 : _Hash, _RangeHash, _Unused, _RehashPolicy, _Traits>::
2168 : _M_erase(size_type __bkt, __node_base_ptr __prev_n, __node_ptr __n)
2169 : -> iterator
2170 : {
2171 : if (__prev_n == _M_buckets[__bkt])
2172 : _M_remove_bucket_begin(__bkt, __n->_M_next(),
2173 : __n->_M_nxt ? _M_bucket_index(*__n->_M_next()) : 0);
2174 : else if (__n->_M_nxt)
2175 : {
2176 : size_type __next_bkt = _M_bucket_index(*__n->_M_next());
2177 : if (__next_bkt != __bkt)
2178 : _M_buckets[__next_bkt] = __prev_n;
2179 : }
2180 :
2181 : __prev_n->_M_nxt = __n->_M_nxt;
2182 : iterator __result(__n->_M_next());
2183 : this->_M_deallocate_node(__n);
2184 : --_M_element_count;
2185 :
2186 : return __result;
2187 : }
2188 :
2189 : template<typename _Key, typename _Value, typename _Alloc,
2190 : typename _ExtractKey, typename _Equal,
2191 : typename _Hash, typename _RangeHash, typename _Unused,
2192 : typename _RehashPolicy, typename _Traits>
2193 : auto
2194 : _Hashtable<_Key, _Value, _Alloc, _ExtractKey, _Equal,
2195 : _Hash, _RangeHash, _Unused, _RehashPolicy, _Traits>::
2196 : _M_erase(true_type /* __uks */, const key_type& __k)
2197 : -> size_type
2198 : {
2199 : __hash_code __code = this->_M_hash_code(__k);
2200 : std::size_t __bkt = _M_bucket_index(__code);
2201 :
2202 : // Look for the node before the first matching node.
2203 : __node_base_ptr __prev_n = _M_find_before_node(__bkt, __k, __code);
2204 : if (!__prev_n)
2205 : return 0;
2206 :
2207 : // We found a matching node, erase it.
2208 : __node_ptr __n = static_cast<__node_ptr>(__prev_n->_M_nxt);
2209 : _M_erase(__bkt, __prev_n, __n);
2210 : return 1;
2211 : }
2212 :
2213 : template<typename _Key, typename _Value, typename _Alloc,
2214 : typename _ExtractKey, typename _Equal,
2215 : typename _Hash, typename _RangeHash, typename _Unused,
2216 : typename _RehashPolicy, typename _Traits>
2217 : auto
2218 : _Hashtable<_Key, _Value, _Alloc, _ExtractKey, _Equal,
2219 : _Hash, _RangeHash, _Unused, _RehashPolicy, _Traits>::
2220 : _M_erase(false_type /* __uks */, const key_type& __k)
2221 : -> size_type
2222 : {
2223 : __hash_code __code = this->_M_hash_code(__k);
2224 : std::size_t __bkt = _M_bucket_index(__code);
2225 :
2226 : // Look for the node before the first matching node.
2227 : __node_base_ptr __prev_n = _M_find_before_node(__bkt, __k, __code);
2228 : if (!__prev_n)
2229 : return 0;
2230 :
2231 : // _GLIBCXX_RESOLVE_LIB_DEFECTS
2232 : // 526. Is it undefined if a function in the standard changes
2233 : // in parameters?
2234 : // We use one loop to find all matching nodes and another to deallocate
2235 : // them so that the key stays valid during the first loop. It might be
2236 : // invalidated indirectly when destroying nodes.
2237 : __node_ptr __n = static_cast<__node_ptr>(__prev_n->_M_nxt);
2238 : __node_ptr __n_last = __n->_M_next();
2239 : while (__n_last && this->_M_node_equals(*__n, *__n_last))
2240 : __n_last = __n_last->_M_next();
2241 :
2242 : std::size_t __n_last_bkt = __n_last ? _M_bucket_index(*__n_last) : __bkt;
2243 :
2244 : // Deallocate nodes.
2245 : size_type __result = 0;
2246 : do
2247 : {
2248 : __node_ptr __p = __n->_M_next();
2249 : this->_M_deallocate_node(__n);
2250 : __n = __p;
2251 : ++__result;
2252 : }
2253 : while (__n != __n_last);
2254 :
2255 : _M_element_count -= __result;
2256 : if (__prev_n == _M_buckets[__bkt])
2257 : _M_remove_bucket_begin(__bkt, __n_last, __n_last_bkt);
2258 : else if (__n_last_bkt != __bkt)
2259 : _M_buckets[__n_last_bkt] = __prev_n;
2260 : __prev_n->_M_nxt = __n_last;
2261 : return __result;
2262 : }
2263 :
2264 : template<typename _Key, typename _Value, typename _Alloc,
2265 : typename _ExtractKey, typename _Equal,
2266 : typename _Hash, typename _RangeHash, typename _Unused,
2267 : typename _RehashPolicy, typename _Traits>
2268 : auto
2269 : _Hashtable<_Key, _Value, _Alloc, _ExtractKey, _Equal,
2270 : _Hash, _RangeHash, _Unused, _RehashPolicy, _Traits>::
2271 : erase(const_iterator __first, const_iterator __last)
2272 : -> iterator
2273 : {
2274 : __node_ptr __n = __first._M_cur;
2275 : __node_ptr __last_n = __last._M_cur;
2276 : if (__n == __last_n)
2277 : return iterator(__n);
2278 :
2279 : std::size_t __bkt = _M_bucket_index(*__n);
2280 :
2281 : __node_base_ptr __prev_n = _M_get_previous_node(__bkt, __n);
2282 : bool __is_bucket_begin = __n == _M_bucket_begin(__bkt);
2283 : std::size_t __n_bkt = __bkt;
2284 : for (;;)
2285 : {
2286 : do
2287 : {
2288 : __node_ptr __tmp = __n;
2289 : __n = __n->_M_next();
2290 : this->_M_deallocate_node(__tmp);
2291 : --_M_element_count;
2292 : if (!__n)
2293 : break;
2294 : __n_bkt = _M_bucket_index(*__n);
2295 : }
2296 : while (__n != __last_n && __n_bkt == __bkt);
2297 : if (__is_bucket_begin)
2298 : _M_remove_bucket_begin(__bkt, __n, __n_bkt);
2299 : if (__n == __last_n)
2300 : break;
2301 : __is_bucket_begin = true;
2302 : __bkt = __n_bkt;
2303 : }
2304 :
2305 : if (__n && (__n_bkt != __bkt || __is_bucket_begin))
2306 : _M_buckets[__n_bkt] = __prev_n;
2307 : __prev_n->_M_nxt = __n;
2308 : return iterator(__n);
2309 : }
2310 :
2311 : template<typename _Key, typename _Value, typename _Alloc,
2312 : typename _ExtractKey, typename _Equal,
2313 : typename _Hash, typename _RangeHash, typename _Unused,
2314 : typename _RehashPolicy, typename _Traits>
2315 : void
2316 286 : _Hashtable<_Key, _Value, _Alloc, _ExtractKey, _Equal,
2317 : _Hash, _RangeHash, _Unused, _RehashPolicy, _Traits>::
2318 : clear() noexcept
2319 : {
2320 286 : this->_M_deallocate_nodes(_M_begin());
2321 286 : __builtin_memset(_M_buckets, 0,
2322 286 : _M_bucket_count * sizeof(__node_base_ptr));
2323 286 : _M_element_count = 0;
2324 286 : _M_before_begin._M_nxt = nullptr;
2325 286 : }
2326 :
2327 : template<typename _Key, typename _Value, typename _Alloc,
2328 : typename _ExtractKey, typename _Equal,
2329 : typename _Hash, typename _RangeHash, typename _Unused,
2330 : typename _RehashPolicy, typename _Traits>
2331 : void
2332 : _Hashtable<_Key, _Value, _Alloc, _ExtractKey, _Equal,
2333 : _Hash, _RangeHash, _Unused, _RehashPolicy, _Traits>::
2334 : rehash(size_type __bkt_count)
2335 : {
2336 : const __rehash_state& __saved_state = _M_rehash_policy._M_state();
2337 : __bkt_count
2338 : = std::max(_M_rehash_policy._M_bkt_for_elements(_M_element_count + 1),
2339 : __bkt_count);
2340 : __bkt_count = _M_rehash_policy._M_next_bkt(__bkt_count);
2341 :
2342 : if (__bkt_count != _M_bucket_count)
2343 : _M_rehash(__bkt_count, __saved_state);
2344 : else
2345 : // No rehash, restore previous state to keep it consistent with
2346 : // container state.
2347 : _M_rehash_policy._M_reset(__saved_state);
2348 : }
2349 :
2350 : template<typename _Key, typename _Value, typename _Alloc,
2351 : typename _ExtractKey, typename _Equal,
2352 : typename _Hash, typename _RangeHash, typename _Unused,
2353 : typename _RehashPolicy, typename _Traits>
2354 : void
2355 114 : _Hashtable<_Key, _Value, _Alloc, _ExtractKey, _Equal,
2356 : _Hash, _RangeHash, _Unused, _RehashPolicy, _Traits>::
2357 : _M_rehash(size_type __bkt_count, const __rehash_state& __state)
2358 : {
2359 : __try
2360 : {
2361 114 : _M_rehash_aux(__bkt_count, __unique_keys{});
2362 : }
2363 0 : __catch(...)
2364 : {
2365 : // A failure here means that buckets allocation failed. We only
2366 : // have to restore hash policy previous state.
2367 0 : _M_rehash_policy._M_reset(__state);
2368 0 : __throw_exception_again;
2369 : }
2370 114 : }
2371 :
2372 : // Rehash when there is no equivalent elements.
2373 : template<typename _Key, typename _Value, typename _Alloc,
2374 : typename _ExtractKey, typename _Equal,
2375 : typename _Hash, typename _RangeHash, typename _Unused,
2376 : typename _RehashPolicy, typename _Traits>
2377 : void
2378 114 : _Hashtable<_Key, _Value, _Alloc, _ExtractKey, _Equal,
2379 : _Hash, _RangeHash, _Unused, _RehashPolicy, _Traits>::
2380 : _M_rehash_aux(size_type __bkt_count, true_type /* __uks */)
2381 : {
2382 114 : __buckets_ptr __new_buckets = _M_allocate_buckets(__bkt_count);
2383 114 : __node_ptr __p = _M_begin();
2384 114 : _M_before_begin._M_nxt = nullptr;
2385 114 : std::size_t __bbegin_bkt = 0;
2386 270 : while (__p)
2387 : {
2388 156 : __node_ptr __next = __p->_M_next();
2389 : std::size_t __bkt
2390 156 : = __hash_code_base::_M_bucket_index(*__p, __bkt_count);
2391 156 : if (!__new_buckets[__bkt])
2392 : {
2393 107 : __p->_M_nxt = _M_before_begin._M_nxt;
2394 107 : _M_before_begin._M_nxt = __p;
2395 107 : __new_buckets[__bkt] = &_M_before_begin;
2396 107 : if (__p->_M_nxt)
2397 95 : __new_buckets[__bbegin_bkt] = __p;
2398 107 : __bbegin_bkt = __bkt;
2399 : }
2400 : else
2401 : {
2402 49 : __p->_M_nxt = __new_buckets[__bkt]->_M_nxt;
2403 49 : __new_buckets[__bkt]->_M_nxt = __p;
2404 : }
2405 :
2406 156 : __p = __next;
2407 : }
2408 :
2409 114 : _M_deallocate_buckets();
2410 114 : _M_bucket_count = __bkt_count;
2411 114 : _M_buckets = __new_buckets;
2412 114 : }
2413 :
2414 : // Rehash when there can be equivalent elements, preserve their relative
2415 : // order.
2416 : template<typename _Key, typename _Value, typename _Alloc,
2417 : typename _ExtractKey, typename _Equal,
2418 : typename _Hash, typename _RangeHash, typename _Unused,
2419 : typename _RehashPolicy, typename _Traits>
2420 : void
2421 : _Hashtable<_Key, _Value, _Alloc, _ExtractKey, _Equal,
2422 : _Hash, _RangeHash, _Unused, _RehashPolicy, _Traits>::
2423 : _M_rehash_aux(size_type __bkt_count, false_type /* __uks */)
2424 : {
2425 : __buckets_ptr __new_buckets = _M_allocate_buckets(__bkt_count);
2426 : __node_ptr __p = _M_begin();
2427 : _M_before_begin._M_nxt = nullptr;
2428 : std::size_t __bbegin_bkt = 0;
2429 : std::size_t __prev_bkt = 0;
2430 : __node_ptr __prev_p = nullptr;
2431 : bool __check_bucket = false;
2432 :
2433 : while (__p)
2434 : {
2435 : __node_ptr __next = __p->_M_next();
2436 : std::size_t __bkt
2437 : = __hash_code_base::_M_bucket_index(*__p, __bkt_count);
2438 :
2439 : if (__prev_p && __prev_bkt == __bkt)
2440 : {
2441 : // Previous insert was already in this bucket, we insert after
2442 : // the previously inserted one to preserve equivalent elements
2443 : // relative order.
2444 : __p->_M_nxt = __prev_p->_M_nxt;
2445 : __prev_p->_M_nxt = __p;
2446 :
2447 : // Inserting after a node in a bucket require to check that we
2448 : // haven't change the bucket last node, in this case next
2449 : // bucket containing its before begin node must be updated. We
2450 : // schedule a check as soon as we move out of the sequence of
2451 : // equivalent nodes to limit the number of checks.
2452 : __check_bucket = true;
2453 : }
2454 : else
2455 : {
2456 : if (__check_bucket)
2457 : {
2458 : // Check if we shall update the next bucket because of
2459 : // insertions into __prev_bkt bucket.
2460 : if (__prev_p->_M_nxt)
2461 : {
2462 : std::size_t __next_bkt
2463 : = __hash_code_base::_M_bucket_index(
2464 : *__prev_p->_M_next(), __bkt_count);
2465 : if (__next_bkt != __prev_bkt)
2466 : __new_buckets[__next_bkt] = __prev_p;
2467 : }
2468 : __check_bucket = false;
2469 : }
2470 :
2471 : if (!__new_buckets[__bkt])
2472 : {
2473 : __p->_M_nxt = _M_before_begin._M_nxt;
2474 : _M_before_begin._M_nxt = __p;
2475 : __new_buckets[__bkt] = &_M_before_begin;
2476 : if (__p->_M_nxt)
2477 : __new_buckets[__bbegin_bkt] = __p;
2478 : __bbegin_bkt = __bkt;
2479 : }
2480 : else
2481 : {
2482 : __p->_M_nxt = __new_buckets[__bkt]->_M_nxt;
2483 : __new_buckets[__bkt]->_M_nxt = __p;
2484 : }
2485 : }
2486 : __prev_p = __p;
2487 : __prev_bkt = __bkt;
2488 : __p = __next;
2489 : }
2490 :
2491 : if (__check_bucket && __prev_p->_M_nxt)
2492 : {
2493 : std::size_t __next_bkt
2494 : = __hash_code_base::_M_bucket_index(*__prev_p->_M_next(),
2495 : __bkt_count);
2496 : if (__next_bkt != __prev_bkt)
2497 : __new_buckets[__next_bkt] = __prev_p;
2498 : }
2499 :
2500 : _M_deallocate_buckets();
2501 : _M_bucket_count = __bkt_count;
2502 : _M_buckets = __new_buckets;
2503 : }
2504 :
2505 : #if __cplusplus > 201402L
2506 : template<typename, typename, typename> class _Hash_merge_helper { };
2507 : #endif // C++17
2508 :
2509 : #if __cpp_deduction_guides >= 201606
2510 : // Used to constrain deduction guides
2511 : template<typename _Hash>
2512 : using _RequireNotAllocatorOrIntegral
2513 : = __enable_if_t<!__or_<is_integral<_Hash>, __is_allocator<_Hash>>::value>;
2514 : #endif
2515 :
2516 : /// @endcond
2517 : _GLIBCXX_END_NAMESPACE_VERSION
2518 : } // namespace std
2519 :
2520 : #endif // _HASHTABLE_H
|