diff --git a/libstdc++-v3/include/bits/hashtable.h b/libstdc++-v3/include/bits/hashtable.h index 011a707605f..b497f16d017 100644 --- a/libstdc++-v3/include/bits/hashtable.h +++ b/libstdc++-v3/include/bits/hashtable.h @@ -290,6 +290,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION __detail::_ReuseOrAllocNode<__node_alloc_type>; using __alloc_node_gen_cache_bbegin_t = __detail::_AllocNode<__node_alloc_type, __detail::_CacheBBeginPolicy>; + using __prealloc_hashtable_nodes_gen_t = + __detail::_PreAllocHashtableNodes<__node_alloc_type>; + using __node_builder_t = __detail::_NodeBuilder<_ExtractKey>; using __no_cache_bbegin_policy_t = @@ -483,6 +486,10 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION void _M_assign(_Ht&&, const _NodeGenerator&); + template + void + _M_assign_stable(_Ht&&, const _NodeGenerator&); + void _M_move_assign(_Hashtable&&, true_type); @@ -1364,10 +1371,10 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION _M_bucket_count = __ht._M_bucket_count; _M_element_count = __ht._M_element_count; _M_rehash_policy = __ht._M_rehash_policy; - __alloc_node_gen_cache_bbegin_t __node_gen(*this); __try { - _M_assign(__ht, __node_gen); + __prealloc_hashtable_nodes_gen_t __node_gen(__ht, *this); + _M_assign_stable(__ht, __node_gen); } __catch(...) { @@ -1487,6 +1494,72 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION } } + template + template + void + _Hashtable<_Key, _Value, _Alloc, _ExtractKey, _Equal, + _Hash, _RangeHash, _Unused, _RehashPolicy, _Traits>:: + _M_assign_stable(_Ht&& __ht, const _NodeGenerator& __node_gen) + { + __buckets_ptr __buckets = nullptr; + if (!_M_buckets) + _M_buckets = __buckets = _M_allocate_buckets(_M_bucket_count); + + __try + { + if (!__ht._M_before_begin._M_nxt) + return; + + __node_ptr __prev_n = nullptr; + for (std::size_t __bkt = 0; __bkt != _M_bucket_count; ++__bkt) + { + if (__ht._M_buckets[__bkt] == nullptr) + continue; + + __node_ptr __ht_n = + static_cast<__node_ptr>(__ht._M_buckets[__bkt]->_M_nxt); + __node_ptr __prev_ht_n = __ht_n; + __node_base_ptr __nxt_bkt_n = __bkt < _M_bucket_count - 1 + ? __ht._M_buckets[__bkt + 1] : nullptr; + do + { + __node_ptr __this_n + = __node_gen(__prev_n, __bkt, + __fwd_value_for<_Ht>(__ht_n->_M_v())); + this->_M_copy_code(*__this_n, *__ht_n); + if (__prev_n) + { + if (!_M_buckets[__bkt]) + _M_buckets[__bkt] = __prev_n; + __prev_n->_M_nxt = __this_n; + } + else + { + _M_buckets[__bkt] = &_M_before_begin; + _M_before_begin._M_nxt = __this_n; + } + + __prev_n = __this_n; + __prev_ht_n = __ht_n; + __ht_n = __ht_n->_M_next(); + } + while (__ht_n + && __ht._M_is_nxt_in_bucket(__bkt, __prev_ht_n, + __nxt_bkt_n)); + } + } + __catch(...) + { + clear(); + if (__buckets) + _M_deallocate_buckets(); + __throw_exception_again; + } + } + template::value, const _Hashtable&, _Hashtable&&>; - _M_assign(std::forward<_Fwd_Ht>(__ht), __node_gen); + _M_assign_stable(std::forward<_Fwd_Ht>(__ht), __node_gen); __ht.clear(); } } diff --git a/libstdc++-v3/include/bits/hashtable_policy.h b/libstdc++-v3/include/bits/hashtable_policy.h index ff206a6ed20..becafcd3249 100644 --- a/libstdc++-v3/include/bits/hashtable_policy.h +++ b/libstdc++-v3/include/bits/hashtable_policy.h @@ -37,6 +37,7 @@ #include // for std::pair #include // for __gnu_cxx::__aligned_buffer #include // for std::__alloc_rebind +#include // for std::get_temporary_buffer. #include // for __gnu_cxx::__int_traits namespace std _GLIBCXX_VISIBILITY(default) @@ -291,6 +292,113 @@ namespace __detail __hashtable_alloc& _M_h; }; + template + struct _PreAllocHashtableNodes + { + private: + using __node_alloc_type = _NodeAlloc; + using __hashtable_alloc = _Hashtable_alloc<__node_alloc_type>; + using __node_alloc_traits = + typename __hashtable_alloc::__node_alloc_traits; + using __node_base = typename __hashtable_alloc::__node_base; + + public: + using __node_ptr = typename __hashtable_alloc::__node_ptr; + + template + _PreAllocHashtableNodes(_Ht& __ht, __hashtable_alloc& __ha) + : _M_h(__ha), _M_before_free_nodes() + , _M_buckets(std::get_temporary_buffer<__node_base*>(__ht.bucket_count())) + { + __builtin_memset(_M_buckets.first, 0, + _M_buckets.second * sizeof(__node_base*)); + __try + { + __node_ptr __hint = nullptr; + __node_base* __prev = std::__addressof(_M_before_free_nodes); + for (std::size_t __bkt = 0; __bkt != _M_buckets.second; ++__bkt) + for (auto __lit = __ht.begin(__bkt); __lit != __ht.end(__bkt); + ++__lit) + { + __node_ptr __tmp = _M_h._M_allocate_node_ptr(__hint); + if (!_M_buckets.first[__bkt]) + _M_buckets.first[__bkt] = __prev; + + __prev->_M_nxt = __tmp; + __prev = __hint = __tmp; + } + } + __catch(...) + { + _M_deallocate_nodes(); + __throw_exception_again; + } + } + + _PreAllocHashtableNodes(const _PreAllocHashtableNodes&) = delete; + + ~_PreAllocHashtableNodes() + { _M_deallocate_nodes(); } + + template + __node_ptr + operator()(__node_ptr __hint, std::size_t __bkt, _Args&&... __args) const + { + if (__bkt >= _M_buckets.second || _M_buckets.first[__bkt] == nullptr) + return _M_h._M_allocate_node(__hint, + std::forward<_Args>(__args)...); + + if (_M_buckets.first[__bkt] != std::__addressof(_M_before_free_nodes)) + { + _M_buckets.first[_M_last_access_bkt] = nullptr; + _M_buckets.first[__bkt] = std::__addressof(_M_before_free_nodes); + } + + _M_last_access_bkt = __bkt; + auto __prev = _M_buckets.first[__bkt]; + __node_ptr __node = static_cast<__node_ptr>(__prev->_M_nxt); + _M_before_free_nodes._M_nxt = __node->_M_nxt; + if (_M_before_free_nodes._M_nxt == nullptr) + _M_buckets.first[__bkt] = nullptr; + + __node->_M_nxt = nullptr; + auto& __a = _M_h._M_node_allocator(); + __try + { + __node_alloc_traits::construct(__a, __node->_M_valptr(), + std::forward<_Args>(__args)...); + } + __catch(...) + { + _M_h._M_deallocate_node_ptr(__node); + __throw_exception_again; + } + + return __node; + } + + private: + void + _M_deallocate_nodes() + { + __node_ptr __n + = static_cast<__node_ptr>(_M_before_free_nodes._M_nxt); + while (__n) + { + __node_ptr __tmp = __n; + __n = __n->_M_next(); + _M_h._M_deallocate_node_ptr(__tmp); + } + + std::return_temporary_buffer(_M_buckets.first); + } + + mutable __node_base _M_before_free_nodes; + mutable std::pair<__node_base**, std::ptrdiff_t> _M_buckets; + mutable std::size_t _M_last_access_bkt = 0; + __hashtable_alloc& _M_h; + }; + // Auxiliary types used for all instantiations of _Hashtable nodes // and iterators. @@ -2031,6 +2139,10 @@ namespace __detail _M_node_allocator() const { return __ebo_node_alloc::_M_cget(); } + // Allocate a node. + __node_ptr + _M_allocate_node_ptr(__node_ptr __hint); + // Allocate a node and construct an element within it. template __node_ptr @@ -2058,6 +2170,27 @@ namespace __detail // Definitions of class template _Hashtable_alloc's out-of-line member // functions. + template + auto + _Hashtable_alloc<_NodeAlloc>::_M_allocate_node_ptr(__node_ptr __hint) + -> __node_ptr + { + auto& __alloc = _M_node_allocator(); + typename __node_alloc_traits::pointer __nptr; + if (__hint) + { + typedef typename __node_alloc_traits::const_pointer _CPtr; + auto __hptr = std::pointer_traits<_CPtr>::pointer_to(*__hint); + __nptr = __node_alloc_traits::allocate(__alloc, 1, __hptr); + } + else + __nptr = __node_alloc_traits::allocate(__alloc, 1); + + __node_ptr __n = std::__to_address(__nptr); + ::new ((void*)__n) __node_type; + return __n; + } + template template auto diff --git a/libstdc++-v3/testsuite/23_containers/unordered_multiset/cons/copy.cc b/libstdc++-v3/testsuite/23_containers/unordered_multiset/cons/copy.cc index 22bedb9065f..c4e521ab662 100644 --- a/libstdc++-v3/testsuite/23_containers/unordered_multiset/cons/copy.cc +++ b/libstdc++-v3/testsuite/23_containers/unordered_multiset/cons/copy.cc @@ -38,6 +38,6 @@ int main() std::unordered_multiset copy(ref); VERIFY( copy.size() == ref.size() ); - VERIFY( std::equal(ref.begin(), ref.end(), copy.begin()) ); + VERIFY( copy == ref ); return 0; } diff --git a/libstdc++-v3/testsuite/util/exception/safety.h b/libstdc++-v3/testsuite/util/exception/safety.h index 8ef91648af6..ad55812be1a 100644 --- a/libstdc++-v3/testsuite/util/exception/safety.h +++ b/libstdc++-v3/testsuite/util/exception/safety.h @@ -235,12 +235,11 @@ namespace __gnu_test "setup_base::compare containers size not equal"); // Should test iterator validity before and after exception. - bool __equal_it = std::equal(__test.begin(), __test.end(), - __control.begin()); + bool __equal = __test == __control; - if (!__equal_it) + if (!__equal) throw std::logic_error( - "setup_base::compare containers iterators not equal"); + "setup_base::compare containers not equal"); return true; }