forked from boostorg/unordered
@ -15,6 +15,8 @@ with serial and parallel variants.
|
||||
* Added debug mode mechanisms for detecting illegal reentrancies into
|
||||
a `boost::concurrent_flat_map` from user code.
|
||||
* Added Boost.Serialization support to all containers and their (non-local) iterator types.
|
||||
* Added support for fancy pointers to open-addressing and concurrent containers.
|
||||
This enables scenarios like the use of Boost.Interprocess allocators to construct containers in shared memory.
|
||||
|
||||
== Release 1.83.0 - Major update
|
||||
|
||||
|
@ -90,9 +90,10 @@ namespace boost {
|
||||
const allocator_type& a);
|
||||
xref:#concurrent_flat_map_destructor[~concurrent_flat_map]();
|
||||
concurrent_flat_map& xref:#concurrent_flat_map_copy_assignment[operator++=++](const concurrent_flat_map& other);
|
||||
concurrent_flat_map& xref:#concurrent_flat_map_move_assignment[operator++=++](concurrent_flat_map&& other)
|
||||
noexcept(boost::allocator_traits<Allocator>::is_always_equal::value ||
|
||||
boost::allocator_traits<Allocator>::propagate_on_container_move_assignment::value);
|
||||
concurrent_flat_map& xref:#concurrent_flat_map_move_assignment[operator++=++](concurrent_flat_map&& other) ++noexcept(
|
||||
(boost::allocator_traits<Allocator>::is_always_equal::value ||
|
||||
boost::allocator_traits<Allocator>::propagate_on_container_move_assignment::value) &&
|
||||
std::is_same<pointer, value_type*>::value);++
|
||||
concurrent_flat_map& xref:#concurrent_flat_map_initializer_list_assignment[operator++=++](std::initializer_list<value_type>);
|
||||
allocator_type xref:#concurrent_flat_map_get_allocator[get_allocator]() const noexcept;
|
||||
|
||||
@ -316,8 +317,7 @@ https://en.cppreference.com/w/cpp/named_req/Erasable[Erasable^] from the table.
|
||||
|
||||
|_Allocator_
|
||||
|An allocator whose value type is the same as the table's value type.
|
||||
`std::allocator_traits<Allocator>::pointer` and `std::allocator_traits<Allocator>::const_pointer`
|
||||
must be convertible to/from `value_type*` and `const value_type*`, respectively.
|
||||
Allocators using https://en.cppreference.com/w/cpp/named_req/Allocator#Fancy_pointers[fancy pointers] are supported.
|
||||
|
||||
|===
|
||||
|
||||
@ -673,8 +673,9 @@ Concurrency:;; Blocking on `*this` and `other`.
|
||||
==== Move Assignment
|
||||
```c++
|
||||
concurrent_flat_map& operator=(concurrent_flat_map&& other)
|
||||
noexcept(boost::allocator_traits<Allocator>::is_always_equal::value ||
|
||||
boost::allocator_traits<Allocator>::propagate_on_container_move_assignment::value);
|
||||
noexcept((boost::allocator_traits<Allocator>::is_always_equal::value ||
|
||||
boost::allocator_traits<Allocator>::propagate_on_container_move_assignment::value) &&
|
||||
std::is_same<pointer, value_type*>::value);
|
||||
```
|
||||
The move assignment operator. Destroys previously existing elements, swaps the hash function and predicate from `other`,
|
||||
and move-assigns the allocator from `other` if `Alloc::propagate_on_container_move_assignment` exists and `Alloc::propagate_on_container_move_assignment::value` is `true`.
|
||||
|
@ -97,9 +97,10 @@ namespace boost {
|
||||
const allocator_type& a);
|
||||
xref:#unordered_flat_map_destructor[~unordered_flat_map]();
|
||||
unordered_flat_map& xref:#unordered_flat_map_copy_assignment[operator++=++](const unordered_flat_map& other);
|
||||
unordered_flat_map& xref:#unordered_flat_map_move_assignment[operator++=++](unordered_flat_map&& other)
|
||||
noexcept(boost::allocator_traits<Allocator>::is_always_equal::value ||
|
||||
boost::allocator_traits<Allocator>::propagate_on_container_move_assignment::value);
|
||||
unordered_flat_map& xref:#unordered_flat_map_move_assignment[operator++=++](unordered_flat_map&& other) ++noexcept(
|
||||
(boost::allocator_traits<Allocator>::is_always_equal::value ||
|
||||
boost::allocator_traits<Allocator>::propagate_on_container_move_assignment::value) &&
|
||||
std::is_same<pointer, value_type*>::value);++
|
||||
unordered_flat_map& xref:#unordered_flat_map_initializer_list_assignment[operator++=++](std::initializer_list<value_type>);
|
||||
allocator_type xref:#unordered_flat_map_get_allocator[get_allocator]() const noexcept;
|
||||
|
||||
@ -312,8 +313,7 @@ https://en.cppreference.com/w/cpp/named_req/Erasable[Erasable^] from the contain
|
||||
|
||||
|_Allocator_
|
||||
|An allocator whose value type is the same as the container's value type.
|
||||
`std::allocator_traits<Allocator>::pointer` and `std::allocator_traits<Allocator>::const_pointer`
|
||||
must be convertible to/from `value_type*` and `const value_type*`, respectively.
|
||||
Allocators using https://en.cppreference.com/w/cpp/named_req/Allocator#Fancy_pointers[fancy pointers] are supported.
|
||||
|
||||
|===
|
||||
|
||||
@ -632,8 +632,9 @@ Requires:;; `value_type` is https://en.cppreference.com/w/cpp/named_req/CopyInse
|
||||
==== Move Assignment
|
||||
```c++
|
||||
unordered_flat_map& operator=(unordered_flat_map&& other)
|
||||
noexcept(boost::allocator_traits<Allocator>::is_always_equal::value ||
|
||||
boost::allocator_traits<Allocator>::propagate_on_container_move_assignment::value);
|
||||
noexcept((boost::allocator_traits<Allocator>::is_always_equal::value ||
|
||||
boost::allocator_traits<Allocator>::propagate_on_container_move_assignment::value) &&
|
||||
std::is_same<pointer, value_type*>::value);
|
||||
```
|
||||
The move assignment operator. Destroys previously existing elements, swaps the hash function and predicate from `other`,
|
||||
and move-assigns the allocator from `other` if `Alloc::propagate_on_container_move_assignment` exists and `Alloc::propagate_on_container_move_assignment::value` is `true`.
|
||||
@ -1325,7 +1326,7 @@ void rehash(size_type n);
|
||||
|
||||
Changes if necessary the size of the bucket array so that there are at least `n` buckets, and so that the load factor is less than or equal to the maximum load factor. When applicable, this will either grow or shrink the `bucket_count()` associated with the container.
|
||||
|
||||
When `size() == 0`, `rehash(0)` will deallocate the underlying buckets array.
|
||||
When `size() == 0`, `rehash(0)` will deallocate the underlying buckets array. If the provided Allocator uses fancy pointers, a default allocation is subsequently performed.
|
||||
|
||||
Invalidates iterators, pointers and references, and changes the order of elements.
|
||||
|
||||
|
@ -91,9 +91,10 @@ namespace boost {
|
||||
const allocator_type& a);
|
||||
xref:#unordered_flat_set_destructor[~unordered_flat_set]();
|
||||
unordered_flat_set& xref:#unordered_flat_set_copy_assignment[operator++=++](const unordered_flat_set& other);
|
||||
unordered_flat_set& xref:#unordered_flat_set_move_assignment[operator++=++](unordered_flat_set&& other)
|
||||
noexcept(boost::allocator_traits<Allocator>::is_always_equal::value ||
|
||||
boost::allocator_traits<Allocator>::propagate_on_container_move_assignment::value);
|
||||
unordered_flat_set& xref:#unordered_flat_set_move_assignment[operator++=++](unordered_flat_set&& other) ++noexcept(
|
||||
(boost::allocator_traits<Allocator>::is_always_equal::value ||
|
||||
boost::allocator_traits<Allocator>::propagate_on_container_move_assignment::value) &&
|
||||
std::is_same<pointer, value_type*>::value);++
|
||||
unordered_flat_set& xref:#unordered_flat_set_initializer_list_assignment[operator++=++](std::initializer_list<value_type>);
|
||||
allocator_type xref:#unordered_flat_set_get_allocator[get_allocator]() const noexcept;
|
||||
|
||||
@ -261,8 +262,7 @@ and https://en.cppreference.com/w/cpp/named_req/Erasable[Erasable^] from the con
|
||||
|
||||
|_Allocator_
|
||||
|An allocator whose value type is the same as the container's value type.
|
||||
`std::allocator_traits<Allocator>::pointer` and `std::allocator_traits<Allocator>::const_pointer`
|
||||
must be convertible to/from `value_type*` and `const value_type*`, respectively.
|
||||
Allocators using https://en.cppreference.com/w/cpp/named_req/Allocator#Fancy_pointers[fancy pointers] are supported.
|
||||
|
||||
|===
|
||||
|
||||
@ -565,8 +565,9 @@ Requires:;; `value_type` is https://en.cppreference.com/w/cpp/named_req/CopyInse
|
||||
==== Move Assignment
|
||||
```c++
|
||||
unordered_flat_set& operator=(unordered_flat_set&& other)
|
||||
noexcept(boost::allocator_traits<Allocator>::is_always_equal::value ||
|
||||
boost::allocator_traits<Allocator>::propagate_on_container_move_assignment::value);
|
||||
noexcept((boost::allocator_traits<Allocator>::is_always_equal::value ||
|
||||
boost::allocator_traits<Allocator>::propagate_on_container_move_assignment::value) &&
|
||||
std::is_same<pointer, value_type*>::value);
|
||||
```
|
||||
The move assignment operator. Destroys previously existing elements, swaps the hash function and predicate from `other`,
|
||||
and move-assigns the allocator from `other` if `Alloc::propagate_on_container_move_assignment` exists and `Alloc::propagate_on_container_move_assignment::value` is `true`.
|
||||
@ -1087,7 +1088,7 @@ void rehash(size_type n);
|
||||
|
||||
Changes if necessary the size of the bucket array so that there are at least `n` buckets, and so that the load factor is less than or equal to the maximum load factor. When applicable, this will either grow or shrink the `bucket_count()` associated with the container.
|
||||
|
||||
When `size() == 0`, `rehash(0)` will deallocate the underlying buckets array.
|
||||
When `size() == 0`, `rehash(0)` will deallocate the underlying buckets array. If the provided Allocator uses fancy pointers, a default allocation is subsequently performed.
|
||||
|
||||
Invalidates iterators, pointers and references, and changes the order of elements.
|
||||
|
||||
|
@ -95,9 +95,10 @@ namespace boost {
|
||||
const allocator_type& a);
|
||||
xref:#unordered_node_map_destructor[~unordered_node_map]();
|
||||
unordered_node_map& xref:#unordered_node_map_copy_assignment[operator++=++](const unordered_node_map& other);
|
||||
unordered_node_map& xref:#unordered_node_map_move_assignment[operator++=++](unordered_node_map&& other)
|
||||
noexcept(boost::allocator_traits<Allocator>::is_always_equal::value ||
|
||||
boost::allocator_traits<Allocator>::propagate_on_container_move_assignment::value);
|
||||
unordered_node_map& xref:#unordered_node_map_move_assignment[operator++=++](unordered_node_map&& other) ++noexcept(
|
||||
(boost::allocator_traits<Allocator>::is_always_equal::value ||
|
||||
boost::allocator_traits<Allocator>::propagate_on_container_move_assignment::value) &&
|
||||
std::is_same<pointer, value_type*>::value);++
|
||||
unordered_node_map& xref:#unordered_node_map_initializer_list_assignment[operator++=++](std::initializer_list<value_type>);
|
||||
allocator_type xref:#unordered_node_map_get_allocator[get_allocator]() const noexcept;
|
||||
|
||||
@ -314,8 +315,7 @@ https://en.cppreference.com/w/cpp/named_req/Erasable[Erasable^] from the contain
|
||||
|
||||
|_Allocator_
|
||||
|An allocator whose value type is the same as the container's value type.
|
||||
`std::allocator_traits<Allocator>::pointer` and `std::allocator_traits<Allocator>::const_pointer`
|
||||
must be convertible to/from `value_type*` and `const value_type*`, respectively.
|
||||
Allocators using https://en.cppreference.com/w/cpp/named_req/Allocator#Fancy_pointers[fancy pointers] are supported.
|
||||
|
||||
|===
|
||||
|
||||
@ -649,8 +649,9 @@ Requires:;; `value_type` is https://en.cppreference.com/w/cpp/named_req/CopyInse
|
||||
==== Move Assignment
|
||||
```c++
|
||||
unordered_node_map& operator=(unordered_node_map&& other)
|
||||
noexcept(boost::allocator_traits<Allocator>::is_always_equal::value ||
|
||||
boost::allocator_traits<Allocator>::propagate_on_container_move_assignment::value);
|
||||
noexcept((boost::allocator_traits<Allocator>::is_always_equal::value ||
|
||||
boost::allocator_traits<Allocator>::propagate_on_container_move_assignment::value) &&
|
||||
std::is_same<pointer, value_type*>::value);
|
||||
```
|
||||
The move assignment operator. Destroys previously existing elements, swaps the hash function and predicate from `other`,
|
||||
and move-assigns the allocator from `other` if `Alloc::propagate_on_container_move_assignment` exists and `Alloc::propagate_on_container_move_assignment::value` is `true`.
|
||||
@ -1406,7 +1407,7 @@ void rehash(size_type n);
|
||||
|
||||
Changes if necessary the size of the bucket array so that there are at least `n` buckets, and so that the load factor is less than or equal to the maximum load factor. When applicable, this will either grow or shrink the `bucket_count()` associated with the container.
|
||||
|
||||
When `size() == 0`, `rehash(0)` will deallocate the underlying buckets array.
|
||||
When `size() == 0`, `rehash(0)` will deallocate the underlying buckets array. If the provided Allocator uses fancy pointers, a default allocation is subsequently performed.
|
||||
|
||||
Invalidates iterators and changes the order of elements.
|
||||
|
||||
|
@ -90,9 +90,10 @@ namespace boost {
|
||||
const allocator_type& a);
|
||||
xref:#unordered_node_set_destructor[~unordered_node_set]();
|
||||
unordered_node_set& xref:#unordered_node_set_copy_assignment[operator++=++](const unordered_node_set& other);
|
||||
unordered_node_set& xref:#unordered_node_set_move_assignment[operator++=++](unordered_node_set&& other)
|
||||
noexcept(boost::allocator_traits<Allocator>::is_always_equal::value ||
|
||||
boost::allocator_traits<Allocator>::propagate_on_container_move_assignment::value);
|
||||
unordered_node_set& xref:#unordered_node_set_move_assignment[operator++=++](unordered_node_set&& other) ++noexcept(
|
||||
(boost::allocator_traits<Allocator>::is_always_equal::value ||
|
||||
boost::allocator_traits<Allocator>::propagate_on_container_move_assignment::value) &&
|
||||
std::is_same<pointer, value_type*>::value);++
|
||||
unordered_node_set& xref:#unordered_node_set_initializer_list_assignment[operator++=++](std::initializer_list<value_type>);
|
||||
allocator_type xref:#unordered_node_set_get_allocator[get_allocator]() const noexcept;
|
||||
|
||||
@ -264,8 +265,7 @@ namespace boost {
|
||||
|
||||
|_Allocator_
|
||||
|An allocator whose value type is the same as the container's value type.
|
||||
`std::allocator_traits<Allocator>::pointer` and `std::allocator_traits<Allocator>::const_pointer`
|
||||
must be convertible to/from `value_type*` and `const value_type*`, respectively.
|
||||
Allocators using https://en.cppreference.com/w/cpp/named_req/Allocator#Fancy_pointers[fancy pointers] are supported.
|
||||
|
||||
|===
|
||||
|
||||
@ -602,8 +602,9 @@ Requires:;; `value_type` is https://en.cppreference.com/w/cpp/named_req/CopyInse
|
||||
==== Move Assignment
|
||||
```c++
|
||||
unordered_node_set& operator=(unordered_node_set&& other)
|
||||
noexcept(boost::allocator_traits<Allocator>::is_always_equal::value ||
|
||||
boost::allocator_traits<Allocator>::propagate_on_container_move_assignment::value);
|
||||
noexcept((boost::allocator_traits<Allocator>::is_always_equal::value ||
|
||||
boost::allocator_traits<Allocator>::propagate_on_container_move_assignment::value) &&
|
||||
std::is_same<pointer, value_type*>::value);
|
||||
```
|
||||
The move assignment operator. Destroys previously existing elements, swaps the hash function and predicate from `other`,
|
||||
and move-assigns the allocator from `other` if `Alloc::propagate_on_container_move_assignment` exists and `Alloc::propagate_on_container_move_assignment::value` is `true`.
|
||||
@ -1188,7 +1189,7 @@ void rehash(size_type n);
|
||||
|
||||
Changes if necessary the size of the bucket array so that there are at least `n` buckets, and so that the load factor is less than or equal to the maximum load factor. When applicable, this will either grow or shrink the `bucket_count()` associated with the container.
|
||||
|
||||
When `size() == 0`, `rehash(0)` will deallocate the underlying buckets array.
|
||||
When `size() == 0`, `rehash(0)` will deallocate the underlying buckets array. If the provided Allocator uses fancy pointers, a default allocation is subsequently performed.
|
||||
|
||||
Invalidates iterators and changes the order of elements.
|
||||
|
||||
|
@ -92,7 +92,10 @@ namespace boost {
|
||||
|
||||
using type_policy = detail::foa::flat_map_types<Key, T>;
|
||||
|
||||
detail::foa::concurrent_table<type_policy, Hash, Pred, Allocator> table_;
|
||||
using table_type =
|
||||
detail::foa::concurrent_table<type_policy, Hash, Pred, Allocator>;
|
||||
|
||||
table_type table_;
|
||||
|
||||
template <class K, class V, class H, class KE, class A>
|
||||
bool friend operator==(concurrent_flat_map<K, V, H, KE, A> const& lhs,
|
||||
@ -248,10 +251,8 @@ namespace boost {
|
||||
return *this;
|
||||
}
|
||||
|
||||
concurrent_flat_map& operator=(concurrent_flat_map&& rhs)
|
||||
noexcept(boost::allocator_is_always_equal<Allocator>::type::value ||
|
||||
boost::allocator_propagate_on_container_move_assignment<
|
||||
Allocator>::type::value)
|
||||
concurrent_flat_map& operator=(concurrent_flat_map&& rhs) noexcept(
|
||||
noexcept(std::declval<table_type&>() = std::declval<table_type&&>()))
|
||||
{
|
||||
table_ = std::move(rhs.table_);
|
||||
return *this;
|
||||
|
@ -247,16 +247,24 @@ group_access* dummy_group_accesses()
|
||||
|
||||
/* subclasses table_arrays to add an additional group_access array */
|
||||
|
||||
template<typename Value,typename Group,typename SizePolicy>
|
||||
struct concurrent_table_arrays:table_arrays<Value,Group,SizePolicy>
|
||||
template<typename Value,typename Group,typename SizePolicy,typename Allocator>
|
||||
struct concurrent_table_arrays:table_arrays<Value,Group,SizePolicy,Allocator>
|
||||
{
|
||||
using super=table_arrays<Value,Group,SizePolicy>;
|
||||
using group_access_allocator_type=
|
||||
typename boost::allocator_rebind<Allocator,group_access>::type;
|
||||
using group_access_pointer=
|
||||
typename boost::allocator_pointer<group_access_allocator_type>::type;
|
||||
|
||||
concurrent_table_arrays(const super& arrays,group_access *pga):
|
||||
super{arrays},group_accesses{pga}{}
|
||||
using super=table_arrays<Value,Group,SizePolicy,Allocator>;
|
||||
|
||||
template<typename Allocator>
|
||||
static concurrent_table_arrays new_(Allocator& al,std::size_t n)
|
||||
concurrent_table_arrays(const super& arrays,group_access_pointer pga):
|
||||
super{arrays},group_accesses_{pga}{}
|
||||
|
||||
group_access* group_accesses()const noexcept{
|
||||
return boost::to_address(group_accesses_);
|
||||
}
|
||||
|
||||
static concurrent_table_arrays new_(group_access_allocator_type al,std::size_t n)
|
||||
{
|
||||
super x{super::new_(al,n)};
|
||||
BOOST_TRY{
|
||||
@ -269,54 +277,61 @@ struct concurrent_table_arrays:table_arrays<Value,Group,SizePolicy>
|
||||
BOOST_CATCH_END
|
||||
}
|
||||
|
||||
template<typename Allocator>
|
||||
static concurrent_table_arrays new_group_access(Allocator& al,const super& x)
|
||||
static void set_group_access(
|
||||
group_access_allocator_type al,concurrent_table_arrays& arrays)
|
||||
{
|
||||
concurrent_table_arrays arrays{x,nullptr};
|
||||
if(!arrays.elements){
|
||||
arrays.group_accesses=dummy_group_accesses<SizePolicy::min_size()>();
|
||||
}
|
||||
else{
|
||||
using access_alloc=
|
||||
typename boost::allocator_rebind<Allocator,group_access>::type;
|
||||
using access_traits=boost::allocator_traits<access_alloc>;
|
||||
set_group_access(
|
||||
al,arrays,std::is_same<group_access*,group_access_pointer>{});
|
||||
}
|
||||
|
||||
auto aal=access_alloc(al);
|
||||
arrays.group_accesses=boost::to_address(
|
||||
access_traits::allocate(aal,arrays.groups_size_mask+1));
|
||||
static void set_group_access(
|
||||
group_access_allocator_type al,
|
||||
concurrent_table_arrays& arrays,
|
||||
std::false_type /* fancy pointers */)
|
||||
{
|
||||
arrays.group_accesses_=
|
||||
boost::allocator_allocate(al,arrays.groups_size_mask+1);
|
||||
|
||||
for(std::size_t i=0;i<arrays.groups_size_mask+1;++i){
|
||||
::new (arrays.group_accesses+i) group_access();
|
||||
::new (arrays.group_accesses()+i) group_access();
|
||||
}
|
||||
}
|
||||
|
||||
static void set_group_access(
|
||||
group_access_allocator_type al,
|
||||
concurrent_table_arrays& arrays,
|
||||
std::true_type /* optimize when elements() is null */)
|
||||
{
|
||||
if(!arrays.elements()){
|
||||
arrays.group_accesses_=
|
||||
dummy_group_accesses<SizePolicy::min_size()>();
|
||||
} else {
|
||||
set_group_access(al,arrays,std::false_type{});
|
||||
}
|
||||
}
|
||||
|
||||
static concurrent_table_arrays new_group_access(group_access_allocator_type al,const super& x)
|
||||
{
|
||||
concurrent_table_arrays arrays{x,nullptr};
|
||||
set_group_access(al,arrays);
|
||||
return arrays;
|
||||
}
|
||||
|
||||
template<typename Allocator>
|
||||
static void delete_(Allocator& al,concurrent_table_arrays& arrays)noexcept
|
||||
static void delete_(group_access_allocator_type al,concurrent_table_arrays& arrays)noexcept
|
||||
{
|
||||
delete_group_access(al,arrays);
|
||||
super::delete_(al,arrays);
|
||||
}
|
||||
|
||||
template<typename Allocator>
|
||||
static void delete_group_access(Allocator& al,concurrent_table_arrays& arrays)noexcept
|
||||
static void delete_group_access(group_access_allocator_type al,concurrent_table_arrays& arrays)noexcept
|
||||
{
|
||||
if(arrays.elements){
|
||||
using access_alloc=
|
||||
typename boost::allocator_rebind<Allocator,group_access>::type;
|
||||
using access_traits=boost::allocator_traits<access_alloc>;
|
||||
using pointer=typename access_traits::pointer;
|
||||
using pointer_traits=boost::pointer_traits<pointer>;
|
||||
|
||||
auto aal=access_alloc(al);
|
||||
access_traits::deallocate(
|
||||
aal,pointer_traits::pointer_to(*arrays.group_accesses),
|
||||
arrays.groups_size_mask+1);
|
||||
if(arrays.elements()){
|
||||
boost::allocator_deallocate(
|
||||
al,arrays.group_accesses_,arrays.groups_size_mask+1);
|
||||
}
|
||||
}
|
||||
|
||||
group_access *group_accesses;
|
||||
group_access_pointer group_accesses_;
|
||||
};
|
||||
|
||||
struct atomic_size_control
|
||||
@ -473,20 +488,32 @@ public:
|
||||
concurrent_table(concurrent_table&& x,const Allocator& al_):
|
||||
concurrent_table(std::move(x),al_,x.exclusive_access()){}
|
||||
|
||||
concurrent_table(compatible_nonconcurrent_table&& x):
|
||||
template<typename ArraysType>
|
||||
concurrent_table(
|
||||
compatible_nonconcurrent_table&& x,
|
||||
arrays_holder<ArraysType,Allocator>&& ah):
|
||||
super{
|
||||
std::move(x.h()),std::move(x.pred()),std::move(x.al()),
|
||||
arrays_type(arrays_type::new_group_access(
|
||||
x.al(),
|
||||
typename arrays_type::super{
|
||||
[&x]{return arrays_type::new_group_access(
|
||||
x.al(),typename arrays_type::super{
|
||||
x.arrays.groups_size_index,x.arrays.groups_size_mask,
|
||||
reinterpret_cast<group_type*>(x.arrays.groups),
|
||||
reinterpret_cast<value_type*>(x.arrays.elements)})),
|
||||
to_pointer<typename arrays_type::group_type_pointer>(
|
||||
reinterpret_cast<group_type*>(x.arrays.groups())),
|
||||
x.arrays.elements_});},
|
||||
size_ctrl_type{x.size_ctrl.ml,x.size_ctrl.size}}
|
||||
{
|
||||
x.empty_initialize();
|
||||
ah.release();
|
||||
x.arrays=ah.get();
|
||||
x.size_ctrl.ml=x.initial_max_load();
|
||||
x.size_ctrl.size=0;
|
||||
}
|
||||
|
||||
concurrent_table(compatible_nonconcurrent_table&& x):
|
||||
concurrent_table(std::move(x), arrays_holder<
|
||||
typename compatible_nonconcurrent_table::arrays_type,Allocator
|
||||
>{compatible_nonconcurrent_table::arrays_type::new_(x.al(),0),x.al()})
|
||||
{}
|
||||
|
||||
~concurrent_table()=default;
|
||||
|
||||
concurrent_table& operator=(const concurrent_table& x)
|
||||
@ -496,7 +523,8 @@ public:
|
||||
return *this;
|
||||
}
|
||||
|
||||
concurrent_table& operator=(concurrent_table&& x)
|
||||
concurrent_table& operator=(concurrent_table&& x)noexcept(
|
||||
noexcept(std::declval<super&>() = std::declval<super&&>()))
|
||||
{
|
||||
auto lck=exclusive_access(*this,x);
|
||||
super::operator=(std::move(x));
|
||||
@ -967,18 +995,18 @@ private:
|
||||
|
||||
inline group_shared_lock_guard access(group_shared,std::size_t pos)const
|
||||
{
|
||||
return this->arrays.group_accesses[pos].shared_access();
|
||||
return this->arrays.group_accesses()[pos].shared_access();
|
||||
}
|
||||
|
||||
inline group_exclusive_lock_guard access(
|
||||
group_exclusive,std::size_t pos)const
|
||||
{
|
||||
return this->arrays.group_accesses[pos].exclusive_access();
|
||||
return this->arrays.group_accesses()[pos].exclusive_access();
|
||||
}
|
||||
|
||||
inline group_insert_counter_type& insert_counter(std::size_t pos)const
|
||||
{
|
||||
return this->arrays.group_accesses[pos].insert_counter();
|
||||
return this->arrays.group_accesses()[pos].insert_counter();
|
||||
}
|
||||
|
||||
/* Const casts value_type& according to the level of group access for
|
||||
@ -1097,10 +1125,10 @@ private:
|
||||
prober pb(pos0);
|
||||
do{
|
||||
auto pos=pb.get();
|
||||
auto pg=this->arrays.groups+pos;
|
||||
auto pg=this->arrays.groups()+pos;
|
||||
auto mask=pg->match(hash);
|
||||
if(mask){
|
||||
auto p=this->arrays.elements+pos*N;
|
||||
auto p=this->arrays.elements()+pos*N;
|
||||
BOOST_UNORDERED_PREFETCH_ELEMENTS(p,N);
|
||||
auto lck=access(access_mode,pos);
|
||||
do{
|
||||
@ -1313,7 +1341,7 @@ private:
|
||||
if(BOOST_LIKELY(rsize.succeeded())){
|
||||
for(prober pb(pos0);;pb.next(this->arrays.groups_size_mask)){
|
||||
auto pos=pb.get();
|
||||
auto pg=this->arrays.groups+pos;
|
||||
auto pg=this->arrays.groups()+pos;
|
||||
auto lck=access(group_exclusive{},pos);
|
||||
auto mask=pg->match_available();
|
||||
if(BOOST_LIKELY(mask!=0)){
|
||||
@ -1323,7 +1351,7 @@ private:
|
||||
/* other thread inserted from pos0, need to start over */
|
||||
goto startover;
|
||||
}
|
||||
auto p=this->arrays.elements+pos*N+n;
|
||||
auto p=this->arrays.elements()+pos*N+n;
|
||||
this->construct_element(p,std::forward<Args>(args)...);
|
||||
rslot.commit();
|
||||
rsize.commit();
|
||||
@ -1373,11 +1401,11 @@ private:
|
||||
auto for_all_elements_while(GroupAccessMode access_mode,F f)const
|
||||
->decltype(f(nullptr,0,nullptr),bool())
|
||||
{
|
||||
auto p=this->arrays.elements;
|
||||
auto p=this->arrays.elements();
|
||||
if(p){
|
||||
for(auto pg=this->arrays.groups,last=pg+this->arrays.groups_size_mask+1;
|
||||
for(auto pg=this->arrays.groups(),last=pg+this->arrays.groups_size_mask+1;
|
||||
pg!=last;++pg,p+=N){
|
||||
auto lck=access(access_mode,(std::size_t)(pg-this->arrays.groups));
|
||||
auto lck=access(access_mode,(std::size_t)(pg-this->arrays.groups()));
|
||||
auto mask=this->match_really_occupied(pg,last);
|
||||
while(mask){
|
||||
auto n=unchecked_countr_zero(mask);
|
||||
@ -1405,13 +1433,13 @@ private:
|
||||
GroupAccessMode access_mode,ExecutionPolicy&& policy,F f)const
|
||||
->decltype(f(nullptr,0,nullptr),void())
|
||||
{
|
||||
if(!this->arrays.elements)return;
|
||||
auto first=this->arrays.groups,
|
||||
if(!this->arrays.elements())return;
|
||||
auto first=this->arrays.groups(),
|
||||
last=first+this->arrays.groups_size_mask+1;
|
||||
std::for_each(std::forward<ExecutionPolicy>(policy),first,last,
|
||||
[&,this](group_type& g){
|
||||
auto pos=static_cast<std::size_t>(&g-first);
|
||||
auto p=this->arrays.elements+pos*N;
|
||||
auto p=this->arrays.elements()+pos*N;
|
||||
auto lck=access(access_mode,pos);
|
||||
auto mask=this->match_really_occupied(&g,last);
|
||||
while(mask){
|
||||
@ -1427,13 +1455,13 @@ private:
|
||||
bool for_all_elements_while(
|
||||
GroupAccessMode access_mode,ExecutionPolicy&& policy,F f)const
|
||||
{
|
||||
if(!this->arrays.elements)return true;
|
||||
auto first=this->arrays.groups,
|
||||
if(!this->arrays.elements())return true;
|
||||
auto first=this->arrays.groups(),
|
||||
last=first+this->arrays.groups_size_mask+1;
|
||||
return std::all_of(std::forward<ExecutionPolicy>(policy),first,last,
|
||||
[&,this](group_type& g){
|
||||
auto pos=static_cast<std::size_t>(&g-first);
|
||||
auto p=this->arrays.elements+pos*N;
|
||||
auto p=this->arrays.elements()+pos*N;
|
||||
auto lck=access(access_mode,pos);
|
||||
auto mask=this->match_really_occupied(&g,last);
|
||||
while(mask){
|
||||
|
@ -938,74 +938,145 @@ Group* dummy_groups()
|
||||
const_cast<typename Group::dummy_group_type*>(storage));
|
||||
}
|
||||
|
||||
template<typename Value,typename Group,typename SizePolicy>
|
||||
template<
|
||||
typename Ptr,typename Ptr2,
|
||||
typename std::enable_if<!std::is_same<Ptr,Ptr2>::value>::type* = nullptr
|
||||
>
|
||||
Ptr to_pointer(Ptr2 p)
|
||||
{
|
||||
if(!p){return nullptr;}
|
||||
return boost::pointer_traits<Ptr>::pointer_to(*p);
|
||||
}
|
||||
|
||||
template<typename Ptr>
|
||||
Ptr to_pointer(Ptr p)
|
||||
{
|
||||
return p;
|
||||
}
|
||||
|
||||
template<typename Arrays,typename Allocator>
|
||||
struct arrays_holder
|
||||
{
|
||||
arrays_holder(Arrays const& arrays, Allocator const& al)
|
||||
:arrays_{arrays},al_{al}
|
||||
{}
|
||||
|
||||
arrays_holder(arrays_holder const&)=delete;
|
||||
arrays_holder& operator=(arrays_holder const&)=delete;
|
||||
|
||||
~arrays_holder()
|
||||
{
|
||||
if (!released_){
|
||||
arrays_.delete_(al_,arrays_);
|
||||
}
|
||||
}
|
||||
|
||||
Arrays const& get()const
|
||||
{
|
||||
return arrays_;
|
||||
}
|
||||
|
||||
void release()
|
||||
{
|
||||
released_=true;
|
||||
}
|
||||
|
||||
private:
|
||||
Arrays arrays_;
|
||||
Allocator al_;
|
||||
bool released_=false;
|
||||
};
|
||||
|
||||
template<typename Value,typename Group,typename SizePolicy,typename Allocator>
|
||||
struct table_arrays
|
||||
{
|
||||
using allocator_type=typename boost::allocator_rebind<Allocator,Value>::type;
|
||||
|
||||
using value_type=Value;
|
||||
using group_type=Group;
|
||||
static constexpr auto N=group_type::N;
|
||||
using size_policy=SizePolicy;
|
||||
using value_type_pointer=
|
||||
typename boost::allocator_pointer<allocator_type>::type;
|
||||
using group_type_pointer=
|
||||
typename boost::pointer_traits<value_type_pointer>::template
|
||||
rebind<group_type>;
|
||||
using group_type_pointer_traits=boost::pointer_traits<group_type_pointer>;
|
||||
|
||||
table_arrays(std::size_t gsi,std::size_t gsm,group_type *pg,value_type *pe):
|
||||
groups_size_index{gsi},groups_size_mask{gsm},groups{pg},elements{pe}{}
|
||||
table_arrays(std::size_t gsi,std::size_t gsm,group_type_pointer pg,value_type_pointer pe):
|
||||
groups_size_index{gsi},groups_size_mask{gsm},groups_{pg},elements_{pe}{}
|
||||
|
||||
template<typename Allocator>
|
||||
static table_arrays new_(Allocator& al,std::size_t n)
|
||||
value_type* elements()const noexcept{return boost::to_address(elements_);}
|
||||
group_type* groups()const noexcept{return boost::to_address(groups_);}
|
||||
|
||||
static void set_arrays(table_arrays& arrays,allocator_type al,std::size_t n)
|
||||
{
|
||||
using storage_allocator=
|
||||
typename boost::allocator_rebind<Allocator, Value>::type;
|
||||
using storage_traits=boost::allocator_traits<storage_allocator>;
|
||||
return set_arrays(
|
||||
arrays,al,n,std::is_same<group_type*,group_type_pointer>{});
|
||||
}
|
||||
|
||||
static void set_arrays(
|
||||
table_arrays& arrays,allocator_type al,std::size_t,std::false_type /* always allocate */)
|
||||
{
|
||||
using storage_traits=boost::allocator_traits<allocator_type>;
|
||||
auto groups_size_index=arrays.groups_size_index;
|
||||
auto groups_size=size_policy::size(groups_size_index);
|
||||
|
||||
auto sal=allocator_type(al);
|
||||
arrays.elements_=storage_traits::allocate(sal,buffer_size(groups_size));
|
||||
|
||||
/* Align arrays.groups to sizeof(group_type). table_iterator critically
|
||||
* depends on such alignment for its increment operation.
|
||||
*/
|
||||
|
||||
auto p=reinterpret_cast<unsigned char*>(arrays.elements()+groups_size*N-1);
|
||||
p+=(uintptr_t(sizeof(group_type))-
|
||||
reinterpret_cast<uintptr_t>(p))%sizeof(group_type);
|
||||
arrays.groups_=group_type_pointer_traits::pointer_to(*reinterpret_cast<group_type*>(p));
|
||||
|
||||
initialize_groups(
|
||||
arrays.groups(),groups_size,
|
||||
std::integral_constant<
|
||||
bool,
|
||||
#if BOOST_WORKAROUND(BOOST_LIBSTDCXX_VERSION,<50000)
|
||||
/* std::is_trivially_constructible not provided */
|
||||
boost::has_trivial_constructor<group_type>::value
|
||||
#else
|
||||
std::is_trivially_constructible<group_type>::value
|
||||
#endif
|
||||
>{});
|
||||
arrays.groups()[groups_size-1].set_sentinel();
|
||||
}
|
||||
|
||||
static void set_arrays(
|
||||
table_arrays& arrays,allocator_type al,std::size_t n,std::true_type /* optimize for n==0*/)
|
||||
{
|
||||
if(!n){
|
||||
arrays.groups_=dummy_groups<group_type,size_policy::min_size()>();
|
||||
}
|
||||
else{
|
||||
set_arrays(arrays,al,n,std::false_type{});
|
||||
}
|
||||
}
|
||||
|
||||
static table_arrays new_(allocator_type al,std::size_t n)
|
||||
{
|
||||
auto groups_size_index=size_index_for<group_type,size_policy>(n);
|
||||
auto groups_size=size_policy::size(groups_size_index);
|
||||
table_arrays arrays{groups_size_index,groups_size-1,nullptr,nullptr};
|
||||
|
||||
if(!n){
|
||||
arrays.groups=dummy_groups<group_type,size_policy::min_size()>();
|
||||
}
|
||||
else{
|
||||
auto sal=storage_allocator(al);
|
||||
arrays.elements=boost::to_address(
|
||||
storage_traits::allocate(sal,buffer_size(groups_size)));
|
||||
|
||||
/* Align arrays.groups to sizeof(group_type). table_iterator critically
|
||||
* depends on such alignment for its increment operation.
|
||||
*/
|
||||
|
||||
auto p=reinterpret_cast<unsigned char*>(arrays.elements+groups_size*N-1);
|
||||
p+=(uintptr_t(sizeof(group_type))-
|
||||
reinterpret_cast<uintptr_t>(p))%sizeof(group_type);
|
||||
arrays.groups=reinterpret_cast<group_type*>(p);
|
||||
|
||||
initialize_groups(
|
||||
arrays.groups,groups_size,
|
||||
std::integral_constant<
|
||||
bool,
|
||||
#if BOOST_WORKAROUND(BOOST_LIBSTDCXX_VERSION,<50000)
|
||||
/* std::is_trivially_constructible not provided */
|
||||
boost::has_trivial_constructor<group_type>::value
|
||||
#else
|
||||
std::is_trivially_constructible<group_type>::value
|
||||
#endif
|
||||
>{});
|
||||
arrays.groups[groups_size-1].set_sentinel();
|
||||
}
|
||||
set_arrays(arrays,al,n);
|
||||
return arrays;
|
||||
}
|
||||
|
||||
template<typename Allocator>
|
||||
static void delete_(Allocator& al,table_arrays& arrays)noexcept
|
||||
static void delete_(allocator_type al,table_arrays& arrays)noexcept
|
||||
{
|
||||
using storage_alloc=typename boost::allocator_rebind<Allocator,Value>::type;
|
||||
using storage_traits=boost::allocator_traits<storage_alloc>;
|
||||
using pointer=typename storage_traits::pointer;
|
||||
using pointer_traits=boost::pointer_traits<pointer>;
|
||||
using storage_traits=boost::allocator_traits<allocator_type>;
|
||||
|
||||
auto sal=storage_alloc(al);
|
||||
if(arrays.elements){
|
||||
auto sal=allocator_type(al);
|
||||
if(arrays.elements()){
|
||||
storage_traits::deallocate(
|
||||
sal,pointer_traits::pointer_to(*arrays.elements),
|
||||
buffer_size(arrays.groups_size_mask+1));
|
||||
sal,arrays.elements_,buffer_size(arrays.groups_size_mask+1));
|
||||
}
|
||||
}
|
||||
|
||||
@ -1024,7 +1095,7 @@ struct table_arrays
|
||||
}
|
||||
|
||||
static void initialize_groups(
|
||||
group_type* groups_,std::size_t size,std::true_type /* memset */)
|
||||
group_type* pg,std::size_t size,std::true_type /* memset */)
|
||||
{
|
||||
/* memset faster/not slower than manual, assumes all zeros is group_type's
|
||||
* default layout.
|
||||
@ -1033,19 +1104,19 @@ struct table_arrays
|
||||
*/
|
||||
|
||||
std::memset(
|
||||
reinterpret_cast<unsigned char*>(groups_),0,sizeof(group_type)*size);
|
||||
reinterpret_cast<unsigned char*>(pg),0,sizeof(group_type)*size);
|
||||
}
|
||||
|
||||
static void initialize_groups(
|
||||
group_type* groups_,std::size_t size,std::false_type /* manual */)
|
||||
group_type* pg,std::size_t size,std::false_type /* manual */)
|
||||
{
|
||||
while(size--!=0)::new (groups_++) group_type();
|
||||
while(size--!=0)::new (pg++) group_type();
|
||||
}
|
||||
|
||||
std::size_t groups_size_index;
|
||||
std::size_t groups_size_mask;
|
||||
group_type *groups;
|
||||
value_type *elements;
|
||||
std::size_t groups_size_index;
|
||||
std::size_t groups_size_mask;
|
||||
group_type_pointer groups_;
|
||||
value_type_pointer elements_;
|
||||
};
|
||||
|
||||
struct if_constexpr_void_else{void operator()()const{}};
|
||||
@ -1258,8 +1329,12 @@ public:
|
||||
>::type;
|
||||
using alloc_traits=boost::allocator_traits<Allocator>;
|
||||
using element_type=typename type_policy::element_type;
|
||||
using arrays_type=Arrays<element_type,group_type,size_policy>;
|
||||
using arrays_type=Arrays<element_type,group_type,size_policy,Allocator>;
|
||||
using size_ctrl_type=SizeControl;
|
||||
static constexpr auto uses_fancy_pointers=!std::is_same<
|
||||
typename alloc_traits::pointer,
|
||||
typename alloc_traits::value_type*
|
||||
>::value;
|
||||
|
||||
using key_type=typename type_policy::key_type;
|
||||
using init_type=typename type_policy::init_type;
|
||||
@ -1283,31 +1358,44 @@ public:
|
||||
size_ctrl{initial_max_load(),0}
|
||||
{}
|
||||
|
||||
/* bare transfer ctor for concurrent/non-concurrent interop */
|
||||
|
||||
/* genericize on an ArraysFn so that we can do things like delay an
|
||||
* allocation for the group_access data required by cfoa after the move
|
||||
* constructors of Hash, Pred have been invoked
|
||||
*/
|
||||
template<typename ArraysFn>
|
||||
table_core(
|
||||
Hash&& h_,Pred&& pred_,Allocator&& al_,
|
||||
const arrays_type& arrays_,const size_ctrl_type& size_ctrl_):
|
||||
ArraysFn arrays_fn,const size_ctrl_type& size_ctrl_):
|
||||
hash_base{empty_init,std::move(h_)},
|
||||
pred_base{empty_init,std::move(pred_)},
|
||||
allocator_base{empty_init,std::move(al_)},
|
||||
arrays(arrays_),size_ctrl(size_ctrl_)
|
||||
arrays(arrays_fn()),size_ctrl(size_ctrl_)
|
||||
{}
|
||||
|
||||
table_core(const table_core& x):
|
||||
table_core{x,alloc_traits::select_on_container_copy_construction(x.al())}{}
|
||||
|
||||
template<typename ArraysFn>
|
||||
table_core(table_core&& x,arrays_holder<arrays_type,Allocator>&& ah,ArraysFn arrays_fn):
|
||||
table_core(
|
||||
std::move(x.h()),std::move(x.pred()),std::move(x.al()),arrays_fn,x.size_ctrl)
|
||||
{
|
||||
ah.release();
|
||||
x.arrays=ah.get();
|
||||
x.size_ctrl.ml=x.initial_max_load();
|
||||
x.size_ctrl.size=0;
|
||||
}
|
||||
|
||||
table_core(table_core&& x)
|
||||
noexcept(
|
||||
std::is_nothrow_move_constructible<Hash>::value&&
|
||||
std::is_nothrow_move_constructible<Pred>::value&&
|
||||
std::is_nothrow_move_constructible<Allocator>::value):
|
||||
std::is_nothrow_move_constructible<Allocator>::value&&
|
||||
!uses_fancy_pointers):
|
||||
table_core{
|
||||
std::move(x.h()),std::move(x.pred()),std::move(x.al()),
|
||||
x.arrays,x.size_ctrl}
|
||||
{
|
||||
x.empty_initialize();
|
||||
}
|
||||
std::move(x),arrays_holder<arrays_type,Allocator>{x.new_arrays(0),x.al()},
|
||||
[&x]{return x.arrays;}}
|
||||
{}
|
||||
|
||||
table_core(const table_core& x,const Allocator& al_):
|
||||
table_core{std::size_t(std::ceil(float(x.size())/mlf)),x.h(),x.pred(),al_}
|
||||
@ -1345,11 +1433,17 @@ public:
|
||||
delete_arrays(arrays);
|
||||
}
|
||||
|
||||
void empty_initialize()noexcept
|
||||
std::size_t initial_max_load()const
|
||||
{
|
||||
arrays=new_arrays(0);
|
||||
size_ctrl.ml=initial_max_load();
|
||||
size_ctrl.size=0;
|
||||
static constexpr std::size_t small_capacity=2*N-1;
|
||||
|
||||
auto capacity_=capacity();
|
||||
if(capacity_<=small_capacity){
|
||||
return capacity_; /* we allow 100% usage */
|
||||
}
|
||||
else{
|
||||
return (std::size_t)(mlf*(float)(capacity_));
|
||||
}
|
||||
}
|
||||
|
||||
table_core& operator=(const table_core& x)
|
||||
@ -1397,8 +1491,8 @@ public:
|
||||
|
||||
table_core& operator=(table_core&& x)
|
||||
noexcept(
|
||||
alloc_traits::propagate_on_container_move_assignment::value||
|
||||
alloc_traits::is_always_equal::value)
|
||||
(alloc_traits::propagate_on_container_move_assignment::value||
|
||||
alloc_traits::is_always_equal::value)&&!uses_fancy_pointers)
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_HASH_PRED(Hash, Pred)
|
||||
|
||||
@ -1489,11 +1583,12 @@ public:
|
||||
prober pb(pos0);
|
||||
do{
|
||||
auto pos=pb.get();
|
||||
auto pg=arrays.groups+pos;
|
||||
auto pg=arrays.groups()+pos;
|
||||
auto mask=pg->match(hash);
|
||||
if(mask){
|
||||
BOOST_UNORDERED_ASSUME(arrays.elements!=nullptr);
|
||||
auto p=arrays.elements+pos*N;
|
||||
auto elements=arrays.elements();
|
||||
BOOST_UNORDERED_ASSUME(elements!=nullptr);
|
||||
auto p=elements+pos*N;
|
||||
BOOST_UNORDERED_PREFETCH_ELEMENTS(p,N);
|
||||
do{
|
||||
auto n=unchecked_countr_zero(mask);
|
||||
@ -1542,9 +1637,9 @@ public:
|
||||
|
||||
void clear()noexcept
|
||||
{
|
||||
auto p=arrays.elements;
|
||||
auto p=arrays.elements();
|
||||
if(p){
|
||||
for(auto pg=arrays.groups,last=pg+arrays.groups_size_mask+1;
|
||||
for(auto pg=arrays.groups(),last=pg+arrays.groups_size_mask+1;
|
||||
pg!=last;++pg,p+=N){
|
||||
auto mask=match_really_occupied(pg,last);
|
||||
while(mask){
|
||||
@ -1554,7 +1649,7 @@ public:
|
||||
/* we wipe the entire metadata to reset the overflow byte as well */
|
||||
pg->initialize();
|
||||
}
|
||||
arrays.groups[arrays.groups_size_mask].set_sentinel();
|
||||
arrays.groups()[arrays.groups_size_mask].set_sentinel();
|
||||
size_ctrl.ml=initial_max_load();
|
||||
size_ctrl.size=0;
|
||||
}
|
||||
@ -1565,7 +1660,7 @@ public:
|
||||
|
||||
std::size_t capacity()const noexcept
|
||||
{
|
||||
return arrays.elements?(arrays.groups_size_mask+1)*N-1:0;
|
||||
return arrays.elements()?(arrays.groups_size_mask+1)*N-1:0;
|
||||
}
|
||||
|
||||
float load_factor()const noexcept
|
||||
@ -1784,9 +1879,9 @@ public:
|
||||
static auto for_all_elements_while(const arrays_type& arrays_,F f)
|
||||
->decltype(f(nullptr,0,nullptr),bool())
|
||||
{
|
||||
auto p=arrays_.elements;
|
||||
auto p=arrays_.elements();
|
||||
if(p){
|
||||
for(auto pg=arrays_.groups,last=pg+arrays_.groups_size_mask+1;
|
||||
for(auto pg=arrays_.groups(),last=pg+arrays_.groups_size_mask+1;
|
||||
pg!=last;++pg,p+=N){
|
||||
auto mask=match_really_occupied(pg,last);
|
||||
while(mask){
|
||||
@ -1887,7 +1982,7 @@ private:
|
||||
|
||||
void fast_copy_elements_from(const table_core& x)
|
||||
{
|
||||
if(arrays.elements&&x.arrays.elements){
|
||||
if(arrays.elements()&&x.arrays.elements()){
|
||||
copy_elements_array_from(x);
|
||||
copy_groups_array_from(x);
|
||||
size_ctrl.ml=std::size_t(x.size_ctrl.ml);
|
||||
@ -1921,8 +2016,8 @@ private:
|
||||
* copy-assignable when we're relying on trivial copy constructibility.
|
||||
*/
|
||||
std::memcpy(
|
||||
reinterpret_cast<unsigned char*>(arrays.elements),
|
||||
reinterpret_cast<unsigned char*>(x.arrays.elements),
|
||||
reinterpret_cast<unsigned char*>(arrays.elements()),
|
||||
reinterpret_cast<unsigned char*>(x.arrays.elements()),
|
||||
x.capacity()*sizeof(value_type));
|
||||
}
|
||||
|
||||
@ -1932,14 +2027,14 @@ private:
|
||||
std::size_t num_constructed=0;
|
||||
BOOST_TRY{
|
||||
x.for_all_elements([&,this](const element_type* p){
|
||||
construct_element(arrays.elements+(p-x.arrays.elements),*p);
|
||||
construct_element(arrays.elements()+(p-x.arrays.elements()),*p);
|
||||
++num_constructed;
|
||||
});
|
||||
}
|
||||
BOOST_CATCH(...){
|
||||
if(num_constructed){
|
||||
x.for_all_elements_while([&,this](const element_type* p){
|
||||
destroy_element(arrays.elements+(p-x.arrays.elements));
|
||||
destroy_element(arrays.elements()+(p-x.arrays.elements()));
|
||||
return --num_constructed!=0;
|
||||
});
|
||||
}
|
||||
@ -1964,15 +2059,17 @@ private:
|
||||
const table_core& x, std::true_type /* -> memcpy */)
|
||||
{
|
||||
std::memcpy(
|
||||
arrays.groups,x.arrays.groups,
|
||||
arrays.groups(),x.arrays.groups(),
|
||||
(arrays.groups_size_mask+1)*sizeof(group_type));
|
||||
}
|
||||
|
||||
void copy_groups_array_from(
|
||||
const table_core& x, std::false_type /* -> manual */)
|
||||
{
|
||||
auto pg=arrays.groups();
|
||||
auto xpg=x.arrays.groups();
|
||||
for(std::size_t i=0;i<arrays.groups_size_mask+1;++i){
|
||||
arrays.groups[i]=x.arrays.groups[i];
|
||||
pg[i]=xpg[i];
|
||||
}
|
||||
}
|
||||
|
||||
@ -1992,19 +2089,6 @@ private:
|
||||
recover_slot(reinterpret_cast<unsigned char*>(pg)+pos);
|
||||
}
|
||||
|
||||
std::size_t initial_max_load()const
|
||||
{
|
||||
static constexpr std::size_t small_capacity=2*N-1;
|
||||
|
||||
auto capacity_=capacity();
|
||||
if(capacity_<=small_capacity){
|
||||
return capacity_; /* we allow 100% usage */
|
||||
}
|
||||
else{
|
||||
return (std::size_t)(mlf*(float)(capacity_));
|
||||
}
|
||||
}
|
||||
|
||||
static std::size_t capacity_for(std::size_t n)
|
||||
{
|
||||
return size_policy::size(size_index_for<group_type,size_policy>(n))*N-1;
|
||||
@ -2102,11 +2186,11 @@ private:
|
||||
{
|
||||
for(prober pb(pos0);;pb.next(arrays_.groups_size_mask)){
|
||||
auto pos=pb.get();
|
||||
auto pg=arrays_.groups+pos;
|
||||
auto pg=arrays_.groups()+pos;
|
||||
auto mask=pg->match_available();
|
||||
if(BOOST_LIKELY(mask!=0)){
|
||||
auto n=unchecked_countr_zero(mask);
|
||||
auto p=arrays_.elements+pos*N+n;
|
||||
auto p=arrays_.elements()+pos*N+n;
|
||||
construct_element(p,std::forward<Args>(args)...);
|
||||
pg->set(n,hash);
|
||||
return {pg,n,p};
|
||||
|
@ -9,26 +9,30 @@
|
||||
#ifndef BOOST_UNORDERED_DETAIL_FOA_ELEMENT_TYPE_HPP
|
||||
#define BOOST_UNORDERED_DETAIL_FOA_ELEMENT_TYPE_HPP
|
||||
|
||||
#include <boost/core/pointer_traits.hpp>
|
||||
|
||||
namespace boost{
|
||||
namespace unordered{
|
||||
namespace detail{
|
||||
namespace foa{
|
||||
|
||||
template<class T>
|
||||
template<class T,class VoidPtr>
|
||||
struct element_type
|
||||
{
|
||||
using value_type=T;
|
||||
value_type* p;
|
||||
using pointer=typename boost::pointer_traits<VoidPtr>::template rebind<T>;
|
||||
|
||||
pointer 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
|
||||
element_type()=default;
|
||||
element_type(pointer p_):p(p_){}
|
||||
element_type(element_type const&)=delete;
|
||||
element_type(element_type&& rhs)noexcept
|
||||
{
|
||||
p = rhs.p;
|
||||
rhs.p = nullptr;
|
||||
|
@ -13,7 +13,7 @@ namespace boost {
|
||||
namespace unordered {
|
||||
namespace detail {
|
||||
namespace foa {
|
||||
template <class Key, class T> struct node_map_types
|
||||
template <class Key, class T, class VoidPtr> struct node_map_types
|
||||
{
|
||||
using key_type = Key;
|
||||
using mapped_type = T;
|
||||
@ -24,7 +24,7 @@ namespace boost {
|
||||
using value_type = std::pair<Key const, T>;
|
||||
using moved_type = std::pair<raw_key_type&&, raw_mapped_type&&>;
|
||||
|
||||
using element_type = foa::element_type<value_type>;
|
||||
using element_type = foa::element_type<value_type, VoidPtr>;
|
||||
|
||||
static value_type& value_from(element_type const& x)
|
||||
{
|
||||
@ -83,18 +83,15 @@ namespace boost {
|
||||
template <class A, class... Args>
|
||||
static void construct(A& al, element_type* p, Args&&... args)
|
||||
{
|
||||
p->p = boost::to_address(boost::allocator_allocate(al, 1));
|
||||
p->p = boost::allocator_allocate(al, 1);
|
||||
BOOST_TRY
|
||||
{
|
||||
boost::allocator_construct(al, p->p, std::forward<Args>(args)...);
|
||||
boost::allocator_construct(
|
||||
al, boost::to_address(p->p), std::forward<Args>(args)...);
|
||||
}
|
||||
BOOST_CATCH(...)
|
||||
{
|
||||
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::allocator_deallocate(al, p->p, 1);
|
||||
BOOST_RETHROW
|
||||
}
|
||||
BOOST_CATCH_END
|
||||
@ -114,12 +111,8 @@ namespace boost {
|
||||
static void destroy(A& al, element_type* p) noexcept
|
||||
{
|
||||
if (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);
|
||||
destroy(al, boost::to_address(p->p));
|
||||
boost::allocator_deallocate(al, p->p, 1);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -14,7 +14,7 @@ namespace boost {
|
||||
namespace detail {
|
||||
namespace foa {
|
||||
|
||||
template <class Key> struct node_set_types
|
||||
template <class Key, class VoidPtr> struct node_set_types
|
||||
{
|
||||
using key_type = Key;
|
||||
using init_type = Key;
|
||||
@ -22,7 +22,7 @@ namespace boost {
|
||||
|
||||
static Key const& extract(value_type const& key) { return key; }
|
||||
|
||||
using element_type = foa::element_type<value_type>;
|
||||
using element_type = foa::element_type<value_type, VoidPtr>;
|
||||
|
||||
static value_type& value_from(element_type const& x) { return *x.p; }
|
||||
static Key const& extract(element_type const& k) { return *k.p; }
|
||||
@ -53,17 +53,15 @@ namespace boost {
|
||||
template <class A, class... Args>
|
||||
static void construct(A& al, element_type* p, Args&&... args)
|
||||
{
|
||||
p->p = boost::to_address(boost::allocator_allocate(al, 1));
|
||||
p->p = boost::allocator_allocate(al, 1);
|
||||
BOOST_TRY
|
||||
{
|
||||
boost::allocator_construct(al, p->p, std::forward<Args>(args)...);
|
||||
boost::allocator_construct(
|
||||
al, boost::to_address(p->p), std::forward<Args>(args)...);
|
||||
}
|
||||
BOOST_CATCH(...)
|
||||
{
|
||||
boost::allocator_deallocate(al,
|
||||
boost::pointer_traits<typename boost::allocator_pointer<
|
||||
A>::type>::pointer_to(*p->p),
|
||||
1);
|
||||
boost::allocator_deallocate(al, p->p, 1);
|
||||
BOOST_RETHROW
|
||||
}
|
||||
BOOST_CATCH_END
|
||||
@ -78,11 +76,8 @@ namespace boost {
|
||||
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);
|
||||
destroy(al, boost::to_address(p->p));
|
||||
boost::allocator_deallocate(al, p->p, 1);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -81,12 +81,17 @@ class table;
|
||||
/* internal conversion from const_iterator to iterator */
|
||||
struct const_iterator_cast_tag{};
|
||||
|
||||
template<typename TypePolicy,typename Group,bool Const>
|
||||
template<typename TypePolicy,typename GroupPtr,bool Const>
|
||||
class table_iterator
|
||||
{
|
||||
using group_pointer_traits=boost::pointer_traits<GroupPtr>;
|
||||
using type_policy=TypePolicy;
|
||||
using table_element_type=typename type_policy::element_type;
|
||||
using group_type=Group;
|
||||
using group_type=typename group_pointer_traits::element_type;
|
||||
using table_element_pointer=
|
||||
typename group_pointer_traits::template rebind<table_element_type>;
|
||||
using char_pointer=
|
||||
typename group_pointer_traits::template rebind<unsigned char>;
|
||||
static constexpr auto N=group_type::N;
|
||||
static constexpr auto regular_layout=group_type::regular_layout;
|
||||
|
||||
@ -103,22 +108,22 @@ public:
|
||||
|
||||
table_iterator()=default;
|
||||
template<bool Const2,typename std::enable_if<!Const2>::type* =nullptr>
|
||||
table_iterator(const table_iterator<TypePolicy,Group,Const2>& x):
|
||||
pc{x.pc},p{x.p}{}
|
||||
table_iterator(const table_iterator<TypePolicy,GroupPtr,Const2>& x):
|
||||
pc_{x.pc_},p_{x.p_}{}
|
||||
table_iterator(
|
||||
const_iterator_cast_tag, const table_iterator<TypePolicy,Group,true>& x):
|
||||
pc{x.pc},p{x.p}{}
|
||||
const_iterator_cast_tag, const table_iterator<TypePolicy,GroupPtr,true>& x):
|
||||
pc_{x.pc_},p_{x.p_}{}
|
||||
|
||||
inline reference operator*()const noexcept
|
||||
{return type_policy::value_from(*p);}
|
||||
{return type_policy::value_from(*p());}
|
||||
inline pointer operator->()const noexcept
|
||||
{return std::addressof(type_policy::value_from(*p));}
|
||||
{return std::addressof(type_policy::value_from(*p()));}
|
||||
inline table_iterator& operator++()noexcept{increment();return *this;}
|
||||
inline table_iterator operator++(int)noexcept
|
||||
{auto x=*this;increment();return x;}
|
||||
friend inline bool operator==(
|
||||
const table_iterator& x,const table_iterator& y)
|
||||
{return x.p==y.p;}
|
||||
{return x.p()==y.p();}
|
||||
friend inline bool operator!=(
|
||||
const table_iterator& x,const table_iterator& y)
|
||||
{return !(x==y);}
|
||||
@ -128,81 +133,91 @@ private:
|
||||
template<typename> friend class table_erase_return_type;
|
||||
template<typename,typename,typename,typename> friend class table;
|
||||
|
||||
table_iterator(Group* pg,std::size_t n,const table_element_type* p_):
|
||||
pc{reinterpret_cast<unsigned char*>(const_cast<group_type*>(pg))+n},
|
||||
p{const_cast<table_element_type*>(p_)}
|
||||
{}
|
||||
table_iterator(group_type* pg,std::size_t n,const table_element_type* p):
|
||||
pc_{to_pointer<char_pointer>(
|
||||
reinterpret_cast<unsigned char*>(const_cast<group_type*>(pg))+n)},
|
||||
p_{to_pointer<table_element_pointer>(const_cast<table_element_type*>(p))}
|
||||
{}
|
||||
|
||||
unsigned char* pc()const noexcept{return boost::to_address(pc_);}
|
||||
table_element_type* p()const noexcept{return boost::to_address(p_);}
|
||||
|
||||
inline void increment()noexcept
|
||||
{
|
||||
BOOST_ASSERT(p!=nullptr);
|
||||
BOOST_ASSERT(p()!=nullptr);
|
||||
increment(std::integral_constant<bool,regular_layout>{});
|
||||
}
|
||||
|
||||
inline void increment(std::true_type /* regular layout */)noexcept
|
||||
{
|
||||
using diff_type=
|
||||
typename boost::pointer_traits<char_pointer>::difference_type;
|
||||
|
||||
for(;;){
|
||||
++p;
|
||||
if(reinterpret_cast<uintptr_t>(pc)%sizeof(group_type)==N-1){
|
||||
pc+=sizeof(group_type)-(N-1);
|
||||
++p_;
|
||||
if(reinterpret_cast<uintptr_t>(pc())%sizeof(group_type)==N-1){
|
||||
pc_+=static_cast<diff_type>(sizeof(group_type)-(N-1));
|
||||
break;
|
||||
}
|
||||
++pc;
|
||||
if(!group_type::is_occupied(pc))continue;
|
||||
if(BOOST_UNLIKELY(group_type::is_sentinel(pc)))p=nullptr;
|
||||
++pc_;
|
||||
if(!group_type::is_occupied(pc()))continue;
|
||||
if(BOOST_UNLIKELY(group_type::is_sentinel(pc())))p_=nullptr;
|
||||
return;
|
||||
}
|
||||
|
||||
for(;;){
|
||||
int mask=reinterpret_cast<group_type*>(pc)->match_occupied();
|
||||
int mask=reinterpret_cast<group_type*>(pc())->match_occupied();
|
||||
if(mask!=0){
|
||||
auto n=unchecked_countr_zero(mask);
|
||||
if(BOOST_UNLIKELY(reinterpret_cast<group_type*>(pc)->is_sentinel(n))){
|
||||
p=nullptr;
|
||||
if(BOOST_UNLIKELY(reinterpret_cast<group_type*>(pc())->is_sentinel(n))){
|
||||
p_=nullptr;
|
||||
}
|
||||
else{
|
||||
pc+=n;
|
||||
p+=n;
|
||||
pc_+=static_cast<diff_type>(n);
|
||||
p_+=static_cast<diff_type>(n);
|
||||
}
|
||||
return;
|
||||
}
|
||||
pc+=sizeof(group_type);
|
||||
p+=N;
|
||||
pc_+=static_cast<diff_type>(sizeof(group_type));
|
||||
p_+=static_cast<diff_type>(N);
|
||||
}
|
||||
}
|
||||
|
||||
inline void increment(std::false_type /* interleaved */)noexcept
|
||||
{
|
||||
std::size_t n0=reinterpret_cast<uintptr_t>(pc)%sizeof(group_type);
|
||||
pc-=n0;
|
||||
using diff_type=
|
||||
typename boost::pointer_traits<char_pointer>::difference_type;
|
||||
|
||||
std::size_t n0=reinterpret_cast<uintptr_t>(pc())%sizeof(group_type);
|
||||
pc_-=static_cast<diff_type>(n0);
|
||||
|
||||
int mask=(
|
||||
reinterpret_cast<group_type*>(pc)->match_occupied()>>(n0+1))<<(n0+1);
|
||||
reinterpret_cast<group_type*>(pc())->match_occupied()>>(n0+1))<<(n0+1);
|
||||
if(!mask){
|
||||
do{
|
||||
pc+=sizeof(group_type);
|
||||
p+=N;
|
||||
pc_+=sizeof(group_type);
|
||||
p_+=N;
|
||||
}
|
||||
while((mask=reinterpret_cast<group_type*>(pc)->match_occupied())==0);
|
||||
while((mask=reinterpret_cast<group_type*>(pc())->match_occupied())==0);
|
||||
}
|
||||
|
||||
auto n=unchecked_countr_zero(mask);
|
||||
if(BOOST_UNLIKELY(reinterpret_cast<group_type*>(pc)->is_sentinel(n))){
|
||||
p=nullptr;
|
||||
if(BOOST_UNLIKELY(reinterpret_cast<group_type*>(pc())->is_sentinel(n))){
|
||||
p_=nullptr;
|
||||
}
|
||||
else{
|
||||
pc+=n;
|
||||
p-=n0;
|
||||
p+=n;
|
||||
pc_+=static_cast<diff_type>(n);
|
||||
p_-=static_cast<diff_type>(n0);
|
||||
p_+=static_cast<diff_type>(n);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename Archive>
|
||||
friend void serialization_track(Archive& ar,const table_iterator& x)
|
||||
{
|
||||
if(x.p){
|
||||
track_address(ar,x.pc);
|
||||
track_address(ar,x.p);
|
||||
if(x.p()){
|
||||
track_address(ar,x.pc_);
|
||||
track_address(ar,x.p_);
|
||||
}
|
||||
}
|
||||
|
||||
@ -211,13 +226,13 @@ private:
|
||||
template<typename Archive>
|
||||
void serialize(Archive& ar,unsigned int)
|
||||
{
|
||||
if(!p)pc=nullptr;
|
||||
serialize_tracked_address(ar,pc);
|
||||
serialize_tracked_address(ar,p);
|
||||
if(!p())pc_=nullptr;
|
||||
serialize_tracked_address(ar,pc_);
|
||||
serialize_tracked_address(ar,p_);
|
||||
}
|
||||
|
||||
unsigned char *pc=nullptr;
|
||||
table_element_type *p=nullptr;
|
||||
char_pointer pc_=nullptr;
|
||||
table_element_pointer p_=nullptr;
|
||||
};
|
||||
|
||||
/* Returned by table::erase([const_]iterator) to avoid iterator increment
|
||||
@ -227,11 +242,11 @@ private:
|
||||
template<typename Iterator>
|
||||
class table_erase_return_type;
|
||||
|
||||
template<typename TypePolicy,typename Group,bool Const>
|
||||
class table_erase_return_type<table_iterator<TypePolicy,Group,Const>>
|
||||
template<typename TypePolicy,typename GroupPtr,bool Const>
|
||||
class table_erase_return_type<table_iterator<TypePolicy,GroupPtr,Const>>
|
||||
{
|
||||
using iterator=table_iterator<TypePolicy,Group,Const>;
|
||||
using const_iterator=table_iterator<TypePolicy,Group,true>;
|
||||
using iterator=table_iterator<TypePolicy,GroupPtr,Const>;
|
||||
using const_iterator=table_iterator<TypePolicy,GroupPtr,true>;
|
||||
|
||||
public:
|
||||
/* can't delete it because VS in pre-C++17 mode needs to see it for RVO */
|
||||
@ -313,6 +328,9 @@ class table:table_core_impl<TypePolicy,Hash,Pred,Allocator>
|
||||
using locator=typename super::locator;
|
||||
using compatible_concurrent_table=
|
||||
concurrent_table<TypePolicy,Hash,Pred,Allocator>;
|
||||
using group_type_pointer=typename boost::pointer_traits<
|
||||
typename boost::allocator_pointer<Allocator>::type
|
||||
>::template rebind<group_type>;
|
||||
friend compatible_concurrent_table;
|
||||
|
||||
public:
|
||||
@ -324,7 +342,6 @@ public:
|
||||
private:
|
||||
static constexpr bool has_mutable_iterator=
|
||||
!std::is_same<key_type,value_type>::value;
|
||||
|
||||
public:
|
||||
using hasher=typename super::hasher;
|
||||
using key_equal=typename super::key_equal;
|
||||
@ -335,10 +352,10 @@ public:
|
||||
using const_reference=typename super::const_reference;
|
||||
using size_type=typename super::size_type;
|
||||
using difference_type=typename super::difference_type;
|
||||
using const_iterator=table_iterator<type_policy,group_type,true>;
|
||||
using const_iterator=table_iterator<type_policy,group_type_pointer,true>;
|
||||
using iterator=typename std::conditional<
|
||||
has_mutable_iterator,
|
||||
table_iterator<type_policy,group_type,false>,
|
||||
table_iterator<type_policy,group_type_pointer,false>,
|
||||
const_iterator>::type;
|
||||
using erase_return_type=table_erase_return_type<iterator>;
|
||||
|
||||
@ -363,9 +380,9 @@ public:
|
||||
|
||||
iterator begin()noexcept
|
||||
{
|
||||
iterator it{this->arrays.groups,0,this->arrays.elements};
|
||||
if(this->arrays.elements&&
|
||||
!(this->arrays.groups[0].match_occupied()&0x1))++it;
|
||||
iterator it{this->arrays.groups(),0,this->arrays.elements()};
|
||||
if(this->arrays.elements()&&
|
||||
!(this->arrays.groups()[0].match_occupied()&0x1))++it;
|
||||
return it;
|
||||
}
|
||||
|
||||
@ -431,7 +448,7 @@ public:
|
||||
BOOST_FORCEINLINE
|
||||
erase_return_type erase(const_iterator pos)noexcept
|
||||
{
|
||||
super::erase(pos.pc,pos.p);
|
||||
super::erase(pos.pc(),pos.p());
|
||||
return {pos};
|
||||
}
|
||||
|
||||
@ -462,7 +479,7 @@ public:
|
||||
BOOST_ASSERT(pos!=end());
|
||||
erase_on_exit e{*this,pos};
|
||||
(void)e;
|
||||
return std::move(*pos.p);
|
||||
return std::move(*pos.p());
|
||||
}
|
||||
|
||||
// TODO: should we accept different allocator too?
|
||||
@ -527,22 +544,32 @@ public:
|
||||
friend bool operator!=(const table& x,const table& y){return !(x==y);}
|
||||
|
||||
private:
|
||||
template<typename ExclusiveLockGuard>
|
||||
table(compatible_concurrent_table&& x,ExclusiveLockGuard):
|
||||
template<typename ArraysType>
|
||||
table(compatible_concurrent_table&& x,arrays_holder<ArraysType,Allocator>&& ah):
|
||||
super{
|
||||
std::move(x.h()),std::move(x.pred()),std::move(x.al()),
|
||||
arrays_type{
|
||||
[&x]{return arrays_type{
|
||||
x.arrays.groups_size_index,x.arrays.groups_size_mask,
|
||||
reinterpret_cast<group_type*>(x.arrays.groups),
|
||||
reinterpret_cast<value_type*>(x.arrays.elements)},
|
||||
size_ctrl_type{
|
||||
x.size_ctrl.ml,x.size_ctrl.size}}
|
||||
to_pointer<group_type_pointer>(
|
||||
reinterpret_cast<group_type*>(x.arrays.groups())),
|
||||
x.arrays.elements_};},
|
||||
size_ctrl_type{x.size_ctrl.ml,x.size_ctrl.size}}
|
||||
{
|
||||
compatible_concurrent_table::arrays_type::delete_group_access(
|
||||
this->al(),x.arrays);
|
||||
x.empty_initialize();
|
||||
ah.release();
|
||||
|
||||
compatible_concurrent_table::arrays_type::delete_group_access(x.al(),x.arrays);
|
||||
x.arrays=ah.get();
|
||||
x.size_ctrl.ml=x.initial_max_load();
|
||||
x.size_ctrl.size=0;
|
||||
}
|
||||
|
||||
template<typename ExclusiveLockGuard>
|
||||
table(compatible_concurrent_table&& x,ExclusiveLockGuard):
|
||||
table(std::move(x),arrays_holder<
|
||||
typename compatible_concurrent_table::arrays_type,Allocator
|
||||
>{compatible_concurrent_table::arrays_type::new_(x.al(),0),x.al()})
|
||||
{}
|
||||
|
||||
struct erase_on_exit
|
||||
{
|
||||
erase_on_exit(table& x_,const_iterator it_):x{x_},it{it_}{}
|
||||
|
@ -141,9 +141,7 @@ namespace boost {
|
||||
}
|
||||
|
||||
unordered_flat_map(unordered_flat_map&& other)
|
||||
noexcept(std::is_nothrow_move_constructible<hasher>::value&&
|
||||
std::is_nothrow_move_constructible<key_equal>::value&&
|
||||
std::is_nothrow_move_constructible<allocator_type>::value)
|
||||
noexcept(std::is_nothrow_move_constructible<table_type>::value)
|
||||
: table_(std::move(other.table_))
|
||||
{
|
||||
}
|
||||
@ -697,10 +695,9 @@ namespace boost {
|
||||
return erase_if(map.table_, pred);
|
||||
}
|
||||
|
||||
template <class Archive,
|
||||
class Key, class T, class Hash, class KeyEqual, class Allocator>
|
||||
void serialize(
|
||||
Archive & ar,
|
||||
template <class Archive, class Key, class T, class Hash, class KeyEqual,
|
||||
class Allocator>
|
||||
void serialize(Archive& ar,
|
||||
unordered_flat_map<Key, T, Hash, KeyEqual, Allocator>& map,
|
||||
unsigned int version)
|
||||
{
|
||||
|
@ -133,9 +133,7 @@ namespace boost {
|
||||
}
|
||||
|
||||
unordered_flat_set(unordered_flat_set&& other)
|
||||
noexcept(std::is_nothrow_move_constructible<hasher>::value&&
|
||||
std::is_nothrow_move_constructible<key_equal>::value&&
|
||||
std::is_nothrow_move_constructible<allocator_type>::value)
|
||||
noexcept(std::is_nothrow_move_constructible<table_type>::value)
|
||||
: table_(std::move(other.table_))
|
||||
{
|
||||
}
|
||||
@ -506,10 +504,9 @@ namespace boost {
|
||||
return erase_if(set.table_, pred);
|
||||
}
|
||||
|
||||
template <class Archive,
|
||||
class Key, class Hash, class KeyEqual, class Allocator>
|
||||
void serialize(
|
||||
Archive & ar,
|
||||
template <class Archive, class Key, class Hash, class KeyEqual,
|
||||
class Allocator>
|
||||
void serialize(Archive& ar,
|
||||
unordered_flat_set<Key, Hash, KeyEqual, Allocator>& set,
|
||||
unsigned int version)
|
||||
{
|
||||
|
@ -75,7 +75,8 @@ namespace boost {
|
||||
template <class Key, class T, class Hash, class KeyEqual, class Allocator>
|
||||
class unordered_node_map
|
||||
{
|
||||
using map_types = detail::foa::node_map_types<Key, T>;
|
||||
using map_types = detail::foa::node_map_types<Key, T,
|
||||
typename boost::allocator_void_pointer<Allocator>::type>;
|
||||
|
||||
using table_type = detail::foa::table<map_types, Hash, KeyEqual,
|
||||
typename boost::allocator_rebind<Allocator,
|
||||
@ -179,9 +180,7 @@ namespace boost {
|
||||
}
|
||||
|
||||
unordered_node_map(unordered_node_map&& other)
|
||||
noexcept(std::is_nothrow_move_constructible<hasher>::value&&
|
||||
std::is_nothrow_move_constructible<key_equal>::value&&
|
||||
std::is_nothrow_move_constructible<allocator_type>::value)
|
||||
noexcept(std::is_nothrow_move_constructible<table_type>::value)
|
||||
: table_(std::move(other.table_))
|
||||
{
|
||||
}
|
||||
@ -789,10 +788,9 @@ namespace boost {
|
||||
return erase_if(map.table_, pred);
|
||||
}
|
||||
|
||||
template <class Archive,
|
||||
class Key, class T, class Hash, class KeyEqual, class Allocator>
|
||||
void serialize(
|
||||
Archive & ar,
|
||||
template <class Archive, class Key, class T, class Hash, class KeyEqual,
|
||||
class Allocator>
|
||||
void serialize(Archive& ar,
|
||||
unordered_node_map<Key, T, Hash, KeyEqual, Allocator>& map,
|
||||
unsigned int version)
|
||||
{
|
||||
|
@ -66,7 +66,8 @@ namespace boost {
|
||||
template <class Key, class Hash, class KeyEqual, class Allocator>
|
||||
class unordered_node_set
|
||||
{
|
||||
using set_types = detail::foa::node_set_types<Key>;
|
||||
using set_types = detail::foa::node_set_types<Key,
|
||||
typename boost::allocator_void_pointer<Allocator>::type>;
|
||||
|
||||
using table_type = detail::foa::table<set_types, Hash, KeyEqual,
|
||||
typename boost::allocator_rebind<Allocator,
|
||||
@ -169,9 +170,7 @@ namespace boost {
|
||||
}
|
||||
|
||||
unordered_node_set(unordered_node_set&& other)
|
||||
noexcept(std::is_nothrow_move_constructible<hasher>::value&&
|
||||
std::is_nothrow_move_constructible<key_equal>::value&&
|
||||
std::is_nothrow_move_constructible<allocator_type>::value)
|
||||
noexcept(std::is_nothrow_move_constructible<table_type>::value)
|
||||
: table_(std::move(other.table_))
|
||||
{
|
||||
}
|
||||
@ -602,10 +601,9 @@ namespace boost {
|
||||
return erase_if(set.table_, pred);
|
||||
}
|
||||
|
||||
template <class Archive,
|
||||
class Key, class Hash, class KeyEqual, class Allocator>
|
||||
void serialize(
|
||||
Archive & ar,
|
||||
template <class Archive, class Key, class Hash, class KeyEqual,
|
||||
class Allocator>
|
||||
void serialize(Archive& ar,
|
||||
unordered_node_set<Key, Hash, KeyEqual, Allocator>& set,
|
||||
unsigned int version)
|
||||
{
|
||||
|
268
test/Jamfile.v2
268
test/Jamfile.v2
@ -5,6 +5,7 @@
|
||||
# file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
import testing ;
|
||||
import ../../config/checks/config : requires ;
|
||||
|
||||
# Adding -Wundef is blocked on (at least)
|
||||
# https://github.com/boostorg/type_traits/issues/165
|
||||
@ -15,6 +16,20 @@ local msvc-flags = /wd4494 ;
|
||||
|
||||
project
|
||||
: requirements
|
||||
[ requires cxx11_rvalue_references
|
||||
cxx11_auto_declarations
|
||||
cxx11_nullptr
|
||||
cxx11_defaulted_functions
|
||||
cxx11_final
|
||||
cxx11_hdr_type_traits
|
||||
cxx11_hdr_initializer_list
|
||||
cxx11_static_assert
|
||||
cxx11_smart_ptr
|
||||
cxx11_constexpr
|
||||
cxx11_noexcept
|
||||
cxx11_decltype
|
||||
cxx11_alignas
|
||||
]
|
||||
|
||||
<warnings>pedantic
|
||||
<toolset>intel:<warnings>on
|
||||
@ -33,63 +48,76 @@ project
|
||||
<toolset>msvc:<warnings-as-errors>on
|
||||
;
|
||||
|
||||
run unordered/prime_fmod_tests.cpp ;
|
||||
run unordered/fwd_set_test.cpp ;
|
||||
run unordered/fwd_map_test.cpp ;
|
||||
run unordered/allocator_traits.cpp ;
|
||||
run unordered/minimal_allocator.cpp ;
|
||||
run unordered/compile_set.cpp ;
|
||||
run unordered/compile_map.cpp ;
|
||||
run unordered/noexcept_tests.cpp ;
|
||||
run unordered/link_test_1.cpp unordered/link_test_2.cpp ;
|
||||
run unordered/incomplete_test.cpp ;
|
||||
run unordered/simple_tests.cpp ;
|
||||
run unordered/equivalent_keys_tests.cpp ;
|
||||
run unordered/constructor_tests.cpp ;
|
||||
run unordered/copy_tests.cpp ;
|
||||
run unordered/move_tests.cpp ;
|
||||
run unordered/post_move_tests.cpp ;
|
||||
run unordered/assign_tests.cpp ;
|
||||
run unordered/insert_tests.cpp ;
|
||||
run unordered/insert_stable_tests.cpp ;
|
||||
run unordered/insert_hint_tests.cpp ;
|
||||
run unordered/emplace_tests.cpp ;
|
||||
run unordered/unnecessary_copy_tests.cpp ;
|
||||
run unordered/erase_tests.cpp : : : <define>BOOST_UNORDERED_SUPPRESS_DEPRECATED ;
|
||||
run unordered/erase_equiv_tests.cpp ;
|
||||
run unordered/extract_tests.cpp ;
|
||||
run unordered/node_handle_tests.cpp ;
|
||||
run unordered/merge_tests.cpp ;
|
||||
compile-fail unordered/insert_node_type_fail.cpp : <define>UNORDERED_TEST_MAP : insert_node_type_fail_map ;
|
||||
compile-fail unordered/insert_node_type_fail.cpp : <define>UNORDERED_TEST_MULTIMAP : insert_node_type_fail_multimap ;
|
||||
compile-fail unordered/insert_node_type_fail.cpp : <define>UNORDERED_TEST_SET : insert_node_type_fail_set ;
|
||||
compile-fail unordered/insert_node_type_fail.cpp : <define>UNORDERED_TEST_MULTISET : insert_node_type_fail_multiset ;
|
||||
run unordered/find_tests.cpp ;
|
||||
run unordered/at_tests.cpp ;
|
||||
run unordered/bucket_tests.cpp ;
|
||||
run unordered/load_factor_tests.cpp ;
|
||||
run unordered/rehash_tests.cpp ;
|
||||
run unordered/equality_tests.cpp ;
|
||||
run unordered/swap_tests.cpp ;
|
||||
run unordered/deduction_tests.cpp ;
|
||||
run unordered/scoped_allocator.cpp : : : <toolset>msvc-14.0:<build>no ;
|
||||
run unordered/transparent_tests.cpp ;
|
||||
run unordered/reserve_tests.cpp ;
|
||||
run unordered/contains_tests.cpp ;
|
||||
run unordered/erase_if.cpp ;
|
||||
run unordered/scary_tests.cpp ;
|
||||
|
||||
run unordered/compile_set.cpp : : : <define>BOOST_UNORDERED_USE_MOVE : bmove_compile_set ;
|
||||
run unordered/compile_map.cpp : : : <define>BOOST_UNORDERED_USE_MOVE : bmove_compile_map ;
|
||||
run unordered/copy_tests.cpp : : : <define>BOOST_UNORDERED_USE_MOVE : bmove_copy ;
|
||||
run unordered/move_tests.cpp : : : <define>BOOST_UNORDERED_USE_MOVE : bmove_move ;
|
||||
run unordered/assign_tests.cpp : : : <define>BOOST_UNORDERED_USE_MOVE : bmove_assign ;
|
||||
|
||||
path-constant BOOST_UNORDERED_TEST_DIR : . ;
|
||||
|
||||
run unordered/serialization_tests.cpp
|
||||
run quick.cpp ;
|
||||
|
||||
local FCA_TESTS =
|
||||
allocator_traits
|
||||
assign_tests
|
||||
at_tests
|
||||
bucket_tests
|
||||
compile_map
|
||||
compile_set
|
||||
constructor_tests
|
||||
contains_tests
|
||||
copy_tests
|
||||
deduction_tests
|
||||
emplace_tests
|
||||
equality_tests
|
||||
equivalent_keys_tests
|
||||
erase_equiv_tests
|
||||
erase_if
|
||||
erase_tests
|
||||
extract_tests
|
||||
find_tests
|
||||
fwd_map_test
|
||||
fwd_set_test
|
||||
incomplete_test
|
||||
insert_hint_tests
|
||||
insert_stable_tests
|
||||
insert_tests
|
||||
load_factor_tests
|
||||
merge_tests
|
||||
minimal_allocator
|
||||
move_tests
|
||||
narrow_cast_tests
|
||||
node_handle_tests
|
||||
noexcept_tests
|
||||
post_move_tests
|
||||
prime_fmod_tests
|
||||
rehash_tests
|
||||
reserve_tests
|
||||
scary_tests
|
||||
scoped_allocator
|
||||
simple_tests
|
||||
swap_tests
|
||||
transparent_tests
|
||||
unnecessary_copy_tests
|
||||
;
|
||||
|
||||
for local test in $(FCA_TESTS)
|
||||
{
|
||||
if $(test) = "erase_tests" {
|
||||
run unordered/$(test).cpp : : : <define>BOOST_UNORDERED_SUPPRESS_DEPRECATED ;
|
||||
} else if $(test) = "scoped_allocator" {
|
||||
run unordered/$(test).cpp : : : <toolset>msvc-14.0:<build>no ;
|
||||
} else {
|
||||
run unordered/$(test).cpp ;
|
||||
}
|
||||
}
|
||||
|
||||
run unordered/link_test_1.cpp unordered/link_test_2.cpp : : : : link_test ;
|
||||
|
||||
run unordered/compile_set.cpp : : : <define>BOOST_UNORDERED_USE_MOVE : bmove_compile_set ;
|
||||
run unordered/compile_map.cpp : : : <define>BOOST_UNORDERED_USE_MOVE : bmove_compile_map ;
|
||||
run unordered/copy_tests.cpp : : : <define>BOOST_UNORDERED_USE_MOVE : bmove_copy ;
|
||||
run unordered/move_tests.cpp : : : <define>BOOST_UNORDERED_USE_MOVE : bmove_move ;
|
||||
run unordered/assign_tests.cpp : : : <define>BOOST_UNORDERED_USE_MOVE : bmove_assign ;
|
||||
|
||||
run unordered/serialization_tests.cpp
|
||||
: $(BOOST_UNORDERED_TEST_DIR)
|
||||
:
|
||||
:
|
||||
: <define>BOOST_UNORDERED_ENABLE_SERIALIZATION_COMPATIBILITY_V0
|
||||
<warnings>off # Boost.Serialization headers are not warning-free
|
||||
<undefined-sanitizer>norecover:<build>no # boost::archive::xml_oarchive does not pass UBSAN
|
||||
@ -101,23 +129,49 @@ run unordered/serialization_tests.cpp
|
||||
<toolset>clang:<optimization>space
|
||||
<library>/boost//serialization/<warnings>off ;
|
||||
|
||||
run exception/constructor_exception_tests.cpp ;
|
||||
run exception/copy_exception_tests.cpp ;
|
||||
run exception/assign_exception_tests.cpp ;
|
||||
run exception/move_assign_exception_tests.cpp ;
|
||||
run exception/insert_exception_tests.cpp ;
|
||||
run exception/erase_exception_tests.cpp ;
|
||||
run exception/rehash_exception_tests.cpp ;
|
||||
run exception/swap_exception_tests.cpp : : : <define>BOOST_UNORDERED_SWAP_METHOD=2 ;
|
||||
run exception/merge_exception_tests.cpp ;
|
||||
run exception/less_tests.cpp ;
|
||||
compile-fail unordered/insert_node_type_fail.cpp : <define>UNORDERED_TEST_MAP : insert_node_type_fail_map ;
|
||||
compile-fail unordered/insert_node_type_fail.cpp : <define>UNORDERED_TEST_MULTIMAP : insert_node_type_fail_multimap ;
|
||||
compile-fail unordered/insert_node_type_fail.cpp : <define>UNORDERED_TEST_SET : insert_node_type_fail_set ;
|
||||
compile-fail unordered/insert_node_type_fail.cpp : <define>UNORDERED_TEST_MULTISET : insert_node_type_fail_multiset ;
|
||||
|
||||
run unordered/narrow_cast_tests.cpp ;
|
||||
run quick.cpp ;
|
||||
local FCA_EXCEPTION_TESTS =
|
||||
constructor_exception_tests
|
||||
copy_exception_tests
|
||||
assign_exception_tests
|
||||
move_assign_exception_tests
|
||||
insert_exception_tests
|
||||
erase_exception_tests
|
||||
rehash_exception_tests
|
||||
merge_exception_tests
|
||||
less_tests
|
||||
swap_exception_tests
|
||||
;
|
||||
|
||||
import ../../config/checks/config : requires ;
|
||||
for local test in $(FCA_EXCEPTION_TESTS)
|
||||
{
|
||||
if $(test) = "swap_exception_tests" {
|
||||
run exception/$(test).cpp : : : <define>BOOST_UNORDERED_SWAP_METHOD=2 ;
|
||||
} else {
|
||||
run exception/$(test).cpp ;
|
||||
}
|
||||
}
|
||||
|
||||
CPP11 = [ requires cxx11_constexpr cxx11_noexcept cxx11_decltype cxx11_alignas ] ;
|
||||
alias fca_exception_tests : $(FCA_EXCEPTION_TESTS) ;
|
||||
alias fca_tests :
|
||||
$(FCA_TESTS)
|
||||
$(FCA_EXCEPTION_TESTS)
|
||||
link_test
|
||||
bmove_compile_set
|
||||
bmove_compile_map
|
||||
bmove_copy
|
||||
bmove_move
|
||||
bmove_assign
|
||||
insert_node_type_fail_map
|
||||
insert_node_type_fail_multimap
|
||||
insert_node_type_fail_set
|
||||
insert_node_type_fail_multiset
|
||||
serialization_tests
|
||||
;
|
||||
|
||||
local FOA_TESTS =
|
||||
fwd_set_test
|
||||
@ -154,21 +208,21 @@ local FOA_TESTS =
|
||||
extract_tests
|
||||
node_handle_tests
|
||||
uses_allocator
|
||||
hash_is_avalanching_test
|
||||
;
|
||||
|
||||
for local test in $(FOA_TESTS)
|
||||
{
|
||||
run unordered/$(test).cpp : : : $(CPP11) <define>BOOST_UNORDERED_FOA_TESTS : foa_$(test) ;
|
||||
run unordered/$(test).cpp : : : <define>BOOST_UNORDERED_FOA_TESTS : foa_$(test) ;
|
||||
}
|
||||
|
||||
run unordered/link_test_1.cpp unordered/link_test_2.cpp : : : $(CPP11) <define>BOOST_UNORDERED_FOA_TESTS : foa_link_test ;
|
||||
run unordered/scoped_allocator.cpp : : : $(CPP11) <toolset>msvc-14.0:<build>no <define>BOOST_UNORDERED_FOA_TESTS : foa_scoped_allocator ;
|
||||
run unordered/hash_is_avalanching_test.cpp ;
|
||||
run unordered/link_test_1.cpp unordered/link_test_2.cpp : : : <define>BOOST_UNORDERED_FOA_TESTS : foa_link_test ;
|
||||
run unordered/scoped_allocator.cpp : : : <toolset>msvc-14.0:<build>no <define>BOOST_UNORDERED_FOA_TESTS : foa_scoped_allocator ;
|
||||
|
||||
run unordered/serialization_tests.cpp
|
||||
:
|
||||
:
|
||||
: $(CPP11) <define>BOOST_UNORDERED_FOA_TESTS
|
||||
:
|
||||
: <define>BOOST_UNORDERED_FOA_TESTS
|
||||
<warnings>off # Boost.Serialization headers are not warning-free
|
||||
<undefined-sanitizer>norecover:<build>no # boost::archive::xml_oarchive does not pass UBSAN
|
||||
<toolset>msvc:<cxxflags>/bigobj
|
||||
@ -179,31 +233,53 @@ run unordered/serialization_tests.cpp
|
||||
<library>/boost//serialization/<warnings>off
|
||||
: foa_serialization_tests ;
|
||||
|
||||
run exception/constructor_exception_tests.cpp : : : $(CPP11) <define>BOOST_UNORDERED_FOA_TESTS : foa_constructor_exception_tests ;
|
||||
run exception/copy_exception_tests.cpp : : : $(CPP11) <define>BOOST_UNORDERED_FOA_TESTS : foa_copy_exception_tests ;
|
||||
run exception/assign_exception_tests.cpp : : : $(CPP11) <define>BOOST_UNORDERED_FOA_TESTS : foa_assign_exception_tests ;
|
||||
run exception/move_assign_exception_tests.cpp : : : $(CPP11) <define>BOOST_UNORDERED_FOA_TESTS : foa_move_assign_exception_tests ;
|
||||
run exception/insert_exception_tests.cpp : : : $(CPP11) <define>BOOST_UNORDERED_FOA_TESTS : foa_insert_exception_tests ;
|
||||
run exception/erase_exception_tests.cpp : : : $(CPP11) <define>BOOST_UNORDERED_FOA_TESTS : foa_erase_exception_tests ;
|
||||
run exception/rehash_exception_tests.cpp : : : $(CPP11) <define>BOOST_UNORDERED_FOA_TESTS : foa_rehash_exception_tests ;
|
||||
run exception/swap_exception_tests.cpp : : : $(CPP11) <define>BOOST_UNORDERED_FOA_TESTS : foa_swap_exception_tests ;
|
||||
run exception/merge_exception_tests.cpp : : : $(CPP11) <define>BOOST_UNORDERED_FOA_TESTS : foa_merge_exception_tests ;
|
||||
local FOA_EXCEPTION_TESTS =
|
||||
constructor_exception_tests
|
||||
copy_exception_tests
|
||||
assign_exception_tests
|
||||
move_assign_exception_tests
|
||||
insert_exception_tests
|
||||
erase_exception_tests
|
||||
rehash_exception_tests
|
||||
swap_exception_tests
|
||||
merge_exception_tests
|
||||
;
|
||||
|
||||
for local test in $(FOA_EXCEPTION_TESTS)
|
||||
{
|
||||
run exception/$(test).cpp : : : <define>BOOST_UNORDERED_FOA_TESTS : foa_$(test) ;
|
||||
}
|
||||
|
||||
local MMAP_CONTAINERS =
|
||||
unordered_flat_map
|
||||
unordered_flat_set
|
||||
unordered_node_map
|
||||
unordered_node_set
|
||||
unordered_map
|
||||
unordered_set
|
||||
unordered_multimap
|
||||
unordered_multiset
|
||||
concurrent_flat_map
|
||||
;
|
||||
|
||||
for local container in $(MMAP_CONTAINERS)
|
||||
{
|
||||
run unordered/mmap_tests.cpp /boost/filesystem//boost_filesystem : :
|
||||
: <define>BOOST_UNORDERED_FOA_MMAP_MAP_TYPE="boost::$(container)"
|
||||
<warnings>off
|
||||
<link>static
|
||||
<target-os>cygwin:<build>no
|
||||
: foa_mmap_$(container)_tests ;
|
||||
}
|
||||
|
||||
alias foa_mmap_tests : foa_mmap_$(MMAP_CONTAINERS)_tests ;
|
||||
alias foa_tests :
|
||||
foa_$(FOA_TESTS)
|
||||
foa_$(FOA_EXCEPTION_TESTS)
|
||||
foa_link_test
|
||||
foa_scoped_allocator
|
||||
hash_is_avalanching_test
|
||||
foa_serialization_tests
|
||||
foa_constructor_exception_tests
|
||||
foa_copy_exception_tests
|
||||
foa_assign_exception_tests
|
||||
foa_move_assign_exception_tests
|
||||
foa_insert_exception_tests
|
||||
foa_erase_exception_tests
|
||||
foa_rehash_exception_tests
|
||||
foa_swap_exception_tests
|
||||
foa_merge_exception_tests
|
||||
foa_mmap_tests
|
||||
;
|
||||
|
||||
local CFOA_TESTS =
|
||||
@ -239,14 +315,14 @@ local CFOA_TESTS =
|
||||
for local test in $(CFOA_TESTS)
|
||||
{
|
||||
run cfoa/$(test).cpp
|
||||
: requirements $(CPP11) <threading>multi
|
||||
: requirements <threading>multi
|
||||
: target-name cfoa_$(test)
|
||||
;
|
||||
}
|
||||
|
||||
run cfoa/serialization_tests.cpp
|
||||
:
|
||||
:
|
||||
:
|
||||
: $(CPP11) <threading>multi
|
||||
<warnings>off # Boost.Serialization headers are not warning-free
|
||||
<undefined-sanitizer>norecover:<build>no # boost::archive::xml_oarchive does not pass UBSAN
|
||||
@ -258,6 +334,6 @@ run cfoa/serialization_tests.cpp
|
||||
<library>/boost//serialization/<warnings>off
|
||||
: cfoa_serialization_tests ;
|
||||
|
||||
alias cfoa_tests :
|
||||
alias cfoa_tests :
|
||||
cfoa_$(CFOA_TESTS)
|
||||
cfoa_serialization_tests ;
|
||||
|
@ -31,7 +31,7 @@ using test::sequential;
|
||||
|
||||
using hasher = stateful_hash;
|
||||
using key_equal = stateful_key_equal;
|
||||
using allocator_type = stateful_allocator<std::pair<raii const, raii> >;
|
||||
using allocator_type = stateful_allocator2<std::pair<raii const, raii> >;
|
||||
|
||||
using flat_map_type = boost::unordered::unordered_flat_map<raii, raii, hasher,
|
||||
key_equal, allocator_type>;
|
||||
@ -847,8 +847,12 @@ namespace {
|
||||
check_raii_counts();
|
||||
}
|
||||
|
||||
template <class G> void flat_map_move_assign(G gen, test::random_generator rg)
|
||||
template <class FlatMapType, class MapType, class G>
|
||||
void flat_map_move_assign(
|
||||
FlatMapType*, MapType*, G gen, test::random_generator rg)
|
||||
{
|
||||
using alloc_type = typename MapType::allocator_type;
|
||||
|
||||
auto values = make_random_values(1024 * 16, [&] { return gen(rg); });
|
||||
auto reference_map =
|
||||
boost::unordered_flat_map<raii, raii>(values.begin(), values.end());
|
||||
@ -864,10 +868,10 @@ namespace {
|
||||
{
|
||||
raii::reset_counts();
|
||||
|
||||
flat_map_type flat_map(values.begin(), values.end(), values.size(),
|
||||
hasher(1), key_equal(2), allocator_type(3));
|
||||
FlatMapType flat_map(values.begin(), values.end(), values.size(),
|
||||
hasher(1), key_equal(2), alloc_type(3));
|
||||
|
||||
map_type map(0, hasher(2), key_equal(1), allocator_type(3));
|
||||
MapType map(0, hasher(2), key_equal(1), alloc_type(3));
|
||||
|
||||
BOOST_TEST(flat_map.get_allocator() == map.get_allocator());
|
||||
|
||||
@ -893,10 +897,10 @@ namespace {
|
||||
{
|
||||
raii::reset_counts();
|
||||
|
||||
map_type map(values.begin(), values.end(), values.size(), hasher(1),
|
||||
key_equal(2), allocator_type(3));
|
||||
MapType map(values.begin(), values.end(), values.size(), hasher(1),
|
||||
key_equal(2), alloc_type(3));
|
||||
|
||||
flat_map_type flat_map(0, hasher(2), key_equal(1), allocator_type(3));
|
||||
FlatMapType flat_map(0, hasher(2), key_equal(1), alloc_type(3));
|
||||
|
||||
BOOST_TEST(flat_map.get_allocator() == map.get_allocator());
|
||||
|
||||
@ -920,10 +924,10 @@ namespace {
|
||||
{
|
||||
raii::reset_counts();
|
||||
|
||||
flat_map_type flat_map(values.begin(), values.end(), values.size(),
|
||||
hasher(1), key_equal(2), allocator_type(3));
|
||||
FlatMapType flat_map(values.begin(), values.end(), values.size(),
|
||||
hasher(1), key_equal(2), alloc_type(3));
|
||||
|
||||
map_type map(0, hasher(2), key_equal(1), allocator_type(4));
|
||||
MapType map(0, hasher(2), key_equal(1), alloc_type(4));
|
||||
|
||||
BOOST_TEST(flat_map.get_allocator() != map.get_allocator());
|
||||
|
||||
@ -950,10 +954,10 @@ namespace {
|
||||
{
|
||||
raii::reset_counts();
|
||||
|
||||
map_type map(values.begin(), values.end(), values.size(), hasher(1),
|
||||
key_equal(2), allocator_type(3));
|
||||
MapType map(values.begin(), values.end(), values.size(), hasher(1),
|
||||
key_equal(2), alloc_type(3));
|
||||
|
||||
flat_map_type flat_map(0, hasher(2), key_equal(1), allocator_type(4));
|
||||
FlatMapType flat_map(0, hasher(2), key_equal(1), alloc_type(4));
|
||||
|
||||
BOOST_TEST(flat_map.get_allocator() != map.get_allocator());
|
||||
|
||||
@ -994,8 +998,27 @@ UNORDERED_TEST(
|
||||
((init_type_generator))
|
||||
((default_generator)(sequential)(limited_range)))
|
||||
|
||||
boost::unordered::unordered_flat_map<raii, raii, hasher,
|
||||
key_equal, stateful_allocator<std::pair<raii const, raii> > >* flat_map_plain;
|
||||
boost::unordered::unordered_flat_map<raii, raii, hasher,
|
||||
key_equal, stateful_allocator2<std::pair<raii const, raii> > >* flat_map_fancy;
|
||||
|
||||
boost::unordered::concurrent_flat_map<raii, raii, hasher,
|
||||
key_equal, stateful_allocator<std::pair<raii const, raii> > >* map_plain;
|
||||
boost::unordered::concurrent_flat_map<raii, raii, hasher,
|
||||
key_equal, stateful_allocator2<std::pair<raii const, raii> > >* map_fancy;
|
||||
|
||||
UNORDERED_TEST(
|
||||
flat_map_move_assign,
|
||||
((flat_map_plain))
|
||||
((map_plain))
|
||||
((init_type_generator))
|
||||
((default_generator)(sequential)(limited_range)))
|
||||
|
||||
UNORDERED_TEST(
|
||||
flat_map_move_assign,
|
||||
((flat_map_fancy))
|
||||
((map_fancy))
|
||||
((init_type_generator))
|
||||
((default_generator)(sequential)(limited_range)))
|
||||
// clang-format on
|
||||
|
@ -123,6 +123,27 @@ struct stateful_key_equal
|
||||
}
|
||||
};
|
||||
|
||||
template <class T> struct cfoa_ptr
|
||||
{
|
||||
private:
|
||||
template <class> friend struct stateful_allocator2;
|
||||
|
||||
T* p_ = nullptr;
|
||||
|
||||
cfoa_ptr(T* p) : p_(p) {}
|
||||
|
||||
public:
|
||||
using element_type = T;
|
||||
|
||||
cfoa_ptr() = default;
|
||||
cfoa_ptr(std::nullptr_t) : p_(nullptr){};
|
||||
template <class U> using rebind = cfoa_ptr<U>;
|
||||
|
||||
T* operator->() const noexcept { return p_; }
|
||||
|
||||
static cfoa_ptr<T> pointer_to(element_type& r) { return {std::addressof(r)}; }
|
||||
};
|
||||
|
||||
template <class T> struct stateful_allocator
|
||||
{
|
||||
int x_ = -1;
|
||||
@ -151,6 +172,36 @@ template <class T> struct stateful_allocator
|
||||
bool operator!=(stateful_allocator const& rhs) const { return x_ != rhs.x_; }
|
||||
};
|
||||
|
||||
template <class T> struct stateful_allocator2
|
||||
{
|
||||
|
||||
int x_ = -1;
|
||||
|
||||
using value_type = T;
|
||||
using pointer = cfoa_ptr<T>;
|
||||
|
||||
stateful_allocator2() = default;
|
||||
stateful_allocator2(stateful_allocator2 const&) = default;
|
||||
stateful_allocator2(stateful_allocator2&&) = default;
|
||||
|
||||
stateful_allocator2(int const x) : x_{x} {}
|
||||
|
||||
template <class U>
|
||||
stateful_allocator2(stateful_allocator2<U> const& rhs) : x_{rhs.x_}
|
||||
{
|
||||
}
|
||||
|
||||
pointer allocate(std::size_t n)
|
||||
{
|
||||
return {static_cast<T*>(::operator new(n * sizeof(T)))};
|
||||
}
|
||||
|
||||
void deallocate(pointer p, std::size_t) { ::operator delete(p.p_); }
|
||||
|
||||
bool operator==(stateful_allocator2 const& rhs) const { return x_ == rhs.x_; }
|
||||
bool operator!=(stateful_allocator2 const& rhs) const { return x_ != rhs.x_; }
|
||||
};
|
||||
|
||||
struct raii
|
||||
{
|
||||
static std::atomic<std::uint32_t> default_constructor;
|
||||
@ -458,6 +509,7 @@ template <class T> class ptr
|
||||
|
||||
public:
|
||||
ptr() : ptr_(0) {}
|
||||
ptr(std::nullptr_t) : ptr_(nullptr) {}
|
||||
explicit ptr(void_ptr const& x) : ptr_((T*)x.ptr_) {}
|
||||
|
||||
T& operator*() const { return *ptr_; }
|
||||
|
@ -307,6 +307,7 @@ namespace test {
|
||||
|
||||
public:
|
||||
ptr() : ptr_(0) {}
|
||||
ptr(std::nullptr_t) : ptr_(0) {}
|
||||
explicit ptr(void_ptr const& x) : ptr_((T*)x.ptr_) {}
|
||||
|
||||
T& operator*() const { return *ptr_; }
|
||||
@ -325,6 +326,18 @@ namespace test {
|
||||
ptr operator+(std::ptrdiff_t s) const { return ptr<T>(ptr_ + s); }
|
||||
friend ptr operator+(std::ptrdiff_t s, ptr p) { return ptr<T>(s + p.ptr_); }
|
||||
|
||||
ptr& operator+=(std::ptrdiff_t s)
|
||||
{
|
||||
ptr_ += s;
|
||||
return *this;
|
||||
}
|
||||
|
||||
ptr& operator-=(std::ptrdiff_t s)
|
||||
{
|
||||
ptr_ -= s;
|
||||
return *this;
|
||||
}
|
||||
|
||||
std::ptrdiff_t operator-(ptr p) const { return ptr_ - p.ptr_; }
|
||||
ptr operator-(std::ptrdiff_t s) const { return ptr(ptr_ - s); }
|
||||
T& operator[](std::ptrdiff_t s) const { return ptr_[s]; }
|
||||
@ -340,6 +353,8 @@ namespace test {
|
||||
|
||||
bool operator==(ptr const& x) const { return ptr_ == x.ptr_; }
|
||||
bool operator!=(ptr const& x) const { return ptr_ != x.ptr_; }
|
||||
bool operator==(std::nullptr_t) const { return ptr_ == nullptr; }
|
||||
bool operator!=(std::nullptr_t) const { return ptr_ != nullptr; }
|
||||
bool operator<(ptr const& x) const { return ptr_ < x.ptr_; }
|
||||
bool operator>(ptr const& x) const { return ptr_ > x.ptr_; }
|
||||
bool operator<=(ptr const& x) const { return ptr_ <= x.ptr_; }
|
||||
@ -660,6 +675,9 @@ namespace boost {
|
||||
{
|
||||
typedef ::test::minimal::ptr<U> type;
|
||||
};
|
||||
|
||||
template<class U>
|
||||
using rebind=typename rebind_to<U>::type;
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -16,6 +16,9 @@
|
||||
#include <boost/limits.hpp>
|
||||
#include <cstddef>
|
||||
|
||||
template <class T> struct allocator1;
|
||||
template <class T> struct allocator2;
|
||||
|
||||
namespace test {
|
||||
// Note that the default hash function will work for any equal_to (but not
|
||||
// very well).
|
||||
@ -506,14 +509,18 @@ namespace test {
|
||||
|
||||
template <class T> class ptr
|
||||
{
|
||||
friend struct ::allocator1<T>;
|
||||
friend struct ::allocator2<T>;
|
||||
friend class allocator2<T>;
|
||||
friend class const_ptr<T>;
|
||||
friend struct void_ptr;
|
||||
|
||||
public:
|
||||
T* ptr_;
|
||||
ptr(T* x) : ptr_(x) {}
|
||||
|
||||
public:
|
||||
ptr() : ptr_(0) {}
|
||||
ptr(std::nullptr_t) : ptr_(nullptr) {}
|
||||
explicit ptr(void_ptr const& x) : ptr_((T*)x.ptr_) {}
|
||||
|
||||
T& operator*() const { return *ptr_; }
|
||||
@ -537,6 +544,7 @@ namespace test {
|
||||
ptr operator-(std::ptrdiff_t s) const { return ptr(ptr_ - s); }
|
||||
|
||||
ptr& operator+=(std::ptrdiff_t s) { ptr_ += s; return *this; }
|
||||
ptr& operator-=(std::ptrdiff_t s) { ptr_ -= s; return *this; }
|
||||
|
||||
T& operator[](std::ptrdiff_t s) const { return ptr_[s]; }
|
||||
bool operator!() const { return !ptr_; }
|
||||
@ -727,6 +735,9 @@ namespace boost {
|
||||
{
|
||||
typedef ::test::ptr<U> type;
|
||||
};
|
||||
|
||||
template<class U>
|
||||
using rebind=typename rebind_to<U>::type;
|
||||
};
|
||||
} // namespace boost
|
||||
|
||||
|
@ -41,7 +41,6 @@ namespace assign_tests {
|
||||
BOOST_TEST(x.empty());
|
||||
BOOST_TEST(test::equivalent(x.hash_function(), hf));
|
||||
BOOST_TEST(test::equivalent(x.key_eq(), eq));
|
||||
BOOST_TEST(test::detail::tracker.count_allocations == 0);
|
||||
}
|
||||
|
||||
BOOST_LIGHTWEIGHT_TEST_OSTREAM << "assign_tests1.2\n";
|
||||
@ -91,7 +90,6 @@ namespace assign_tests {
|
||||
BOOST_TEST(test::equivalent(x1.key_eq(), eq1));
|
||||
BOOST_TEST(test::equivalent(x2.hash_function(), hf1));
|
||||
BOOST_TEST(test::equivalent(x2.key_eq(), eq1));
|
||||
BOOST_TEST(test::detail::tracker.count_allocations == 0);
|
||||
test::check_container(x1, x2);
|
||||
}
|
||||
|
||||
|
@ -6,13 +6,13 @@
|
||||
|
||||
#include "../helpers/unordered.hpp"
|
||||
|
||||
#include "../helpers/test.hpp"
|
||||
#include "../objects/test.hpp"
|
||||
#include "../helpers/random_values.hpp"
|
||||
#include "../helpers/tracker.hpp"
|
||||
#include "../helpers/equivalent.hpp"
|
||||
#include "../helpers/input_iterator.hpp"
|
||||
#include "../helpers/invariants.hpp"
|
||||
#include "../helpers/random_values.hpp"
|
||||
#include "../helpers/test.hpp"
|
||||
#include "../helpers/tracker.hpp"
|
||||
#include "../objects/test.hpp"
|
||||
|
||||
#include <vector>
|
||||
|
||||
@ -33,7 +33,6 @@ namespace constructor_tests {
|
||||
|
||||
T x(0, hf, eq);
|
||||
BOOST_TEST(x.empty());
|
||||
BOOST_TEST_EQ(x.bucket_count(), 0u);
|
||||
BOOST_TEST(test::equivalent(x.hash_function(), hf));
|
||||
BOOST_TEST(test::equivalent(x.key_eq(), eq));
|
||||
BOOST_TEST(test::equivalent(x.get_allocator(), al));
|
||||
@ -72,7 +71,6 @@ namespace constructor_tests {
|
||||
|
||||
T x;
|
||||
BOOST_TEST(x.empty());
|
||||
BOOST_TEST_EQ(x.bucket_count(), 0u);
|
||||
BOOST_TEST(test::equivalent(x.hash_function(), hf));
|
||||
BOOST_TEST(test::equivalent(x.key_eq(), eq));
|
||||
BOOST_TEST(test::equivalent(x.get_allocator(), al));
|
||||
@ -140,7 +138,6 @@ namespace constructor_tests {
|
||||
|
||||
T x(0, hf, eq, al);
|
||||
BOOST_TEST(x.empty());
|
||||
BOOST_TEST_EQ(x.bucket_count(), 0u);
|
||||
BOOST_TEST(test::equivalent(x.hash_function(), hf));
|
||||
BOOST_TEST(test::equivalent(x.key_eq(), eq));
|
||||
BOOST_TEST(test::equivalent(x.get_allocator(), al));
|
||||
@ -167,7 +164,6 @@ namespace constructor_tests {
|
||||
|
||||
T x(al);
|
||||
BOOST_TEST(x.empty());
|
||||
BOOST_TEST_EQ(x.bucket_count(), 0u);
|
||||
BOOST_TEST(test::equivalent(x.hash_function(), hf));
|
||||
BOOST_TEST(test::equivalent(x.key_eq(), eq));
|
||||
BOOST_TEST(test::equivalent(x.get_allocator(), al));
|
||||
@ -349,7 +345,6 @@ namespace constructor_tests {
|
||||
{
|
||||
T x(list);
|
||||
BOOST_TEST(x.empty());
|
||||
BOOST_TEST_EQ(x.bucket_count(), 0u);
|
||||
BOOST_TEST(test::equivalent(x.hash_function(), hf));
|
||||
BOOST_TEST(test::equivalent(x.key_eq(), eq));
|
||||
BOOST_TEST(test::equivalent(x.get_allocator(), al));
|
||||
@ -536,6 +531,16 @@ namespace constructor_tests {
|
||||
template <class T>
|
||||
void no_alloc_default_construct_test(T*, test::random_generator)
|
||||
{
|
||||
|
||||
#ifdef BOOST_UNORDERED_FOA_TESTS
|
||||
using allocator_type = typename T::allocator_type;
|
||||
using value_type =
|
||||
typename boost::allocator_value_type<allocator_type>::type;
|
||||
using pointer = typename boost::allocator_pointer<allocator_type>::type;
|
||||
static_assert(std::is_same<pointer, value_type*>::value,
|
||||
"only raw pointers for this test");
|
||||
#endif
|
||||
|
||||
UNORDERED_SUB_TEST("Construct 1")
|
||||
{
|
||||
T x;
|
||||
@ -653,6 +658,15 @@ namespace constructor_tests {
|
||||
boost::unordered_flat_map<test::object, test::object, test::hash,
|
||||
test::equal_to, std::allocator<test::object> >* test_map_std_alloc;
|
||||
|
||||
boost::unordered_flat_set<test::object, test::hash, test::equal_to,
|
||||
test::allocator1<test::object> >* test_set_raw_ptr;
|
||||
boost::unordered_node_set<test::object, test::hash, test::equal_to,
|
||||
test::allocator1<test::object> >* test_node_set_raw_ptr;
|
||||
boost::unordered_flat_map<test::object, test::object, test::hash,
|
||||
test::equal_to, test::allocator1<test::object> >* test_map_raw_ptr;
|
||||
boost::unordered_node_map<test::object, test::object, test::hash,
|
||||
test::equal_to, test::allocator1<test::object> >* test_node_map_raw_ptr;
|
||||
|
||||
boost::unordered_flat_set<test::object, test::hash, test::equal_to,
|
||||
test::allocator1<test::object> >* test_set;
|
||||
boost::unordered_node_set<test::object, test::hash, test::equal_to,
|
||||
@ -675,7 +689,7 @@ namespace constructor_tests {
|
||||
(default_generator)(generate_collisions)(limited_range)))
|
||||
|
||||
UNORDERED_TEST(no_alloc_default_construct_test,
|
||||
((test_set)(test_node_set)(test_map)(test_node_map))(
|
||||
((test_set_raw_ptr)(test_node_set_raw_ptr)(test_map_raw_ptr)(test_node_map_raw_ptr))(
|
||||
(default_generator)(generate_collisions)(limited_range)))
|
||||
#else
|
||||
boost::unordered_map<test::object, test::object, test::hash, test::equal_to,
|
||||
@ -740,6 +754,6 @@ namespace constructor_tests {
|
||||
}
|
||||
|
||||
#endif
|
||||
}
|
||||
} // namespace constructor_tests
|
||||
|
||||
RUN_TESTS_QUIET()
|
||||
|
@ -22,12 +22,11 @@ template <class X> void max_load_tests(X*, test::random_generator generator)
|
||||
test::reset_sequence();
|
||||
|
||||
X x;
|
||||
size_type max_load = x.max_load();
|
||||
|
||||
BOOST_TEST_EQ(max_load, 0u);
|
||||
|
||||
x.reserve(1000);
|
||||
max_load = x.max_load();
|
||||
size_type max_load = x.max_load();
|
||||
|
||||
BOOST_TEST_GT(max_load, 0u);
|
||||
|
||||
size_type bucket_count = x.bucket_count();
|
||||
BOOST_TEST_GE(bucket_count, 1000u);
|
||||
|
312
test/unordered/mmap_tests.cpp
Normal file
312
test/unordered/mmap_tests.cpp
Normal file
@ -0,0 +1,312 @@
|
||||
// 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 <boost/config.hpp>
|
||||
|
||||
#if defined(BOOST_CLANG_VERSION) && BOOST_CLANG_VERSION < 30900
|
||||
#include <boost/config/pragma_message.hpp>
|
||||
BOOST_PRAGMA_MESSAGE(
|
||||
"This version of clang is incompatible with Boost.Process");
|
||||
int main() {}
|
||||
#else
|
||||
#include "../helpers/test.hpp"
|
||||
|
||||
#include <boost/unordered/unordered_flat_map.hpp>
|
||||
#include <boost/unordered/unordered_map.hpp>
|
||||
#include <boost/unordered/unordered_node_map.hpp>
|
||||
|
||||
#include <boost/unordered/unordered_flat_set.hpp>
|
||||
#include <boost/unordered/unordered_node_set.hpp>
|
||||
#include <boost/unordered/unordered_set.hpp>
|
||||
|
||||
#include <boost/unordered/concurrent_flat_map.hpp>
|
||||
|
||||
#include <boost/interprocess/allocators/allocator.hpp>
|
||||
#include <boost/interprocess/containers/string.hpp>
|
||||
#include <boost/interprocess/managed_shared_memory.hpp>
|
||||
|
||||
#include <boost/process/child.hpp>
|
||||
#include <boost/process/filesystem.hpp>
|
||||
|
||||
#include <boost/uuid/random_generator.hpp>
|
||||
#include <boost/uuid/uuid_io.hpp>
|
||||
|
||||
#include <algorithm>
|
||||
#include <iostream>
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
|
||||
#ifndef BOOST_UNORDERED_FOA_MMAP_MAP_TYPE
|
||||
#error "this requires a class template be passed as a macro"
|
||||
#endif
|
||||
|
||||
using char_allocator = boost::interprocess::allocator<char,
|
||||
boost::interprocess::managed_shared_memory::segment_manager>;
|
||||
|
||||
using string_type = boost::interprocess::basic_string<char,
|
||||
std::char_traits<char>, char_allocator>;
|
||||
|
||||
using pair_type = std::pair<const string_type, string_type>;
|
||||
|
||||
using string_pair_type = std::pair<string_type, string_type>;
|
||||
|
||||
using string_pair_allocator = boost::interprocess::allocator<string_pair_type,
|
||||
boost::interprocess::managed_shared_memory::segment_manager>;
|
||||
|
||||
using pair_allocator = boost::interprocess::allocator<pair_type,
|
||||
boost::interprocess::managed_shared_memory::segment_manager>;
|
||||
|
||||
template <template <class, class, class, class, class> class Map,
|
||||
class MapType = Map<string_type, string_type, boost::hash<string_type>,
|
||||
std::equal_to<string_type>, pair_allocator> >
|
||||
typename std::enable_if<
|
||||
!std::is_same<typename MapType::value_type, string_pair_type>::value,
|
||||
MapType>::type
|
||||
get_container_type()
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
template <template <class, class, class, class> class Set,
|
||||
class SetType = Set<string_pair_type, boost::hash<string_pair_type>,
|
||||
std::equal_to<string_pair_type>, string_pair_allocator> >
|
||||
typename std::enable_if<
|
||||
std::is_same<typename SetType::value_type, string_pair_type>::value,
|
||||
SetType>::type
|
||||
get_container_type()
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
using concurrent_map = decltype(
|
||||
get_container_type<boost::concurrent_flat_map>());
|
||||
|
||||
static char const* shm_map_name = "shared_map";
|
||||
|
||||
template <class C>
|
||||
void parent(std::string const& shm_name_, char const* exe_name, C*)
|
||||
{
|
||||
struct shm_remove
|
||||
{
|
||||
char const* shm_name;
|
||||
|
||||
shm_remove(char const* shm_name_) : shm_name(shm_name_)
|
||||
{
|
||||
boost::interprocess::shared_memory_object::remove(shm_name);
|
||||
}
|
||||
~shm_remove()
|
||||
{
|
||||
boost::interprocess::shared_memory_object::remove(shm_name);
|
||||
}
|
||||
} remover{shm_name_.c_str()};
|
||||
|
||||
using container_type = C;
|
||||
|
||||
std::size_t const shm_size = 64 * 1024;
|
||||
|
||||
auto shm_name = remover.shm_name;
|
||||
boost::interprocess::managed_shared_memory segment(
|
||||
boost::interprocess::create_only, shm_name, shm_size);
|
||||
|
||||
auto segment_mngr = segment.get_segment_manager();
|
||||
char_allocator char_alloc(segment_mngr);
|
||||
pair_allocator pair_alloc(segment_mngr);
|
||||
|
||||
using iterator_type = typename C::iterator;
|
||||
|
||||
container_type* c =
|
||||
segment.construct<container_type>(shm_map_name)(pair_alloc);
|
||||
|
||||
iterator_type* it = segment.construct<iterator_type>("shared_iterator")();
|
||||
|
||||
auto const old_bc = c->bucket_count();
|
||||
|
||||
BOOST_TEST(c->empty());
|
||||
|
||||
boost::process::child child(exe_name, shm_name);
|
||||
child.wait();
|
||||
int ret = child.exit_code();
|
||||
|
||||
BOOST_TEST(*it == c->begin());
|
||||
BOOST_TEST((**it) == *(c->begin()));
|
||||
|
||||
c->erase(*it);
|
||||
|
||||
auto inputs =
|
||||
std::vector<std::pair<std::string, std::string> >{{"hello", "world"}};
|
||||
|
||||
for (const auto& kvp : inputs) {
|
||||
auto const& key = kvp.first;
|
||||
auto const& value = kvp.second;
|
||||
c->emplace(string_type(key.data(), char_alloc),
|
||||
string_type(value.data(), char_alloc));
|
||||
}
|
||||
|
||||
BOOST_TEST_EQ(ret, 0);
|
||||
BOOST_TEST_GT(c->size(), inputs.size());
|
||||
BOOST_TEST_GT(c->bucket_count(), old_bc);
|
||||
|
||||
segment.destroy<iterator_type>("shared_iterator");
|
||||
segment.destroy<container_type>(shm_map_name);
|
||||
}
|
||||
|
||||
template <class C> void child(std::string const& shm_name, C*)
|
||||
{
|
||||
using container_type = C;
|
||||
using iterator = typename container_type::iterator;
|
||||
|
||||
boost::interprocess::managed_shared_memory segment(
|
||||
boost::interprocess::open_only, shm_name.c_str());
|
||||
|
||||
container_type* c = segment.find<container_type>(shm_map_name).first;
|
||||
|
||||
iterator* it = segment.find<iterator>("shared_iterator").first;
|
||||
|
||||
BOOST_TEST(c->empty());
|
||||
|
||||
std::vector<std::pair<std::string, std::string> > inputs = {
|
||||
{"aaa", "AAA"}, {"bbb", "BBB"}, {"ccc", "CCCC"}};
|
||||
|
||||
if (BOOST_TEST_NE(c, nullptr)) {
|
||||
auto a = segment.get_segment_manager();
|
||||
|
||||
for (const auto& input : inputs) {
|
||||
c->emplace(string_type(input.first.c_str(), a),
|
||||
string_type(input.second.c_str(), a));
|
||||
}
|
||||
|
||||
c->rehash(c->bucket_count() + 1);
|
||||
|
||||
if (BOOST_TEST_NE(it, nullptr)) {
|
||||
*it = c->begin();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void parent(std::string const& shm_name_, char const* exe_name, concurrent_map*)
|
||||
{
|
||||
struct shm_remove
|
||||
{
|
||||
char const* shm_name;
|
||||
|
||||
shm_remove(char const* shm_name_) : shm_name(shm_name_)
|
||||
{
|
||||
boost::interprocess::shared_memory_object::remove(shm_name);
|
||||
}
|
||||
~shm_remove()
|
||||
{
|
||||
boost::interprocess::shared_memory_object::remove(shm_name);
|
||||
}
|
||||
} remover{shm_name_.c_str()};
|
||||
|
||||
using container_type = concurrent_map;
|
||||
|
||||
std::size_t const shm_size = 64 * 1024;
|
||||
|
||||
auto shm_name = remover.shm_name;
|
||||
boost::interprocess::managed_shared_memory segment(
|
||||
boost::interprocess::create_only, shm_name, shm_size);
|
||||
|
||||
auto segment_mngr = segment.get_segment_manager();
|
||||
char_allocator char_alloc(segment_mngr);
|
||||
pair_allocator pair_alloc(segment_mngr);
|
||||
|
||||
container_type* c =
|
||||
segment.construct<container_type>(shm_map_name)(pair_alloc);
|
||||
|
||||
auto const old_bc = c->bucket_count();
|
||||
|
||||
BOOST_TEST(c->empty());
|
||||
|
||||
boost::process::child child(exe_name, shm_name);
|
||||
child.wait();
|
||||
int ret = child.exit_code();
|
||||
|
||||
auto inputs =
|
||||
std::vector<std::pair<std::string, std::string> >{{"hello", "world"}};
|
||||
for (const auto& kvp : inputs) {
|
||||
auto const& key = kvp.first;
|
||||
auto const& value = kvp.second;
|
||||
c->emplace(string_type(key.data(), char_alloc),
|
||||
string_type(value.data(), char_alloc));
|
||||
}
|
||||
|
||||
BOOST_TEST_EQ(ret, 0);
|
||||
BOOST_TEST_GT(c->size(), inputs.size());
|
||||
BOOST_TEST_GT(c->bucket_count(), old_bc);
|
||||
|
||||
segment.destroy<container_type>(shm_map_name);
|
||||
}
|
||||
|
||||
void child(std::string const& shm_name, concurrent_map*)
|
||||
{
|
||||
using container_type = concurrent_map;
|
||||
|
||||
boost::interprocess::managed_shared_memory segment(
|
||||
boost::interprocess::open_only, shm_name.c_str());
|
||||
|
||||
container_type* c = segment.find<container_type>(shm_map_name).first;
|
||||
|
||||
BOOST_TEST(c->empty());
|
||||
|
||||
std::vector<std::pair<std::string, std::string> > inputs = {
|
||||
{"aaa", "AAA"}, {"bbb", "BBB"}, {"ccc", "CCCC"}};
|
||||
|
||||
if (BOOST_TEST_NE(c, nullptr)) {
|
||||
auto a = segment.get_segment_manager();
|
||||
|
||||
for (const auto& input : inputs) {
|
||||
c->emplace(string_type(input.first.c_str(), a),
|
||||
string_type(input.second.c_str(), a));
|
||||
}
|
||||
|
||||
c->rehash(c->bucket_count() + 1);
|
||||
}
|
||||
}
|
||||
|
||||
std::string shm_name_sanitize(std::string const& exe_name)
|
||||
{
|
||||
std::string s(exe_name);
|
||||
auto pos = std::remove_if(s.begin(), s.end(), [](char const c) {
|
||||
switch (c) {
|
||||
case '/':
|
||||
case '.':
|
||||
case '\\':
|
||||
case '-':
|
||||
case '_':
|
||||
return true;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
});
|
||||
s.erase(pos, s.end());
|
||||
s = "/" + s;
|
||||
return s.substr(0, 255);
|
||||
}
|
||||
|
||||
void mmap_test(int argc, char const** argv)
|
||||
{
|
||||
using container_type =
|
||||
decltype(get_container_type<BOOST_UNORDERED_FOA_MMAP_MAP_TYPE>());
|
||||
|
||||
if (argc == 1) {
|
||||
auto uuid = to_string(boost::uuids::random_generator()());
|
||||
auto exe_name = argv[0];
|
||||
auto shm_name = shm_name_sanitize(std::string(exe_name) + uuid);
|
||||
container_type* p = nullptr;
|
||||
parent(shm_name, exe_name, p);
|
||||
} else {
|
||||
auto shm_name = std::string(argv[1]);
|
||||
container_type* p = nullptr;
|
||||
child(shm_name, p);
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char const** argv)
|
||||
{
|
||||
mmap_test(argc, argv);
|
||||
return boost::report_errors();
|
||||
}
|
||||
#endif
|
@ -6,13 +6,13 @@
|
||||
|
||||
#include "../helpers/unordered.hpp"
|
||||
|
||||
#include "../helpers/test.hpp"
|
||||
#include "../objects/test.hpp"
|
||||
#include "../objects/cxx11_allocator.hpp"
|
||||
#include "../helpers/random_values.hpp"
|
||||
#include "../helpers/tracker.hpp"
|
||||
#include "../helpers/equivalent.hpp"
|
||||
#include "../helpers/invariants.hpp"
|
||||
#include "../helpers/random_values.hpp"
|
||||
#include "../helpers/test.hpp"
|
||||
#include "../helpers/tracker.hpp"
|
||||
#include "../objects/cxx11_allocator.hpp"
|
||||
#include "../objects/test.hpp"
|
||||
|
||||
#include <boost/core/ignore_unused.hpp>
|
||||
#include <iterator>
|
||||
@ -75,7 +75,18 @@ namespace move_tests {
|
||||
test::check_equivalent_keys(y);
|
||||
#if defined(BOOST_UNORDERED_USE_MOVE) || \
|
||||
!defined(BOOST_NO_CXX11_RVALUE_REFERENCES)
|
||||
|
||||
#ifdef BOOST_UNORDERED_FOA_TESTS
|
||||
using allocator_type = typename T::allocator_type;
|
||||
using value_type =
|
||||
typename boost::allocator_value_type<allocator_type>::type;
|
||||
using pointer = typename boost::allocator_pointer<allocator_type>::type;
|
||||
if (std::is_same<pointer, value_type*>::value) {
|
||||
BOOST_TEST_EQ(test::detail::tracker.count_allocations, 0u);
|
||||
}
|
||||
#else
|
||||
BOOST_TEST_EQ(test::detail::tracker.count_allocations, 0u);
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
@ -117,7 +128,17 @@ namespace move_tests {
|
||||
y = empty(p);
|
||||
#if defined(BOOST_UNORDERED_USE_MOVE) || \
|
||||
!defined(BOOST_NO_CXX11_RVALUE_REFERENCES)
|
||||
#ifdef BOOST_UNORDERED_FOA_TESTS
|
||||
using allocator_type = typename T::allocator_type;
|
||||
using value_type =
|
||||
typename boost::allocator_value_type<allocator_type>::type;
|
||||
using pointer = typename boost::allocator_pointer<allocator_type>::type;
|
||||
if (std::is_same<pointer, value_type*>::value) {
|
||||
BOOST_TEST_EQ(test::detail::tracker.count_allocations, 0u);
|
||||
}
|
||||
#else
|
||||
BOOST_TEST_EQ(test::detail::tracker.count_allocations, 0u);
|
||||
#endif
|
||||
#endif
|
||||
test::check_container(y, v);
|
||||
test::check_equivalent_keys(y);
|
||||
@ -279,12 +300,34 @@ namespace move_tests {
|
||||
T x(0, hf, eq, al2);
|
||||
x.max_load_factor(0.25);
|
||||
|
||||
#ifdef BOOST_UNORDERED_FOA_TESTS
|
||||
{
|
||||
using value_type =
|
||||
typename boost::allocator_value_type<allocator_type>::type;
|
||||
using pointer = typename boost::allocator_pointer<allocator_type>::type;
|
||||
if (std::is_same<pointer, value_type*>::value) {
|
||||
BOOST_TEST_EQ(test::detail::tracker.count_allocations, 0u);
|
||||
}
|
||||
}
|
||||
#else
|
||||
BOOST_TEST_EQ(test::detail::tracker.count_allocations, 0u);
|
||||
#endif
|
||||
|
||||
y = boost::move(x);
|
||||
#if defined(BOOST_UNORDERED_USE_MOVE) || \
|
||||
!defined(BOOST_NO_CXX11_RVALUE_REFERENCES)
|
||||
#ifdef BOOST_UNORDERED_FOA_TESTS
|
||||
{
|
||||
using value_type =
|
||||
typename boost::allocator_value_type<allocator_type>::type;
|
||||
using pointer = typename boost::allocator_pointer<allocator_type>::type;
|
||||
if (std::is_same<pointer, value_type*>::value) {
|
||||
BOOST_TEST_EQ(test::detail::tracker.count_allocations, 0u);
|
||||
}
|
||||
}
|
||||
#else
|
||||
BOOST_TEST_EQ(test::detail::tracker.count_allocations, 0u);
|
||||
#endif
|
||||
#endif
|
||||
test::check_container(y, v);
|
||||
test::check_equivalent_keys(y);
|
||||
@ -515,32 +558,18 @@ namespace move_tests {
|
||||
test::no_propagate_move> >* test_multimap_no_prop_move;
|
||||
|
||||
UNORDERED_TEST(move_construct_tests1,
|
||||
((test_map_std_alloc)(test_set)(test_multiset)(test_map)(test_multimap)(
|
||||
test_set_prop_move)(test_multiset_prop_move)(test_map_prop_move)(
|
||||
test_multimap_prop_move)(test_set_no_prop_move)(
|
||||
test_multiset_no_prop_move)(test_map_no_prop_move)(
|
||||
test_multimap_no_prop_move))(
|
||||
((test_map_std_alloc)(test_set)(test_multiset)(test_map)(test_multimap)(test_set_prop_move)(test_multiset_prop_move)(test_map_prop_move)(test_multimap_prop_move)(test_set_no_prop_move)(test_multiset_no_prop_move)(test_map_no_prop_move)(test_multimap_no_prop_move))(
|
||||
(default_generator)(generate_collisions)(limited_range)))
|
||||
UNORDERED_TEST(move_assign_tests1,
|
||||
((test_map_std_alloc)(test_set)(test_multiset)(test_map)(test_multimap)(
|
||||
test_set_prop_move)(test_multiset_prop_move)(test_map_prop_move)(
|
||||
test_multimap_prop_move)(test_set_no_prop_move)(
|
||||
test_multiset_no_prop_move)(test_map_no_prop_move)(
|
||||
test_multimap_no_prop_move))(
|
||||
((test_map_std_alloc)(test_set)(test_multiset)(test_map)(test_multimap)(test_set_prop_move)(test_multiset_prop_move)(test_map_prop_move)(test_multimap_prop_move)(test_set_no_prop_move)(test_multiset_no_prop_move)(test_map_no_prop_move)(test_multimap_no_prop_move))(
|
||||
(default_generator)(generate_collisions)(limited_range)))
|
||||
UNORDERED_TEST(move_construct_tests2,
|
||||
((test_set)(test_multiset)(test_map)(test_multimap)(test_set_prop_move)(
|
||||
test_multiset_prop_move)(test_map_prop_move)(test_multimap_prop_move)(
|
||||
test_set_no_prop_move)(test_multiset_no_prop_move)(test_map_no_prop_move)(
|
||||
test_multimap_no_prop_move))(
|
||||
((test_set)(test_multiset)(test_map)(test_multimap)(test_set_prop_move)(test_multiset_prop_move)(test_map_prop_move)(test_multimap_prop_move)(test_set_no_prop_move)(test_multiset_no_prop_move)(test_map_no_prop_move)(test_multimap_no_prop_move))(
|
||||
(default_generator)(generate_collisions)(limited_range)))
|
||||
UNORDERED_TEST(move_assign_tests2,
|
||||
((test_set)(test_multiset)(test_map)(test_multimap)(test_set_prop_move)(
|
||||
test_multiset_prop_move)(test_map_prop_move)(test_multimap_prop_move)(
|
||||
test_set_no_prop_move)(test_multiset_no_prop_move)(test_map_no_prop_move)(
|
||||
test_multimap_no_prop_move))(
|
||||
((test_set)(test_multiset)(test_map)(test_multimap)(test_set_prop_move)(test_multiset_prop_move)(test_map_prop_move)(test_multimap_prop_move)(test_set_no_prop_move)(test_multiset_no_prop_move)(test_map_no_prop_move)(test_multimap_no_prop_move))(
|
||||
(default_generator)(generate_collisions)(limited_range)))
|
||||
#endif
|
||||
}
|
||||
} // namespace move_tests
|
||||
|
||||
RUN_TESTS()
|
||||
|
@ -3,14 +3,14 @@
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or move at http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#include "../helpers/unordered.hpp"
|
||||
#include "../helpers/test.hpp"
|
||||
#include "../objects/test.hpp"
|
||||
#include "../objects/cxx11_allocator.hpp"
|
||||
#include "../helpers/random_values.hpp"
|
||||
#include "../helpers/tracker.hpp"
|
||||
#include "../helpers/equivalent.hpp"
|
||||
#include "../helpers/invariants.hpp"
|
||||
#include "../helpers/random_values.hpp"
|
||||
#include "../helpers/test.hpp"
|
||||
#include "../helpers/tracker.hpp"
|
||||
#include "../helpers/unordered.hpp"
|
||||
#include "../objects/cxx11_allocator.hpp"
|
||||
#include "../objects/test.hpp"
|
||||
|
||||
#include <boost/core/ignore_unused.hpp>
|
||||
#include <iterator>
|
||||
@ -59,7 +59,8 @@ namespace move_tests {
|
||||
|
||||
template <class T> T const& get_value(T const& t) { return t; }
|
||||
|
||||
template <class K, class V> K const& get_value(std::pair<K const, V> const& kv)
|
||||
template <class K, class V>
|
||||
K const& get_value(std::pair<K const, V> const& kv)
|
||||
{
|
||||
return kv.second;
|
||||
}
|
||||
@ -466,8 +467,23 @@ namespace move_tests {
|
||||
!defined(BOOST_NO_CXX11_RVALUE_REFERENCES)
|
||||
BOOST_TEST(y.empty());
|
||||
BOOST_TEST(y.begin() == y.end());
|
||||
|
||||
#ifdef BOOST_UNORDERED_FOA_TESTS
|
||||
{
|
||||
using allocator_type = typename T::allocator_type;
|
||||
using value_type =
|
||||
typename boost::allocator_value_type<allocator_type>::type;
|
||||
using pointer = typename boost::allocator_pointer<allocator_type>::type;
|
||||
if (std::is_same<pointer, value_type*>::value) {
|
||||
BOOST_TEST_EQ(y.bucket_count(), 0u);
|
||||
BOOST_TEST_EQ(test::detail::tracker.count_allocations, num_allocs);
|
||||
}
|
||||
}
|
||||
#else
|
||||
BOOST_TEST_EQ(y.bucket_count(), 0u);
|
||||
BOOST_TEST_EQ(test::detail::tracker.count_allocations, num_allocs);
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
fps[i](y, v);
|
||||
@ -520,8 +536,23 @@ namespace move_tests {
|
||||
!defined(BOOST_NO_CXX11_RVALUE_REFERENCES)
|
||||
BOOST_TEST(y.empty());
|
||||
BOOST_TEST(y.begin() == y.end());
|
||||
|
||||
#ifdef BOOST_UNORDERED_FOA_TESTS
|
||||
{
|
||||
using allocator_type = typename T::allocator_type;
|
||||
using value_type =
|
||||
typename boost::allocator_value_type<allocator_type>::type;
|
||||
using pointer = typename boost::allocator_pointer<allocator_type>::type;
|
||||
if (std::is_same<pointer, value_type*>::value) {
|
||||
BOOST_TEST_EQ(y.bucket_count(), 0u);
|
||||
BOOST_TEST_EQ(test::detail::tracker.count_allocations, num_allocs);
|
||||
}
|
||||
}
|
||||
#else
|
||||
BOOST_TEST_EQ(y.bucket_count(), 0u);
|
||||
BOOST_TEST_EQ(test::detail::tracker.count_allocations, num_allocs);
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
fps[i](y, v);
|
||||
@ -687,7 +718,7 @@ namespace move_tests {
|
||||
test::cxx11_allocator<std::pair<test::object const, test::object>,
|
||||
test::no_propagate_move> >* test_multimap_no_prop_move;
|
||||
|
||||
// clang-format off
|
||||
// clang-format off
|
||||
UNORDERED_TEST(post_move_tests,
|
||||
((test_set)(test_multiset)(test_map)(test_multimap)(test_set_prop_move)(
|
||||
test_multiset_prop_move)(test_map_prop_move)(test_multimap_prop_move)(
|
||||
@ -696,6 +727,6 @@ namespace move_tests {
|
||||
(default_generator)(generate_collisions)(limited_range)))
|
||||
// clang-format on
|
||||
#endif
|
||||
}
|
||||
} // namespace move_tests
|
||||
|
||||
RUN_TESTS()
|
||||
|
@ -216,8 +216,6 @@ template <class C1, class C2> void scary_test()
|
||||
typename C2::const_iterator cbegin(x.cbegin());
|
||||
BOOST_TEST(cbegin == x.cend());
|
||||
|
||||
BOOST_TEST_EQ(x.bucket_count(), 0u);
|
||||
|
||||
#ifndef BOOST_UNORDERED_FOA_TESTS
|
||||
typename C2::local_iterator lbegin(x.begin(0));
|
||||
BOOST_TEST(lbegin == x.end(0));
|
||||
|
Reference in New Issue
Block a user