@@ -1024,7 +1024,21 @@ private:
10241024 }
10251025 _LIBCPP_HIDE_FROM_ABI void __move_assign_alloc (__hash_table&, false_type) _NOEXCEPT {}
10261026
1027- _LIBCPP_HIDE_FROM_ABI void __deallocate_node (__next_pointer __np) _NOEXCEPT;
1027+ _LIBCPP_HIDE_FROM_ABI void __deallocate_node (__node_pointer __nd) _NOEXCEPT {
1028+ auto & __alloc = __node_alloc ();
1029+ __node_traits::destroy (__alloc, std::addressof (__nd->__get_value ()));
1030+ std::__destroy_at (std::__to_address (__nd));
1031+ __node_traits::deallocate (__alloc, __nd, 1 );
1032+ }
1033+
1034+ _LIBCPP_HIDE_FROM_ABI void __deallocate_node_list (__next_pointer __np) _NOEXCEPT {
1035+ while (__np != nullptr ) {
1036+ __next_pointer __next = __np->__next_ ;
1037+ __deallocate_node (__np->__upcast ());
1038+ __np = __next;
1039+ }
1040+ }
1041+
10281042 _LIBCPP_HIDE_FROM_ABI __next_pointer __detach () _NOEXCEPT;
10291043
10301044 template <class _From , class _ValueT = _Tp, __enable_if_t <__is_hash_value_type<_ValueT>::value, int > = 0 >
@@ -1162,7 +1176,7 @@ __hash_table<_Tp, _Hash, _Equal, _Alloc>::~__hash_table() {
11621176 static_assert (is_copy_constructible<hasher>::value, " Hasher must be copy-constructible." );
11631177#endif
11641178
1165- __deallocate_node (__first_node_.__next_ );
1179+ __deallocate_node_list (__first_node_.__next_ );
11661180}
11671181
11681182template <class _Tp , class _Hash , class _Equal , class _Alloc >
@@ -1238,7 +1252,7 @@ __hash_table<_Tp, _Hash, _Equal, _Alloc>::operator=(const __hash_table& __other)
12381252 // At this point we either have consumed the whole incoming hash table, or we don't have any more nodes to reuse in
12391253 // the destination. Either continue with constructing new nodes, or deallocate the left over nodes.
12401254 if (__own_iter->__next_ ) {
1241- __deallocate_node (__own_iter->__next_ );
1255+ __deallocate_node_list (__own_iter->__next_ );
12421256 __own_iter->__next_ = nullptr ;
12431257 } else {
12441258 __copy_construct (__other_iter, __own_iter, __current_chash);
@@ -1249,19 +1263,6 @@ __hash_table<_Tp, _Hash, _Equal, _Alloc>::operator=(const __hash_table& __other)
12491263 return *this ;
12501264}
12511265
1252- template <class _Tp , class _Hash , class _Equal , class _Alloc >
1253- void __hash_table<_Tp, _Hash, _Equal, _Alloc>::__deallocate_node(__next_pointer __np) _NOEXCEPT {
1254- __node_allocator& __na = __node_alloc ();
1255- while (__np != nullptr ) {
1256- __next_pointer __next = __np->__next_ ;
1257- __node_pointer __real_np = __np->__upcast ();
1258- __node_traits::destroy (__na, std::addressof (__real_np->__get_value ()));
1259- std::__destroy_at (std::addressof (*__real_np));
1260- __node_traits::deallocate (__na, __real_np, 1 );
1261- __np = __next;
1262- }
1263- }
1264-
12651266template <class _Tp , class _Hash , class _Equal , class _Alloc >
12661267typename __hash_table<_Tp, _Hash, _Equal, _Alloc>::__next_pointer
12671268__hash_table<_Tp, _Hash, _Equal, _Alloc>::__detach() _NOEXCEPT {
@@ -1317,11 +1318,11 @@ void __hash_table<_Tp, _Hash, _Equal, _Alloc>::__move_assign(__hash_table& __u,
13171318 }
13181319#if _LIBCPP_HAS_EXCEPTIONS
13191320 } catch (...) {
1320- __deallocate_node (__cache);
1321+ __deallocate_node_list (__cache);
13211322 throw ;
13221323 }
13231324#endif // _LIBCPP_HAS_EXCEPTIONS
1324- __deallocate_node (__cache);
1325+ __deallocate_node_list (__cache);
13251326 }
13261327 const_iterator __i = __u.begin ();
13271328 while (__u.size () != 0 )
@@ -1360,11 +1361,11 @@ void __hash_table<_Tp, _Hash, _Equal, _Alloc>::__assign_unique(_InputIterator __
13601361 }
13611362#if _LIBCPP_HAS_EXCEPTIONS
13621363 } catch (...) {
1363- __deallocate_node (__cache);
1364+ __deallocate_node_list (__cache);
13641365 throw ;
13651366 }
13661367#endif // _LIBCPP_HAS_EXCEPTIONS
1367- __deallocate_node (__cache);
1368+ __deallocate_node_list (__cache);
13681369 }
13691370 for (; __first != __last; ++__first)
13701371 __emplace_unique (*__first);
@@ -1390,11 +1391,11 @@ void __hash_table<_Tp, _Hash, _Equal, _Alloc>::__assign_multi(_InputIterator __f
13901391 }
13911392#if _LIBCPP_HAS_EXCEPTIONS
13921393 } catch (...) {
1393- __deallocate_node (__cache);
1394+ __deallocate_node_list (__cache);
13941395 throw ;
13951396 }
13961397#endif // _LIBCPP_HAS_EXCEPTIONS
1397- __deallocate_node (__cache);
1398+ __deallocate_node_list (__cache);
13981399 }
13991400 for (; __first != __last; ++__first)
14001401 __emplace_multi (*__first);
@@ -1427,7 +1428,7 @@ __hash_table<_Tp, _Hash, _Equal, _Alloc>::end() const _NOEXCEPT {
14271428template <class _Tp , class _Hash , class _Equal , class _Alloc >
14281429void __hash_table<_Tp, _Hash, _Equal, _Alloc>::clear () _NOEXCEPT {
14291430 if (size () > 0 ) {
1430- __deallocate_node (__first_node_.__next_ );
1431+ __deallocate_node_list (__first_node_.__next_ );
14311432 __first_node_.__next_ = nullptr ;
14321433 size_type __bc = bucket_count ();
14331434 for (size_type __i = 0 ; __i < __bc; ++__i)
@@ -1955,12 +1956,57 @@ __hash_table<_Tp, _Hash, _Equal, _Alloc>::erase(const_iterator __p) {
19551956template <class _Tp , class _Hash , class _Equal , class _Alloc >
19561957typename __hash_table<_Tp, _Hash, _Equal, _Alloc>::iterator
19571958__hash_table<_Tp, _Hash, _Equal, _Alloc>::erase (const_iterator __first, const_iterator __last) {
1958- for (const_iterator __p = __first; __first != __last; __p = __first) {
1959- ++__first;
1960- erase (__p);
1959+ if (__first == __last)
1960+ return iterator (__last.__node_ );
1961+
1962+ // current node
1963+ __next_pointer __current = __first.__node_ ;
1964+ size_type __bucket_count = bucket_count ();
1965+ size_t __chash = std::__constrain_hash (__current->__hash (), __bucket_count);
1966+ // find previous node
1967+ __next_pointer __before_first = __bucket_list_[__chash];
1968+ for (; __before_first->__next_ != __current; __before_first = __before_first->__next_ )
1969+ ;
1970+
1971+ __next_pointer __last_node = __last.__node_ ;
1972+
1973+ // If __before_first is in the same bucket (i.e. the first element we erase is not the first in the bucket), clear
1974+ // this bucket first without re-linking it
1975+ if (__before_first != __first_node_.__ptr () &&
1976+ std::__constrain_hash (__before_first->__hash (), __bucket_count) == __chash) {
1977+ while (__current != __last_node) {
1978+ if (auto __next_chash = std::__constrain_hash (__current->__hash (), __bucket_count); __next_chash != __chash) {
1979+ __chash = __next_chash;
1980+ break ;
1981+ }
1982+ auto __next = __current->__next_ ;
1983+ __deallocate_node (__current->__upcast ());
1984+ __current = __next;
1985+ --__size_;
1986+ }
19611987 }
1962- __next_pointer __np = __last.__node_ ;
1963- return iterator (__np);
1988+
1989+ while (__current != __last_node) {
1990+ auto __next = __current->__next_ ;
1991+ __deallocate_node (__current->__upcast ());
1992+ __current = __next;
1993+ --__size_;
1994+
1995+ // When switching buckets, set the old bucket to be empty and update the next bucket to have __before_first as its
1996+ // before-first element
1997+ if (__next) {
1998+ if (auto __next_chash = std::__constrain_hash (__next->__hash (), __bucket_count); __next_chash != __chash) {
1999+ __bucket_list_[__chash] = nullptr ;
2000+ __chash = __next_chash;
2001+ __bucket_list_[__chash] = __before_first;
2002+ }
2003+ }
2004+ }
2005+
2006+ // re-link __before_first with __last
2007+ __before_first->__next_ = __current;
2008+
2009+ return iterator (__last.__node_ );
19642010}
19652011
19662012template <class _Tp , class _Hash , class _Equal , class _Alloc >
0 commit comments