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