From 0ff1fa0f6e33b4add4361dc181edde6b183fe47f Mon Sep 17 00:00:00 2001 From: Christian Mazakas Date: Thu, 23 Feb 2023 11:05:38 -0800 Subject: [PATCH 01/18] Add test that ensures proper uses-allocator construction is followed --- test/Jamfile.v2 | 1 + test/unordered/uses_allocator.cpp | 189 ++++++++++++++++++++++++++++++ 2 files changed, 190 insertions(+) create mode 100644 test/unordered/uses_allocator.cpp diff --git a/test/Jamfile.v2 b/test/Jamfile.v2 index 4f7a537f..31b17efd 100644 --- a/test/Jamfile.v2 +++ b/test/Jamfile.v2 @@ -144,6 +144,7 @@ build_foa init_type_insert_tests ; build_foa max_load_tests ; build_foa extract_tests ; build_foa node_handle_tests ; +build_foa uses_allocator ; run unordered/hash_is_avalanching_test.cpp ; diff --git a/test/unordered/uses_allocator.cpp b/test/unordered/uses_allocator.cpp new file mode 100644 index 00000000..066a3585 --- /dev/null +++ b/test/unordered/uses_allocator.cpp @@ -0,0 +1,189 @@ + +// Copyright 2023 Christian Mazakas. +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#include "../helpers/test.hpp" + +#include +#include +#include +#include +#include + +#include +#include +#include + +#if BOOST_CXX_VERSION <= 199711L || \ + BOOST_WORKAROUND(BOOST_GCC_VERSION, < 40800) || \ + (defined(BOOST_LIBSTDCXX_VERSION) && BOOST_CXX_VERSION > 201703L) || \ + (defined(BOOST_MSVC_FULL_VER) && BOOST_MSVC_FULL_VER >= 192000000 && \ + BOOST_MSVC_FULL_VER < 193000000) + +// automatically disable this test for C++03 builds so we can use the STL's +// scoped_allocator_adaptor +// we remove C++20 support for libstdc++ builds because of: +// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=108952 +// +// msvc-14.2 w/ C++20 is similarly affected +// + + +BOOST_PRAGMA_MESSAGE("uses_allocator tests require C++11, scoped_allocator") + +int main() {} + +#else + +#include +#include +#include +#include + +template struct allocator +{ + typedef T value_type; + + int tag_ = -1; + + allocator() = default; + allocator(int tag) : tag_{tag} {} + allocator(allocator const&) = default; + allocator(allocator&&) = default; + + template allocator(allocator const& rhs) : tag_{rhs.tag_} {} + + BOOST_ATTRIBUTE_NODISCARD T* allocate(std::size_t n) + { + return static_cast(::operator new(n * sizeof(T))); + } + + void deallocate(T* p, std::size_t) noexcept { ::operator delete(p); } + + allocator& operator=(allocator const& rhs) + { + tag_ = rhs.tag_; + return *this; + } + + allocator& operator=(allocator&& rhs) noexcept + { + tag_ = rhs.tag_; + return *this; + } + + bool operator==(allocator const&) const { return true; } + bool operator!=(allocator const&) const { return false; } +}; + +struct raii_tracker +{ + static int count; + static int copy_count; + static int move_count; + static int alloc_move_count; + + using allocator_type = allocator; + + allocator_type a_; + + raii_tracker(allocator_type a) : a_(a) { ++count; } + raii_tracker(int, allocator_type const& a) : a_(a) { ++count; } + + raii_tracker(raii_tracker const&) { ++copy_count; } + raii_tracker(raii_tracker&&) noexcept { ++move_count; } + raii_tracker(raii_tracker&&, allocator_type const& a) noexcept : a_(a) + { + ++alloc_move_count; + } + + allocator_type get_allocator() const noexcept { return a_; } + + friend bool operator==(raii_tracker const&, raii_tracker const&) + { + return true; + } +}; + +int raii_tracker::count = 0; +int raii_tracker::copy_count = 0; +int raii_tracker::move_count = 0; +int raii_tracker::alloc_move_count = 0; + +static void reset_counts() +{ + raii_tracker::count = 0; + raii_tracker::copy_count = 0; + raii_tracker::move_count = 0; + raii_tracker::alloc_move_count = 0; +} + +std::size_t hash_value(raii_tracker const&) { return 0; } + +using map_allocator_type = std::scoped_allocator_adaptor< + allocator >, allocator >; + +using set_allocator_type = + std::scoped_allocator_adaptor, allocator >; + +using map_type = boost::unordered_flat_map, std::equal_to, map_allocator_type>; + +using node_map_type = boost::unordered_node_map, std::equal_to, map_allocator_type>; + +using set_type = boost::unordered_flat_set, std::equal_to, set_allocator_type>; + +using node_set_type = boost::unordered_node_set, std::equal_to, set_allocator_type>; + +map_type* flat_map; +node_map_type* node_map; + +set_type* flat_set; +node_set_type* node_set; + +template static void map_uses_allocator_construction(X*) +{ + reset_counts(); + + map_allocator_type alloc( + allocator >{12}, + allocator{34}); + + X map(1, alloc); + map.emplace( + std::piecewise_construct, std::make_tuple(1337), std::make_tuple(7331)); + + BOOST_TEST_EQ(raii_tracker::count, 2); + BOOST_TEST_EQ(raii_tracker::move_count, 0); + BOOST_TEST_EQ(raii_tracker::alloc_move_count, 2); + + BOOST_TEST_EQ(map.begin()->first.get_allocator().tag_, 34); + BOOST_TEST_EQ(map.begin()->second.get_allocator().tag_, 34); +} + +template static void set_uses_allocator_construction(X*) +{ + reset_counts(); + + set_allocator_type alloc(allocator{12}, allocator{34}); + + X set(1, alloc); + set.emplace(); + + BOOST_TEST_EQ(raii_tracker::count, 1); + BOOST_TEST_EQ(raii_tracker::move_count, 0); + BOOST_TEST_EQ(raii_tracker::alloc_move_count, 1); + + BOOST_TEST_EQ(set.begin()->get_allocator().tag_, 34); +} + +UNORDERED_TEST(map_uses_allocator_construction, ((flat_map)(node_map))) +UNORDERED_TEST(set_uses_allocator_construction, ((flat_set)(node_set))) + +RUN_TESTS() + +#endif From 86318c1e881e6c10ecde05a3d9a6f172a17e98fe Mon Sep 17 00:00:00 2001 From: Christian Mazakas Date: Thu, 23 Feb 2023 11:06:14 -0800 Subject: [PATCH 02/18] Update foa to construct stack-locals with the user's Allocator during emplace() --- include/boost/unordered/detail/foa.hpp | 90 ++++++++++--------- .../unordered/detail/foa/node_handle.hpp | 13 ++- .../boost/unordered/unordered_flat_map.hpp | 22 ++++- .../boost/unordered/unordered_node_map.hpp | 45 +++++++--- .../boost/unordered/unordered_node_set.hpp | 25 +++--- 5 files changed, 126 insertions(+), 69 deletions(-) diff --git a/include/boost/unordered/detail/foa.hpp b/include/boost/unordered/detail/foa.hpp index 0f9f1b79..5c6ca111 100644 --- a/include/boost/unordered/detail/foa.hpp +++ b/include/boost/unordered/detail/foa.hpp @@ -1129,6 +1129,25 @@ _STL_RESTORE_DEPRECATED_WARNING */ constexpr static float const mlf = 0.875f; +template +union storage +{ + T t_; + storage(){} + ~storage(){} +}; + +template +struct drop_guard +{ + using type_policy=TypePolicy; + + A& a; + T* p; + ~drop_guard(){type_policy::destroy(a,p);}; +}; + + /* foa::table interface departs in a number of ways from that of C++ unordered * associative containers because it's not for end-user consumption * (boost::unordered_[flat|node]_[map|set]) wrappers complete it as @@ -1418,18 +1437,35 @@ public: template BOOST_FORCEINLINE std::pair emplace(Args&&... args) { - /* We dispatch based on whether or not the value_type is constructible from - * an rvalue reference of the deduced emplace_type. We do this specifically - * for the case of the node-based containers. To this end, we're able to - * avoid allocating a node when a duplicate element is attempted to be - * inserted. For immovable types, we instead dispatch to the routine that - * unconditionally allocates via `type_policy::construct()`. - */ - return emplace_value( + using emplace_type=typename std::conditional< + std::is_constructible::value, + init_type, + value_type + >::type; + + using insert_type=typename std::conditional< std::is_constructible< - value_type, - emplace_type&&>{}, - std::forward(args)...); + value_type,emplace_type>::value, + emplace_type,element_type + >::type; + + using alloc_insert_type=typename std::conditional< + std::is_constructible< + value_type,emplace_type>::value, + emplace_type,value_type + >::type; + + using alloc_type= + typename boost::allocator_rebind::type; + + storage s; + alloc_type alloc{al()}; + auto *p=std::addressof(s.t_); + + type_policy::construct(alloc,p,std::forward(args)...); + + drop_guard guard{alloc,p}; + return emplace_impl(type_policy::move(*p)); } template @@ -1614,15 +1650,6 @@ private: template friend class table; using arrays_type=table_arrays; - template - using emplace_type = typename std::conditional< - std::is_constructible< - init_type,Args... - >::value, - init_type, - value_type - >::type; - struct clear_on_exit { ~clear_on_exit(){x.clear();} @@ -1904,29 +1931,6 @@ private: #pragma warning(pop) /* C4800 */ #endif - template - BOOST_FORCEINLINE std::pair emplace_value( - std::true_type /* movable value_type */,Args&&... args - ) { - using emplace_type_t = emplace_type; - return emplace_impl(emplace_type_t(std::forward(args)...)); - } - - template - BOOST_FORCEINLINE std::pair emplace_value( - std::false_type /* immovable value_type */,Args&&... args - ) { - alignas(element_type) - unsigned char buf[sizeof(element_type)]; - element_type* p = reinterpret_cast(buf); - - type_policy::construct(al(),p,std::forward(args)...); - destroy_element_on_exit d{this,p}; - (void)d; - - return emplace_impl(type_policy::move(*p)); - } - template BOOST_FORCEINLINE std::pair emplace_impl(Args&&... args) { diff --git a/include/boost/unordered/detail/foa/node_handle.hpp b/include/boost/unordered/detail/foa/node_handle.hpp index 1f6319db..49edd4f4 100644 --- a/include/boost/unordered/detail/foa/node_handle.hpp +++ b/include/boost/unordered/detail/foa/node_handle.hpp @@ -105,6 +105,9 @@ struct node_handle_base node_handle_base& operator=(node_handle_base&& nh)noexcept { + element_type x; + x.p=p_; + if(this!=&nh){ if(empty()){ if(nh.empty()){ /* empty(), nh.empty() */ @@ -115,7 +118,7 @@ struct node_handle_base } }else{ if(nh.empty()){ /* !empty(), nh.empty() */ - type_policy::destroy(al(),p_); + type_policy::destroy(al(),&x); reset(); }else{ /* !empty(), !nh.empty() */ bool const pocma= @@ -124,7 +127,7 @@ struct node_handle_base BOOST_ASSERT(pocma||al()==nh.al()); - type_policy::destroy(al(),p_); + type_policy::destroy(al(),&x); if(pocma){ al()=std::move(nh.al()); } @@ -137,7 +140,7 @@ struct node_handle_base if(empty()){ /* empty(), nh.empty() */ /* nothing to do */ }else{ /* !empty(), !nh.empty() */ - type_policy::destroy(al(),p_); + type_policy::destroy(al(),&x); reset(); } } @@ -147,7 +150,9 @@ struct node_handle_base ~node_handle_base() { if(!empty()){ - type_policy::destroy(al(),p_); + element_type x; + x.p=p_; + type_policy::destroy(al(),&x); reset(); } } diff --git a/include/boost/unordered/unordered_flat_map.hpp b/include/boost/unordered/unordered_flat_map.hpp index d419739b..758694a1 100644 --- a/include/boost/unordered/unordered_flat_map.hpp +++ b/include/boost/unordered/unordered_flat_map.hpp @@ -53,6 +53,11 @@ namespace boost { return kv.first; } + static moved_type move(init_type& x) + { + return {std::get<0>(std::move(x)), std::get<1>(std::move(x))}; + } + static moved_type move(element_type& x) { // TODO: we probably need to launder here @@ -61,11 +66,26 @@ namespace boost { } template - static void construct(A& al, element_type* p, Args&&... args) + static void construct(A& al, init_type* p, Args&&... args) { boost::allocator_construct(al, p, std::forward(args)...); } + template + static void construct(A& al, element_type* p, Args&&... args) + { + using alloc_type = + typename boost::allocator_rebind::type; + alloc_type alloc(al); + std::allocator_traits::construct( + alloc, p, std::forward(args)...); + } + + template static void destroy(A& al, init_type* p) noexcept + { + boost::allocator_destroy(al, p); + } + template static void destroy(A& al, element_type* p) noexcept { boost::allocator_destroy(al, p); diff --git a/include/boost/unordered/unordered_node_map.hpp b/include/boost/unordered/unordered_node_map.hpp index 3802d494..60eb5cc0 100644 --- a/include/boost/unordered/unordered_node_map.hpp +++ b/include/boost/unordered/unordered_node_map.hpp @@ -77,6 +77,11 @@ namespace boost { } static element_type&& move(element_type& x) { return std::move(x); } + static moved_type move(init_type& x) + { + return {std::get<0>(std::move(x)), std::get<1>(std::move(x))}; + } + static moved_type move(value_type& x) { return {std::move(const_cast(x.first)), @@ -96,6 +101,18 @@ namespace boost { construct(al, p, *copy.p); } + template + static void construct(A& al, init_type* p, Args&&... args) + { + boost::allocator_construct(al, p, std::forward(args)...); + } + + template + static void construct(A& al, value_type* p, Args&&... args) + { + boost::allocator_construct(al, p, std::forward(args)...); + } + template static void construct(A& al, element_type* p, Args&&... args) { @@ -106,10 +123,11 @@ namespace boost { } BOOST_CATCH(...) { - boost::allocator_deallocate(al, - boost::pointer_traits< - typename boost::allocator_pointer::type>::pointer_to(*p->p), - 1); + using pointer_type=typename boost::allocator_pointer::type; + using pointer_traits=boost::pointer_traits; + + boost::allocator_deallocate( + al,pointer_traits::pointer_to(*(p->p)),1); BOOST_RETHROW } BOOST_CATCH_END @@ -118,16 +136,22 @@ namespace boost { template static void destroy(A& al, value_type* p) noexcept { boost::allocator_destroy(al, p); - boost::allocator_deallocate(al, - boost::pointer_traits< - typename boost::allocator_pointer::type>::pointer_to(*p), - 1); + } + + template static void destroy(A& al, init_type* p) noexcept + { + boost::allocator_destroy(al, p); } template static void destroy(A& al, element_type* p) noexcept { if (p->p) { - destroy(al,p->p); + using pointer_type=typename boost::allocator_pointer::type; + using pointer_traits=boost::pointer_traits; + + destroy(al, p->p); + boost::allocator_deallocate( + al,pointer_traits::pointer_to(*(p->p)), 1); } } }; @@ -137,8 +161,7 @@ namespace boost { : public detail::foa::node_handle_base { private: - using base_type = - detail::foa::node_handle_base; + using base_type = detail::foa::node_handle_base; using typename base_type::type_policy; diff --git a/include/boost/unordered/unordered_node_set.hpp b/include/boost/unordered/unordered_node_set.hpp index c6fc8617..0fbd715c 100644 --- a/include/boost/unordered/unordered_node_set.hpp +++ b/include/boost/unordered/unordered_node_set.hpp @@ -78,6 +78,12 @@ namespace boost { x.p = nullptr; } + template + static void construct(A& al, value_type* p, Args&&... args) + { + boost::allocator_construct(al, p, std::forward(args)...); + } + template static void construct(A& al, element_type* p, Args&&... args) { @@ -100,16 +106,16 @@ namespace boost { template static void destroy(A& al, value_type* p) noexcept { boost::allocator_destroy(al, p); - boost::allocator_deallocate(al, - boost::pointer_traits< - typename boost::allocator_pointer::type>::pointer_to(*p), - 1); } template static void destroy(A& al, element_type* p) noexcept { if (p->p) { destroy(al, p->p); + boost::allocator_deallocate(al, + boost::pointer_traits::type>::pointer_to(*(p->p)), + 1); } } }; @@ -119,8 +125,7 @@ namespace boost { : public detail::foa::node_handle_base { private: - using base_type = - detail::foa::node_handle_base; + using base_type = detail::foa::node_handle_base; using typename base_type::type_policy; @@ -391,7 +396,7 @@ namespace boost { BOOST_ASSERT(get_allocator() == nh.get_allocator()); typename set_types::element_type x; - x.p=std::addressof(nh.element()); + x.p = std::addressof(nh.element()); auto itp = table_.insert(std::move(x)); if (itp.second) { @@ -411,7 +416,7 @@ namespace boost { BOOST_ASSERT(get_allocator() == nh.get_allocator()); typename set_types::element_type x; - x.p=std::addressof(nh.element()); + x.p = std::addressof(nh.element()); auto itp = table_.insert(std::move(x)); if (itp.second) { @@ -478,7 +483,7 @@ namespace boost { node_type extract(key_type const& key) { auto pos = find(key); - return pos!=end()?extract(pos):node_type(); + return pos != end() ? extract(pos) : node_type(); } template @@ -489,7 +494,7 @@ namespace boost { extract(K const& key) { auto pos = find(key); - return pos!=end()?extract(pos):node_type(); + return pos != end() ? extract(pos) : node_type(); } template From 5c63cd2bdb8cc5467c579fd5e7e239cc3b581263 Mon Sep 17 00:00:00 2001 From: Christian Mazakas Date: Mon, 27 Feb 2023 10:07:40 -0800 Subject: [PATCH 03/18] Update drone to run using the same branches as listed in GHA's ci.yml --- .drone.jsonnet | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.drone.jsonnet b/.drone.jsonnet index ec0258b7..51340c4c 100644 --- a/.drone.jsonnet +++ b/.drone.jsonnet @@ -6,7 +6,7 @@ local library = "unordered"; local triggers = { - branch: [ "master", "develop", "feature/*", "bugfix/*" ] + branch: [ "master", "develop", "feature/*", "bugfix/*", "fix/*", "pr/*" ] }; local ubsan = { UBSAN: '1', UBSAN_OPTIONS: 'print_stacktrace=1' }; From 45d6c07be6f77ac84b468e91b669d5ab9f8e9312 Mon Sep 17 00:00:00 2001 From: Christian Mazakas Date: Thu, 2 Mar 2023 15:47:18 -0800 Subject: [PATCH 04/18] Update emplace() to no longer needlessly rebind its allocator --- include/boost/unordered/detail/foa.hpp | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/include/boost/unordered/detail/foa.hpp b/include/boost/unordered/detail/foa.hpp index 5c6ca111..7770a9c2 100644 --- a/include/boost/unordered/detail/foa.hpp +++ b/include/boost/unordered/detail/foa.hpp @@ -1449,22 +1449,12 @@ public: emplace_type,element_type >::type; - using alloc_insert_type=typename std::conditional< - std::is_constructible< - value_type,emplace_type>::value, - emplace_type,value_type - >::type; - - using alloc_type= - typename boost::allocator_rebind::type; - storage s; - alloc_type alloc{al()}; auto *p=std::addressof(s.t_); - type_policy::construct(alloc,p,std::forward(args)...); + type_policy::construct(al(),p,std::forward(args)...); - drop_guard guard{alloc,p}; + drop_guard guard{al(),p}; return emplace_impl(type_policy::move(*p)); } From 5f6f1691e529d09dbe00d074057753b7ddd8f00a Mon Sep 17 00:00:00 2001 From: Christian Mazakas Date: Thu, 2 Mar 2023 15:47:48 -0800 Subject: [PATCH 05/18] Format node_map --- include/boost/unordered/unordered_node_map.hpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/include/boost/unordered/unordered_node_map.hpp b/include/boost/unordered/unordered_node_map.hpp index 60eb5cc0..c6f25b83 100644 --- a/include/boost/unordered/unordered_node_map.hpp +++ b/include/boost/unordered/unordered_node_map.hpp @@ -123,11 +123,11 @@ namespace boost { } BOOST_CATCH(...) { - using pointer_type=typename boost::allocator_pointer::type; - using pointer_traits=boost::pointer_traits; + using pointer_type = typename boost::allocator_pointer::type; + using pointer_traits = boost::pointer_traits; boost::allocator_deallocate( - al,pointer_traits::pointer_to(*(p->p)),1); + al, pointer_traits::pointer_to(*(p->p)), 1); BOOST_RETHROW } BOOST_CATCH_END @@ -146,12 +146,12 @@ namespace boost { template static void destroy(A& al, element_type* p) noexcept { if (p->p) { - using pointer_type=typename boost::allocator_pointer::type; - using pointer_traits=boost::pointer_traits; + using pointer_type = typename boost::allocator_pointer::type; + using pointer_traits = boost::pointer_traits; destroy(al, p->p); boost::allocator_deallocate( - al,pointer_traits::pointer_to(*(p->p)), 1); + al, pointer_traits::pointer_to(*(p->p)), 1); } } }; From 088941dc7b04f769de667ecf6843391d937b981f Mon Sep 17 00:00:00 2001 From: Christian Mazakas Date: Thu, 2 Mar 2023 15:48:21 -0800 Subject: [PATCH 06/18] Clean up usage of Allocators for flat containers --- include/boost/unordered/unordered_flat_map.hpp | 10 +++------- include/boost/unordered/unordered_flat_set.hpp | 4 ++-- 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/include/boost/unordered/unordered_flat_map.hpp b/include/boost/unordered/unordered_flat_map.hpp index 758694a1..df2717c6 100644 --- a/include/boost/unordered/unordered_flat_map.hpp +++ b/include/boost/unordered/unordered_flat_map.hpp @@ -72,13 +72,9 @@ namespace boost { } template - static void construct(A& al, element_type* p, Args&&... args) + static void construct(A& al, value_type* p, Args&&... args) { - using alloc_type = - typename boost::allocator_rebind::type; - alloc_type alloc(al); - std::allocator_traits::construct( - alloc, p, std::forward(args)...); + boost::allocator_construct(al, p, std::forward(args)...); } template static void destroy(A& al, init_type* p) noexcept @@ -86,7 +82,7 @@ namespace boost { boost::allocator_destroy(al, p); } - template static void destroy(A& al, element_type* p) noexcept + template static void destroy(A& al, value_type* p) noexcept { boost::allocator_destroy(al, p); } diff --git a/include/boost/unordered/unordered_flat_set.hpp b/include/boost/unordered/unordered_flat_set.hpp index 8829742e..2138a9f3 100644 --- a/include/boost/unordered/unordered_flat_set.hpp +++ b/include/boost/unordered/unordered_flat_set.hpp @@ -46,12 +46,12 @@ namespace boost { static element_type&& move(element_type& x) { return std::move(x); } template - static void construct(A& al, element_type* p, Args&&... args) + static void construct(A& al, value_type* p, Args&&... args) { boost::allocator_construct(al, p, std::forward(args)...); } - template static void destroy(A& al, element_type* p) noexcept + template static void destroy(A& al, value_type* p) noexcept { boost::allocator_destroy(al, p); } From 5a5c31de350be0839f5e6b5374e05d0e50754b20 Mon Sep 17 00:00:00 2001 From: Christian Mazakas Date: Thu, 2 Mar 2023 15:49:25 -0800 Subject: [PATCH 07/18] Rename `storage` to `uninitialized_storage` --- include/boost/unordered/detail/foa.hpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/include/boost/unordered/detail/foa.hpp b/include/boost/unordered/detail/foa.hpp index 7770a9c2..684aefdd 100644 --- a/include/boost/unordered/detail/foa.hpp +++ b/include/boost/unordered/detail/foa.hpp @@ -1130,11 +1130,11 @@ _STL_RESTORE_DEPRECATED_WARNING constexpr static float const mlf = 0.875f; template -union storage +union uninitialized_storage { T t_; - storage(){} - ~storage(){} + uninitialized_storage(){} + ~uninitialized_storage(){} }; template @@ -1449,8 +1449,8 @@ public: emplace_type,element_type >::type; - storage s; - auto *p=std::addressof(s.t_); + uninitialized_storage s; + auto *p=std::addressof(s.t_); type_policy::construct(al(),p,std::forward(args)...); From 48e92afd9209a780d37a2b55083658b7391ee065 Mon Sep 17 00:00:00 2001 From: Christian Mazakas Date: Thu, 2 Mar 2023 15:55:56 -0800 Subject: [PATCH 08/18] Refactor drop_guard into destroy_on_exit --- include/boost/unordered/detail/foa.hpp | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/include/boost/unordered/detail/foa.hpp b/include/boost/unordered/detail/foa.hpp index 684aefdd..b7c7d8b3 100644 --- a/include/boost/unordered/detail/foa.hpp +++ b/include/boost/unordered/detail/foa.hpp @@ -1137,17 +1137,6 @@ union uninitialized_storage ~uninitialized_storage(){} }; -template -struct drop_guard -{ - using type_policy=TypePolicy; - - A& a; - T* p; - ~drop_guard(){type_policy::destroy(a,p);}; -}; - - /* foa::table interface departs in a number of ways from that of C++ unordered * associative containers because it's not for end-user consumption * (boost::unordered_[flat|node]_[map|set]) wrappers complete it as @@ -1454,7 +1443,7 @@ public: type_policy::construct(al(),p,std::forward(args)...); - drop_guard guard{al(),p}; + destroy_on_exit guard{al(),p}; return emplace_impl(type_policy::move(*p)); } @@ -1658,6 +1647,16 @@ private: bool rollback_=false; }; + template + struct destroy_on_exit + { + using type_policy=TypePolicy; + + Allocator &a; + T *p; + ~destroy_on_exit(){type_policy::destroy(a,p);}; + }; + Hash& h(){return hash_base::get();} const Hash& h()const{return hash_base::get();} Pred& pred(){return pred_base::get();} From 86d3f9f6321489497c974ec71d64b0c67e126f47 Mon Sep 17 00:00:00 2001 From: Christian Mazakas Date: Thu, 2 Mar 2023 15:57:17 -0800 Subject: [PATCH 09/18] Refactor `move(init_type&)` to handle defects in C++20 versions of libstdc++ for gcc versions <=12 --- include/boost/unordered/unordered_flat_map.hpp | 2 +- include/boost/unordered/unordered_node_map.hpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/include/boost/unordered/unordered_flat_map.hpp b/include/boost/unordered/unordered_flat_map.hpp index df2717c6..7fb736f6 100644 --- a/include/boost/unordered/unordered_flat_map.hpp +++ b/include/boost/unordered/unordered_flat_map.hpp @@ -55,7 +55,7 @@ namespace boost { static moved_type move(init_type& x) { - return {std::get<0>(std::move(x)), std::get<1>(std::move(x))}; + return {std::move(x.first), std::move(x.second)}; } static moved_type move(element_type& x) diff --git a/include/boost/unordered/unordered_node_map.hpp b/include/boost/unordered/unordered_node_map.hpp index c6f25b83..6c5611fc 100644 --- a/include/boost/unordered/unordered_node_map.hpp +++ b/include/boost/unordered/unordered_node_map.hpp @@ -79,7 +79,7 @@ namespace boost { static element_type&& move(element_type& x) { return std::move(x); } static moved_type move(init_type& x) { - return {std::get<0>(std::move(x)), std::get<1>(std::move(x))}; + return {std::move(x.first), std::move(x.second)}; } static moved_type move(value_type& x) From 8429d1a6aa0626bbd62e1ea4f94dcf6bc09521cc Mon Sep 17 00:00:00 2001 From: Christian Mazakas Date: Fri, 3 Mar 2023 11:12:29 -0800 Subject: [PATCH 10/18] Refactor node_handle to directly store element_type by modularizing and extending it --- .../unordered/detail/foa/element_type.hpp | 60 ++++++++++++++++ .../unordered/detail/foa/node_handle.hpp | 68 ++++++++++--------- .../boost/unordered/unordered_node_map.hpp | 33 ++------- .../boost/unordered/unordered_node_set.hpp | 31 ++------- 4 files changed, 106 insertions(+), 86 deletions(-) create mode 100644 include/boost/unordered/detail/foa/element_type.hpp diff --git a/include/boost/unordered/detail/foa/element_type.hpp b/include/boost/unordered/detail/foa/element_type.hpp new file mode 100644 index 00000000..650e3b4f --- /dev/null +++ b/include/boost/unordered/detail/foa/element_type.hpp @@ -0,0 +1,60 @@ +/* Copyright 2023 Christian Mazakas. + * Distributed under the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See https://www.boost.org/libs/unordered for library home page. + */ + +#ifndef BOOST_UNORDERED_DETAIL_FOA_ELEMENT_TYPE_HPP +#define BOOST_UNORDERED_DETAIL_FOA_ELEMENT_TYPE_HPP + +namespace boost{ +namespace unordered{ +namespace detail{ +namespace foa{ + +template +struct element_type +{ + using value_type=T; + value_type* p; + + /* + * we use a deleted copy constructor here so the type is no longer + * trivially copy-constructible which inhibits our memcpy + * optimizations when copying the tables + */ + element_type() = default; + element_type(value_type* p_):p(p_){} + element_type(element_type const&) = delete; + element_type(element_type&& rhs) noexcept + { + p = rhs.p; + rhs.p = nullptr; + } + + element_type& operator=(element_type const&)=delete; + element_type& operator=(element_type&& rhs)noexcept + { + if (this!=&rhs){ + p=rhs.p; + rhs.p=nullptr; + } + return *this; + } + + void swap(element_type& rhs)noexcept + { + auto tmp=p; + p=rhs.p; + rhs.p=tmp; + } +}; + +} +} +} +} + +#endif // BOOST_UNORDERED_DETAIL_FOA_ELEMENT_TYPE_HPP diff --git a/include/boost/unordered/detail/foa/node_handle.hpp b/include/boost/unordered/detail/foa/node_handle.hpp index 49edd4f4..ddf34414 100644 --- a/include/boost/unordered/detail/foa/node_handle.hpp +++ b/include/boost/unordered/detail/foa/node_handle.hpp @@ -45,20 +45,30 @@ struct node_handle_base private: using node_value_type=typename type_policy::value_type; - node_value_type* p_=nullptr; + element_type p_; BOOST_ATTRIBUTE_NO_UNIQUE_ADDRESS opt_storage a_; protected: - node_value_type& element()noexcept + node_value_type& data()noexcept { - BOOST_ASSERT(!empty()); - return *p_; + return *(p_.p); } - node_value_type const& element()const noexcept + node_value_type const& data()const noexcept + { + return *(p_.p); + } + + element_type& element()noexcept { BOOST_ASSERT(!empty()); - return *p_; + return p_; + } + + element_type const& element()const noexcept + { + BOOST_ASSERT(!empty()); + return p_; } Allocator& al()noexcept @@ -73,52 +83,46 @@ struct node_handle_base return a_.t_; } - void emplace(node_value_type* p,Allocator a) - { - BOOST_ASSERT(empty()); - p_=p; - new(&a_.t_)Allocator(a); - } - void emplace(element_type&& x,Allocator a) { - emplace(x.p,a); + BOOST_ASSERT(empty()); + auto* p=x.p; + p_.p=p; + new(&a_.t_)Allocator(a); x.p=nullptr; } void reset() { - al().~Allocator(); - p_=nullptr; + a_.t_.~Allocator(); + p_.p=nullptr; } public: - constexpr node_handle_base()noexcept{} + constexpr node_handle_base()noexcept:p_{nullptr}{} node_handle_base(node_handle_base&& nh) noexcept { + p_.p = nullptr; if (!nh.empty()){ - emplace(nh.p_,nh.al()); + emplace(std::move(nh.p_),nh.al()); nh.reset(); } } node_handle_base& operator=(node_handle_base&& nh)noexcept { - element_type x; - x.p=p_; - if(this!=&nh){ if(empty()){ if(nh.empty()){ /* empty(), nh.empty() */ /* nothing to do */ }else{ /* empty(), !nh.empty() */ - emplace(nh.p_,std::move(nh.al())); + emplace(std::move(nh.p_),std::move(nh.al())); nh.reset(); } }else{ if(nh.empty()){ /* !empty(), nh.empty() */ - type_policy::destroy(al(),&x); + type_policy::destroy(al(),&p_); reset(); }else{ /* !empty(), !nh.empty() */ bool const pocma= @@ -127,12 +131,12 @@ struct node_handle_base BOOST_ASSERT(pocma||al()==nh.al()); - type_policy::destroy(al(),&x); + type_policy::destroy(al(),&p_); if(pocma){ al()=std::move(nh.al()); } - p_=nh.p_; + p_=std::move(nh.p_); nh.reset(); } } @@ -140,7 +144,7 @@ struct node_handle_base if(empty()){ /* empty(), nh.empty() */ /* nothing to do */ }else{ /* !empty(), !nh.empty() */ - type_policy::destroy(al(),&x); + type_policy::destroy(al(),&p_); reset(); } } @@ -150,16 +154,14 @@ struct node_handle_base ~node_handle_base() { if(!empty()){ - element_type x; - x.p=p_; - type_policy::destroy(al(),&x); + type_policy::destroy(al(),&p_); reset(); } } allocator_type get_allocator()const noexcept{return al();} explicit operator bool()const noexcept{ return !empty();} - BOOST_ATTRIBUTE_NODISCARD bool empty()const noexcept{return p_==nullptr;} + BOOST_ATTRIBUTE_NODISCARD bool empty()const noexcept{return p_.p==nullptr;} void swap(node_handle_base& nh) noexcept( boost::allocator_is_always_equal::type::value|| @@ -170,12 +172,12 @@ struct node_handle_base if(nh.empty()) { /* nothing to do here */ } else { - emplace(nh.p_, nh.al()); + emplace(std::move(nh.p_), nh.al()); nh.reset(); } }else{ if(nh.empty()){ - nh.emplace(p_,al()); + nh.emplace(std::move(p_),al()); reset(); }else{ bool const pocs= @@ -185,7 +187,7 @@ struct node_handle_base BOOST_ASSERT(pocs || al()==nh.al()); using std::swap; - swap(p_,nh.p_); + p_.swap(nh.p_); if(pocs)swap(al(),nh.al()); } } diff --git a/include/boost/unordered/unordered_node_map.hpp b/include/boost/unordered/unordered_node_map.hpp index 6c5611fc..450e7809 100644 --- a/include/boost/unordered/unordered_node_map.hpp +++ b/include/boost/unordered/unordered_node_map.hpp @@ -11,6 +11,7 @@ #endif #include +#include #include #include #include @@ -45,23 +46,7 @@ namespace boost { using value_type = std::pair; using moved_type = std::pair; - struct element_type - { - value_type* p; - - /* - * we use a deleted copy constructor here so the type is no longer - * trivially copy-constructible which inhibits our memcpy - * optimizations when copying the tables - */ - element_type() = default; - element_type(element_type const&) = delete; - element_type(element_type&& rhs) noexcept - { - p = rhs.p; - rhs.p = nullptr; - } - }; + using element_type=foa::element_type; static value_type& value_from(element_type const& x) { return *(x.p); } @@ -180,13 +165,13 @@ namespace boost { key_type& key() const { BOOST_ASSERT(!this->empty()); - return const_cast(this->element().first); + return const_cast(this->data().first); } mapped_type& mapped() const { BOOST_ASSERT(!this->empty()); - return const_cast(this->element().second); + return const_cast(this->data().second); } }; } // namespace detail @@ -425,10 +410,7 @@ namespace boost { BOOST_ASSERT(get_allocator() == nh.get_allocator()); - typename map_types::element_type x; - x.p = std::addressof(nh.element()); - - auto itp = table_.insert(std::move(x)); + auto itp = table_.insert(std::move(nh.element())); if (itp.second) { nh.reset(); return {itp.first, true, node_type{}}; @@ -445,10 +427,7 @@ namespace boost { BOOST_ASSERT(get_allocator() == nh.get_allocator()); - typename map_types::element_type x; - x.p = std::addressof(nh.element()); - - auto itp = table_.insert(std::move(x)); + auto itp = table_.insert(std::move(nh.element())); if (itp.second) { nh.reset(); return itp.first; diff --git a/include/boost/unordered/unordered_node_set.hpp b/include/boost/unordered/unordered_node_set.hpp index 0fbd715c..30a63502 100644 --- a/include/boost/unordered/unordered_node_set.hpp +++ b/include/boost/unordered/unordered_node_set.hpp @@ -11,6 +11,7 @@ #endif #include +#include #include #include #include @@ -41,23 +42,7 @@ namespace boost { static Key const& extract(value_type const& key) { return key; } - struct element_type - { - value_type* p; - - /* - * we use a deleted copy constructor here so the type is no longer - * trivially copy-constructible which inhibits our memcpy - * optimizations when copying the tables - */ - element_type() = default; - element_type(element_type const&) = delete; - element_type(element_type&& rhs) noexcept - { - p = rhs.p; - rhs.p = nullptr; - } - }; + using element_type=foa::element_type; static value_type& value_from(element_type const& x) { return *x.p; } static Key const& extract(element_type const& k) { return *k.p; } @@ -142,7 +127,7 @@ namespace boost { value_type& value() const { BOOST_ASSERT(!this->empty()); - return const_cast(this->element()); + return const_cast(this->data()); } }; } // namespace detail @@ -395,10 +380,7 @@ namespace boost { BOOST_ASSERT(get_allocator() == nh.get_allocator()); - typename set_types::element_type x; - x.p = std::addressof(nh.element()); - - auto itp = table_.insert(std::move(x)); + auto itp = table_.insert(std::move(nh.element())); if (itp.second) { nh.reset(); return {itp.first, true, node_type{}}; @@ -415,10 +397,7 @@ namespace boost { BOOST_ASSERT(get_allocator() == nh.get_allocator()); - typename set_types::element_type x; - x.p = std::addressof(nh.element()); - - auto itp = table_.insert(std::move(x)); + auto itp = table_.insert(std::move(nh.element())); if (itp.second) { nh.reset(); return itp.first; From 47dd02a4b352a4d83debc8130bd9c4292d097d02 Mon Sep 17 00:00:00 2001 From: Christian Mazakas Date: Fri, 3 Mar 2023 11:18:41 -0800 Subject: [PATCH 11/18] Add bugfix notes to release notes --- doc/unordered/changes.adoc | 3 +++ 1 file changed, 3 insertions(+) diff --git a/doc/unordered/changes.adoc b/doc/unordered/changes.adoc index c99d69a5..8f1c37b0 100644 --- a/doc/unordered/changes.adoc +++ b/doc/unordered/changes.adoc @@ -16,6 +16,9 @@ https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2023/p2363r5.html[P2363]. * Replaced the previous post-mixing process for open-addressing containers with a new algorithm based on extended multiplication by a constant. +* Fixed bug in internal emplace() impl where stack-local types were not properly + constructed using the Allocator of the container which breaks uses-allocator + construction. == Release 1.81.0 - Major update From 6c5b5b31862f3cee71d194f48f088481ea39f508 Mon Sep 17 00:00:00 2001 From: Christian Mazakas Date: Fri, 3 Mar 2023 11:33:32 -0800 Subject: [PATCH 12/18] Update comments on type policy to include changes to construct, destroy and move --- include/boost/unordered/detail/foa.hpp | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/include/boost/unordered/detail/foa.hpp b/include/boost/unordered/detail/foa.hpp index b7c7d8b3..1c080d08 100644 --- a/include/boost/unordered/detail/foa.hpp +++ b/include/boost/unordered/detail/foa.hpp @@ -1171,11 +1171,20 @@ union uninitialized_storage * a copyable const std::string&&. foa::table::insert is extended to accept * both init_type and value_type references. * - * - TypePolicy::move(element_type&) returns a temporary object for value - * transfer on rehashing, move copy/assignment, and merge. For flat map, this - * object is a std::pair, which is generally cheaper to move - * than std::pair&& because of the constness in Key. For - * node-based tables, this is used to transfer ownership of pointer. + * - TypePolicy::construct and TypePolicy::destroy are used for the + * construction and destruction of the internal types: value_type, init_type + * and element_type. For flat containers, these are often all synonyms for + * each other but for the node-based map, each one is a distinct type. These + * are used for allocator-aware construction and destruction of the types + * during insertion. + * + * - TypePolicy::move is used to provide move semantics for the internal + * types used by the container during rehashing and emplace. These types + * are init_type, value_type and emplace_type. During insertion, a + * stack-local type will be created based on the constructibility of the + * value_type and the supplied arguments. TypePolicy::move is used here + * for transfer of ownership. Similarly, TypePolicy::move is also used + * during rehashing when elements are moved to the new table. * * - TypePolicy::extract returns a const reference to the key part of * a value of type value_type, init_type, element_type or From dbba3cec7b3f868cbb02c8542d5f78a30ecd5d01 Mon Sep 17 00:00:00 2001 From: Peter Dimov Date: Mon, 6 Mar 2023 09:43:45 +0200 Subject: [PATCH 13/18] Add C++03 deprecation notice --- .../boost/unordered/detail/requires_cxx11.hpp | 21 +++++++++++++++++++ include/boost/unordered/unordered_map.hpp | 1 + include/boost/unordered/unordered_set.hpp | 1 + 3 files changed, 23 insertions(+) create mode 100644 include/boost/unordered/detail/requires_cxx11.hpp diff --git a/include/boost/unordered/detail/requires_cxx11.hpp b/include/boost/unordered/detail/requires_cxx11.hpp new file mode 100644 index 00000000..b7c7b813 --- /dev/null +++ b/include/boost/unordered/detail/requires_cxx11.hpp @@ -0,0 +1,21 @@ +#ifndef BOOST_UNORDERED_DETAIL_REQUIRES_CXX11_HPP_INCLUDED +#define BOOST_UNORDERED_DETAIL_REQUIRES_CXX11_HPP_INCLUDED + +// Copyright 2023 Peter Dimov +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt + +#include +#include + +#if defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES) || \ + defined(BOOST_NO_CXX11_RVALUE_REFERENCES) || \ + defined(BOOST_NO_CXX11_DECLTYPE) || \ + defined(BOOST_NO_CXX11_CONSTEXPR) || \ + defined(BOOST_NO_CXX11_NOEXCEPT) + +BOOST_PRAGMA_MESSAGE("C++03 support is deprecated in Boost.Unordered 1.82 and will be removed in Boost.Unordered 1.84.") + +#endif + +#endif // #ifndef BOOST_UNORDERED_DETAIL_REQUIRES_CXX11_HPP_INCLUDED diff --git a/include/boost/unordered/unordered_map.hpp b/include/boost/unordered/unordered_map.hpp index 6d05471c..8a8bc062 100644 --- a/include/boost/unordered/unordered_map.hpp +++ b/include/boost/unordered/unordered_map.hpp @@ -15,6 +15,7 @@ #pragma once #endif +#include #include #include #include diff --git a/include/boost/unordered/unordered_set.hpp b/include/boost/unordered/unordered_set.hpp index 1bd4ba3d..23837b2d 100644 --- a/include/boost/unordered/unordered_set.hpp +++ b/include/boost/unordered/unordered_set.hpp @@ -15,6 +15,7 @@ #pragma once #endif +#include #include #include #include From 82acad379468a8b22639dfea8b5aedb54ceaaa8c Mon Sep 17 00:00:00 2001 From: Peter Dimov Date: Mon, 6 Mar 2023 17:04:06 +0200 Subject: [PATCH 14/18] Increase GHA timeout to 180 minutes --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 03a8f51c..da3f5b2c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -75,7 +75,7 @@ jobs: - { compiler: clang, cxxstd: '03,11,14,17,2a', os: macos-11, } - { compiler: clang, cxxstd: '03,11,14,17,2a', os: macos-12, sanitize: yes } - timeout-minutes: 120 + timeout-minutes: 180 runs-on: ${{matrix.os}} container: ${{matrix.container}} env: {B2_USE_CCACHE: 1} From 1ae21c6d1ad5246e7688e32b3d9a77bd8e4a55c0 Mon Sep 17 00:00:00 2001 From: Christian Mazakas Date: Mon, 6 Mar 2023 09:47:33 -0800 Subject: [PATCH 15/18] Remove extraneous typedef from destroy_on_exit --- include/boost/unordered/detail/foa.hpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/include/boost/unordered/detail/foa.hpp b/include/boost/unordered/detail/foa.hpp index 1c080d08..d65d07a2 100644 --- a/include/boost/unordered/detail/foa.hpp +++ b/include/boost/unordered/detail/foa.hpp @@ -1659,8 +1659,6 @@ private: template struct destroy_on_exit { - using type_policy=TypePolicy; - Allocator &a; T *p; ~destroy_on_exit(){type_policy::destroy(a,p);}; From e56cb4418cc5b1217ee42776dc32546212a7d4c1 Mon Sep 17 00:00:00 2001 From: Christian Mazakas Date: Mon, 6 Mar 2023 09:48:26 -0800 Subject: [PATCH 16/18] Remove extraneous information from TypePolicy's construct, destroy docs --- include/boost/unordered/detail/foa.hpp | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/include/boost/unordered/detail/foa.hpp b/include/boost/unordered/detail/foa.hpp index d65d07a2..93036768 100644 --- a/include/boost/unordered/detail/foa.hpp +++ b/include/boost/unordered/detail/foa.hpp @@ -1173,10 +1173,7 @@ union uninitialized_storage * * - TypePolicy::construct and TypePolicy::destroy are used for the * construction and destruction of the internal types: value_type, init_type - * and element_type. For flat containers, these are often all synonyms for - * each other but for the node-based map, each one is a distinct type. These - * are used for allocator-aware construction and destruction of the types - * during insertion. + * and element_type. * * - TypePolicy::move is used to provide move semantics for the internal * types used by the container during rehashing and emplace. These types From 15c9bc40f7e72668f2859876097b595c221a7bca Mon Sep 17 00:00:00 2001 From: joaquintides Date: Mon, 6 Mar 2023 11:56:49 +0100 Subject: [PATCH 17/18] added fast table_iterator::increment variant for regular-layout groups --- include/boost/unordered/detail/foa.hpp | 89 +++++++++++++++++++++----- 1 file changed, 73 insertions(+), 16 deletions(-) diff --git a/include/boost/unordered/detail/foa.hpp b/include/boost/unordered/detail/foa.hpp index 93036768..85bbc701 100644 --- a/include/boost/unordered/detail/foa.hpp +++ b/include/boost/unordered/detail/foa.hpp @@ -161,6 +161,7 @@ static const std::size_t default_bucket_count = 0; struct group15 { static constexpr int N=15; + static constexpr bool regular_layout=true; struct dummy_group_type { @@ -186,6 +187,11 @@ struct group15 return at(pos)==sentinel_; } + static inline bool is_sentinel(unsigned char* pc)noexcept + { + return *pc==sentinel_; + } + inline void reset(std::size_t pos) { BOOST_ASSERT(pos friend class table; table_iterator(Group* pg,std::size_t n,const table_element_type* p_): - pc{reinterpret_cast(const_cast(pg))+n}, + pc{reinterpret_cast(const_cast(pg))+n}, p{const_cast(p_)} {} - inline std::size_t rebase() noexcept - { - std::size_t off=reinterpret_cast(pc)%sizeof(Group); - pc-=off; - return off; - } - inline void increment()noexcept { - std::size_t n0=rebase(); + increment(std::integral_constant{}); + } - int mask=(reinterpret_cast(pc)->match_occupied()>>(n0+1))<<(n0+1); + inline void increment(std::true_type /* regular layout */)noexcept + { + for(;;){ + ++p; + if(reinterpret_cast(pc)%sizeof(group_type)==N-1){ + pc+=sizeof(group_type)-(N-1); + break; + } + ++pc; + if(!group_type::is_occupied(pc))continue; + if(BOOST_UNLIKELY(group_type::is_sentinel(pc)))p=nullptr; + return; + } + + for(;;){ + int mask=reinterpret_cast(pc)->match_occupied(); + if(mask!=0){ + auto n=unchecked_countr_zero(mask); + if(BOOST_UNLIKELY(reinterpret_cast(pc)->is_sentinel(n))){ + p=nullptr; + } + else{ + pc+=n; + p+=n; + } + return; + } + pc+=sizeof(group_type); + p+=N; + } + } + + inline void increment(std::false_type /* interleaved */)noexcept + { + std::size_t n0=reinterpret_cast(pc)%sizeof(group_type); + pc-=n0; + + int mask=( + reinterpret_cast(pc)->match_occupied()>>(n0+1))<<(n0+1); if(!mask){ do{ - pc+=sizeof(Group); - p+=Group::N; + pc+=sizeof(group_type); + p+=N; } - while((mask=reinterpret_cast(pc)->match_occupied())==0); + while((mask=reinterpret_cast(pc)->match_occupied())==0); } auto n=unchecked_countr_zero(mask); - if(BOOST_UNLIKELY(reinterpret_cast(pc)->is_sentinel(n))){ + if(BOOST_UNLIKELY(reinterpret_cast(pc)->is_sentinel(n))){ p=nullptr; } else{ From ced8b45add865d6ef60e0ad8be047f75ec1cfe23 Mon Sep 17 00:00:00 2001 From: joaquintides Date: Mon, 6 Mar 2023 13:50:25 +0100 Subject: [PATCH 18/18] fixed UB with begin when using fast iteration --- include/boost/unordered/detail/foa.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/boost/unordered/detail/foa.hpp b/include/boost/unordered/detail/foa.hpp index 85bbc701..c5c7abe6 100644 --- a/include/boost/unordered/detail/foa.hpp +++ b/include/boost/unordered/detail/foa.hpp @@ -1471,7 +1471,7 @@ public: iterator begin()noexcept { iterator it{arrays.groups,0,arrays.elements}; - if(!(arrays.groups[0].match_occupied()&0x1))++it; + if(arrays.elements&&!(arrays.groups[0].match_occupied()&0x1))++it; return it; }