mirror of
https://github.com/boostorg/unordered.git
synced 2025-05-09 15:13:59 +00:00
Merge branch 'feature/foa_fast_iteration' of https://github.com/boostorg/unordered into feature/foa_fast_iteration
This commit is contained in:
commit
7faec20f26
@ -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' };
|
||||
|
2
.github/workflows/ci.yml
vendored
2
.github/workflows/ci.yml
vendored
@ -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}
|
||||
|
@ -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
|
||||
|
||||
|
@ -1186,6 +1186,14 @@ _STL_RESTORE_DEPRECATED_WARNING
|
||||
*/
|
||||
constexpr static float const mlf = 0.875f;
|
||||
|
||||
template <class T>
|
||||
union uninitialized_storage
|
||||
{
|
||||
T t_;
|
||||
uninitialized_storage(){}
|
||||
~uninitialized_storage(){}
|
||||
};
|
||||
|
||||
/* 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
|
||||
@ -1220,11 +1228,17 @@ constexpr static float const mlf = 0.875f;
|
||||
* 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<Key&&,T&&>, which is generally cheaper to move
|
||||
* than std::pair<const Key,T>&& 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.
|
||||
*
|
||||
* - 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
|
||||
@ -1475,18 +1489,25 @@ public:
|
||||
template<typename... Args>
|
||||
BOOST_FORCEINLINE std::pair<iterator,bool> 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<init_type,Args...>::value,
|
||||
init_type,
|
||||
value_type
|
||||
>::type;
|
||||
|
||||
using insert_type=typename std::conditional<
|
||||
std::is_constructible<
|
||||
value_type,
|
||||
emplace_type<Args...>&&>{},
|
||||
std::forward<Args>(args)...);
|
||||
value_type,emplace_type>::value,
|
||||
emplace_type,element_type
|
||||
>::type;
|
||||
|
||||
uninitialized_storage<insert_type> s;
|
||||
auto *p=std::addressof(s.t_);
|
||||
|
||||
type_policy::construct(al(),p,std::forward<Args>(args)...);
|
||||
|
||||
destroy_on_exit<insert_type> guard{al(),p};
|
||||
return emplace_impl(type_policy::move(*p));
|
||||
}
|
||||
|
||||
template<typename Key,typename... Args>
|
||||
@ -1671,15 +1692,6 @@ private:
|
||||
template<typename,typename,typename,typename> friend class table;
|
||||
using arrays_type=table_arrays<element_type,group_type,size_policy>;
|
||||
|
||||
template<typename... Args>
|
||||
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();}
|
||||
@ -1698,6 +1710,14 @@ private:
|
||||
bool rollback_=false;
|
||||
};
|
||||
|
||||
template <class T>
|
||||
struct destroy_on_exit
|
||||
{
|
||||
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();}
|
||||
@ -1961,29 +1981,6 @@ private:
|
||||
#pragma warning(pop) /* C4800 */
|
||||
#endif
|
||||
|
||||
template<typename... Args>
|
||||
BOOST_FORCEINLINE std::pair<iterator,bool> emplace_value(
|
||||
std::true_type /* movable value_type */,Args&&... args
|
||||
) {
|
||||
using emplace_type_t = emplace_type<Args...>;
|
||||
return emplace_impl(emplace_type_t(std::forward<Args>(args)...));
|
||||
}
|
||||
|
||||
template<typename... Args>
|
||||
BOOST_FORCEINLINE std::pair<iterator,bool> emplace_value(
|
||||
std::false_type /* immovable value_type */,Args&&... args
|
||||
) {
|
||||
alignas(element_type)
|
||||
unsigned char buf[sizeof(element_type)];
|
||||
element_type* p = reinterpret_cast<element_type*>(buf);
|
||||
|
||||
type_policy::construct(al(),p,std::forward<Args>(args)...);
|
||||
destroy_element_on_exit d{this,p};
|
||||
(void)d;
|
||||
|
||||
return emplace_impl(type_policy::move(*p));
|
||||
}
|
||||
|
||||
template<typename... Args>
|
||||
BOOST_FORCEINLINE std::pair<iterator,bool> emplace_impl(Args&&... args)
|
||||
{
|
||||
|
60
include/boost/unordered/detail/foa/element_type.hpp
Normal file
60
include/boost/unordered/detail/foa/element_type.hpp
Normal file
@ -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<class T>
|
||||
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
|
@ -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<Allocator> 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,32 +83,29 @@ 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();
|
||||
}
|
||||
}
|
||||
@ -110,12 +117,12 @@ struct node_handle_base
|
||||
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(),p_);
|
||||
type_policy::destroy(al(),&p_);
|
||||
reset();
|
||||
}else{ /* !empty(), !nh.empty() */
|
||||
bool const pocma=
|
||||
@ -124,12 +131,12 @@ struct node_handle_base
|
||||
|
||||
BOOST_ASSERT(pocma||al()==nh.al());
|
||||
|
||||
type_policy::destroy(al(),p_);
|
||||
type_policy::destroy(al(),&p_);
|
||||
if(pocma){
|
||||
al()=std::move(nh.al());
|
||||
}
|
||||
|
||||
p_=nh.p_;
|
||||
p_=std::move(nh.p_);
|
||||
nh.reset();
|
||||
}
|
||||
}
|
||||
@ -137,7 +144,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(),&p_);
|
||||
reset();
|
||||
}
|
||||
}
|
||||
@ -147,14 +154,14 @@ struct node_handle_base
|
||||
~node_handle_base()
|
||||
{
|
||||
if(!empty()){
|
||||
type_policy::destroy(al(),p_);
|
||||
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<Allocator>::type::value||
|
||||
@ -165,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=
|
||||
@ -180,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());
|
||||
}
|
||||
}
|
||||
|
21
include/boost/unordered/detail/requires_cxx11.hpp
Normal file
21
include/boost/unordered/detail/requires_cxx11.hpp
Normal file
@ -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 <boost/config.hpp>
|
||||
#include <boost/config/pragma_message.hpp>
|
||||
|
||||
#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
|
@ -53,6 +53,11 @@ namespace boost {
|
||||
return kv.first;
|
||||
}
|
||||
|
||||
static moved_type move(init_type& x)
|
||||
{
|
||||
return {std::move(x.first), std::move(x.second)};
|
||||
}
|
||||
|
||||
static moved_type move(element_type& x)
|
||||
{
|
||||
// TODO: we probably need to launder here
|
||||
@ -61,12 +66,23 @@ namespace boost {
|
||||
}
|
||||
|
||||
template <class A, class... Args>
|
||||
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>(args)...);
|
||||
}
|
||||
|
||||
template <class A> static void destroy(A& al, element_type* p) noexcept
|
||||
template <class A, class... Args>
|
||||
static void construct(A& al, value_type* p, Args&&... args)
|
||||
{
|
||||
boost::allocator_construct(al, p, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template <class A> static void destroy(A& al, init_type* p) noexcept
|
||||
{
|
||||
boost::allocator_destroy(al, p);
|
||||
}
|
||||
|
||||
template <class A> static void destroy(A& al, value_type* p) noexcept
|
||||
{
|
||||
boost::allocator_destroy(al, p);
|
||||
}
|
||||
|
@ -46,12 +46,12 @@ namespace boost {
|
||||
static element_type&& move(element_type& x) { return std::move(x); }
|
||||
|
||||
template <class A, class... Args>
|
||||
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>(args)...);
|
||||
}
|
||||
|
||||
template <class A> static void destroy(A& al, element_type* p) noexcept
|
||||
template <class A> static void destroy(A& al, value_type* p) noexcept
|
||||
{
|
||||
boost::allocator_destroy(al, p);
|
||||
}
|
||||
|
@ -15,6 +15,7 @@
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
#include <boost/unordered/detail/requires_cxx11.hpp>
|
||||
#include <boost/core/explicit_operator_bool.hpp>
|
||||
#include <boost/functional/hash.hpp>
|
||||
#include <boost/move/move.hpp>
|
||||
|
@ -11,6 +11,7 @@
|
||||
#endif
|
||||
|
||||
#include <boost/unordered/detail/foa.hpp>
|
||||
#include <boost/unordered/detail/foa/element_type.hpp>
|
||||
#include <boost/unordered/detail/foa/node_handle.hpp>
|
||||
#include <boost/unordered/detail/type_traits.hpp>
|
||||
#include <boost/unordered/unordered_node_map_fwd.hpp>
|
||||
@ -45,23 +46,7 @@ namespace boost {
|
||||
using value_type = std::pair<Key const, T>;
|
||||
using moved_type = std::pair<raw_key_type&&, raw_mapped_type&&>;
|
||||
|
||||
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<value_type>;
|
||||
|
||||
static value_type& value_from(element_type const& x) { return *(x.p); }
|
||||
|
||||
@ -77,6 +62,11 @@ namespace boost {
|
||||
}
|
||||
|
||||
static element_type&& move(element_type& x) { return std::move(x); }
|
||||
static moved_type move(init_type& x)
|
||||
{
|
||||
return {std::move(x.first), std::move(x.second)};
|
||||
}
|
||||
|
||||
static moved_type move(value_type& x)
|
||||
{
|
||||
return {std::move(const_cast<raw_key_type&>(x.first)),
|
||||
@ -96,6 +86,18 @@ namespace boost {
|
||||
construct(al, p, *copy.p);
|
||||
}
|
||||
|
||||
template <class A, class... Args>
|
||||
static void construct(A& al, init_type* p, Args&&... args)
|
||||
{
|
||||
boost::allocator_construct(al, p, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template <class A, class... Args>
|
||||
static void construct(A& al, value_type* p, Args&&... args)
|
||||
{
|
||||
boost::allocator_construct(al, p, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template <class A, class... Args>
|
||||
static void construct(A& al, element_type* p, Args&&... args)
|
||||
{
|
||||
@ -106,10 +108,11 @@ namespace boost {
|
||||
}
|
||||
BOOST_CATCH(...)
|
||||
{
|
||||
boost::allocator_deallocate(al,
|
||||
boost::pointer_traits<
|
||||
typename boost::allocator_pointer<A>::type>::pointer_to(*p->p),
|
||||
1);
|
||||
using pointer_type = typename boost::allocator_pointer<A>::type;
|
||||
using pointer_traits = boost::pointer_traits<pointer_type>;
|
||||
|
||||
boost::allocator_deallocate(
|
||||
al, pointer_traits::pointer_to(*(p->p)), 1);
|
||||
BOOST_RETHROW
|
||||
}
|
||||
BOOST_CATCH_END
|
||||
@ -118,16 +121,22 @@ namespace boost {
|
||||
template <class A> 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<A>::type>::pointer_to(*p),
|
||||
1);
|
||||
}
|
||||
|
||||
template <class A> static void destroy(A& al, init_type* p) noexcept
|
||||
{
|
||||
boost::allocator_destroy(al, p);
|
||||
}
|
||||
|
||||
template <class A> static void destroy(A& al, element_type* p) noexcept
|
||||
{
|
||||
if (p->p) {
|
||||
destroy(al,p->p);
|
||||
using pointer_type = typename boost::allocator_pointer<A>::type;
|
||||
using pointer_traits = boost::pointer_traits<pointer_type>;
|
||||
|
||||
destroy(al, p->p);
|
||||
boost::allocator_deallocate(
|
||||
al, pointer_traits::pointer_to(*(p->p)), 1);
|
||||
}
|
||||
}
|
||||
};
|
||||
@ -137,8 +146,7 @@ namespace boost {
|
||||
: public detail::foa::node_handle_base<TypePolicy, Allocator>
|
||||
{
|
||||
private:
|
||||
using base_type =
|
||||
detail::foa::node_handle_base<TypePolicy, Allocator>;
|
||||
using base_type = detail::foa::node_handle_base<TypePolicy, Allocator>;
|
||||
|
||||
using typename base_type::type_policy;
|
||||
|
||||
@ -157,13 +165,13 @@ namespace boost {
|
||||
key_type& key() const
|
||||
{
|
||||
BOOST_ASSERT(!this->empty());
|
||||
return const_cast<key_type&>(this->element().first);
|
||||
return const_cast<key_type&>(this->data().first);
|
||||
}
|
||||
|
||||
mapped_type& mapped() const
|
||||
{
|
||||
BOOST_ASSERT(!this->empty());
|
||||
return const_cast<mapped_type&>(this->element().second);
|
||||
return const_cast<mapped_type&>(this->data().second);
|
||||
}
|
||||
};
|
||||
} // namespace detail
|
||||
@ -402,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{}};
|
||||
@ -422,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;
|
||||
|
@ -11,6 +11,7 @@
|
||||
#endif
|
||||
|
||||
#include <boost/unordered/detail/foa.hpp>
|
||||
#include <boost/unordered/detail/foa/element_type.hpp>
|
||||
#include <boost/unordered/detail/foa/node_handle.hpp>
|
||||
#include <boost/unordered/detail/type_traits.hpp>
|
||||
#include <boost/unordered/unordered_node_set_fwd.hpp>
|
||||
@ -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<value_type>;
|
||||
|
||||
static value_type& value_from(element_type const& x) { return *x.p; }
|
||||
static Key const& extract(element_type const& k) { return *k.p; }
|
||||
@ -78,6 +63,12 @@ namespace boost {
|
||||
x.p = nullptr;
|
||||
}
|
||||
|
||||
template <class A, class... Args>
|
||||
static void construct(A& al, value_type* p, Args&&... args)
|
||||
{
|
||||
boost::allocator_construct(al, p, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template <class A, class... Args>
|
||||
static void construct(A& al, element_type* p, Args&&... args)
|
||||
{
|
||||
@ -100,16 +91,16 @@ namespace boost {
|
||||
template <class A> 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<A>::type>::pointer_to(*p),
|
||||
1);
|
||||
}
|
||||
|
||||
template <class A> static void destroy(A& al, element_type* p) noexcept
|
||||
{
|
||||
if (p->p) {
|
||||
destroy(al, p->p);
|
||||
boost::allocator_deallocate(al,
|
||||
boost::pointer_traits<typename boost::allocator_pointer<
|
||||
A>::type>::pointer_to(*(p->p)),
|
||||
1);
|
||||
}
|
||||
}
|
||||
};
|
||||
@ -119,8 +110,7 @@ namespace boost {
|
||||
: public detail::foa::node_handle_base<TypePolicy, Allocator>
|
||||
{
|
||||
private:
|
||||
using base_type =
|
||||
detail::foa::node_handle_base<TypePolicy, Allocator>;
|
||||
using base_type = detail::foa::node_handle_base<TypePolicy, Allocator>;
|
||||
|
||||
using typename base_type::type_policy;
|
||||
|
||||
@ -137,7 +127,7 @@ namespace boost {
|
||||
value_type& value() const
|
||||
{
|
||||
BOOST_ASSERT(!this->empty());
|
||||
return const_cast<value_type&>(this->element());
|
||||
return const_cast<value_type&>(this->data());
|
||||
}
|
||||
};
|
||||
} // namespace detail
|
||||
@ -390,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{}};
|
||||
@ -410,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;
|
||||
@ -478,7 +462,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 <class K>
|
||||
@ -489,7 +473,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 <class H2, class P2>
|
||||
|
@ -15,6 +15,7 @@
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
#include <boost/unordered/detail/requires_cxx11.hpp>
|
||||
#include <boost/core/explicit_operator_bool.hpp>
|
||||
#include <boost/functional/hash.hpp>
|
||||
#include <boost/move/move.hpp>
|
||||
|
@ -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 ;
|
||||
|
||||
|
189
test/unordered/uses_allocator.cpp
Normal file
189
test/unordered/uses_allocator.cpp
Normal file
@ -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 <boost/unordered/detail/implementation.hpp>
|
||||
#include <boost/unordered/unordered_flat_map.hpp>
|
||||
#include <boost/unordered/unordered_flat_set.hpp>
|
||||
#include <boost/unordered/unordered_node_map.hpp>
|
||||
#include <boost/unordered/unordered_node_set.hpp>
|
||||
|
||||
#include <boost/config.hpp>
|
||||
#include <boost/config/pragma_message.hpp>
|
||||
#include <boost/config/workaround.hpp>
|
||||
|
||||
#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 <memory>
|
||||
#include <scoped_allocator>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
template <class T> struct allocator
|
||||
{
|
||||
typedef T value_type;
|
||||
|
||||
int tag_ = -1;
|
||||
|
||||
allocator() = default;
|
||||
allocator(int tag) : tag_{tag} {}
|
||||
allocator(allocator const&) = default;
|
||||
allocator(allocator&&) = default;
|
||||
|
||||
template <class U> allocator(allocator<U> const& rhs) : tag_{rhs.tag_} {}
|
||||
|
||||
BOOST_ATTRIBUTE_NODISCARD T* allocate(std::size_t n)
|
||||
{
|
||||
return static_cast<T*>(::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<int>;
|
||||
|
||||
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<std::pair<raii_tracker const, raii_tracker> >, allocator<int> >;
|
||||
|
||||
using set_allocator_type =
|
||||
std::scoped_allocator_adaptor<allocator<raii_tracker>, allocator<int> >;
|
||||
|
||||
using map_type = boost::unordered_flat_map<raii_tracker, raii_tracker,
|
||||
boost::hash<raii_tracker>, std::equal_to<raii_tracker>, map_allocator_type>;
|
||||
|
||||
using node_map_type = boost::unordered_node_map<raii_tracker, raii_tracker,
|
||||
boost::hash<raii_tracker>, std::equal_to<raii_tracker>, map_allocator_type>;
|
||||
|
||||
using set_type = boost::unordered_flat_set<raii_tracker,
|
||||
boost::hash<raii_tracker>, std::equal_to<raii_tracker>, set_allocator_type>;
|
||||
|
||||
using node_set_type = boost::unordered_node_set<raii_tracker,
|
||||
boost::hash<raii_tracker>, std::equal_to<raii_tracker>, set_allocator_type>;
|
||||
|
||||
map_type* flat_map;
|
||||
node_map_type* node_map;
|
||||
|
||||
set_type* flat_set;
|
||||
node_set_type* node_set;
|
||||
|
||||
template <class X> static void map_uses_allocator_construction(X*)
|
||||
{
|
||||
reset_counts();
|
||||
|
||||
map_allocator_type alloc(
|
||||
allocator<std::pair<raii_tracker const, raii_tracker> >{12},
|
||||
allocator<int>{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 <class X> static void set_uses_allocator_construction(X*)
|
||||
{
|
||||
reset_counts();
|
||||
|
||||
set_allocator_type alloc(allocator<raii_tracker>{12}, allocator<int>{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
|
Loading…
x
Reference in New Issue
Block a user