Merge pull request #211 from boostorg/fix/gh-189

fix/gh-189
This commit is contained in:
Christian Mazakas
2023-09-15 14:59:00 -07:00
committed by GitHub
29 changed files with 1194 additions and 504 deletions

View File

@ -15,6 +15,8 @@ with serial and parallel variants.
* Added debug mode mechanisms for detecting illegal reentrancies into * Added debug mode mechanisms for detecting illegal reentrancies into
a `boost::concurrent_flat_map` from user code. a `boost::concurrent_flat_map` from user code.
* Added Boost.Serialization support to all containers and their (non-local) iterator types. * 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 == Release 1.83.0 - Major update

View File

@ -90,9 +90,10 @@ namespace boost {
const allocator_type& a); const allocator_type& a);
xref:#concurrent_flat_map_destructor[~concurrent_flat_map](); 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_copy_assignment[operator++=++](const concurrent_flat_map& other);
concurrent_flat_map& xref:#concurrent_flat_map_move_assignment[operator++=++](concurrent_flat_map&& other) concurrent_flat_map& xref:#concurrent_flat_map_move_assignment[operator++=++](concurrent_flat_map&& other) ++noexcept(
noexcept(boost::allocator_traits<Allocator>::is_always_equal::value || (boost::allocator_traits<Allocator>::is_always_equal::value ||
boost::allocator_traits<Allocator>::propagate_on_container_move_assignment::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>); 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; 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_ |_Allocator_
|An allocator whose value type is the same as the table's value type. |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` Allocators using https://en.cppreference.com/w/cpp/named_req/Allocator#Fancy_pointers[fancy pointers] are supported.
must be convertible to/from `value_type*` and `const value_type*`, respectively.
|=== |===
@ -673,8 +673,9 @@ Concurrency:;; Blocking on `*this` and `other`.
==== Move Assignment ==== Move Assignment
```c++ ```c++
concurrent_flat_map& operator=(concurrent_flat_map&& other) concurrent_flat_map& operator=(concurrent_flat_map&& other)
noexcept(boost::allocator_traits<Allocator>::is_always_equal::value || noexcept((boost::allocator_traits<Allocator>::is_always_equal::value ||
boost::allocator_traits<Allocator>::propagate_on_container_move_assignment::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`, 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`. 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`.

View File

@ -97,9 +97,10 @@ namespace boost {
const allocator_type& a); const allocator_type& a);
xref:#unordered_flat_map_destructor[~unordered_flat_map](); 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_copy_assignment[operator++=++](const unordered_flat_map& other);
unordered_flat_map& xref:#unordered_flat_map_move_assignment[operator++=++](unordered_flat_map&& other) unordered_flat_map& xref:#unordered_flat_map_move_assignment[operator++=++](unordered_flat_map&& other) ++noexcept(
noexcept(boost::allocator_traits<Allocator>::is_always_equal::value || (boost::allocator_traits<Allocator>::is_always_equal::value ||
boost::allocator_traits<Allocator>::propagate_on_container_move_assignment::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>); 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; 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_ |_Allocator_
|An allocator whose value type is the same as the container's value type. |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` Allocators using https://en.cppreference.com/w/cpp/named_req/Allocator#Fancy_pointers[fancy pointers] are supported.
must be convertible to/from `value_type*` and `const value_type*`, respectively.
|=== |===
@ -632,8 +632,9 @@ Requires:;; `value_type` is https://en.cppreference.com/w/cpp/named_req/CopyInse
==== Move Assignment ==== Move Assignment
```c++ ```c++
unordered_flat_map& operator=(unordered_flat_map&& other) unordered_flat_map& operator=(unordered_flat_map&& other)
noexcept(boost::allocator_traits<Allocator>::is_always_equal::value || noexcept((boost::allocator_traits<Allocator>::is_always_equal::value ||
boost::allocator_traits<Allocator>::propagate_on_container_move_assignment::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`, 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`. 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. 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. Invalidates iterators, pointers and references, and changes the order of elements.

View File

@ -91,9 +91,10 @@ namespace boost {
const allocator_type& a); const allocator_type& a);
xref:#unordered_flat_set_destructor[~unordered_flat_set](); 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_copy_assignment[operator++=++](const unordered_flat_set& other);
unordered_flat_set& xref:#unordered_flat_set_move_assignment[operator++=++](unordered_flat_set&& other) unordered_flat_set& xref:#unordered_flat_set_move_assignment[operator++=++](unordered_flat_set&& other) ++noexcept(
noexcept(boost::allocator_traits<Allocator>::is_always_equal::value || (boost::allocator_traits<Allocator>::is_always_equal::value ||
boost::allocator_traits<Allocator>::propagate_on_container_move_assignment::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>); 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; 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_ |_Allocator_
|An allocator whose value type is the same as the container's value type. |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` Allocators using https://en.cppreference.com/w/cpp/named_req/Allocator#Fancy_pointers[fancy pointers] are supported.
must be convertible to/from `value_type*` and `const value_type*`, respectively.
|=== |===
@ -565,8 +565,9 @@ Requires:;; `value_type` is https://en.cppreference.com/w/cpp/named_req/CopyInse
==== Move Assignment ==== Move Assignment
```c++ ```c++
unordered_flat_set& operator=(unordered_flat_set&& other) unordered_flat_set& operator=(unordered_flat_set&& other)
noexcept(boost::allocator_traits<Allocator>::is_always_equal::value || noexcept((boost::allocator_traits<Allocator>::is_always_equal::value ||
boost::allocator_traits<Allocator>::propagate_on_container_move_assignment::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`, 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`. 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. 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. Invalidates iterators, pointers and references, and changes the order of elements.

View File

@ -95,9 +95,10 @@ namespace boost {
const allocator_type& a); const allocator_type& a);
xref:#unordered_node_map_destructor[~unordered_node_map](); 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_copy_assignment[operator++=++](const unordered_node_map& other);
unordered_node_map& xref:#unordered_node_map_move_assignment[operator++=++](unordered_node_map&& other) unordered_node_map& xref:#unordered_node_map_move_assignment[operator++=++](unordered_node_map&& other) ++noexcept(
noexcept(boost::allocator_traits<Allocator>::is_always_equal::value || (boost::allocator_traits<Allocator>::is_always_equal::value ||
boost::allocator_traits<Allocator>::propagate_on_container_move_assignment::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>); 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; 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_ |_Allocator_
|An allocator whose value type is the same as the container's value type. |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` Allocators using https://en.cppreference.com/w/cpp/named_req/Allocator#Fancy_pointers[fancy pointers] are supported.
must be convertible to/from `value_type*` and `const value_type*`, respectively.
|=== |===
@ -649,8 +649,9 @@ Requires:;; `value_type` is https://en.cppreference.com/w/cpp/named_req/CopyInse
==== Move Assignment ==== Move Assignment
```c++ ```c++
unordered_node_map& operator=(unordered_node_map&& other) unordered_node_map& operator=(unordered_node_map&& other)
noexcept(boost::allocator_traits<Allocator>::is_always_equal::value || noexcept((boost::allocator_traits<Allocator>::is_always_equal::value ||
boost::allocator_traits<Allocator>::propagate_on_container_move_assignment::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`, 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`. 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. 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. Invalidates iterators and changes the order of elements.

View File

@ -90,9 +90,10 @@ namespace boost {
const allocator_type& a); const allocator_type& a);
xref:#unordered_node_set_destructor[~unordered_node_set](); 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_copy_assignment[operator++=++](const unordered_node_set& other);
unordered_node_set& xref:#unordered_node_set_move_assignment[operator++=++](unordered_node_set&& other) unordered_node_set& xref:#unordered_node_set_move_assignment[operator++=++](unordered_node_set&& other) ++noexcept(
noexcept(boost::allocator_traits<Allocator>::is_always_equal::value || (boost::allocator_traits<Allocator>::is_always_equal::value ||
boost::allocator_traits<Allocator>::propagate_on_container_move_assignment::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>); 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; allocator_type xref:#unordered_node_set_get_allocator[get_allocator]() const noexcept;
@ -264,8 +265,7 @@ namespace boost {
|_Allocator_ |_Allocator_
|An allocator whose value type is the same as the container's value type. |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` Allocators using https://en.cppreference.com/w/cpp/named_req/Allocator#Fancy_pointers[fancy pointers] are supported.
must be convertible to/from `value_type*` and `const value_type*`, respectively.
|=== |===
@ -602,8 +602,9 @@ Requires:;; `value_type` is https://en.cppreference.com/w/cpp/named_req/CopyInse
==== Move Assignment ==== Move Assignment
```c++ ```c++
unordered_node_set& operator=(unordered_node_set&& other) unordered_node_set& operator=(unordered_node_set&& other)
noexcept(boost::allocator_traits<Allocator>::is_always_equal::value || noexcept((boost::allocator_traits<Allocator>::is_always_equal::value ||
boost::allocator_traits<Allocator>::propagate_on_container_move_assignment::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`, 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`. 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. 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. Invalidates iterators and changes the order of elements.

View File

@ -92,7 +92,10 @@ namespace boost {
using type_policy = detail::foa::flat_map_types<Key, T>; 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> template <class K, class V, class H, class KE, class A>
bool friend operator==(concurrent_flat_map<K, V, H, KE, A> const& lhs, bool friend operator==(concurrent_flat_map<K, V, H, KE, A> const& lhs,
@ -248,10 +251,8 @@ namespace boost {
return *this; return *this;
} }
concurrent_flat_map& operator=(concurrent_flat_map&& rhs) concurrent_flat_map& operator=(concurrent_flat_map&& rhs) noexcept(
noexcept(boost::allocator_is_always_equal<Allocator>::type::value || noexcept(std::declval<table_type&>() = std::declval<table_type&&>()))
boost::allocator_propagate_on_container_move_assignment<
Allocator>::type::value)
{ {
table_ = std::move(rhs.table_); table_ = std::move(rhs.table_);
return *this; return *this;

View File

@ -247,16 +247,24 @@ group_access* dummy_group_accesses()
/* subclasses table_arrays to add an additional group_access array */ /* subclasses table_arrays to add an additional group_access array */
template<typename Value,typename Group,typename SizePolicy> template<typename Value,typename Group,typename SizePolicy,typename Allocator>
struct concurrent_table_arrays:table_arrays<Value,Group,SizePolicy> 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): using super=table_arrays<Value,Group,SizePolicy,Allocator>;
super{arrays},group_accesses{pga}{}
template<typename Allocator> concurrent_table_arrays(const super& arrays,group_access_pointer pga):
static concurrent_table_arrays new_(Allocator& al,std::size_t n) 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)}; super x{super::new_(al,n)};
BOOST_TRY{ BOOST_TRY{
@ -269,54 +277,61 @@ struct concurrent_table_arrays:table_arrays<Value,Group,SizePolicy>
BOOST_CATCH_END BOOST_CATCH_END
} }
template<typename Allocator> static void set_group_access(
static concurrent_table_arrays new_group_access(Allocator& al,const super& x) group_access_allocator_type al,concurrent_table_arrays& arrays)
{ {
concurrent_table_arrays arrays{x,nullptr}; set_group_access(
if(!arrays.elements){ al,arrays,std::is_same<group_access*,group_access_pointer>{});
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>;
auto aal=access_alloc(al); static void set_group_access(
arrays.group_accesses=boost::to_address( group_access_allocator_type al,
access_traits::allocate(aal,arrays.groups_size_mask+1)); 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){ 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; return arrays;
} }
template<typename Allocator> static void delete_(group_access_allocator_type al,concurrent_table_arrays& arrays)noexcept
static void delete_(Allocator& al,concurrent_table_arrays& arrays)noexcept
{ {
delete_group_access(al,arrays); delete_group_access(al,arrays);
super::delete_(al,arrays); super::delete_(al,arrays);
} }
template<typename Allocator> static void delete_group_access(group_access_allocator_type al,concurrent_table_arrays& arrays)noexcept
static void delete_group_access(Allocator& al,concurrent_table_arrays& arrays)noexcept
{ {
if(arrays.elements){ if(arrays.elements()){
using access_alloc= boost::allocator_deallocate(
typename boost::allocator_rebind<Allocator,group_access>::type; al,arrays.group_accesses_,arrays.groups_size_mask+1);
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);
} }
} }
group_access *group_accesses; group_access_pointer group_accesses_;
}; };
struct atomic_size_control struct atomic_size_control
@ -473,20 +488,32 @@ public:
concurrent_table(concurrent_table&& x,const Allocator& al_): concurrent_table(concurrent_table&& x,const Allocator& al_):
concurrent_table(std::move(x),al_,x.exclusive_access()){} 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{ super{
std::move(x.h()),std::move(x.pred()),std::move(x.al()), std::move(x.h()),std::move(x.pred()),std::move(x.al()),
arrays_type(arrays_type::new_group_access( [&x]{return arrays_type::new_group_access(
x.al(), x.al(),typename arrays_type::super{
typename arrays_type::super{
x.arrays.groups_size_index,x.arrays.groups_size_mask, x.arrays.groups_size_index,x.arrays.groups_size_mask,
reinterpret_cast<group_type*>(x.arrays.groups), to_pointer<typename arrays_type::group_type_pointer>(
reinterpret_cast<value_type*>(x.arrays.elements)})), reinterpret_cast<group_type*>(x.arrays.groups())),
x.arrays.elements_});},
size_ctrl_type{x.size_ctrl.ml,x.size_ctrl.size}} 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()=default;
concurrent_table& operator=(const concurrent_table& x) concurrent_table& operator=(const concurrent_table& x)
@ -496,7 +523,8 @@ public:
return *this; 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); auto lck=exclusive_access(*this,x);
super::operator=(std::move(x)); super::operator=(std::move(x));
@ -967,18 +995,18 @@ private:
inline group_shared_lock_guard access(group_shared,std::size_t pos)const 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( inline group_exclusive_lock_guard access(
group_exclusive,std::size_t pos)const 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 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 /* Const casts value_type& according to the level of group access for
@ -1097,10 +1125,10 @@ private:
prober pb(pos0); prober pb(pos0);
do{ do{
auto pos=pb.get(); auto pos=pb.get();
auto pg=this->arrays.groups+pos; auto pg=this->arrays.groups()+pos;
auto mask=pg->match(hash); auto mask=pg->match(hash);
if(mask){ if(mask){
auto p=this->arrays.elements+pos*N; auto p=this->arrays.elements()+pos*N;
BOOST_UNORDERED_PREFETCH_ELEMENTS(p,N); BOOST_UNORDERED_PREFETCH_ELEMENTS(p,N);
auto lck=access(access_mode,pos); auto lck=access(access_mode,pos);
do{ do{
@ -1313,7 +1341,7 @@ private:
if(BOOST_LIKELY(rsize.succeeded())){ if(BOOST_LIKELY(rsize.succeeded())){
for(prober pb(pos0);;pb.next(this->arrays.groups_size_mask)){ for(prober pb(pos0);;pb.next(this->arrays.groups_size_mask)){
auto pos=pb.get(); auto pos=pb.get();
auto pg=this->arrays.groups+pos; auto pg=this->arrays.groups()+pos;
auto lck=access(group_exclusive{},pos); auto lck=access(group_exclusive{},pos);
auto mask=pg->match_available(); auto mask=pg->match_available();
if(BOOST_LIKELY(mask!=0)){ if(BOOST_LIKELY(mask!=0)){
@ -1323,7 +1351,7 @@ private:
/* other thread inserted from pos0, need to start over */ /* other thread inserted from pos0, need to start over */
goto startover; 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)...); this->construct_element(p,std::forward<Args>(args)...);
rslot.commit(); rslot.commit();
rsize.commit(); rsize.commit();
@ -1373,11 +1401,11 @@ private:
auto for_all_elements_while(GroupAccessMode access_mode,F f)const auto for_all_elements_while(GroupAccessMode access_mode,F f)const
->decltype(f(nullptr,0,nullptr),bool()) ->decltype(f(nullptr,0,nullptr),bool())
{ {
auto p=this->arrays.elements; auto p=this->arrays.elements();
if(p){ 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){ 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); auto mask=this->match_really_occupied(pg,last);
while(mask){ while(mask){
auto n=unchecked_countr_zero(mask); auto n=unchecked_countr_zero(mask);
@ -1405,13 +1433,13 @@ private:
GroupAccessMode access_mode,ExecutionPolicy&& policy,F f)const GroupAccessMode access_mode,ExecutionPolicy&& policy,F f)const
->decltype(f(nullptr,0,nullptr),void()) ->decltype(f(nullptr,0,nullptr),void())
{ {
if(!this->arrays.elements)return; if(!this->arrays.elements())return;
auto first=this->arrays.groups, auto first=this->arrays.groups(),
last=first+this->arrays.groups_size_mask+1; last=first+this->arrays.groups_size_mask+1;
std::for_each(std::forward<ExecutionPolicy>(policy),first,last, std::for_each(std::forward<ExecutionPolicy>(policy),first,last,
[&,this](group_type& g){ [&,this](group_type& g){
auto pos=static_cast<std::size_t>(&g-first); 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 lck=access(access_mode,pos);
auto mask=this->match_really_occupied(&g,last); auto mask=this->match_really_occupied(&g,last);
while(mask){ while(mask){
@ -1427,13 +1455,13 @@ private:
bool for_all_elements_while( bool for_all_elements_while(
GroupAccessMode access_mode,ExecutionPolicy&& policy,F f)const GroupAccessMode access_mode,ExecutionPolicy&& policy,F f)const
{ {
if(!this->arrays.elements)return true; if(!this->arrays.elements())return true;
auto first=this->arrays.groups, auto first=this->arrays.groups(),
last=first+this->arrays.groups_size_mask+1; last=first+this->arrays.groups_size_mask+1;
return std::all_of(std::forward<ExecutionPolicy>(policy),first,last, return std::all_of(std::forward<ExecutionPolicy>(policy),first,last,
[&,this](group_type& g){ [&,this](group_type& g){
auto pos=static_cast<std::size_t>(&g-first); 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 lck=access(access_mode,pos);
auto mask=this->match_really_occupied(&g,last); auto mask=this->match_really_occupied(&g,last);
while(mask){ while(mask){

View File

@ -938,74 +938,145 @@ Group* dummy_groups()
const_cast<typename Group::dummy_group_type*>(storage)); 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 struct table_arrays
{ {
using allocator_type=typename boost::allocator_rebind<Allocator,Value>::type;
using value_type=Value; using value_type=Value;
using group_type=Group; using group_type=Group;
static constexpr auto N=group_type::N; static constexpr auto N=group_type::N;
using size_policy=SizePolicy; 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): 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}{} groups_size_index{gsi},groups_size_mask{gsm},groups_{pg},elements_{pe}{}
template<typename Allocator> value_type* elements()const noexcept{return boost::to_address(elements_);}
static table_arrays new_(Allocator& al,std::size_t n) 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= return set_arrays(
typename boost::allocator_rebind<Allocator, Value>::type; arrays,al,n,std::is_same<group_type*,group_type_pointer>{});
using storage_traits=boost::allocator_traits<storage_allocator>; }
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_index=size_index_for<group_type,size_policy>(n);
auto groups_size=size_policy::size(groups_size_index); auto groups_size=size_policy::size(groups_size_index);
table_arrays arrays{groups_size_index,groups_size-1,nullptr,nullptr}; table_arrays arrays{groups_size_index,groups_size-1,nullptr,nullptr};
if(!n){ set_arrays(arrays,al,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();
}
return arrays; return arrays;
} }
template<typename Allocator> static void delete_(allocator_type al,table_arrays& arrays)noexcept
static void delete_(Allocator& al,table_arrays& arrays)noexcept
{ {
using storage_alloc=typename boost::allocator_rebind<Allocator,Value>::type; using storage_traits=boost::allocator_traits<allocator_type>;
using storage_traits=boost::allocator_traits<storage_alloc>;
using pointer=typename storage_traits::pointer;
using pointer_traits=boost::pointer_traits<pointer>;
auto sal=storage_alloc(al); auto sal=allocator_type(al);
if(arrays.elements){ if(arrays.elements()){
storage_traits::deallocate( storage_traits::deallocate(
sal,pointer_traits::pointer_to(*arrays.elements), sal,arrays.elements_,buffer_size(arrays.groups_size_mask+1));
buffer_size(arrays.groups_size_mask+1));
} }
} }
@ -1024,7 +1095,7 @@ struct table_arrays
} }
static void initialize_groups( 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 /* memset faster/not slower than manual, assumes all zeros is group_type's
* default layout. * default layout.
@ -1033,19 +1104,19 @@ struct table_arrays
*/ */
std::memset( 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( 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_index;
std::size_t groups_size_mask; std::size_t groups_size_mask;
group_type *groups; group_type_pointer groups_;
value_type *elements; value_type_pointer elements_;
}; };
struct if_constexpr_void_else{void operator()()const{}}; struct if_constexpr_void_else{void operator()()const{}};
@ -1258,8 +1329,12 @@ public:
>::type; >::type;
using alloc_traits=boost::allocator_traits<Allocator>; using alloc_traits=boost::allocator_traits<Allocator>;
using element_type=typename type_policy::element_type; 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; 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 key_type=typename type_policy::key_type;
using init_type=typename type_policy::init_type; using init_type=typename type_policy::init_type;
@ -1283,31 +1358,44 @@ public:
size_ctrl{initial_max_load(),0} 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( table_core(
Hash&& h_,Pred&& pred_,Allocator&& al_, 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_)}, hash_base{empty_init,std::move(h_)},
pred_base{empty_init,std::move(pred_)}, pred_base{empty_init,std::move(pred_)},
allocator_base{empty_init,std::move(al_)}, 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(const table_core& x):
table_core{x,alloc_traits::select_on_container_copy_construction(x.al())}{} 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) table_core(table_core&& x)
noexcept( noexcept(
std::is_nothrow_move_constructible<Hash>::value&& std::is_nothrow_move_constructible<Hash>::value&&
std::is_nothrow_move_constructible<Pred>::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{ table_core{
std::move(x.h()),std::move(x.pred()),std::move(x.al()), std::move(x),arrays_holder<arrays_type,Allocator>{x.new_arrays(0),x.al()},
x.arrays,x.size_ctrl} [&x]{return x.arrays;}}
{ {}
x.empty_initialize();
}
table_core(const table_core& x,const Allocator& al_): 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_} table_core{std::size_t(std::ceil(float(x.size())/mlf)),x.h(),x.pred(),al_}
@ -1345,11 +1433,17 @@ public:
delete_arrays(arrays); delete_arrays(arrays);
} }
void empty_initialize()noexcept std::size_t initial_max_load()const
{ {
arrays=new_arrays(0); static constexpr std::size_t small_capacity=2*N-1;
size_ctrl.ml=initial_max_load();
size_ctrl.size=0; 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) table_core& operator=(const table_core& x)
@ -1397,8 +1491,8 @@ public:
table_core& operator=(table_core&& x) table_core& operator=(table_core&& x)
noexcept( noexcept(
alloc_traits::propagate_on_container_move_assignment::value|| (alloc_traits::propagate_on_container_move_assignment::value||
alloc_traits::is_always_equal::value) alloc_traits::is_always_equal::value)&&!uses_fancy_pointers)
{ {
BOOST_UNORDERED_STATIC_ASSERT_HASH_PRED(Hash, Pred) BOOST_UNORDERED_STATIC_ASSERT_HASH_PRED(Hash, Pred)
@ -1489,11 +1583,12 @@ public:
prober pb(pos0); prober pb(pos0);
do{ do{
auto pos=pb.get(); auto pos=pb.get();
auto pg=arrays.groups+pos; auto pg=arrays.groups()+pos;
auto mask=pg->match(hash); auto mask=pg->match(hash);
if(mask){ if(mask){
BOOST_UNORDERED_ASSUME(arrays.elements!=nullptr); auto elements=arrays.elements();
auto p=arrays.elements+pos*N; BOOST_UNORDERED_ASSUME(elements!=nullptr);
auto p=elements+pos*N;
BOOST_UNORDERED_PREFETCH_ELEMENTS(p,N); BOOST_UNORDERED_PREFETCH_ELEMENTS(p,N);
do{ do{
auto n=unchecked_countr_zero(mask); auto n=unchecked_countr_zero(mask);
@ -1542,9 +1637,9 @@ public:
void clear()noexcept void clear()noexcept
{ {
auto p=arrays.elements; auto p=arrays.elements();
if(p){ 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){ pg!=last;++pg,p+=N){
auto mask=match_really_occupied(pg,last); auto mask=match_really_occupied(pg,last);
while(mask){ while(mask){
@ -1554,7 +1649,7 @@ public:
/* we wipe the entire metadata to reset the overflow byte as well */ /* we wipe the entire metadata to reset the overflow byte as well */
pg->initialize(); 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.ml=initial_max_load();
size_ctrl.size=0; size_ctrl.size=0;
} }
@ -1565,7 +1660,7 @@ public:
std::size_t capacity()const noexcept 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 float load_factor()const noexcept
@ -1784,9 +1879,9 @@ public:
static auto for_all_elements_while(const arrays_type& arrays_,F f) static auto for_all_elements_while(const arrays_type& arrays_,F f)
->decltype(f(nullptr,0,nullptr),bool()) ->decltype(f(nullptr,0,nullptr),bool())
{ {
auto p=arrays_.elements; auto p=arrays_.elements();
if(p){ 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){ pg!=last;++pg,p+=N){
auto mask=match_really_occupied(pg,last); auto mask=match_really_occupied(pg,last);
while(mask){ while(mask){
@ -1887,7 +1982,7 @@ private:
void fast_copy_elements_from(const table_core& x) 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_elements_array_from(x);
copy_groups_array_from(x); copy_groups_array_from(x);
size_ctrl.ml=std::size_t(x.size_ctrl.ml); 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. * copy-assignable when we're relying on trivial copy constructibility.
*/ */
std::memcpy( std::memcpy(
reinterpret_cast<unsigned char*>(arrays.elements), reinterpret_cast<unsigned char*>(arrays.elements()),
reinterpret_cast<unsigned char*>(x.arrays.elements), reinterpret_cast<unsigned char*>(x.arrays.elements()),
x.capacity()*sizeof(value_type)); x.capacity()*sizeof(value_type));
} }
@ -1932,14 +2027,14 @@ private:
std::size_t num_constructed=0; std::size_t num_constructed=0;
BOOST_TRY{ BOOST_TRY{
x.for_all_elements([&,this](const element_type* p){ 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; ++num_constructed;
}); });
} }
BOOST_CATCH(...){ BOOST_CATCH(...){
if(num_constructed){ if(num_constructed){
x.for_all_elements_while([&,this](const element_type* p){ 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; return --num_constructed!=0;
}); });
} }
@ -1964,15 +2059,17 @@ private:
const table_core& x, std::true_type /* -> memcpy */) const table_core& x, std::true_type /* -> memcpy */)
{ {
std::memcpy( std::memcpy(
arrays.groups,x.arrays.groups, arrays.groups(),x.arrays.groups(),
(arrays.groups_size_mask+1)*sizeof(group_type)); (arrays.groups_size_mask+1)*sizeof(group_type));
} }
void copy_groups_array_from( void copy_groups_array_from(
const table_core& x, std::false_type /* -> manual */) 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){ 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); 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) static std::size_t capacity_for(std::size_t n)
{ {
return size_policy::size(size_index_for<group_type,size_policy>(n))*N-1; 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)){ for(prober pb(pos0);;pb.next(arrays_.groups_size_mask)){
auto pos=pb.get(); auto pos=pb.get();
auto pg=arrays_.groups+pos; auto pg=arrays_.groups()+pos;
auto mask=pg->match_available(); auto mask=pg->match_available();
if(BOOST_LIKELY(mask!=0)){ if(BOOST_LIKELY(mask!=0)){
auto n=unchecked_countr_zero(mask); 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)...); construct_element(p,std::forward<Args>(args)...);
pg->set(n,hash); pg->set(n,hash);
return {pg,n,p}; return {pg,n,p};

View File

@ -9,26 +9,30 @@
#ifndef BOOST_UNORDERED_DETAIL_FOA_ELEMENT_TYPE_HPP #ifndef BOOST_UNORDERED_DETAIL_FOA_ELEMENT_TYPE_HPP
#define BOOST_UNORDERED_DETAIL_FOA_ELEMENT_TYPE_HPP #define BOOST_UNORDERED_DETAIL_FOA_ELEMENT_TYPE_HPP
#include <boost/core/pointer_traits.hpp>
namespace boost{ namespace boost{
namespace unordered{ namespace unordered{
namespace detail{ namespace detail{
namespace foa{ namespace foa{
template<class T> template<class T,class VoidPtr>
struct element_type struct element_type
{ {
using value_type=T; 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 * we use a deleted copy constructor here so the type is no longer
* trivially copy-constructible which inhibits our memcpy * trivially copy-constructible which inhibits our memcpy
* optimizations when copying the tables * optimizations when copying the tables
*/ */
element_type() = default; element_type()=default;
element_type(value_type* p_):p(p_){} element_type(pointer p_):p(p_){}
element_type(element_type const&) = delete; element_type(element_type const&)=delete;
element_type(element_type&& rhs) noexcept element_type(element_type&& rhs)noexcept
{ {
p = rhs.p; p = rhs.p;
rhs.p = nullptr; rhs.p = nullptr;

View File

@ -13,7 +13,7 @@ namespace boost {
namespace unordered { namespace unordered {
namespace detail { namespace detail {
namespace foa { 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 key_type = Key;
using mapped_type = T; using mapped_type = T;
@ -24,7 +24,7 @@ namespace boost {
using value_type = std::pair<Key const, T>; using value_type = std::pair<Key const, T>;
using moved_type = std::pair<raw_key_type&&, raw_mapped_type&&>; 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) static value_type& value_from(element_type const& x)
{ {
@ -83,18 +83,15 @@ namespace boost {
template <class A, class... Args> template <class A, class... Args>
static void construct(A& al, element_type* p, Args&&... 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_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_CATCH(...)
{ {
using pointer_type = typename boost::allocator_pointer<A>::type; boost::allocator_deallocate(al, p->p, 1);
using pointer_traits = boost::pointer_traits<pointer_type>;
boost::allocator_deallocate(
al, pointer_traits::pointer_to(*(p->p)), 1);
BOOST_RETHROW BOOST_RETHROW
} }
BOOST_CATCH_END BOOST_CATCH_END
@ -114,12 +111,8 @@ namespace boost {
static void destroy(A& al, element_type* p) noexcept static void destroy(A& al, element_type* p) noexcept
{ {
if (p->p) { if (p->p) {
using pointer_type = typename boost::allocator_pointer<A>::type; destroy(al, boost::to_address(p->p));
using pointer_traits = boost::pointer_traits<pointer_type>; boost::allocator_deallocate(al, p->p, 1);
destroy(al, p->p);
boost::allocator_deallocate(
al, pointer_traits::pointer_to(*(p->p)), 1);
} }
} }
}; };

View File

@ -14,7 +14,7 @@ namespace boost {
namespace detail { namespace detail {
namespace foa { namespace foa {
template <class Key> struct node_set_types template <class Key, class VoidPtr> struct node_set_types
{ {
using key_type = Key; using key_type = Key;
using init_type = Key; using init_type = Key;
@ -22,7 +22,7 @@ namespace boost {
static Key const& extract(value_type const& key) { return key; } 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 value_type& value_from(element_type const& x) { return *x.p; }
static Key const& extract(element_type const& k) { return *k.p; } static Key const& extract(element_type const& k) { return *k.p; }
@ -53,17 +53,15 @@ namespace boost {
template <class A, class... Args> template <class A, class... Args>
static void construct(A& al, element_type* p, Args&&... 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_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_CATCH(...)
{ {
boost::allocator_deallocate(al, boost::allocator_deallocate(al, p->p, 1);
boost::pointer_traits<typename boost::allocator_pointer<
A>::type>::pointer_to(*p->p),
1);
BOOST_RETHROW BOOST_RETHROW
} }
BOOST_CATCH_END BOOST_CATCH_END
@ -78,11 +76,8 @@ namespace boost {
static void destroy(A& al, element_type* p) noexcept static void destroy(A& al, element_type* p) noexcept
{ {
if (p->p) { if (p->p) {
destroy(al, p->p); destroy(al, boost::to_address(p->p));
boost::allocator_deallocate(al, boost::allocator_deallocate(al, p->p, 1);
boost::pointer_traits<typename boost::allocator_pointer<
A>::type>::pointer_to(*(p->p)),
1);
} }
} }
}; };

View File

@ -81,12 +81,17 @@ class table;
/* internal conversion from const_iterator to iterator */ /* internal conversion from const_iterator to iterator */
struct const_iterator_cast_tag{}; struct const_iterator_cast_tag{};
template<typename TypePolicy,typename Group,bool Const> template<typename TypePolicy,typename GroupPtr,bool Const>
class table_iterator class table_iterator
{ {
using group_pointer_traits=boost::pointer_traits<GroupPtr>;
using type_policy=TypePolicy; using type_policy=TypePolicy;
using table_element_type=typename type_policy::element_type; 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 N=group_type::N;
static constexpr auto regular_layout=group_type::regular_layout; static constexpr auto regular_layout=group_type::regular_layout;
@ -103,22 +108,22 @@ public:
table_iterator()=default; table_iterator()=default;
template<bool Const2,typename std::enable_if<!Const2>::type* =nullptr> template<bool Const2,typename std::enable_if<!Const2>::type* =nullptr>
table_iterator(const table_iterator<TypePolicy,Group,Const2>& x): table_iterator(const table_iterator<TypePolicy,GroupPtr,Const2>& x):
pc{x.pc},p{x.p}{} pc_{x.pc_},p_{x.p_}{}
table_iterator( table_iterator(
const_iterator_cast_tag, const table_iterator<TypePolicy,Group,true>& x): const_iterator_cast_tag, const table_iterator<TypePolicy,GroupPtr,true>& x):
pc{x.pc},p{x.p}{} pc_{x.pc_},p_{x.p_}{}
inline reference operator*()const noexcept inline reference operator*()const noexcept
{return type_policy::value_from(*p);} {return type_policy::value_from(*p());}
inline pointer operator->()const noexcept 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++()noexcept{increment();return *this;}
inline table_iterator operator++(int)noexcept inline table_iterator operator++(int)noexcept
{auto x=*this;increment();return x;} {auto x=*this;increment();return x;}
friend inline bool operator==( friend inline bool operator==(
const table_iterator& x,const table_iterator& y) const table_iterator& x,const table_iterator& y)
{return x.p==y.p;} {return x.p()==y.p();}
friend inline bool operator!=( friend inline bool operator!=(
const table_iterator& x,const table_iterator& y) const table_iterator& x,const table_iterator& y)
{return !(x==y);} {return !(x==y);}
@ -128,81 +133,91 @@ private:
template<typename> friend class table_erase_return_type; template<typename> friend class table_erase_return_type;
template<typename,typename,typename,typename> friend class table; template<typename,typename,typename,typename> friend class table;
table_iterator(Group* pg,std::size_t n,const table_element_type* p_): table_iterator(group_type* pg,std::size_t n,const table_element_type* p):
pc{reinterpret_cast<unsigned char*>(const_cast<group_type*>(pg))+n}, pc_{to_pointer<char_pointer>(
p{const_cast<table_element_type*>(p_)} 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 inline void increment()noexcept
{ {
BOOST_ASSERT(p!=nullptr); BOOST_ASSERT(p()!=nullptr);
increment(std::integral_constant<bool,regular_layout>{}); increment(std::integral_constant<bool,regular_layout>{});
} }
inline void increment(std::true_type /* regular layout */)noexcept inline void increment(std::true_type /* regular layout */)noexcept
{ {
using diff_type=
typename boost::pointer_traits<char_pointer>::difference_type;
for(;;){ for(;;){
++p; ++p_;
if(reinterpret_cast<uintptr_t>(pc)%sizeof(group_type)==N-1){ if(reinterpret_cast<uintptr_t>(pc())%sizeof(group_type)==N-1){
pc+=sizeof(group_type)-(N-1); pc_+=static_cast<diff_type>(sizeof(group_type)-(N-1));
break; break;
} }
++pc; ++pc_;
if(!group_type::is_occupied(pc))continue; if(!group_type::is_occupied(pc()))continue;
if(BOOST_UNLIKELY(group_type::is_sentinel(pc)))p=nullptr; if(BOOST_UNLIKELY(group_type::is_sentinel(pc())))p_=nullptr;
return; return;
} }
for(;;){ for(;;){
int mask=reinterpret_cast<group_type*>(pc)->match_occupied(); int mask=reinterpret_cast<group_type*>(pc())->match_occupied();
if(mask!=0){ if(mask!=0){
auto n=unchecked_countr_zero(mask); auto n=unchecked_countr_zero(mask);
if(BOOST_UNLIKELY(reinterpret_cast<group_type*>(pc)->is_sentinel(n))){ if(BOOST_UNLIKELY(reinterpret_cast<group_type*>(pc())->is_sentinel(n))){
p=nullptr; p_=nullptr;
} }
else{ else{
pc+=n; pc_+=static_cast<diff_type>(n);
p+=n; p_+=static_cast<diff_type>(n);
} }
return; return;
} }
pc+=sizeof(group_type); pc_+=static_cast<diff_type>(sizeof(group_type));
p+=N; p_+=static_cast<diff_type>(N);
} }
} }
inline void increment(std::false_type /* interleaved */)noexcept inline void increment(std::false_type /* interleaved */)noexcept
{ {
std::size_t n0=reinterpret_cast<uintptr_t>(pc)%sizeof(group_type); using diff_type=
pc-=n0; 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=( 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){ if(!mask){
do{ do{
pc+=sizeof(group_type); pc_+=sizeof(group_type);
p+=N; 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); auto n=unchecked_countr_zero(mask);
if(BOOST_UNLIKELY(reinterpret_cast<group_type*>(pc)->is_sentinel(n))){ if(BOOST_UNLIKELY(reinterpret_cast<group_type*>(pc())->is_sentinel(n))){
p=nullptr; p_=nullptr;
} }
else{ else{
pc+=n; pc_+=static_cast<diff_type>(n);
p-=n0; p_-=static_cast<diff_type>(n0);
p+=n; p_+=static_cast<diff_type>(n);
} }
} }
template<typename Archive> template<typename Archive>
friend void serialization_track(Archive& ar,const table_iterator& x) friend void serialization_track(Archive& ar,const table_iterator& x)
{ {
if(x.p){ if(x.p()){
track_address(ar,x.pc); track_address(ar,x.pc_);
track_address(ar,x.p); track_address(ar,x.p_);
} }
} }
@ -211,13 +226,13 @@ private:
template<typename Archive> template<typename Archive>
void serialize(Archive& ar,unsigned int) void serialize(Archive& ar,unsigned int)
{ {
if(!p)pc=nullptr; if(!p())pc_=nullptr;
serialize_tracked_address(ar,pc); serialize_tracked_address(ar,pc_);
serialize_tracked_address(ar,p); serialize_tracked_address(ar,p_);
} }
unsigned char *pc=nullptr; char_pointer pc_=nullptr;
table_element_type *p=nullptr; table_element_pointer p_=nullptr;
}; };
/* Returned by table::erase([const_]iterator) to avoid iterator increment /* Returned by table::erase([const_]iterator) to avoid iterator increment
@ -227,11 +242,11 @@ private:
template<typename Iterator> template<typename Iterator>
class table_erase_return_type; class table_erase_return_type;
template<typename TypePolicy,typename Group,bool Const> template<typename TypePolicy,typename GroupPtr,bool Const>
class table_erase_return_type<table_iterator<TypePolicy,Group,Const>> class table_erase_return_type<table_iterator<TypePolicy,GroupPtr,Const>>
{ {
using iterator=table_iterator<TypePolicy,Group,Const>; using iterator=table_iterator<TypePolicy,GroupPtr,Const>;
using const_iterator=table_iterator<TypePolicy,Group,true>; using const_iterator=table_iterator<TypePolicy,GroupPtr,true>;
public: public:
/* can't delete it because VS in pre-C++17 mode needs to see it for RVO */ /* 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 locator=typename super::locator;
using compatible_concurrent_table= using compatible_concurrent_table=
concurrent_table<TypePolicy,Hash,Pred,Allocator>; 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; friend compatible_concurrent_table;
public: public:
@ -324,7 +342,6 @@ public:
private: private:
static constexpr bool has_mutable_iterator= static constexpr bool has_mutable_iterator=
!std::is_same<key_type,value_type>::value; !std::is_same<key_type,value_type>::value;
public: public:
using hasher=typename super::hasher; using hasher=typename super::hasher;
using key_equal=typename super::key_equal; using key_equal=typename super::key_equal;
@ -335,10 +352,10 @@ public:
using const_reference=typename super::const_reference; using const_reference=typename super::const_reference;
using size_type=typename super::size_type; using size_type=typename super::size_type;
using difference_type=typename super::difference_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< using iterator=typename std::conditional<
has_mutable_iterator, has_mutable_iterator,
table_iterator<type_policy,group_type,false>, table_iterator<type_policy,group_type_pointer,false>,
const_iterator>::type; const_iterator>::type;
using erase_return_type=table_erase_return_type<iterator>; using erase_return_type=table_erase_return_type<iterator>;
@ -363,9 +380,9 @@ public:
iterator begin()noexcept iterator begin()noexcept
{ {
iterator it{this->arrays.groups,0,this->arrays.elements}; iterator it{this->arrays.groups(),0,this->arrays.elements()};
if(this->arrays.elements&& if(this->arrays.elements()&&
!(this->arrays.groups[0].match_occupied()&0x1))++it; !(this->arrays.groups()[0].match_occupied()&0x1))++it;
return it; return it;
} }
@ -431,7 +448,7 @@ public:
BOOST_FORCEINLINE BOOST_FORCEINLINE
erase_return_type erase(const_iterator pos)noexcept erase_return_type erase(const_iterator pos)noexcept
{ {
super::erase(pos.pc,pos.p); super::erase(pos.pc(),pos.p());
return {pos}; return {pos};
} }
@ -462,7 +479,7 @@ public:
BOOST_ASSERT(pos!=end()); BOOST_ASSERT(pos!=end());
erase_on_exit e{*this,pos}; erase_on_exit e{*this,pos};
(void)e; (void)e;
return std::move(*pos.p); return std::move(*pos.p());
} }
// TODO: should we accept different allocator too? // TODO: should we accept different allocator too?
@ -527,22 +544,32 @@ public:
friend bool operator!=(const table& x,const table& y){return !(x==y);} friend bool operator!=(const table& x,const table& y){return !(x==y);}
private: private:
template<typename ExclusiveLockGuard> template<typename ArraysType>
table(compatible_concurrent_table&& x,ExclusiveLockGuard): table(compatible_concurrent_table&& x,arrays_holder<ArraysType,Allocator>&& ah):
super{ super{
std::move(x.h()),std::move(x.pred()),std::move(x.al()), 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, x.arrays.groups_size_index,x.arrays.groups_size_mask,
reinterpret_cast<group_type*>(x.arrays.groups), to_pointer<group_type_pointer>(
reinterpret_cast<value_type*>(x.arrays.elements)}, reinterpret_cast<group_type*>(x.arrays.groups())),
size_ctrl_type{ x.arrays.elements_};},
x.size_ctrl.ml,x.size_ctrl.size}} size_ctrl_type{x.size_ctrl.ml,x.size_ctrl.size}}
{ {
compatible_concurrent_table::arrays_type::delete_group_access( ah.release();
this->al(),x.arrays);
x.empty_initialize(); 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 struct erase_on_exit
{ {
erase_on_exit(table& x_,const_iterator it_):x{x_},it{it_}{} erase_on_exit(table& x_,const_iterator it_):x{x_},it{it_}{}

View File

@ -141,9 +141,7 @@ namespace boost {
} }
unordered_flat_map(unordered_flat_map&& other) unordered_flat_map(unordered_flat_map&& other)
noexcept(std::is_nothrow_move_constructible<hasher>::value&& noexcept(std::is_nothrow_move_constructible<table_type>::value)
std::is_nothrow_move_constructible<key_equal>::value&&
std::is_nothrow_move_constructible<allocator_type>::value)
: table_(std::move(other.table_)) : table_(std::move(other.table_))
{ {
} }
@ -697,10 +695,9 @@ namespace boost {
return erase_if(map.table_, pred); return erase_if(map.table_, pred);
} }
template <class Archive, template <class Archive, class Key, class T, class Hash, class KeyEqual,
class Key, class T, class Hash, class KeyEqual, class Allocator> class Allocator>
void serialize( void serialize(Archive& ar,
Archive & ar,
unordered_flat_map<Key, T, Hash, KeyEqual, Allocator>& map, unordered_flat_map<Key, T, Hash, KeyEqual, Allocator>& map,
unsigned int version) unsigned int version)
{ {

View File

@ -133,9 +133,7 @@ namespace boost {
} }
unordered_flat_set(unordered_flat_set&& other) unordered_flat_set(unordered_flat_set&& other)
noexcept(std::is_nothrow_move_constructible<hasher>::value&& noexcept(std::is_nothrow_move_constructible<table_type>::value)
std::is_nothrow_move_constructible<key_equal>::value&&
std::is_nothrow_move_constructible<allocator_type>::value)
: table_(std::move(other.table_)) : table_(std::move(other.table_))
{ {
} }
@ -506,10 +504,9 @@ namespace boost {
return erase_if(set.table_, pred); return erase_if(set.table_, pred);
} }
template <class Archive, template <class Archive, class Key, class Hash, class KeyEqual,
class Key, class Hash, class KeyEqual, class Allocator> class Allocator>
void serialize( void serialize(Archive& ar,
Archive & ar,
unordered_flat_set<Key, Hash, KeyEqual, Allocator>& set, unordered_flat_set<Key, Hash, KeyEqual, Allocator>& set,
unsigned int version) unsigned int version)
{ {

View File

@ -75,7 +75,8 @@ namespace boost {
template <class Key, class T, class Hash, class KeyEqual, class Allocator> template <class Key, class T, class Hash, class KeyEqual, class Allocator>
class unordered_node_map 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, using table_type = detail::foa::table<map_types, Hash, KeyEqual,
typename boost::allocator_rebind<Allocator, typename boost::allocator_rebind<Allocator,
@ -179,9 +180,7 @@ namespace boost {
} }
unordered_node_map(unordered_node_map&& other) unordered_node_map(unordered_node_map&& other)
noexcept(std::is_nothrow_move_constructible<hasher>::value&& noexcept(std::is_nothrow_move_constructible<table_type>::value)
std::is_nothrow_move_constructible<key_equal>::value&&
std::is_nothrow_move_constructible<allocator_type>::value)
: table_(std::move(other.table_)) : table_(std::move(other.table_))
{ {
} }
@ -789,10 +788,9 @@ namespace boost {
return erase_if(map.table_, pred); return erase_if(map.table_, pred);
} }
template <class Archive, template <class Archive, class Key, class T, class Hash, class KeyEqual,
class Key, class T, class Hash, class KeyEqual, class Allocator> class Allocator>
void serialize( void serialize(Archive& ar,
Archive & ar,
unordered_node_map<Key, T, Hash, KeyEqual, Allocator>& map, unordered_node_map<Key, T, Hash, KeyEqual, Allocator>& map,
unsigned int version) unsigned int version)
{ {

View File

@ -66,7 +66,8 @@ namespace boost {
template <class Key, class Hash, class KeyEqual, class Allocator> template <class Key, class Hash, class KeyEqual, class Allocator>
class unordered_node_set 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, using table_type = detail::foa::table<set_types, Hash, KeyEqual,
typename boost::allocator_rebind<Allocator, typename boost::allocator_rebind<Allocator,
@ -169,9 +170,7 @@ namespace boost {
} }
unordered_node_set(unordered_node_set&& other) unordered_node_set(unordered_node_set&& other)
noexcept(std::is_nothrow_move_constructible<hasher>::value&& noexcept(std::is_nothrow_move_constructible<table_type>::value)
std::is_nothrow_move_constructible<key_equal>::value&&
std::is_nothrow_move_constructible<allocator_type>::value)
: table_(std::move(other.table_)) : table_(std::move(other.table_))
{ {
} }
@ -602,10 +601,9 @@ namespace boost {
return erase_if(set.table_, pred); return erase_if(set.table_, pred);
} }
template <class Archive, template <class Archive, class Key, class Hash, class KeyEqual,
class Key, class Hash, class KeyEqual, class Allocator> class Allocator>
void serialize( void serialize(Archive& ar,
Archive & ar,
unordered_node_set<Key, Hash, KeyEqual, Allocator>& set, unordered_node_set<Key, Hash, KeyEqual, Allocator>& set,
unsigned int version) unsigned int version)
{ {

View File

@ -5,6 +5,7 @@
# file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) # file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
import testing ; import testing ;
import ../../config/checks/config : requires ;
# Adding -Wundef is blocked on (at least) # Adding -Wundef is blocked on (at least)
# https://github.com/boostorg/type_traits/issues/165 # https://github.com/boostorg/type_traits/issues/165
@ -15,6 +16,20 @@ local msvc-flags = /wd4494 ;
project project
: requirements : 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 <warnings>pedantic
<toolset>intel:<warnings>on <toolset>intel:<warnings>on
@ -33,63 +48,76 @@ project
<toolset>msvc:<warnings-as-errors>on <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 : . ; 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) : $(BOOST_UNORDERED_TEST_DIR)
: :
: <define>BOOST_UNORDERED_ENABLE_SERIALIZATION_COMPATIBILITY_V0 : <define>BOOST_UNORDERED_ENABLE_SERIALIZATION_COMPATIBILITY_V0
<warnings>off # Boost.Serialization headers are not warning-free <warnings>off # Boost.Serialization headers are not warning-free
<undefined-sanitizer>norecover:<build>no # boost::archive::xml_oarchive does not pass UBSAN <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 <toolset>clang:<optimization>space
<library>/boost//serialization/<warnings>off ; <library>/boost//serialization/<warnings>off ;
run exception/constructor_exception_tests.cpp ; compile-fail unordered/insert_node_type_fail.cpp : <define>UNORDERED_TEST_MAP : insert_node_type_fail_map ;
run exception/copy_exception_tests.cpp ; compile-fail unordered/insert_node_type_fail.cpp : <define>UNORDERED_TEST_MULTIMAP : insert_node_type_fail_multimap ;
run exception/assign_exception_tests.cpp ; compile-fail unordered/insert_node_type_fail.cpp : <define>UNORDERED_TEST_SET : insert_node_type_fail_set ;
run exception/move_assign_exception_tests.cpp ; compile-fail unordered/insert_node_type_fail.cpp : <define>UNORDERED_TEST_MULTISET : insert_node_type_fail_multiset ;
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 ;
run unordered/narrow_cast_tests.cpp ; local FCA_EXCEPTION_TESTS =
run quick.cpp ; 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 = local FOA_TESTS =
fwd_set_test fwd_set_test
@ -154,21 +208,21 @@ local FOA_TESTS =
extract_tests extract_tests
node_handle_tests node_handle_tests
uses_allocator uses_allocator
hash_is_avalanching_test
; ;
for local test in $(FOA_TESTS) 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/link_test_1.cpp unordered/link_test_2.cpp : : : <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/scoped_allocator.cpp : : : <toolset>msvc-14.0:<build>no <define>BOOST_UNORDERED_FOA_TESTS : foa_scoped_allocator ;
run unordered/hash_is_avalanching_test.cpp ;
run unordered/serialization_tests.cpp 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 <warnings>off # Boost.Serialization headers are not warning-free
<undefined-sanitizer>norecover:<build>no # boost::archive::xml_oarchive does not pass UBSAN <undefined-sanitizer>norecover:<build>no # boost::archive::xml_oarchive does not pass UBSAN
<toolset>msvc:<cxxflags>/bigobj <toolset>msvc:<cxxflags>/bigobj
@ -179,31 +233,53 @@ run unordered/serialization_tests.cpp
<library>/boost//serialization/<warnings>off <library>/boost//serialization/<warnings>off
: foa_serialization_tests ; : foa_serialization_tests ;
run exception/constructor_exception_tests.cpp : : : $(CPP11) <define>BOOST_UNORDERED_FOA_TESTS : foa_constructor_exception_tests ; local FOA_EXCEPTION_TESTS =
run exception/copy_exception_tests.cpp : : : $(CPP11) <define>BOOST_UNORDERED_FOA_TESTS : foa_copy_exception_tests ; constructor_exception_tests
run exception/assign_exception_tests.cpp : : : $(CPP11) <define>BOOST_UNORDERED_FOA_TESTS : foa_assign_exception_tests ; copy_exception_tests
run exception/move_assign_exception_tests.cpp : : : $(CPP11) <define>BOOST_UNORDERED_FOA_TESTS : foa_move_assign_exception_tests ; assign_exception_tests
run exception/insert_exception_tests.cpp : : : $(CPP11) <define>BOOST_UNORDERED_FOA_TESTS : foa_insert_exception_tests ; move_assign_exception_tests
run exception/erase_exception_tests.cpp : : : $(CPP11) <define>BOOST_UNORDERED_FOA_TESTS : foa_erase_exception_tests ; insert_exception_tests
run exception/rehash_exception_tests.cpp : : : $(CPP11) <define>BOOST_UNORDERED_FOA_TESTS : foa_rehash_exception_tests ; erase_exception_tests
run exception/swap_exception_tests.cpp : : : $(CPP11) <define>BOOST_UNORDERED_FOA_TESTS : foa_swap_exception_tests ; rehash_exception_tests
run exception/merge_exception_tests.cpp : : : $(CPP11) <define>BOOST_UNORDERED_FOA_TESTS : foa_merge_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 : alias foa_tests :
foa_$(FOA_TESTS) foa_$(FOA_TESTS)
foa_$(FOA_EXCEPTION_TESTS)
foa_link_test foa_link_test
foa_scoped_allocator foa_scoped_allocator
hash_is_avalanching_test
foa_serialization_tests foa_serialization_tests
foa_constructor_exception_tests foa_mmap_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
; ;
local CFOA_TESTS = local CFOA_TESTS =
@ -239,14 +315,14 @@ local CFOA_TESTS =
for local test in $(CFOA_TESTS) for local test in $(CFOA_TESTS)
{ {
run cfoa/$(test).cpp run cfoa/$(test).cpp
: requirements $(CPP11) <threading>multi : requirements <threading>multi
: target-name cfoa_$(test) : target-name cfoa_$(test)
; ;
} }
run cfoa/serialization_tests.cpp run cfoa/serialization_tests.cpp
: :
: :
: $(CPP11) <threading>multi : $(CPP11) <threading>multi
<warnings>off # Boost.Serialization headers are not warning-free <warnings>off # Boost.Serialization headers are not warning-free
<undefined-sanitizer>norecover:<build>no # boost::archive::xml_oarchive does not pass UBSAN <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 <library>/boost//serialization/<warnings>off
: cfoa_serialization_tests ; : cfoa_serialization_tests ;
alias cfoa_tests : alias cfoa_tests :
cfoa_$(CFOA_TESTS) cfoa_$(CFOA_TESTS)
cfoa_serialization_tests ; cfoa_serialization_tests ;

View File

@ -31,7 +31,7 @@ using test::sequential;
using hasher = stateful_hash; using hasher = stateful_hash;
using key_equal = stateful_key_equal; 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, using flat_map_type = boost::unordered::unordered_flat_map<raii, raii, hasher,
key_equal, allocator_type>; key_equal, allocator_type>;
@ -847,8 +847,12 @@ namespace {
check_raii_counts(); 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 values = make_random_values(1024 * 16, [&] { return gen(rg); });
auto reference_map = auto reference_map =
boost::unordered_flat_map<raii, raii>(values.begin(), values.end()); boost::unordered_flat_map<raii, raii>(values.begin(), values.end());
@ -864,10 +868,10 @@ namespace {
{ {
raii::reset_counts(); raii::reset_counts();
flat_map_type flat_map(values.begin(), values.end(), values.size(), FlatMapType flat_map(values.begin(), values.end(), values.size(),
hasher(1), key_equal(2), allocator_type(3)); 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()); BOOST_TEST(flat_map.get_allocator() == map.get_allocator());
@ -893,10 +897,10 @@ namespace {
{ {
raii::reset_counts(); raii::reset_counts();
map_type map(values.begin(), values.end(), values.size(), hasher(1), MapType map(values.begin(), values.end(), values.size(), hasher(1),
key_equal(2), allocator_type(3)); 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()); BOOST_TEST(flat_map.get_allocator() == map.get_allocator());
@ -920,10 +924,10 @@ namespace {
{ {
raii::reset_counts(); raii::reset_counts();
flat_map_type flat_map(values.begin(), values.end(), values.size(), FlatMapType flat_map(values.begin(), values.end(), values.size(),
hasher(1), key_equal(2), allocator_type(3)); 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()); BOOST_TEST(flat_map.get_allocator() != map.get_allocator());
@ -950,10 +954,10 @@ namespace {
{ {
raii::reset_counts(); raii::reset_counts();
map_type map(values.begin(), values.end(), values.size(), hasher(1), MapType map(values.begin(), values.end(), values.size(), hasher(1),
key_equal(2), allocator_type(3)); 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()); BOOST_TEST(flat_map.get_allocator() != map.get_allocator());
@ -994,8 +998,27 @@ UNORDERED_TEST(
((init_type_generator)) ((init_type_generator))
((default_generator)(sequential)(limited_range))) ((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( UNORDERED_TEST(
flat_map_move_assign, 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)) ((init_type_generator))
((default_generator)(sequential)(limited_range))) ((default_generator)(sequential)(limited_range)))
// clang-format on // clang-format on

View File

@ -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 template <class T> struct stateful_allocator
{ {
int x_ = -1; int x_ = -1;
@ -151,6 +172,36 @@ template <class T> struct stateful_allocator
bool operator!=(stateful_allocator const& rhs) const { return x_ != rhs.x_; } 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 struct raii
{ {
static std::atomic<std::uint32_t> default_constructor; static std::atomic<std::uint32_t> default_constructor;
@ -458,6 +509,7 @@ template <class T> class ptr
public: public:
ptr() : ptr_(0) {} ptr() : ptr_(0) {}
ptr(std::nullptr_t) : ptr_(nullptr) {}
explicit ptr(void_ptr const& x) : ptr_((T*)x.ptr_) {} explicit ptr(void_ptr const& x) : ptr_((T*)x.ptr_) {}
T& operator*() const { return *ptr_; } T& operator*() const { return *ptr_; }

View File

@ -307,6 +307,7 @@ namespace test {
public: public:
ptr() : ptr_(0) {} ptr() : ptr_(0) {}
ptr(std::nullptr_t) : ptr_(0) {}
explicit ptr(void_ptr const& x) : ptr_((T*)x.ptr_) {} explicit ptr(void_ptr const& x) : ptr_((T*)x.ptr_) {}
T& operator*() const { return *ptr_; } T& operator*() const { return *ptr_; }
@ -325,6 +326,18 @@ namespace test {
ptr operator+(std::ptrdiff_t s) const { return ptr<T>(ptr_ + s); } 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_); } 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_; } std::ptrdiff_t operator-(ptr p) const { return ptr_ - p.ptr_; }
ptr operator-(std::ptrdiff_t s) const { return ptr(ptr_ - s); } ptr operator-(std::ptrdiff_t s) const { return ptr(ptr_ - s); }
T& operator[](std::ptrdiff_t s) const { return 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!=(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_; } 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; typedef ::test::minimal::ptr<U> type;
}; };
template<class U>
using rebind=typename rebind_to<U>::type;
}; };
} }

View File

@ -16,6 +16,9 @@
#include <boost/limits.hpp> #include <boost/limits.hpp>
#include <cstddef> #include <cstddef>
template <class T> struct allocator1;
template <class T> struct allocator2;
namespace test { namespace test {
// Note that the default hash function will work for any equal_to (but not // Note that the default hash function will work for any equal_to (but not
// very well). // very well).
@ -506,14 +509,18 @@ namespace test {
template <class T> class ptr template <class T> class ptr
{ {
friend struct ::allocator1<T>;
friend struct ::allocator2<T>;
friend class allocator2<T>; friend class allocator2<T>;
friend class const_ptr<T>; friend class const_ptr<T>;
friend struct void_ptr; friend struct void_ptr;
public:
T* ptr_; T* ptr_;
ptr(T* x) : ptr_(x) {} ptr(T* x) : ptr_(x) {}
public:
ptr() : ptr_(0) {} ptr() : ptr_(0) {}
ptr(std::nullptr_t) : ptr_(nullptr) {}
explicit ptr(void_ptr const& x) : ptr_((T*)x.ptr_) {} explicit ptr(void_ptr const& x) : ptr_((T*)x.ptr_) {}
T& operator*() const { return *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) 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; }
ptr& operator-=(std::ptrdiff_t s) { ptr_ -= s; return *this; }
T& operator[](std::ptrdiff_t s) const { return ptr_[s]; } T& operator[](std::ptrdiff_t s) const { return ptr_[s]; }
bool operator!() const { return !ptr_; } bool operator!() const { return !ptr_; }
@ -727,6 +735,9 @@ namespace boost {
{ {
typedef ::test::ptr<U> type; typedef ::test::ptr<U> type;
}; };
template<class U>
using rebind=typename rebind_to<U>::type;
}; };
} // namespace boost } // namespace boost

View File

@ -41,7 +41,6 @@ namespace assign_tests {
BOOST_TEST(x.empty()); BOOST_TEST(x.empty());
BOOST_TEST(test::equivalent(x.hash_function(), hf)); BOOST_TEST(test::equivalent(x.hash_function(), hf));
BOOST_TEST(test::equivalent(x.key_eq(), eq)); BOOST_TEST(test::equivalent(x.key_eq(), eq));
BOOST_TEST(test::detail::tracker.count_allocations == 0);
} }
BOOST_LIGHTWEIGHT_TEST_OSTREAM << "assign_tests1.2\n"; 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(x1.key_eq(), eq1));
BOOST_TEST(test::equivalent(x2.hash_function(), hf1)); BOOST_TEST(test::equivalent(x2.hash_function(), hf1));
BOOST_TEST(test::equivalent(x2.key_eq(), eq1)); BOOST_TEST(test::equivalent(x2.key_eq(), eq1));
BOOST_TEST(test::detail::tracker.count_allocations == 0);
test::check_container(x1, x2); test::check_container(x1, x2);
} }

View File

@ -6,13 +6,13 @@
#include "../helpers/unordered.hpp" #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/equivalent.hpp"
#include "../helpers/input_iterator.hpp" #include "../helpers/input_iterator.hpp"
#include "../helpers/invariants.hpp" #include "../helpers/invariants.hpp"
#include "../helpers/random_values.hpp"
#include "../helpers/test.hpp"
#include "../helpers/tracker.hpp"
#include "../objects/test.hpp"
#include <vector> #include <vector>
@ -33,7 +33,6 @@ namespace constructor_tests {
T x(0, hf, eq); T x(0, hf, eq);
BOOST_TEST(x.empty()); BOOST_TEST(x.empty());
BOOST_TEST_EQ(x.bucket_count(), 0u);
BOOST_TEST(test::equivalent(x.hash_function(), hf)); BOOST_TEST(test::equivalent(x.hash_function(), hf));
BOOST_TEST(test::equivalent(x.key_eq(), eq)); BOOST_TEST(test::equivalent(x.key_eq(), eq));
BOOST_TEST(test::equivalent(x.get_allocator(), al)); BOOST_TEST(test::equivalent(x.get_allocator(), al));
@ -72,7 +71,6 @@ namespace constructor_tests {
T x; T x;
BOOST_TEST(x.empty()); BOOST_TEST(x.empty());
BOOST_TEST_EQ(x.bucket_count(), 0u);
BOOST_TEST(test::equivalent(x.hash_function(), hf)); BOOST_TEST(test::equivalent(x.hash_function(), hf));
BOOST_TEST(test::equivalent(x.key_eq(), eq)); BOOST_TEST(test::equivalent(x.key_eq(), eq));
BOOST_TEST(test::equivalent(x.get_allocator(), al)); BOOST_TEST(test::equivalent(x.get_allocator(), al));
@ -140,7 +138,6 @@ namespace constructor_tests {
T x(0, hf, eq, al); T x(0, hf, eq, al);
BOOST_TEST(x.empty()); BOOST_TEST(x.empty());
BOOST_TEST_EQ(x.bucket_count(), 0u);
BOOST_TEST(test::equivalent(x.hash_function(), hf)); BOOST_TEST(test::equivalent(x.hash_function(), hf));
BOOST_TEST(test::equivalent(x.key_eq(), eq)); BOOST_TEST(test::equivalent(x.key_eq(), eq));
BOOST_TEST(test::equivalent(x.get_allocator(), al)); BOOST_TEST(test::equivalent(x.get_allocator(), al));
@ -167,7 +164,6 @@ namespace constructor_tests {
T x(al); T x(al);
BOOST_TEST(x.empty()); BOOST_TEST(x.empty());
BOOST_TEST_EQ(x.bucket_count(), 0u);
BOOST_TEST(test::equivalent(x.hash_function(), hf)); BOOST_TEST(test::equivalent(x.hash_function(), hf));
BOOST_TEST(test::equivalent(x.key_eq(), eq)); BOOST_TEST(test::equivalent(x.key_eq(), eq));
BOOST_TEST(test::equivalent(x.get_allocator(), al)); BOOST_TEST(test::equivalent(x.get_allocator(), al));
@ -349,7 +345,6 @@ namespace constructor_tests {
{ {
T x(list); T x(list);
BOOST_TEST(x.empty()); BOOST_TEST(x.empty());
BOOST_TEST_EQ(x.bucket_count(), 0u);
BOOST_TEST(test::equivalent(x.hash_function(), hf)); BOOST_TEST(test::equivalent(x.hash_function(), hf));
BOOST_TEST(test::equivalent(x.key_eq(), eq)); BOOST_TEST(test::equivalent(x.key_eq(), eq));
BOOST_TEST(test::equivalent(x.get_allocator(), al)); BOOST_TEST(test::equivalent(x.get_allocator(), al));
@ -536,6 +531,16 @@ namespace constructor_tests {
template <class T> template <class T>
void no_alloc_default_construct_test(T*, test::random_generator) 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") UNORDERED_SUB_TEST("Construct 1")
{ {
T x; T x;
@ -653,6 +658,15 @@ namespace constructor_tests {
boost::unordered_flat_map<test::object, test::object, test::hash, boost::unordered_flat_map<test::object, test::object, test::hash,
test::equal_to, std::allocator<test::object> >* test_map_std_alloc; 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, boost::unordered_flat_set<test::object, test::hash, test::equal_to,
test::allocator1<test::object> >* test_set; test::allocator1<test::object> >* test_set;
boost::unordered_node_set<test::object, test::hash, test::equal_to, boost::unordered_node_set<test::object, test::hash, test::equal_to,
@ -675,7 +689,7 @@ namespace constructor_tests {
(default_generator)(generate_collisions)(limited_range))) (default_generator)(generate_collisions)(limited_range)))
UNORDERED_TEST(no_alloc_default_construct_test, 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))) (default_generator)(generate_collisions)(limited_range)))
#else #else
boost::unordered_map<test::object, test::object, test::hash, test::equal_to, boost::unordered_map<test::object, test::object, test::hash, test::equal_to,
@ -740,6 +754,6 @@ namespace constructor_tests {
} }
#endif #endif
} } // namespace constructor_tests
RUN_TESTS_QUIET() RUN_TESTS_QUIET()

View File

@ -22,12 +22,11 @@ template <class X> void max_load_tests(X*, test::random_generator generator)
test::reset_sequence(); test::reset_sequence();
X x; X x;
size_type max_load = x.max_load();
BOOST_TEST_EQ(max_load, 0u);
x.reserve(1000); 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(); size_type bucket_count = x.bucket_count();
BOOST_TEST_GE(bucket_count, 1000u); BOOST_TEST_GE(bucket_count, 1000u);

View 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

View File

@ -6,13 +6,13 @@
#include "../helpers/unordered.hpp" #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/equivalent.hpp"
#include "../helpers/invariants.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 <boost/core/ignore_unused.hpp>
#include <iterator> #include <iterator>
@ -75,7 +75,18 @@ namespace move_tests {
test::check_equivalent_keys(y); test::check_equivalent_keys(y);
#if defined(BOOST_UNORDERED_USE_MOVE) || \ #if defined(BOOST_UNORDERED_USE_MOVE) || \
!defined(BOOST_NO_CXX11_RVALUE_REFERENCES) !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); BOOST_TEST_EQ(test::detail::tracker.count_allocations, 0u);
#endif
#endif #endif
} }
@ -117,7 +128,17 @@ namespace move_tests {
y = empty(p); y = empty(p);
#if defined(BOOST_UNORDERED_USE_MOVE) || \ #if defined(BOOST_UNORDERED_USE_MOVE) || \
!defined(BOOST_NO_CXX11_RVALUE_REFERENCES) !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); BOOST_TEST_EQ(test::detail::tracker.count_allocations, 0u);
#endif
#endif #endif
test::check_container(y, v); test::check_container(y, v);
test::check_equivalent_keys(y); test::check_equivalent_keys(y);
@ -279,12 +300,34 @@ namespace move_tests {
T x(0, hf, eq, al2); T x(0, hf, eq, al2);
x.max_load_factor(0.25); 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); BOOST_TEST_EQ(test::detail::tracker.count_allocations, 0u);
#endif
y = boost::move(x); y = boost::move(x);
#if defined(BOOST_UNORDERED_USE_MOVE) || \ #if defined(BOOST_UNORDERED_USE_MOVE) || \
!defined(BOOST_NO_CXX11_RVALUE_REFERENCES) !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); BOOST_TEST_EQ(test::detail::tracker.count_allocations, 0u);
#endif
#endif #endif
test::check_container(y, v); test::check_container(y, v);
test::check_equivalent_keys(y); test::check_equivalent_keys(y);
@ -515,32 +558,18 @@ namespace move_tests {
test::no_propagate_move> >* test_multimap_no_prop_move; test::no_propagate_move> >* test_multimap_no_prop_move;
UNORDERED_TEST(move_construct_tests1, UNORDERED_TEST(move_construct_tests1,
((test_map_std_alloc)(test_set)(test_multiset)(test_map)(test_multimap)( ((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_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))) (default_generator)(generate_collisions)(limited_range)))
UNORDERED_TEST(move_assign_tests1, UNORDERED_TEST(move_assign_tests1,
((test_map_std_alloc)(test_set)(test_multiset)(test_map)(test_multimap)( ((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_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))) (default_generator)(generate_collisions)(limited_range)))
UNORDERED_TEST(move_construct_tests2, UNORDERED_TEST(move_construct_tests2,
((test_set)(test_multiset)(test_map)(test_multimap)(test_set_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))(
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))) (default_generator)(generate_collisions)(limited_range)))
UNORDERED_TEST(move_assign_tests2, UNORDERED_TEST(move_assign_tests2,
((test_set)(test_multiset)(test_map)(test_multimap)(test_set_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))(
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))) (default_generator)(generate_collisions)(limited_range)))
#endif #endif
} } // namespace move_tests
RUN_TESTS() RUN_TESTS()

View File

@ -3,14 +3,14 @@
// Distributed under the Boost Software License, Version 1.0. (See accompanying // 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) // 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/equivalent.hpp"
#include "../helpers/invariants.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 <boost/core/ignore_unused.hpp>
#include <iterator> #include <iterator>
@ -59,7 +59,8 @@ namespace move_tests {
template <class T> T const& get_value(T const& t) { return t; } 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; return kv.second;
} }
@ -466,8 +467,23 @@ namespace move_tests {
!defined(BOOST_NO_CXX11_RVALUE_REFERENCES) !defined(BOOST_NO_CXX11_RVALUE_REFERENCES)
BOOST_TEST(y.empty()); BOOST_TEST(y.empty());
BOOST_TEST(y.begin() == y.end()); 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(y.bucket_count(), 0u);
BOOST_TEST_EQ(test::detail::tracker.count_allocations, num_allocs); BOOST_TEST_EQ(test::detail::tracker.count_allocations, num_allocs);
#endif
#endif #endif
fps[i](y, v); fps[i](y, v);
@ -520,8 +536,23 @@ namespace move_tests {
!defined(BOOST_NO_CXX11_RVALUE_REFERENCES) !defined(BOOST_NO_CXX11_RVALUE_REFERENCES)
BOOST_TEST(y.empty()); BOOST_TEST(y.empty());
BOOST_TEST(y.begin() == y.end()); 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(y.bucket_count(), 0u);
BOOST_TEST_EQ(test::detail::tracker.count_allocations, num_allocs); BOOST_TEST_EQ(test::detail::tracker.count_allocations, num_allocs);
#endif
#endif #endif
fps[i](y, v); fps[i](y, v);
@ -687,7 +718,7 @@ namespace move_tests {
test::cxx11_allocator<std::pair<test::object const, test::object>, test::cxx11_allocator<std::pair<test::object const, test::object>,
test::no_propagate_move> >* test_multimap_no_prop_move; test::no_propagate_move> >* test_multimap_no_prop_move;
// clang-format off // clang-format off
UNORDERED_TEST(post_move_tests, UNORDERED_TEST(post_move_tests,
((test_set)(test_multiset)(test_map)(test_multimap)(test_set_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_multiset_prop_move)(test_map_prop_move)(test_multimap_prop_move)(
@ -696,6 +727,6 @@ namespace move_tests {
(default_generator)(generate_collisions)(limited_range))) (default_generator)(generate_collisions)(limited_range)))
// clang-format on // clang-format on
#endif #endif
} } // namespace move_tests
RUN_TESTS() RUN_TESTS()

View File

@ -216,8 +216,6 @@ template <class C1, class C2> void scary_test()
typename C2::const_iterator cbegin(x.cbegin()); typename C2::const_iterator cbegin(x.cbegin());
BOOST_TEST(cbegin == x.cend()); BOOST_TEST(cbegin == x.cend());
BOOST_TEST_EQ(x.bucket_count(), 0u);
#ifndef BOOST_UNORDERED_FOA_TESTS #ifndef BOOST_UNORDERED_FOA_TESTS
typename C2::local_iterator lbegin(x.begin(0)); typename C2::local_iterator lbegin(x.begin(0));
BOOST_TEST(lbegin == x.end(0)); BOOST_TEST(lbegin == x.end(0));