mirror of
https://github.com/boostorg/unordered.git
synced 2025-05-09 23:23:59 +00:00
Feature/concurrent node containers (#271)
* added concurrent node containers * removed spurious typename * added missing includes * avoided unused param warning * worked around Clang bug * s/{}/() to work around GCC4.8 problems with aggregate initialization * used /bigobj for cfoa/visit_tests.cpp * suppressed localized maybe-uninitialized warnings * fixed comments * added /bigobj to cfoa/insert_tests.cpp * instrumented double exact comparison to spot a spurious error * fixed pedantic error * refactored byte_span machinery * compromised on sub-epsilon equality for doubles that should be identical * documented boost::concurrent_node_(map|set) * added concurrent_node_set * added missing AlternativeType * tested empty node insertion * tested node_handle allocator management * added nonassignable_allocator and node_handle_allocator_swap_tests * fixed warning disabling * silenced spurious GCC warning * broadened scope of previous pragma * broadened even more * worked around spurious constexpr-related msvc-14.0 bug https://godbolt.org/z/v78545Ebf * added workaround back * replaced previous workaround with built-in one * added workaround back on top of built-in solution (which doesn't work 100% of the time)
This commit is contained in:
parent
35bdabf259
commit
f734e399e3
@ -126,7 +126,7 @@ h|*Method* h|*Description*
|
||||
|
||||
|`float max_load_factor(float z)`
|
||||
|Changes the container's maximum load factor, using `z` as a hint. +
|
||||
**Open-addressing containers:** this function does nothing: users are not allowed to change the maximum load factor.
|
||||
**Open-addressing and concurrent containers:** this function does nothing: users are not allowed to change the maximum load factor.
|
||||
|
||||
|`void rehash(size_type n)`
|
||||
|Changes the number of buckets so that there at least `n` buckets, and so that the load factor is less than the maximum load factor.
|
||||
|
@ -6,8 +6,9 @@
|
||||
:github-pr-url: https://github.com/boostorg/unordered/pull
|
||||
:cpp: C++
|
||||
|
||||
== Release 1.87.0
|
||||
== Release 1.87.0 - Major update
|
||||
|
||||
* Added concurrent, node-based containers `boost::concurrent_node_map` and `boost::concurrent_node_set`.
|
||||
* Made visitation exclusive-locked within certain
|
||||
`boost::concurrent_flat_set` operations to allow for safe mutable modification of elements
|
||||
({github-pr-url}/265[PR#265^]).
|
||||
|
@ -89,7 +89,8 @@ The main differences with C++ unordered associative containers are:
|
||||
== Concurrent Containers
|
||||
|
||||
There is currently no specification in the C++ standard for this or any other type of concurrent
|
||||
data structure. The APIs of `boost::concurrent_flat_set` and `boost::concurrent_flat_map`
|
||||
data structure. The APIs of `boost::concurrent_flat_set`/`boost::concurrent_node_set` and
|
||||
`boost::concurrent_flat_map`/`boost::concurrent_node_map`
|
||||
are modelled after `std::unordered_flat_set` and `std::unordered_flat_map`, respectively,
|
||||
with the crucial difference that iterators are not provided
|
||||
due to their inherent problems in concurrent scenarios (high contention, prone to deadlocking):
|
||||
@ -105,7 +106,7 @@ In a non-concurrent unordered container, iterators serve two main purposes:
|
||||
* Access to an element previously located via lookup.
|
||||
* Container traversal.
|
||||
|
||||
In place of iterators, `boost::concurrent_flat_set` and `boost::concurrent_flat_map` use _internal visitation_
|
||||
In place of iterators, Boost.Unordered concurrent containers use _internal visitation_
|
||||
facilities as a thread-safe substitute. Classical operations returning an iterator to an
|
||||
element already existing in the container, like for instance:
|
||||
|
||||
@ -141,7 +142,8 @@ respectively, here visitation is granted mutable or const access depending on
|
||||
the constness of the member function used (there are also `*cvisit` overloads for
|
||||
explicit const visitation); In the case of `boost::concurrent_flat_set`, visitation is always const.
|
||||
|
||||
One notable operation not provided by `boost::concurrent_flat_map` is `operator[]`/`at`, which can be
|
||||
One notable operation not provided by `boost::concurrent_flat_map`/`boost::concurrent_node_map`
|
||||
is `operator[]`/`at`, which can be
|
||||
replaced, if in a more convoluted manner, by
|
||||
xref:#concurrent_flat_map_try_emplace_or_cvisit[`try_emplace_or_visit`].
|
||||
|
||||
|
@ -3,7 +3,8 @@
|
||||
|
||||
:idprefix: concurrent_
|
||||
|
||||
Boost.Unordered provides `boost::concurrent_flat_set` and `boost::concurrent_flat_map`,
|
||||
Boost.Unordered provides `boost::concurrent_node_set`, `boost::concurrent_node_map`,
|
||||
`boost::concurrent_flat_set` and `boost::concurrent_flat_map`,
|
||||
hash tables that allow concurrent write/read access from
|
||||
different threads without having to implement any synchronzation mechanism on the user's side.
|
||||
|
||||
@ -43,7 +44,7 @@ logical cores in the CPU).
|
||||
|
||||
== Visitation-based API
|
||||
|
||||
The first thing a new user of `boost::concurrent_flat_set` or `boost::concurrent_flat_map`
|
||||
The first thing a new user of Boost.Unordered concurrent containers
|
||||
will notice is that these classes _do not provide iterators_ (which makes them technically
|
||||
not https://en.cppreference.com/w/cpp/named_req/Container[Containers^]
|
||||
in the C++ standard sense). The reason for this is that iterators are inherently
|
||||
@ -62,7 +63,7 @@ thread issues an `m.erase(k)` operation between A and B. There are designs that
|
||||
can remedy this by making iterators lock the element they point to, but this
|
||||
approach lends itself to high contention and can easily produce deadlocks in a program.
|
||||
`operator[]` has similar concurrency issues, and is not provided by
|
||||
`boost::concurrent_flat_map` either. Instead, element access is done through
|
||||
`boost::concurrent_flat_map`/`boost::concurrent_node_map` either. Instead, element access is done through
|
||||
so-called _visitation functions_:
|
||||
|
||||
[source,c++]
|
||||
@ -112,7 +113,7 @@ if (found) {
|
||||
}
|
||||
----
|
||||
|
||||
Visitation is prominent in the API provided by `boost::concurrent_flat_set` and `boost::concurrent_flat_map`, and
|
||||
Visitation is prominent in the API provided by concurrent containers, and
|
||||
many classical operations have visitation-enabled variations:
|
||||
|
||||
[source,c++]
|
||||
@ -125,13 +126,15 @@ m.insert_or_visit(x, [](auto& y) {
|
||||
----
|
||||
|
||||
Note that in this last example the visitation function could actually _modify_
|
||||
the element: as a general rule, operations on a `boost::concurrent_flat_map` `m`
|
||||
the element: as a general rule, operations on a concurrent map `m`
|
||||
will grant visitation functions const/non-const access to the element depending on whether
|
||||
`m` is const/non-const. Const access can be always be explicitly requested
|
||||
by using `cvisit` overloads (for instance, `insert_or_cvisit`) and may result
|
||||
in higher parallelization. For `boost::concurrent_flat_set`, on the other hand,
|
||||
in higher parallelization. For concurrent sets, on the other hand,
|
||||
visitation is always const access.
|
||||
Consult the references of
|
||||
xref:#concurrent_node_set[`boost::concurrent_node_set`],
|
||||
xref:#concurrent_flat_map[`boost::concurrent_node_map`],
|
||||
xref:#concurrent_flat_set[`boost::concurrent_flat_set`] and
|
||||
xref:#concurrent_flat_map[`boost::concurrent_flat_map`]
|
||||
for the complete list of visitation-enabled operations.
|
||||
@ -245,7 +248,7 @@ may yield worse performance.
|
||||
|
||||
== Blocking Operations
|
||||
|
||||
``boost::concurrent_flat_set``s and ``boost::concurrent_flat_map``s can be copied, assigned, cleared and merged just like any
|
||||
Concurrent containers can be copied, assigned, cleared and merged just like any other
|
||||
Boost.Unordered container. Unlike most other operations, these are _blocking_,
|
||||
that is, all other threads are prevented from accesing the tables involved while a copy, assignment,
|
||||
clear or merge operation is in progress. Blocking is taken care of automatically by the library
|
||||
@ -258,9 +261,25 @@ reserving space in advance of bulk insertions will generally speed up the proces
|
||||
== Interoperability with non-concurrent containers
|
||||
|
||||
As open-addressing and concurrent containers are based on the same internal data structure,
|
||||
`boost::unordered_flat_set` and `boost::unordered_flat_map` can
|
||||
be efficiently move-constructed from `boost::concurrent_flat_set` and `boost::concurrent_flat_map`,
|
||||
respectively, and vice versa.
|
||||
they can be efficiently move-constructed from their non-concurrent counterpart, and vice versa.
|
||||
|
||||
[caption=, title='Table {counter:table-counter}. Concurrent/non-concurrent interoperatibility']
|
||||
[cols="1,1", frame=all, grid=all]
|
||||
|===
|
||||
^|`boost::concurrent_node_set`
|
||||
^|`boost::unordered_node_set`
|
||||
|
||||
^|`boost::concurrent_node_map`
|
||||
^|`boost::unordered_node_map`
|
||||
|
||||
^|`boost::concurrent_flat_set`
|
||||
^|`boost::unordered_flat_set`
|
||||
|
||||
^|`boost::concurrent_flat_map`
|
||||
^|`boost::unordered_flat_map`
|
||||
|
||||
|===
|
||||
|
||||
This interoperability comes handy in multistage scenarios where parts of the data processing happen
|
||||
in parallel whereas other steps are non-concurrent (or non-modifying). In the following example,
|
||||
we want to construct a histogram from a huge input vector of words:
|
||||
|
1765
doc/unordered/concurrent_node_map.adoc
Normal file
1765
doc/unordered/concurrent_node_map.adoc
Normal file
File diff suppressed because it is too large
Load Diff
1599
doc/unordered/concurrent_node_set.adoc
Normal file
1599
doc/unordered/concurrent_node_set.adoc
Normal file
File diff suppressed because it is too large
Load Diff
@ -43,7 +43,8 @@ boost::unordered_node_map
|
||||
boost::unordered_flat_map
|
||||
|
||||
^.^h|*Concurrent*
|
||||
^|
|
||||
^| `boost::concurrent_node_set` +
|
||||
`boost::concurrent_node_map`
|
||||
^| `boost::concurrent_flat_set` +
|
||||
`boost::concurrent_flat_map`
|
||||
|
||||
@ -59,6 +60,7 @@ There are two variants: **flat** (the fastest) and **node-based**, which
|
||||
provide pointer stability under rehashing at the expense of being slower.
|
||||
* Finally, **concurrent containers** are designed and implemented to be used in high-performance
|
||||
multithreaded scenarios. Their interface is radically different from that of regular C++ containers.
|
||||
Flat and node-based variants are provided.
|
||||
|
||||
All sets and maps in Boost.Unordered are instantiatied similarly as
|
||||
`std::unordered_set` and `std::unordered_map`, respectively:
|
||||
@ -72,8 +74,8 @@ namespace boost {
|
||||
class Pred = std::equal_to<Key>,
|
||||
class Alloc = std::allocator<Key> >
|
||||
class unordered_set;
|
||||
// same for unordered_multiset, unordered_flat_set, unordered_node_set
|
||||
// and concurrent_flat_set
|
||||
// same for unordered_multiset, unordered_flat_set, unordered_node_set,
|
||||
// concurrent_flat_set and concurrent_node_set
|
||||
|
||||
template <
|
||||
class Key, class Mapped,
|
||||
@ -81,8 +83,8 @@ namespace boost {
|
||||
class Pred = std::equal_to<Key>,
|
||||
class Alloc = std::allocator<std::pair<Key const, Mapped> > >
|
||||
class unordered_map;
|
||||
// same for unordered_multimap, unordered_flat_map, unordered_node_map
|
||||
// and concurrent_flat_map
|
||||
// same for unordered_multimap, unordered_flat_map, unordered_node_map,
|
||||
// concurrent_flat_map and concurrent_node_map
|
||||
}
|
||||
----
|
||||
|
||||
|
@ -121,7 +121,8 @@ for Visual Studio on an x64-mode Intel CPU with SSE2 and for GCC on an IBM s390x
|
||||
== Concurrent Containers
|
||||
|
||||
The same data structure used by Boost.Unordered open-addressing containers has been chosen
|
||||
also as the foundation of `boost::concurrent_flat_set` and `boost::concurrent_flat_map`:
|
||||
also as the foundation of `boost::concurrent_flat_set`/`boost::concurrent_node_set` and
|
||||
`boost::concurrent_flat_map`/`boost::concurrent_node_map`:
|
||||
|
||||
* Open-addressing is faster than closed-addressing alternatives, both in non-concurrent and
|
||||
concurrent scenarios.
|
||||
@ -130,7 +131,7 @@ with minimal locking. In particular, the metadata array can be used for implemen
|
||||
lookup that are lock-free up to the last step of actual element comparison.
|
||||
* Layout compatibility with Boost.Unordered flat containers allows for
|
||||
xref:#concurrent_interoperability_with_non_concurrent_containers[fast transfer]
|
||||
of all elements between `boost::concurrent_flat_map` and `boost::unordered_flat_map`,
|
||||
of all elements between a concurrent container and its non-concurrent counterpart,
|
||||
and vice versa.
|
||||
|
||||
=== Hash Function and Platform Interoperability
|
||||
|
@ -13,3 +13,5 @@ include::unordered_node_map.adoc[]
|
||||
include::unordered_node_set.adoc[]
|
||||
include::concurrent_flat_map.adoc[]
|
||||
include::concurrent_flat_set.adoc[]
|
||||
include::concurrent_node_map.adoc[]
|
||||
include::concurrent_node_set.adoc[]
|
||||
|
@ -129,7 +129,8 @@ xref:#rationale_open_addresing_containers[corresponding section].
|
||||
|
||||
== Concurrent Containers
|
||||
|
||||
`boost::concurrent_flat_set` and `boost::concurrent_flat_map` use the basic
|
||||
`boost::concurrent_flat_set`/`boost::concurrent_node_set` and
|
||||
`boost::concurrent_flat_map`/`boost::concurrent_node_map` use the basic
|
||||
xref:#structures_open_addressing_containers[open-addressing layout] described above
|
||||
augmented with synchronization mechanisms.
|
||||
|
||||
|
@ -78,6 +78,7 @@ namespace boost {
|
||||
explicit xref:#unordered_node_map_allocator_constructor[unordered_node_map](const Allocator& a);
|
||||
xref:#unordered_node_map_copy_constructor_with_allocator[unordered_node_map](const unordered_node_map& other, const Allocator& a);
|
||||
xref:#unordered_node_map_move_constructor_with_allocator[unordered_node_map](unordered_node_map&& other, const Allocator& a);
|
||||
xref:#unordered_node_map_move_constructor_from_concurrent_node_map[unordered_node_map](concurrent_node_map<Key, T, Hash, Pred, Allocator>&& other);
|
||||
xref:#unordered_node_map_initializer_list_constructor[unordered_node_map](std::initializer_list<value_type> il,
|
||||
size_type n = _implementation-defined_
|
||||
const hasher& hf = hasher(),
|
||||
@ -537,6 +538,24 @@ and always calls `other.reset_stats()`.
|
||||
|
||||
---
|
||||
|
||||
==== Move Constructor from concurrent_node_map
|
||||
|
||||
```c++
|
||||
unordered_node_map(concurrent_node_map<Key, T, Hash, Pred, Allocator>&& other);
|
||||
```
|
||||
|
||||
Move construction from a xref:#concurrent_node_map[`concurrent_node_map`].
|
||||
The internal bucket array of `other` is transferred directly to the new container.
|
||||
The hash function, predicate and allocator are moved-constructed from `other`.
|
||||
If statistics are xref:unordered_node_map_boost_unordered_enable_stats[enabled],
|
||||
transfers the internal statistical information from `other` and calls `other.reset_stats()`.
|
||||
|
||||
[horizontal]
|
||||
Complexity:;; Constant time.
|
||||
Concurrency:;; Blocking on `other`.
|
||||
|
||||
---
|
||||
|
||||
==== Initializer List Constructor
|
||||
[source,c++,subs="+quotes"]
|
||||
----
|
||||
@ -1219,8 +1238,8 @@ Throws:;; Nothing.
|
||||
|
||||
==== Extract by Key
|
||||
```c++
|
||||
node_type erase(const key_type& k);
|
||||
template<class K> node_type erase(K&& k);
|
||||
node_type extract(const key_type& k);
|
||||
template<class K> node_type extract(K&& k);
|
||||
```
|
||||
|
||||
Extracts the element with key equivalent to `k`, if it exists.
|
||||
@ -1228,7 +1247,7 @@ Extracts the element with key equivalent to `k`, if it exists.
|
||||
[horizontal]
|
||||
Returns:;; A `node_type` object holding the extracted element, or empty if no element was extracted.
|
||||
Throws:;; Only throws an exception if it is thrown by `hasher` or `key_equal`.
|
||||
Notes:;; The `template<class K>` overload only participates in overload resolution if `Hash::is_transparent` and `Pred::is_transparent` are valid member typedefs and neither `iterator` nor `const_iterator` are implicitly convertible from `K`. The library assumes that `Hash` is callable with both `K` and `Key` and that `Pred` is transparent. This enables heterogeneous lookup which avoids the cost of instantiating an instance of the `Key` type.
|
||||
Notes:;; The `template<class K>` overload only participates in overload resolution if `Hash::is_transparent` and `Pred::is_transparent` are valid member typedefs. The library assumes that `Hash` is callable with both `K` and `Key` and that `Pred` is transparent. This enables heterogeneous lookup which avoids the cost of instantiating an instance of the `Key` type.
|
||||
|
||||
---
|
||||
|
||||
|
@ -73,6 +73,7 @@ namespace boost {
|
||||
explicit xref:#unordered_node_set_allocator_constructor[unordered_node_set](const Allocator& a);
|
||||
xref:#unordered_node_set_copy_constructor_with_allocator[unordered_node_set](const unordered_node_set& other, const Allocator& a);
|
||||
xref:#unordered_node_set_move_constructor_with_allocator[unordered_node_set](unordered_node_set&& other, const Allocator& a);
|
||||
xref:#unordered_node_set_move_constructor_from_concurrent_node_set[unordered_node_set](concurrent_node_set<Key, Hash, Pred, Allocator>&& other);
|
||||
xref:#unordered_node_set_initializer_list_constructor[unordered_node_set](std::initializer_list<value_type> il,
|
||||
size_type n = _implementation-defined_
|
||||
const hasher& hf = hasher(),
|
||||
@ -489,6 +490,24 @@ and always calls `other.reset_stats()`.
|
||||
|
||||
---
|
||||
|
||||
==== Move Constructor from concurrent_node_set
|
||||
|
||||
```c++
|
||||
unordered_node_set(concurrent_node_set<Key, Hash, Pred, Allocator>&& other);
|
||||
```
|
||||
|
||||
Move construction from a xref:#concurrent_node_set[`concurrent_node_set`].
|
||||
The internal bucket array of `other` is transferred directly to the new container.
|
||||
The hash function, predicate and allocator are moved-constructed from `other`.
|
||||
If statistics are xref:unordered_node_set_boost_unordered_enable_stats[enabled],
|
||||
transfers the internal statistical information from `other` and calls `other.reset_stats()`.
|
||||
|
||||
[horizontal]
|
||||
Complexity:;; Constant time.
|
||||
Concurrency:;; Blocking on `other`.
|
||||
|
||||
---
|
||||
|
||||
==== Initializer List Constructor
|
||||
[source,c++,subs="+quotes"]
|
||||
----
|
||||
@ -1028,8 +1047,8 @@ Throws:;; Nothing.
|
||||
|
||||
==== Extract by Key
|
||||
```c++
|
||||
node_type erase(const key_type& k);
|
||||
template<class K> node_type erase(K&& k);
|
||||
node_type extract(const key_type& k);
|
||||
template<class K> node_type extract(K&& k);
|
||||
```
|
||||
|
||||
Extracts the element with key equivalent to `k`, if it exists.
|
||||
@ -1037,7 +1056,7 @@ Extracts the element with key equivalent to `k`, if it exists.
|
||||
[horizontal]
|
||||
Returns:;; A `node_type` object holding the extracted element, or empty if no element was extracted.
|
||||
Throws:;; Only throws an exception if it is thrown by `hasher` or `key_equal`.
|
||||
Notes:;; The `template<class K>` overload only participates in overload resolution if `Hash::is_transparent` and `Pred::is_transparent` are valid member typedefs and neither `iterator` nor `const_iterator` are implicitly convertible from `K`. The library assumes that `Hash` is callable with both `K` and `Key` and that `Pred` is transparent. This enables heterogeneous lookup which avoids the cost of instantiating an instance of the `Key` type.
|
||||
Notes:;; The `template<class K>` overload only participates in overload resolution if `Hash::is_transparent` and `Pred::is_transparent` are valid member typedefs. The library assumes that `Hash` is callable with both `K` and `Key` and that `Pred` is transparent. This enables heterogeneous lookup which avoids the cost of instantiating an instance of the `Key` type.
|
||||
|
||||
---
|
||||
|
||||
|
@ -421,6 +421,8 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
<AlternativeType Name="boost::unordered::unordered_node_set<*>" />
|
||||
<AlternativeType Name="boost::unordered::concurrent_flat_map<*>" />
|
||||
<AlternativeType Name="boost::unordered::concurrent_flat_set<*>" />
|
||||
<AlternativeType Name="boost::unordered::concurrent_node_map<*>" />
|
||||
<AlternativeType Name="boost::unordered::concurrent_node_set<*>" />
|
||||
<DisplayString>{{ size={table_.size_ctrl.size} }}</DisplayString>
|
||||
<Expand>
|
||||
<Item Name="[hash_function]" ExcludeView="simple">*reinterpret_cast<hasher*>(static_cast<table_type::super::hash_base*>(&table_))</Item>
|
||||
@ -433,6 +435,7 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
<Type Name="boost::unordered::unordered_flat_map<*>" Priority="MediumHigh" ExcludeView="ShowElementsByIndex">
|
||||
<AlternativeType Name="boost::unordered::unordered_node_map<*>" />
|
||||
<AlternativeType Name="boost::unordered::concurrent_flat_map<*>" />
|
||||
<AlternativeType Name="boost::unordered::concurrent_node_map<*>" />
|
||||
<DisplayString>{{ size={table_.size_ctrl.size} }}</DisplayString>
|
||||
<Expand>
|
||||
<Item Name="[hash_function]" ExcludeView="simple">*reinterpret_cast<hasher*>(static_cast<table_type::super::hash_base*>(&table_))</Item>
|
||||
|
975
include/boost/unordered/concurrent_node_map.hpp
Normal file
975
include/boost/unordered/concurrent_node_map.hpp
Normal file
@ -0,0 +1,975 @@
|
||||
/* Fast open-addressing, node-based concurrent hashmap.
|
||||
*
|
||||
* Copyright 2023 Christian Mazakas.
|
||||
* Copyright 2023-2024 Joaquin M Lopez Munoz.
|
||||
* Distributed under the Boost Software License, Version 1.0.
|
||||
* (See accompanying file LICENSE_1_0.txt or copy at
|
||||
* http://www.boost.org/LICENSE_1_0.txt)
|
||||
*
|
||||
* See https://www.boost.org/libs/unordered for library home page.
|
||||
*/
|
||||
|
||||
#ifndef BOOST_UNORDERED_CONCURRENT_NODE_MAP_HPP
|
||||
#define BOOST_UNORDERED_CONCURRENT_NODE_MAP_HPP
|
||||
|
||||
#include <boost/unordered/concurrent_node_map_fwd.hpp>
|
||||
#include <boost/unordered/detail/concurrent_static_asserts.hpp>
|
||||
#include <boost/unordered/detail/foa/concurrent_table.hpp>
|
||||
#include <boost/unordered/detail/foa/element_type.hpp>
|
||||
#include <boost/unordered/detail/foa/node_map_handle.hpp>
|
||||
#include <boost/unordered/detail/foa/node_map_types.hpp>
|
||||
#include <boost/unordered/detail/type_traits.hpp>
|
||||
#include <boost/unordered/unordered_node_map_fwd.hpp>
|
||||
|
||||
#include <boost/container_hash/hash.hpp>
|
||||
#include <boost/core/allocator_access.hpp>
|
||||
#include <boost/core/serialization.hpp>
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
namespace boost {
|
||||
namespace unordered {
|
||||
template <class Key, class T, class Hash, class Pred, class Allocator>
|
||||
class concurrent_node_map
|
||||
{
|
||||
private:
|
||||
template <class Key2, class T2, class Hash2, class Pred2,
|
||||
class Allocator2>
|
||||
friend class concurrent_node_map;
|
||||
template <class Key2, class T2, class Hash2, class Pred2,
|
||||
class Allocator2>
|
||||
friend class unordered_node_map;
|
||||
|
||||
using type_policy = detail::foa::node_map_types<Key, T,
|
||||
typename boost::allocator_void_pointer<Allocator>::type>;
|
||||
|
||||
using table_type =
|
||||
detail::foa::concurrent_table<type_policy, Hash, Pred, Allocator>;
|
||||
|
||||
table_type table_;
|
||||
|
||||
template <class K, class V, class H, class KE, class A>
|
||||
bool friend operator==(concurrent_node_map<K, V, H, KE, A> const& lhs,
|
||||
concurrent_node_map<K, V, H, KE, A> const& rhs);
|
||||
|
||||
template <class K, class V, class H, class KE, class A, class Predicate>
|
||||
friend typename concurrent_node_map<K, V, H, KE, A>::size_type erase_if(
|
||||
concurrent_node_map<K, V, H, KE, A>& set, Predicate pred);
|
||||
|
||||
template<class Archive, class K, class V, class H, class KE, class A>
|
||||
friend void serialize(
|
||||
Archive& ar, concurrent_node_map<K, V, H, KE, A>& c,
|
||||
unsigned int version);
|
||||
|
||||
public:
|
||||
using key_type = Key;
|
||||
using mapped_type = T;
|
||||
using value_type = typename type_policy::value_type;
|
||||
using init_type = typename type_policy::init_type;
|
||||
using size_type = std::size_t;
|
||||
using difference_type = std::ptrdiff_t;
|
||||
using hasher = typename boost::unordered::detail::type_identity<Hash>::type;
|
||||
using key_equal = typename boost::unordered::detail::type_identity<Pred>::type;
|
||||
using allocator_type = typename boost::unordered::detail::type_identity<Allocator>::type;
|
||||
using reference = value_type&;
|
||||
using const_reference = value_type const&;
|
||||
using pointer = typename boost::allocator_pointer<allocator_type>::type;
|
||||
using const_pointer =
|
||||
typename boost::allocator_const_pointer<allocator_type>::type;
|
||||
using node_type = detail::foa::node_map_handle<type_policy,
|
||||
typename boost::allocator_rebind<Allocator,
|
||||
typename type_policy::value_type>::type>;
|
||||
using insert_return_type =
|
||||
detail::foa::iteratorless_insert_return_type<node_type>;
|
||||
static constexpr size_type bulk_visit_size = table_type::bulk_visit_size;
|
||||
|
||||
#if defined(BOOST_UNORDERED_ENABLE_STATS)
|
||||
using stats = typename table_type::stats;
|
||||
#endif
|
||||
|
||||
concurrent_node_map()
|
||||
: concurrent_node_map(detail::foa::default_bucket_count)
|
||||
{
|
||||
}
|
||||
|
||||
explicit concurrent_node_map(size_type n, const hasher& hf = hasher(),
|
||||
const key_equal& eql = key_equal(),
|
||||
const allocator_type& a = allocator_type())
|
||||
: table_(n, hf, eql, a)
|
||||
{
|
||||
}
|
||||
|
||||
template <class InputIterator>
|
||||
concurrent_node_map(InputIterator f, InputIterator l,
|
||||
size_type n = detail::foa::default_bucket_count,
|
||||
const hasher& hf = hasher(), const key_equal& eql = key_equal(),
|
||||
const allocator_type& a = allocator_type())
|
||||
: table_(n, hf, eql, a)
|
||||
{
|
||||
this->insert(f, l);
|
||||
}
|
||||
|
||||
concurrent_node_map(concurrent_node_map const& rhs)
|
||||
: table_(rhs.table_,
|
||||
boost::allocator_select_on_container_copy_construction(
|
||||
rhs.get_allocator()))
|
||||
{
|
||||
}
|
||||
|
||||
concurrent_node_map(concurrent_node_map&& rhs)
|
||||
: table_(std::move(rhs.table_))
|
||||
{
|
||||
}
|
||||
|
||||
template <class InputIterator>
|
||||
concurrent_node_map(
|
||||
InputIterator f, InputIterator l, allocator_type const& a)
|
||||
: concurrent_node_map(f, l, 0, hasher(), key_equal(), a)
|
||||
{
|
||||
}
|
||||
|
||||
explicit concurrent_node_map(allocator_type const& a)
|
||||
: table_(detail::foa::default_bucket_count, hasher(), key_equal(), a)
|
||||
{
|
||||
}
|
||||
|
||||
concurrent_node_map(
|
||||
concurrent_node_map const& rhs, allocator_type const& a)
|
||||
: table_(rhs.table_, a)
|
||||
{
|
||||
}
|
||||
|
||||
concurrent_node_map(concurrent_node_map&& rhs, allocator_type const& a)
|
||||
: table_(std::move(rhs.table_), a)
|
||||
{
|
||||
}
|
||||
|
||||
concurrent_node_map(std::initializer_list<value_type> il,
|
||||
size_type n = detail::foa::default_bucket_count,
|
||||
const hasher& hf = hasher(), const key_equal& eql = key_equal(),
|
||||
const allocator_type& a = allocator_type())
|
||||
: concurrent_node_map(n, hf, eql, a)
|
||||
{
|
||||
this->insert(il.begin(), il.end());
|
||||
}
|
||||
|
||||
concurrent_node_map(size_type n, const allocator_type& a)
|
||||
: concurrent_node_map(n, hasher(), key_equal(), a)
|
||||
{
|
||||
}
|
||||
|
||||
concurrent_node_map(
|
||||
size_type n, const hasher& hf, const allocator_type& a)
|
||||
: concurrent_node_map(n, hf, key_equal(), a)
|
||||
{
|
||||
}
|
||||
|
||||
template <typename InputIterator>
|
||||
concurrent_node_map(
|
||||
InputIterator f, InputIterator l, size_type n, const allocator_type& a)
|
||||
: concurrent_node_map(f, l, n, hasher(), key_equal(), a)
|
||||
{
|
||||
}
|
||||
|
||||
template <typename InputIterator>
|
||||
concurrent_node_map(InputIterator f, InputIterator l, size_type n,
|
||||
const hasher& hf, const allocator_type& a)
|
||||
: concurrent_node_map(f, l, n, hf, key_equal(), a)
|
||||
{
|
||||
}
|
||||
|
||||
concurrent_node_map(
|
||||
std::initializer_list<value_type> il, const allocator_type& a)
|
||||
: concurrent_node_map(
|
||||
il, detail::foa::default_bucket_count, hasher(), key_equal(), a)
|
||||
{
|
||||
}
|
||||
|
||||
concurrent_node_map(std::initializer_list<value_type> il, size_type n,
|
||||
const allocator_type& a)
|
||||
: concurrent_node_map(il, n, hasher(), key_equal(), a)
|
||||
{
|
||||
}
|
||||
|
||||
concurrent_node_map(std::initializer_list<value_type> il, size_type n,
|
||||
const hasher& hf, const allocator_type& a)
|
||||
: concurrent_node_map(il, n, hf, key_equal(), a)
|
||||
{
|
||||
}
|
||||
|
||||
concurrent_node_map(
|
||||
unordered_node_map<Key, T, Hash, Pred, Allocator>&& other)
|
||||
: table_(std::move(other.table_))
|
||||
{
|
||||
}
|
||||
|
||||
~concurrent_node_map() = default;
|
||||
|
||||
concurrent_node_map& operator=(concurrent_node_map const& rhs)
|
||||
{
|
||||
table_ = rhs.table_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
concurrent_node_map& operator=(concurrent_node_map&& rhs) noexcept(
|
||||
noexcept(std::declval<table_type&>() = std::declval<table_type&&>()))
|
||||
{
|
||||
table_ = std::move(rhs.table_);
|
||||
return *this;
|
||||
}
|
||||
|
||||
concurrent_node_map& operator=(std::initializer_list<value_type> ilist)
|
||||
{
|
||||
table_ = ilist;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// Capacity
|
||||
///
|
||||
|
||||
size_type size() const noexcept { return table_.size(); }
|
||||
size_type max_size() const noexcept { return table_.max_size(); }
|
||||
|
||||
BOOST_ATTRIBUTE_NODISCARD bool empty() const noexcept
|
||||
{
|
||||
return size() == 0;
|
||||
}
|
||||
|
||||
template <class F>
|
||||
BOOST_FORCEINLINE size_type visit(key_type const& k, F f)
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(F)
|
||||
return table_.visit(k, f);
|
||||
}
|
||||
|
||||
template <class F>
|
||||
BOOST_FORCEINLINE size_type visit(key_type const& k, F f) const
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
|
||||
return table_.visit(k, f);
|
||||
}
|
||||
|
||||
template <class F>
|
||||
BOOST_FORCEINLINE size_type cvisit(key_type const& k, F f) const
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
|
||||
return table_.visit(k, f);
|
||||
}
|
||||
|
||||
template <class K, class F>
|
||||
BOOST_FORCEINLINE typename std::enable_if<
|
||||
detail::are_transparent<K, hasher, key_equal>::value, size_type>::type
|
||||
visit(K&& k, F f)
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(F)
|
||||
return table_.visit(std::forward<K>(k), f);
|
||||
}
|
||||
|
||||
template <class K, class F>
|
||||
BOOST_FORCEINLINE typename std::enable_if<
|
||||
detail::are_transparent<K, hasher, key_equal>::value, size_type>::type
|
||||
visit(K&& k, F f) const
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
|
||||
return table_.visit(std::forward<K>(k), f);
|
||||
}
|
||||
|
||||
template <class K, class F>
|
||||
BOOST_FORCEINLINE typename std::enable_if<
|
||||
detail::are_transparent<K, hasher, key_equal>::value, size_type>::type
|
||||
cvisit(K&& k, F f) const
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
|
||||
return table_.visit(std::forward<K>(k), f);
|
||||
}
|
||||
|
||||
template<class FwdIterator, class F>
|
||||
BOOST_FORCEINLINE
|
||||
size_t visit(FwdIterator first, FwdIterator last, F f)
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_BULK_VISIT_ITERATOR(FwdIterator)
|
||||
BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(F)
|
||||
return table_.visit(first, last, f);
|
||||
}
|
||||
|
||||
template<class FwdIterator, class F>
|
||||
BOOST_FORCEINLINE
|
||||
size_t visit(FwdIterator first, FwdIterator last, F f) const
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_BULK_VISIT_ITERATOR(FwdIterator)
|
||||
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
|
||||
return table_.visit(first, last, f);
|
||||
}
|
||||
|
||||
template<class FwdIterator, class F>
|
||||
BOOST_FORCEINLINE
|
||||
size_t cvisit(FwdIterator first, FwdIterator last, F f) const
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_BULK_VISIT_ITERATOR(FwdIterator)
|
||||
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
|
||||
return table_.visit(first, last, f);
|
||||
}
|
||||
|
||||
template <class F> size_type visit_all(F f)
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(F)
|
||||
return table_.visit_all(f);
|
||||
}
|
||||
|
||||
template <class F> size_type visit_all(F f) const
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
|
||||
return table_.visit_all(f);
|
||||
}
|
||||
|
||||
template <class F> size_type cvisit_all(F f) const
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
|
||||
return table_.cvisit_all(f);
|
||||
}
|
||||
|
||||
#if defined(BOOST_UNORDERED_PARALLEL_ALGORITHMS)
|
||||
template <class ExecPolicy, class F>
|
||||
typename std::enable_if<detail::is_execution_policy<ExecPolicy>::value,
|
||||
void>::type
|
||||
visit_all(ExecPolicy&& p, F f)
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(F)
|
||||
BOOST_UNORDERED_STATIC_ASSERT_EXEC_POLICY(ExecPolicy)
|
||||
table_.visit_all(p, f);
|
||||
}
|
||||
|
||||
template <class ExecPolicy, class F>
|
||||
typename std::enable_if<detail::is_execution_policy<ExecPolicy>::value,
|
||||
void>::type
|
||||
visit_all(ExecPolicy&& p, F f) const
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
|
||||
BOOST_UNORDERED_STATIC_ASSERT_EXEC_POLICY(ExecPolicy)
|
||||
table_.visit_all(p, f);
|
||||
}
|
||||
|
||||
template <class ExecPolicy, class F>
|
||||
typename std::enable_if<detail::is_execution_policy<ExecPolicy>::value,
|
||||
void>::type
|
||||
cvisit_all(ExecPolicy&& p, F f) const
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
|
||||
BOOST_UNORDERED_STATIC_ASSERT_EXEC_POLICY(ExecPolicy)
|
||||
table_.cvisit_all(p, f);
|
||||
}
|
||||
#endif
|
||||
|
||||
template <class F> bool visit_while(F f)
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(F)
|
||||
return table_.visit_while(f);
|
||||
}
|
||||
|
||||
template <class F> bool visit_while(F f) const
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
|
||||
return table_.visit_while(f);
|
||||
}
|
||||
|
||||
template <class F> bool cvisit_while(F f) const
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
|
||||
return table_.cvisit_while(f);
|
||||
}
|
||||
|
||||
#if defined(BOOST_UNORDERED_PARALLEL_ALGORITHMS)
|
||||
template <class ExecPolicy, class F>
|
||||
typename std::enable_if<detail::is_execution_policy<ExecPolicy>::value,
|
||||
bool>::type
|
||||
visit_while(ExecPolicy&& p, F f)
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(F)
|
||||
BOOST_UNORDERED_STATIC_ASSERT_EXEC_POLICY(ExecPolicy)
|
||||
return table_.visit_while(p, f);
|
||||
}
|
||||
|
||||
template <class ExecPolicy, class F>
|
||||
typename std::enable_if<detail::is_execution_policy<ExecPolicy>::value,
|
||||
bool>::type
|
||||
visit_while(ExecPolicy&& p, F f) const
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
|
||||
BOOST_UNORDERED_STATIC_ASSERT_EXEC_POLICY(ExecPolicy)
|
||||
return table_.visit_while(p, f);
|
||||
}
|
||||
|
||||
template <class ExecPolicy, class F>
|
||||
typename std::enable_if<detail::is_execution_policy<ExecPolicy>::value,
|
||||
bool>::type
|
||||
cvisit_while(ExecPolicy&& p, F f) const
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
|
||||
BOOST_UNORDERED_STATIC_ASSERT_EXEC_POLICY(ExecPolicy)
|
||||
return table_.cvisit_while(p, f);
|
||||
}
|
||||
#endif
|
||||
|
||||
/// Modifiers
|
||||
///
|
||||
|
||||
template <class Ty>
|
||||
BOOST_FORCEINLINE auto insert(Ty&& value)
|
||||
-> decltype(table_.insert(std::forward<Ty>(value)))
|
||||
{
|
||||
return table_.insert(std::forward<Ty>(value));
|
||||
}
|
||||
|
||||
BOOST_FORCEINLINE bool insert(init_type&& obj)
|
||||
{
|
||||
return table_.insert(std::move(obj));
|
||||
}
|
||||
|
||||
template <class InputIterator>
|
||||
void insert(InputIterator begin, InputIterator end)
|
||||
{
|
||||
for (auto pos = begin; pos != end; ++pos) {
|
||||
table_.emplace(*pos);
|
||||
}
|
||||
}
|
||||
|
||||
void insert(std::initializer_list<value_type> ilist)
|
||||
{
|
||||
this->insert(ilist.begin(), ilist.end());
|
||||
}
|
||||
|
||||
insert_return_type insert(node_type&& nh)
|
||||
{
|
||||
using access = detail::foa::node_handle_access;
|
||||
|
||||
if (nh.empty()) {
|
||||
return {false, node_type{}};
|
||||
}
|
||||
|
||||
// Caveat: get_allocator() incurs synchronization (not cheap)
|
||||
BOOST_ASSERT(get_allocator() == nh.get_allocator());
|
||||
|
||||
if (table_.insert(std::move(access::element(nh)))) {
|
||||
access::reset(nh);
|
||||
return {true, node_type{}};
|
||||
} else {
|
||||
return {false, std::move(nh)};
|
||||
}
|
||||
}
|
||||
|
||||
template <class M>
|
||||
BOOST_FORCEINLINE bool insert_or_assign(key_type const& k, M&& obj)
|
||||
{
|
||||
return table_.try_emplace_or_visit(k, std::forward<M>(obj),
|
||||
[&](value_type& m) { m.second = std::forward<M>(obj); });
|
||||
}
|
||||
|
||||
template <class M>
|
||||
BOOST_FORCEINLINE bool insert_or_assign(key_type&& k, M&& obj)
|
||||
{
|
||||
return table_.try_emplace_or_visit(std::move(k), std::forward<M>(obj),
|
||||
[&](value_type& m) { m.second = std::forward<M>(obj); });
|
||||
}
|
||||
|
||||
template <class K, class M>
|
||||
BOOST_FORCEINLINE typename std::enable_if<
|
||||
detail::are_transparent<K, hasher, key_equal>::value, bool>::type
|
||||
insert_or_assign(K&& k, M&& obj)
|
||||
{
|
||||
return table_.try_emplace_or_visit(std::forward<K>(k),
|
||||
std::forward<M>(obj),
|
||||
[&](value_type& m) { m.second = std::forward<M>(obj); });
|
||||
}
|
||||
|
||||
template <class Ty, class F>
|
||||
BOOST_FORCEINLINE auto insert_or_visit(Ty&& value, F f)
|
||||
-> decltype(table_.insert_or_visit(std::forward<Ty>(value), f))
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(F)
|
||||
return table_.insert_or_visit(std::forward<Ty>(value), f);
|
||||
}
|
||||
|
||||
template <class F>
|
||||
BOOST_FORCEINLINE bool insert_or_visit(init_type&& obj, F f)
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(F)
|
||||
return table_.insert_or_visit(std::move(obj), f);
|
||||
}
|
||||
|
||||
template <class InputIterator, class F>
|
||||
void insert_or_visit(InputIterator first, InputIterator last, F f)
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(F)
|
||||
for (; first != last; ++first) {
|
||||
table_.emplace_or_visit(*first, f);
|
||||
}
|
||||
}
|
||||
|
||||
template <class F>
|
||||
void insert_or_visit(std::initializer_list<value_type> ilist, F f)
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(F)
|
||||
this->insert_or_visit(ilist.begin(), ilist.end(), f);
|
||||
}
|
||||
|
||||
template <class F>
|
||||
insert_return_type insert_or_visit(node_type&& nh, F f)
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(F)
|
||||
using access = detail::foa::node_handle_access;
|
||||
|
||||
if (nh.empty()) {
|
||||
return {false, node_type{}};
|
||||
}
|
||||
|
||||
// Caveat: get_allocator() incurs synchronization (not cheap)
|
||||
BOOST_ASSERT(get_allocator() == nh.get_allocator());
|
||||
|
||||
if (table_.insert_or_visit(std::move(access::element(nh)), f)) {
|
||||
access::reset(nh);
|
||||
return {true, node_type{}};
|
||||
} else {
|
||||
return {false, std::move(nh)};
|
||||
}
|
||||
}
|
||||
|
||||
template <class Ty, class F>
|
||||
BOOST_FORCEINLINE auto insert_or_cvisit(Ty&& value, F f)
|
||||
-> decltype(table_.insert_or_cvisit(std::forward<Ty>(value), f))
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
|
||||
return table_.insert_or_cvisit(std::forward<Ty>(value), f);
|
||||
}
|
||||
|
||||
template <class F>
|
||||
BOOST_FORCEINLINE bool insert_or_cvisit(init_type&& obj, F f)
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
|
||||
return table_.insert_or_cvisit(std::move(obj), f);
|
||||
}
|
||||
|
||||
template <class InputIterator, class F>
|
||||
void insert_or_cvisit(InputIterator first, InputIterator last, F f)
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
|
||||
for (; first != last; ++first) {
|
||||
table_.emplace_or_cvisit(*first, f);
|
||||
}
|
||||
}
|
||||
|
||||
template <class F>
|
||||
void insert_or_cvisit(std::initializer_list<value_type> ilist, F f)
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
|
||||
this->insert_or_cvisit(ilist.begin(), ilist.end(), f);
|
||||
}
|
||||
|
||||
template <class F>
|
||||
insert_return_type insert_or_cvisit(node_type&& nh, F f)
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
|
||||
using access = detail::foa::node_handle_access;
|
||||
|
||||
if (nh.empty()) {
|
||||
return {false, node_type{}};
|
||||
}
|
||||
|
||||
// Caveat: get_allocator() incurs synchronization (not cheap)
|
||||
BOOST_ASSERT(get_allocator() == nh.get_allocator());
|
||||
|
||||
if (table_.insert_or_cvisit(std::move(access::element(nh)), f)) {
|
||||
access::reset(nh);
|
||||
return {true, node_type{}};
|
||||
} else {
|
||||
return {false, std::move(nh)};
|
||||
}
|
||||
}
|
||||
|
||||
template <class... Args> BOOST_FORCEINLINE bool emplace(Args&&... args)
|
||||
{
|
||||
return table_.emplace(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template <class Arg, class... Args>
|
||||
BOOST_FORCEINLINE bool emplace_or_visit(Arg&& arg, Args&&... args)
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_LAST_ARG_INVOCABLE(Arg, Args...)
|
||||
return table_.emplace_or_visit(
|
||||
std::forward<Arg>(arg), std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template <class Arg, class... Args>
|
||||
BOOST_FORCEINLINE bool emplace_or_cvisit(Arg&& arg, Args&&... args)
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_LAST_ARG_CONST_INVOCABLE(Arg, Args...)
|
||||
return table_.emplace_or_cvisit(
|
||||
std::forward<Arg>(arg), std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template <class... Args>
|
||||
BOOST_FORCEINLINE bool try_emplace(key_type const& k, Args&&... args)
|
||||
{
|
||||
return table_.try_emplace(k, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template <class... Args>
|
||||
BOOST_FORCEINLINE bool try_emplace(key_type&& k, Args&&... args)
|
||||
{
|
||||
return table_.try_emplace(std::move(k), std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template <class K, class... Args>
|
||||
BOOST_FORCEINLINE typename std::enable_if<
|
||||
detail::are_transparent<K, hasher, key_equal>::value, bool>::type
|
||||
try_emplace(K&& k, Args&&... args)
|
||||
{
|
||||
return table_.try_emplace(
|
||||
std::forward<K>(k), std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template <class Arg, class... Args>
|
||||
BOOST_FORCEINLINE bool try_emplace_or_visit(
|
||||
key_type const& k, Arg&& arg, Args&&... args)
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_LAST_ARG_INVOCABLE(Arg, Args...)
|
||||
return table_.try_emplace_or_visit(
|
||||
k, std::forward<Arg>(arg), std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template <class Arg, class... Args>
|
||||
BOOST_FORCEINLINE bool try_emplace_or_cvisit(
|
||||
key_type const& k, Arg&& arg, Args&&... args)
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_LAST_ARG_CONST_INVOCABLE(Arg, Args...)
|
||||
return table_.try_emplace_or_cvisit(
|
||||
k, std::forward<Arg>(arg), std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template <class Arg, class... Args>
|
||||
BOOST_FORCEINLINE bool try_emplace_or_visit(
|
||||
key_type&& k, Arg&& arg, Args&&... args)
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_LAST_ARG_INVOCABLE(Arg, Args...)
|
||||
return table_.try_emplace_or_visit(
|
||||
std::move(k), std::forward<Arg>(arg), std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template <class Arg, class... Args>
|
||||
BOOST_FORCEINLINE bool try_emplace_or_cvisit(
|
||||
key_type&& k, Arg&& arg, Args&&... args)
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_LAST_ARG_CONST_INVOCABLE(Arg, Args...)
|
||||
return table_.try_emplace_or_cvisit(
|
||||
std::move(k), std::forward<Arg>(arg), std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template <class K, class Arg, class... Args>
|
||||
BOOST_FORCEINLINE bool try_emplace_or_visit(
|
||||
K&& k, Arg&& arg, Args&&... args)
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_LAST_ARG_INVOCABLE(Arg, Args...)
|
||||
return table_.try_emplace_or_visit(std::forward<K>(k),
|
||||
std::forward<Arg>(arg), std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template <class K, class Arg, class... Args>
|
||||
BOOST_FORCEINLINE bool try_emplace_or_cvisit(
|
||||
K&& k, Arg&& arg, Args&&... args)
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_LAST_ARG_CONST_INVOCABLE(Arg, Args...)
|
||||
return table_.try_emplace_or_cvisit(std::forward<K>(k),
|
||||
std::forward<Arg>(arg), std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
BOOST_FORCEINLINE size_type erase(key_type const& k)
|
||||
{
|
||||
return table_.erase(k);
|
||||
}
|
||||
|
||||
template <class K>
|
||||
BOOST_FORCEINLINE typename std::enable_if<
|
||||
detail::are_transparent<K, hasher, key_equal>::value, size_type>::type
|
||||
erase(K&& k)
|
||||
{
|
||||
return table_.erase(std::forward<K>(k));
|
||||
}
|
||||
|
||||
template <class F>
|
||||
BOOST_FORCEINLINE size_type erase_if(key_type const& k, F f)
|
||||
{
|
||||
return table_.erase_if(k, f);
|
||||
}
|
||||
|
||||
template <class K, class F>
|
||||
BOOST_FORCEINLINE typename std::enable_if<
|
||||
detail::are_transparent<K, hasher, key_equal>::value &&
|
||||
!detail::is_execution_policy<K>::value,
|
||||
size_type>::type
|
||||
erase_if(K&& k, F f)
|
||||
{
|
||||
return table_.erase_if(std::forward<K>(k), f);
|
||||
}
|
||||
|
||||
#if defined(BOOST_UNORDERED_PARALLEL_ALGORITHMS)
|
||||
template <class ExecPolicy, class F>
|
||||
typename std::enable_if<detail::is_execution_policy<ExecPolicy>::value,
|
||||
void>::type
|
||||
erase_if(ExecPolicy&& p, F f)
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_EXEC_POLICY(ExecPolicy)
|
||||
table_.erase_if(p, f);
|
||||
}
|
||||
#endif
|
||||
|
||||
template <class F> size_type erase_if(F f) { return table_.erase_if(f); }
|
||||
|
||||
void swap(concurrent_node_map& other) noexcept(
|
||||
boost::allocator_is_always_equal<Allocator>::type::value ||
|
||||
boost::allocator_propagate_on_container_swap<Allocator>::type::value)
|
||||
{
|
||||
return table_.swap(other.table_);
|
||||
}
|
||||
|
||||
node_type extract(key_type const& key)
|
||||
{
|
||||
node_type nh;
|
||||
table_.extract(key, detail::foa::node_handle_emplacer(nh));
|
||||
return nh;
|
||||
}
|
||||
|
||||
template <class K>
|
||||
typename std::enable_if<
|
||||
detail::are_transparent<K, hasher, key_equal>::value, node_type>::type
|
||||
extract(K const& key)
|
||||
{
|
||||
node_type nh;
|
||||
table_.extract(key, detail::foa::node_handle_emplacer(nh));
|
||||
return nh;
|
||||
}
|
||||
|
||||
template <class F>
|
||||
node_type extract_if(key_type const& key, F f)
|
||||
{
|
||||
node_type nh;
|
||||
table_.extract_if(key, f, detail::foa::node_handle_emplacer(nh));
|
||||
return nh;
|
||||
}
|
||||
|
||||
template <class K, class F>
|
||||
typename std::enable_if<
|
||||
detail::are_transparent<K, hasher, key_equal>::value, node_type>::type
|
||||
extract_if(K const& key, F f)
|
||||
{
|
||||
node_type nh;
|
||||
table_.extract_if(key, f, detail::foa::node_handle_emplacer(nh));
|
||||
return nh;
|
||||
}
|
||||
|
||||
void clear() noexcept { table_.clear(); }
|
||||
|
||||
template <typename H2, typename P2>
|
||||
size_type merge(concurrent_node_map<Key, T, H2, P2, Allocator>& x)
|
||||
{
|
||||
BOOST_ASSERT(get_allocator() == x.get_allocator());
|
||||
return table_.merge(x.table_);
|
||||
}
|
||||
|
||||
template <typename H2, typename P2>
|
||||
size_type merge(concurrent_node_map<Key, T, H2, P2, Allocator>&& x)
|
||||
{
|
||||
return merge(x);
|
||||
}
|
||||
|
||||
BOOST_FORCEINLINE size_type count(key_type const& k) const
|
||||
{
|
||||
return table_.count(k);
|
||||
}
|
||||
|
||||
template <class K>
|
||||
BOOST_FORCEINLINE typename std::enable_if<
|
||||
detail::are_transparent<K, hasher, key_equal>::value, size_type>::type
|
||||
count(K const& k)
|
||||
{
|
||||
return table_.count(k);
|
||||
}
|
||||
|
||||
BOOST_FORCEINLINE bool contains(key_type const& k) const
|
||||
{
|
||||
return table_.contains(k);
|
||||
}
|
||||
|
||||
template <class K>
|
||||
BOOST_FORCEINLINE typename std::enable_if<
|
||||
detail::are_transparent<K, hasher, key_equal>::value, bool>::type
|
||||
contains(K const& k) const
|
||||
{
|
||||
return table_.contains(k);
|
||||
}
|
||||
|
||||
/// Hash Policy
|
||||
///
|
||||
size_type bucket_count() const noexcept { return table_.capacity(); }
|
||||
|
||||
float load_factor() const noexcept { return table_.load_factor(); }
|
||||
float max_load_factor() const noexcept
|
||||
{
|
||||
return table_.max_load_factor();
|
||||
}
|
||||
void max_load_factor(float) {}
|
||||
size_type max_load() const noexcept { return table_.max_load(); }
|
||||
|
||||
void rehash(size_type n) { table_.rehash(n); }
|
||||
void reserve(size_type n) { table_.reserve(n); }
|
||||
|
||||
#if defined(BOOST_UNORDERED_ENABLE_STATS)
|
||||
/// Stats
|
||||
///
|
||||
stats get_stats() const { return table_.get_stats(); }
|
||||
|
||||
void reset_stats() noexcept { table_.reset_stats(); }
|
||||
#endif
|
||||
|
||||
/// Observers
|
||||
///
|
||||
allocator_type get_allocator() const noexcept
|
||||
{
|
||||
return table_.get_allocator();
|
||||
}
|
||||
|
||||
hasher hash_function() const { return table_.hash_function(); }
|
||||
key_equal key_eq() const { return table_.key_eq(); }
|
||||
};
|
||||
|
||||
template <class Key, class T, class Hash, class KeyEqual, class Allocator>
|
||||
bool operator==(
|
||||
concurrent_node_map<Key, T, Hash, KeyEqual, Allocator> const& lhs,
|
||||
concurrent_node_map<Key, T, Hash, KeyEqual, Allocator> const& rhs)
|
||||
{
|
||||
return lhs.table_ == rhs.table_;
|
||||
}
|
||||
|
||||
template <class Key, class T, class Hash, class KeyEqual, class Allocator>
|
||||
bool operator!=(
|
||||
concurrent_node_map<Key, T, Hash, KeyEqual, Allocator> const& lhs,
|
||||
concurrent_node_map<Key, T, Hash, KeyEqual, Allocator> const& rhs)
|
||||
{
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
|
||||
template <class Key, class T, class Hash, class Pred, class Alloc>
|
||||
void swap(concurrent_node_map<Key, T, Hash, Pred, Alloc>& x,
|
||||
concurrent_node_map<Key, T, Hash, Pred, Alloc>& y)
|
||||
noexcept(noexcept(x.swap(y)))
|
||||
{
|
||||
x.swap(y);
|
||||
}
|
||||
|
||||
template <class K, class T, class H, class P, class A, class Predicate>
|
||||
typename concurrent_node_map<K, T, H, P, A>::size_type erase_if(
|
||||
concurrent_node_map<K, T, H, P, A>& c, Predicate pred)
|
||||
{
|
||||
return c.table_.erase_if(pred);
|
||||
}
|
||||
|
||||
template<class Archive, class K, class V, class H, class KE, class A>
|
||||
void serialize(
|
||||
Archive& ar, concurrent_node_map<K, V, H, KE, A>& c, unsigned int)
|
||||
{
|
||||
ar & core::make_nvp("table",c.table_);
|
||||
}
|
||||
|
||||
#if BOOST_UNORDERED_TEMPLATE_DEDUCTION_GUIDES
|
||||
|
||||
template <class InputIterator,
|
||||
class Hash =
|
||||
boost::hash<boost::unordered::detail::iter_key_t<InputIterator> >,
|
||||
class Pred =
|
||||
std::equal_to<boost::unordered::detail::iter_key_t<InputIterator> >,
|
||||
class Allocator = std::allocator<
|
||||
boost::unordered::detail::iter_to_alloc_t<InputIterator> >,
|
||||
class = std::enable_if_t<detail::is_input_iterator_v<InputIterator> >,
|
||||
class = std::enable_if_t<detail::is_hash_v<Hash> >,
|
||||
class = std::enable_if_t<detail::is_pred_v<Pred> >,
|
||||
class = std::enable_if_t<detail::is_allocator_v<Allocator> > >
|
||||
concurrent_node_map(InputIterator, InputIterator,
|
||||
std::size_t = boost::unordered::detail::foa::default_bucket_count,
|
||||
Hash = Hash(), Pred = Pred(), Allocator = Allocator())
|
||||
-> concurrent_node_map<
|
||||
boost::unordered::detail::iter_key_t<InputIterator>,
|
||||
boost::unordered::detail::iter_val_t<InputIterator>, Hash, Pred,
|
||||
Allocator>;
|
||||
|
||||
template <class Key, class T,
|
||||
class Hash = boost::hash<std::remove_const_t<Key> >,
|
||||
class Pred = std::equal_to<std::remove_const_t<Key> >,
|
||||
class Allocator = std::allocator<std::pair<const Key, T> >,
|
||||
class = std::enable_if_t<detail::is_hash_v<Hash> >,
|
||||
class = std::enable_if_t<detail::is_pred_v<Pred> >,
|
||||
class = std::enable_if_t<detail::is_allocator_v<Allocator> > >
|
||||
concurrent_node_map(std::initializer_list<std::pair<Key, T> >,
|
||||
std::size_t = boost::unordered::detail::foa::default_bucket_count,
|
||||
Hash = Hash(), Pred = Pred(), Allocator = Allocator())
|
||||
-> concurrent_node_map<std::remove_const_t<Key>, T, Hash, Pred,
|
||||
Allocator>;
|
||||
|
||||
template <class InputIterator, class Allocator,
|
||||
class = std::enable_if_t<detail::is_input_iterator_v<InputIterator> >,
|
||||
class = std::enable_if_t<detail::is_allocator_v<Allocator> > >
|
||||
concurrent_node_map(InputIterator, InputIterator, std::size_t, Allocator)
|
||||
-> concurrent_node_map<
|
||||
boost::unordered::detail::iter_key_t<InputIterator>,
|
||||
boost::unordered::detail::iter_val_t<InputIterator>,
|
||||
boost::hash<boost::unordered::detail::iter_key_t<InputIterator> >,
|
||||
std::equal_to<boost::unordered::detail::iter_key_t<InputIterator> >,
|
||||
Allocator>;
|
||||
|
||||
template <class InputIterator, class Allocator,
|
||||
class = std::enable_if_t<detail::is_input_iterator_v<InputIterator> >,
|
||||
class = std::enable_if_t<detail::is_allocator_v<Allocator> > >
|
||||
concurrent_node_map(InputIterator, InputIterator, Allocator)
|
||||
-> concurrent_node_map<
|
||||
boost::unordered::detail::iter_key_t<InputIterator>,
|
||||
boost::unordered::detail::iter_val_t<InputIterator>,
|
||||
boost::hash<boost::unordered::detail::iter_key_t<InputIterator> >,
|
||||
std::equal_to<boost::unordered::detail::iter_key_t<InputIterator> >,
|
||||
Allocator>;
|
||||
|
||||
template <class InputIterator, class Hash, class Allocator,
|
||||
class = std::enable_if_t<detail::is_hash_v<Hash> >,
|
||||
class = std::enable_if_t<detail::is_input_iterator_v<InputIterator> >,
|
||||
class = std::enable_if_t<detail::is_allocator_v<Allocator> > >
|
||||
concurrent_node_map(
|
||||
InputIterator, InputIterator, std::size_t, Hash, Allocator)
|
||||
-> concurrent_node_map<
|
||||
boost::unordered::detail::iter_key_t<InputIterator>,
|
||||
boost::unordered::detail::iter_val_t<InputIterator>, Hash,
|
||||
std::equal_to<boost::unordered::detail::iter_key_t<InputIterator> >,
|
||||
Allocator>;
|
||||
|
||||
template <class Key, class T, class Allocator,
|
||||
class = std::enable_if_t<detail::is_allocator_v<Allocator> > >
|
||||
concurrent_node_map(std::initializer_list<std::pair<Key, T> >, std::size_t,
|
||||
Allocator) -> concurrent_node_map<std::remove_const_t<Key>, T,
|
||||
boost::hash<std::remove_const_t<Key> >,
|
||||
std::equal_to<std::remove_const_t<Key> >, Allocator>;
|
||||
|
||||
template <class Key, class T, class Allocator,
|
||||
class = std::enable_if_t<detail::is_allocator_v<Allocator> > >
|
||||
concurrent_node_map(std::initializer_list<std::pair<Key, T> >, Allocator)
|
||||
-> concurrent_node_map<std::remove_const_t<Key>, T,
|
||||
boost::hash<std::remove_const_t<Key> >,
|
||||
std::equal_to<std::remove_const_t<Key> >, Allocator>;
|
||||
|
||||
template <class Key, class T, class Hash, class Allocator,
|
||||
class = std::enable_if_t<detail::is_hash_v<Hash> >,
|
||||
class = std::enable_if_t<detail::is_allocator_v<Allocator> > >
|
||||
concurrent_node_map(std::initializer_list<std::pair<Key, T> >, std::size_t,
|
||||
Hash, Allocator) -> concurrent_node_map<std::remove_const_t<Key>, T,
|
||||
Hash, std::equal_to<std::remove_const_t<Key> >, Allocator>;
|
||||
|
||||
#endif
|
||||
|
||||
} // namespace unordered
|
||||
} // namespace boost
|
||||
|
||||
#endif // BOOST_UNORDERED_CONCURRENT_NODE_MAP_HPP
|
67
include/boost/unordered/concurrent_node_map_fwd.hpp
Normal file
67
include/boost/unordered/concurrent_node_map_fwd.hpp
Normal file
@ -0,0 +1,67 @@
|
||||
/* Fast open-addressing, node-based concurrent hashmap.
|
||||
*
|
||||
* Copyright 2023 Christian Mazakas.
|
||||
* Copyright 2024 Braden Ganetsky.
|
||||
* Copyright 2024 Joaquin M Lopez Munoz.
|
||||
* Distributed under the Boost Software License, Version 1.0.
|
||||
* (See accompanying file LICENSE_1_0.txt or copy at
|
||||
* http://www.boost.org/LICENSE_1_0.txt)
|
||||
*
|
||||
* See https://www.boost.org/libs/unordered for library home page.
|
||||
*/
|
||||
|
||||
#ifndef BOOST_UNORDERED_CONCURRENT_NODE_MAP_FWD_HPP
|
||||
#define BOOST_UNORDERED_CONCURRENT_NODE_MAP_FWD_HPP
|
||||
|
||||
#include <boost/config.hpp>
|
||||
#include <boost/container_hash/hash_fwd.hpp>
|
||||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
|
||||
#ifndef BOOST_NO_CXX17_HDR_MEMORY_RESOURCE
|
||||
#include <memory_resource>
|
||||
#endif
|
||||
|
||||
namespace boost {
|
||||
namespace unordered {
|
||||
|
||||
template <class Key, class T, class Hash = boost::hash<Key>,
|
||||
class Pred = std::equal_to<Key>,
|
||||
class Allocator = std::allocator<std::pair<Key const, T> > >
|
||||
class concurrent_node_map;
|
||||
|
||||
template <class Key, class T, class Hash, class KeyEqual, class Allocator>
|
||||
bool operator==(
|
||||
concurrent_node_map<Key, T, Hash, KeyEqual, Allocator> const& lhs,
|
||||
concurrent_node_map<Key, T, Hash, KeyEqual, Allocator> const& rhs);
|
||||
|
||||
template <class Key, class T, class Hash, class KeyEqual, class Allocator>
|
||||
bool operator!=(
|
||||
concurrent_node_map<Key, T, Hash, KeyEqual, Allocator> const& lhs,
|
||||
concurrent_node_map<Key, T, Hash, KeyEqual, Allocator> const& rhs);
|
||||
|
||||
template <class Key, class T, class Hash, class Pred, class Alloc>
|
||||
void swap(concurrent_node_map<Key, T, Hash, Pred, Alloc>& x,
|
||||
concurrent_node_map<Key, T, Hash, Pred, Alloc>& y)
|
||||
noexcept(noexcept(x.swap(y)));
|
||||
|
||||
template <class K, class T, class H, class P, class A, class Predicate>
|
||||
typename concurrent_node_map<K, T, H, P, A>::size_type erase_if(
|
||||
concurrent_node_map<K, T, H, P, A>& c, Predicate pred);
|
||||
|
||||
#ifndef BOOST_NO_CXX17_HDR_MEMORY_RESOURCE
|
||||
namespace pmr {
|
||||
template <class Key, class T, class Hash = boost::hash<Key>,
|
||||
class Pred = std::equal_to<Key> >
|
||||
using concurrent_node_map = boost::unordered::concurrent_node_map<Key, T,
|
||||
Hash, Pred, std::pmr::polymorphic_allocator<std::pair<Key const, T> > >;
|
||||
} // namespace pmr
|
||||
#endif
|
||||
|
||||
} // namespace unordered
|
||||
|
||||
using boost::unordered::concurrent_node_map;
|
||||
} // namespace boost
|
||||
|
||||
#endif // BOOST_UNORDERED_CONCURRENT_NODE_MAP_FWD_HPP
|
888
include/boost/unordered/concurrent_node_set.hpp
Normal file
888
include/boost/unordered/concurrent_node_set.hpp
Normal file
@ -0,0 +1,888 @@
|
||||
/* Fast open-addressing, node-based concurrent hashset.
|
||||
*
|
||||
* Copyright 2023 Christian Mazakas.
|
||||
* Copyright 2023-2024 Joaquin M Lopez Munoz.
|
||||
* Distributed under the Boost Software License, Version 1.0.
|
||||
* (See accompanying file LICENSE_1_0.txt or copy at
|
||||
* http://www.boost.org/LICENSE_1_0.txt)
|
||||
*
|
||||
* See https://www.boost.org/libs/unordered for library home page.
|
||||
*/
|
||||
|
||||
#ifndef BOOST_UNORDERED_CONCURRENT_NODE_SET_HPP
|
||||
#define BOOST_UNORDERED_CONCURRENT_NODE_SET_HPP
|
||||
|
||||
#include <boost/unordered/concurrent_node_set_fwd.hpp>
|
||||
#include <boost/unordered/detail/concurrent_static_asserts.hpp>
|
||||
#include <boost/unordered/detail/foa/concurrent_table.hpp>
|
||||
#include <boost/unordered/detail/foa/element_type.hpp>
|
||||
#include <boost/unordered/detail/foa/node_set_handle.hpp>
|
||||
#include <boost/unordered/detail/foa/node_set_types.hpp>
|
||||
#include <boost/unordered/detail/type_traits.hpp>
|
||||
#include <boost/unordered/unordered_node_set_fwd.hpp>
|
||||
|
||||
#include <boost/container_hash/hash.hpp>
|
||||
#include <boost/core/allocator_access.hpp>
|
||||
#include <boost/core/serialization.hpp>
|
||||
|
||||
#include <utility>
|
||||
|
||||
namespace boost {
|
||||
namespace unordered {
|
||||
template <class Key, class Hash, class Pred, class Allocator>
|
||||
class concurrent_node_set
|
||||
{
|
||||
private:
|
||||
template <class Key2, class Hash2, class Pred2, class Allocator2>
|
||||
friend class concurrent_node_set;
|
||||
template <class Key2, class Hash2, class Pred2, class Allocator2>
|
||||
friend class unordered_node_set;
|
||||
|
||||
using type_policy = detail::foa::node_set_types<Key,
|
||||
typename boost::allocator_void_pointer<Allocator>::type>;
|
||||
|
||||
using table_type =
|
||||
detail::foa::concurrent_table<type_policy, Hash, Pred, Allocator>;
|
||||
|
||||
table_type table_;
|
||||
|
||||
template <class K, class H, class KE, class A>
|
||||
bool friend operator==(concurrent_node_set<K, H, KE, A> const& lhs,
|
||||
concurrent_node_set<K, H, KE, A> const& rhs);
|
||||
|
||||
template <class K, class H, class KE, class A, class Predicate>
|
||||
friend typename concurrent_node_set<K, H, KE, A>::size_type erase_if(
|
||||
concurrent_node_set<K, H, KE, A>& set, Predicate pred);
|
||||
|
||||
template<class Archive, class K, class H, class KE, class A>
|
||||
friend void serialize(
|
||||
Archive& ar, concurrent_node_set<K, H, KE, A>& c,
|
||||
unsigned int version);
|
||||
|
||||
public:
|
||||
using key_type = Key;
|
||||
using value_type = typename type_policy::value_type;
|
||||
using init_type = typename type_policy::init_type;
|
||||
using size_type = std::size_t;
|
||||
using difference_type = std::ptrdiff_t;
|
||||
using hasher = typename boost::unordered::detail::type_identity<Hash>::type;
|
||||
using key_equal = typename boost::unordered::detail::type_identity<Pred>::type;
|
||||
using allocator_type = typename boost::unordered::detail::type_identity<Allocator>::type;
|
||||
using reference = value_type&;
|
||||
using const_reference = value_type const&;
|
||||
using pointer = typename boost::allocator_pointer<allocator_type>::type;
|
||||
using const_pointer =
|
||||
typename boost::allocator_const_pointer<allocator_type>::type;
|
||||
using node_type = detail::foa::node_set_handle<type_policy,
|
||||
typename boost::allocator_rebind<Allocator,
|
||||
typename type_policy::value_type>::type>;
|
||||
using insert_return_type =
|
||||
detail::foa::iteratorless_insert_return_type<node_type>;
|
||||
static constexpr size_type bulk_visit_size = table_type::bulk_visit_size;
|
||||
|
||||
#if defined(BOOST_UNORDERED_ENABLE_STATS)
|
||||
using stats = typename table_type::stats;
|
||||
#endif
|
||||
|
||||
concurrent_node_set()
|
||||
: concurrent_node_set(detail::foa::default_bucket_count)
|
||||
{
|
||||
}
|
||||
|
||||
explicit concurrent_node_set(size_type n, const hasher& hf = hasher(),
|
||||
const key_equal& eql = key_equal(),
|
||||
const allocator_type& a = allocator_type())
|
||||
: table_(n, hf, eql, a)
|
||||
{
|
||||
}
|
||||
|
||||
template <class InputIterator>
|
||||
concurrent_node_set(InputIterator f, InputIterator l,
|
||||
size_type n = detail::foa::default_bucket_count,
|
||||
const hasher& hf = hasher(), const key_equal& eql = key_equal(),
|
||||
const allocator_type& a = allocator_type())
|
||||
: table_(n, hf, eql, a)
|
||||
{
|
||||
this->insert(f, l);
|
||||
}
|
||||
|
||||
concurrent_node_set(concurrent_node_set const& rhs)
|
||||
: table_(rhs.table_,
|
||||
boost::allocator_select_on_container_copy_construction(
|
||||
rhs.get_allocator()))
|
||||
{
|
||||
}
|
||||
|
||||
concurrent_node_set(concurrent_node_set&& rhs)
|
||||
: table_(std::move(rhs.table_))
|
||||
{
|
||||
}
|
||||
|
||||
template <class InputIterator>
|
||||
concurrent_node_set(
|
||||
InputIterator f, InputIterator l, allocator_type const& a)
|
||||
: concurrent_node_set(f, l, 0, hasher(), key_equal(), a)
|
||||
{
|
||||
}
|
||||
|
||||
explicit concurrent_node_set(allocator_type const& a)
|
||||
: table_(detail::foa::default_bucket_count, hasher(), key_equal(), a)
|
||||
{
|
||||
}
|
||||
|
||||
concurrent_node_set(
|
||||
concurrent_node_set const& rhs, allocator_type const& a)
|
||||
: table_(rhs.table_, a)
|
||||
{
|
||||
}
|
||||
|
||||
concurrent_node_set(concurrent_node_set&& rhs, allocator_type const& a)
|
||||
: table_(std::move(rhs.table_), a)
|
||||
{
|
||||
}
|
||||
|
||||
concurrent_node_set(std::initializer_list<value_type> il,
|
||||
size_type n = detail::foa::default_bucket_count,
|
||||
const hasher& hf = hasher(), const key_equal& eql = key_equal(),
|
||||
const allocator_type& a = allocator_type())
|
||||
: concurrent_node_set(n, hf, eql, a)
|
||||
{
|
||||
this->insert(il.begin(), il.end());
|
||||
}
|
||||
|
||||
concurrent_node_set(size_type n, const allocator_type& a)
|
||||
: concurrent_node_set(n, hasher(), key_equal(), a)
|
||||
{
|
||||
}
|
||||
|
||||
concurrent_node_set(
|
||||
size_type n, const hasher& hf, const allocator_type& a)
|
||||
: concurrent_node_set(n, hf, key_equal(), a)
|
||||
{
|
||||
}
|
||||
|
||||
template <typename InputIterator>
|
||||
concurrent_node_set(
|
||||
InputIterator f, InputIterator l, size_type n, const allocator_type& a)
|
||||
: concurrent_node_set(f, l, n, hasher(), key_equal(), a)
|
||||
{
|
||||
}
|
||||
|
||||
template <typename InputIterator>
|
||||
concurrent_node_set(InputIterator f, InputIterator l, size_type n,
|
||||
const hasher& hf, const allocator_type& a)
|
||||
: concurrent_node_set(f, l, n, hf, key_equal(), a)
|
||||
{
|
||||
}
|
||||
|
||||
concurrent_node_set(
|
||||
std::initializer_list<value_type> il, const allocator_type& a)
|
||||
: concurrent_node_set(
|
||||
il, detail::foa::default_bucket_count, hasher(), key_equal(), a)
|
||||
{
|
||||
}
|
||||
|
||||
concurrent_node_set(std::initializer_list<value_type> il, size_type n,
|
||||
const allocator_type& a)
|
||||
: concurrent_node_set(il, n, hasher(), key_equal(), a)
|
||||
{
|
||||
}
|
||||
|
||||
concurrent_node_set(std::initializer_list<value_type> il, size_type n,
|
||||
const hasher& hf, const allocator_type& a)
|
||||
: concurrent_node_set(il, n, hf, key_equal(), a)
|
||||
{
|
||||
}
|
||||
|
||||
concurrent_node_set(
|
||||
unordered_node_set<Key, Hash, Pred, Allocator>&& other)
|
||||
: table_(std::move(other.table_))
|
||||
{
|
||||
}
|
||||
|
||||
~concurrent_node_set() = default;
|
||||
|
||||
concurrent_node_set& operator=(concurrent_node_set const& rhs)
|
||||
{
|
||||
table_ = rhs.table_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
concurrent_node_set& operator=(concurrent_node_set&& rhs)
|
||||
noexcept(boost::allocator_is_always_equal<Allocator>::type::value ||
|
||||
boost::allocator_propagate_on_container_move_assignment<
|
||||
Allocator>::type::value)
|
||||
{
|
||||
table_ = std::move(rhs.table_);
|
||||
return *this;
|
||||
}
|
||||
|
||||
concurrent_node_set& operator=(std::initializer_list<value_type> ilist)
|
||||
{
|
||||
table_ = ilist;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// Capacity
|
||||
///
|
||||
|
||||
size_type size() const noexcept { return table_.size(); }
|
||||
size_type max_size() const noexcept { return table_.max_size(); }
|
||||
|
||||
BOOST_ATTRIBUTE_NODISCARD bool empty() const noexcept
|
||||
{
|
||||
return size() == 0;
|
||||
}
|
||||
|
||||
template <class F>
|
||||
BOOST_FORCEINLINE size_type visit(key_type const& k, F f)
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
|
||||
return table_.visit(k, f);
|
||||
}
|
||||
|
||||
template <class F>
|
||||
BOOST_FORCEINLINE size_type visit(key_type const& k, F f) const
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
|
||||
return table_.visit(k, f);
|
||||
}
|
||||
|
||||
template <class F>
|
||||
BOOST_FORCEINLINE size_type cvisit(key_type const& k, F f) const
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
|
||||
return table_.visit(k, f);
|
||||
}
|
||||
|
||||
template <class K, class F>
|
||||
BOOST_FORCEINLINE typename std::enable_if<
|
||||
detail::are_transparent<K, hasher, key_equal>::value, size_type>::type
|
||||
visit(K&& k, F f)
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
|
||||
return table_.visit(std::forward<K>(k), f);
|
||||
}
|
||||
|
||||
template <class K, class F>
|
||||
BOOST_FORCEINLINE typename std::enable_if<
|
||||
detail::are_transparent<K, hasher, key_equal>::value, size_type>::type
|
||||
visit(K&& k, F f) const
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
|
||||
return table_.visit(std::forward<K>(k), f);
|
||||
}
|
||||
|
||||
template <class K, class F>
|
||||
BOOST_FORCEINLINE typename std::enable_if<
|
||||
detail::are_transparent<K, hasher, key_equal>::value, size_type>::type
|
||||
cvisit(K&& k, F f) const
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
|
||||
return table_.visit(std::forward<K>(k), f);
|
||||
}
|
||||
|
||||
template<class FwdIterator, class F>
|
||||
BOOST_FORCEINLINE
|
||||
size_t visit(FwdIterator first, FwdIterator last, F f)
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_BULK_VISIT_ITERATOR(FwdIterator)
|
||||
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
|
||||
return table_.visit(first, last, f);
|
||||
}
|
||||
|
||||
template<class FwdIterator, class F>
|
||||
BOOST_FORCEINLINE
|
||||
size_t visit(FwdIterator first, FwdIterator last, F f) const
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_BULK_VISIT_ITERATOR(FwdIterator)
|
||||
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
|
||||
return table_.visit(first, last, f);
|
||||
}
|
||||
|
||||
template<class FwdIterator, class F>
|
||||
BOOST_FORCEINLINE
|
||||
size_t cvisit(FwdIterator first, FwdIterator last, F f) const
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_BULK_VISIT_ITERATOR(FwdIterator)
|
||||
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
|
||||
return table_.visit(first, last, f);
|
||||
}
|
||||
|
||||
template <class F> size_type visit_all(F f)
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
|
||||
return table_.visit_all(f);
|
||||
}
|
||||
|
||||
template <class F> size_type visit_all(F f) const
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
|
||||
return table_.visit_all(f);
|
||||
}
|
||||
|
||||
template <class F> size_type cvisit_all(F f) const
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
|
||||
return table_.cvisit_all(f);
|
||||
}
|
||||
|
||||
#if defined(BOOST_UNORDERED_PARALLEL_ALGORITHMS)
|
||||
template <class ExecPolicy, class F>
|
||||
typename std::enable_if<detail::is_execution_policy<ExecPolicy>::value,
|
||||
void>::type
|
||||
visit_all(ExecPolicy&& p, F f)
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
|
||||
BOOST_UNORDERED_STATIC_ASSERT_EXEC_POLICY(ExecPolicy)
|
||||
table_.visit_all(p, f);
|
||||
}
|
||||
|
||||
template <class ExecPolicy, class F>
|
||||
typename std::enable_if<detail::is_execution_policy<ExecPolicy>::value,
|
||||
void>::type
|
||||
visit_all(ExecPolicy&& p, F f) const
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
|
||||
BOOST_UNORDERED_STATIC_ASSERT_EXEC_POLICY(ExecPolicy)
|
||||
table_.visit_all(p, f);
|
||||
}
|
||||
|
||||
template <class ExecPolicy, class F>
|
||||
typename std::enable_if<detail::is_execution_policy<ExecPolicy>::value,
|
||||
void>::type
|
||||
cvisit_all(ExecPolicy&& p, F f) const
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
|
||||
BOOST_UNORDERED_STATIC_ASSERT_EXEC_POLICY(ExecPolicy)
|
||||
table_.cvisit_all(p, f);
|
||||
}
|
||||
#endif
|
||||
|
||||
template <class F> bool visit_while(F f)
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
|
||||
return table_.visit_while(f);
|
||||
}
|
||||
|
||||
template <class F> bool visit_while(F f) const
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
|
||||
return table_.visit_while(f);
|
||||
}
|
||||
|
||||
template <class F> bool cvisit_while(F f) const
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
|
||||
return table_.cvisit_while(f);
|
||||
}
|
||||
|
||||
#if defined(BOOST_UNORDERED_PARALLEL_ALGORITHMS)
|
||||
template <class ExecPolicy, class F>
|
||||
typename std::enable_if<detail::is_execution_policy<ExecPolicy>::value,
|
||||
bool>::type
|
||||
visit_while(ExecPolicy&& p, F f)
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
|
||||
BOOST_UNORDERED_STATIC_ASSERT_EXEC_POLICY(ExecPolicy)
|
||||
return table_.visit_while(p, f);
|
||||
}
|
||||
|
||||
template <class ExecPolicy, class F>
|
||||
typename std::enable_if<detail::is_execution_policy<ExecPolicy>::value,
|
||||
bool>::type
|
||||
visit_while(ExecPolicy&& p, F f) const
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
|
||||
BOOST_UNORDERED_STATIC_ASSERT_EXEC_POLICY(ExecPolicy)
|
||||
return table_.visit_while(p, f);
|
||||
}
|
||||
|
||||
template <class ExecPolicy, class F>
|
||||
typename std::enable_if<detail::is_execution_policy<ExecPolicy>::value,
|
||||
bool>::type
|
||||
cvisit_while(ExecPolicy&& p, F f) const
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
|
||||
BOOST_UNORDERED_STATIC_ASSERT_EXEC_POLICY(ExecPolicy)
|
||||
return table_.cvisit_while(p, f);
|
||||
}
|
||||
#endif
|
||||
|
||||
/// Modifiers
|
||||
///
|
||||
|
||||
BOOST_FORCEINLINE bool insert(value_type const& obj)
|
||||
{
|
||||
return table_.insert(obj);
|
||||
}
|
||||
|
||||
BOOST_FORCEINLINE bool insert(value_type&& obj)
|
||||
{
|
||||
return table_.insert(std::move(obj));
|
||||
}
|
||||
|
||||
template <class K>
|
||||
BOOST_FORCEINLINE typename std::enable_if<
|
||||
detail::are_transparent<K, hasher, key_equal>::value,
|
||||
bool >::type
|
||||
insert(K&& k)
|
||||
{
|
||||
return table_.try_emplace(std::forward<K>(k));
|
||||
}
|
||||
|
||||
template <class InputIterator>
|
||||
void insert(InputIterator begin, InputIterator end)
|
||||
{
|
||||
for (auto pos = begin; pos != end; ++pos) {
|
||||
table_.emplace(*pos);
|
||||
}
|
||||
}
|
||||
|
||||
void insert(std::initializer_list<value_type> ilist)
|
||||
{
|
||||
this->insert(ilist.begin(), ilist.end());
|
||||
}
|
||||
|
||||
insert_return_type insert(node_type&& nh)
|
||||
{
|
||||
using access = detail::foa::node_handle_access;
|
||||
|
||||
if (nh.empty()) {
|
||||
return {false, node_type{}};
|
||||
}
|
||||
|
||||
// Caveat: get_allocator() incurs synchronization (not cheap)
|
||||
BOOST_ASSERT(get_allocator() == nh.get_allocator());
|
||||
|
||||
if (table_.insert(std::move(access::element(nh)))) {
|
||||
access::reset(nh);
|
||||
return {true, node_type{}};
|
||||
} else {
|
||||
return {false, std::move(nh)};
|
||||
}
|
||||
}
|
||||
|
||||
template <class F>
|
||||
BOOST_FORCEINLINE bool insert_or_visit(value_type const& obj, F f)
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
|
||||
return table_.insert_or_visit(obj, f);
|
||||
}
|
||||
|
||||
template <class F>
|
||||
BOOST_FORCEINLINE bool insert_or_visit(value_type&& obj, F f)
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
|
||||
return table_.insert_or_visit(std::move(obj), f);
|
||||
}
|
||||
|
||||
template <class K, class F>
|
||||
BOOST_FORCEINLINE typename std::enable_if<
|
||||
detail::are_transparent<K, hasher, key_equal>::value,
|
||||
bool >::type
|
||||
insert_or_visit(K&& k, F f)
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
|
||||
return table_.try_emplace_or_visit(std::forward<K>(k), f);
|
||||
}
|
||||
|
||||
template <class InputIterator, class F>
|
||||
void insert_or_visit(InputIterator first, InputIterator last, F f)
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
|
||||
for (; first != last; ++first) {
|
||||
table_.emplace_or_visit(*first, f);
|
||||
}
|
||||
}
|
||||
|
||||
template <class F>
|
||||
void insert_or_visit(std::initializer_list<value_type> ilist, F f)
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
|
||||
this->insert_or_visit(ilist.begin(), ilist.end(), f);
|
||||
}
|
||||
|
||||
template <class F>
|
||||
insert_return_type insert_or_visit(node_type&& nh, F f)
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
|
||||
using access = detail::foa::node_handle_access;
|
||||
|
||||
if (nh.empty()) {
|
||||
return {false, node_type{}};
|
||||
}
|
||||
|
||||
// Caveat: get_allocator() incurs synchronization (not cheap)
|
||||
BOOST_ASSERT(get_allocator() == nh.get_allocator());
|
||||
|
||||
if (table_.insert_or_visit(std::move(access::element(nh)), f)) {
|
||||
access::reset(nh);
|
||||
return {true, node_type{}};
|
||||
} else {
|
||||
return {false, std::move(nh)};
|
||||
}
|
||||
}
|
||||
|
||||
template <class F>
|
||||
BOOST_FORCEINLINE bool insert_or_cvisit(value_type const& obj, F f)
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
|
||||
return table_.insert_or_cvisit(obj, f);
|
||||
}
|
||||
|
||||
template <class F>
|
||||
BOOST_FORCEINLINE bool insert_or_cvisit(value_type&& obj, F f)
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
|
||||
return table_.insert_or_cvisit(std::move(obj), f);
|
||||
}
|
||||
|
||||
template <class K, class F>
|
||||
BOOST_FORCEINLINE typename std::enable_if<
|
||||
detail::are_transparent<K, hasher, key_equal>::value,
|
||||
bool >::type
|
||||
insert_or_cvisit(K&& k, F f)
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
|
||||
return table_.try_emplace_or_cvisit(std::forward<K>(k), f);
|
||||
}
|
||||
|
||||
template <class InputIterator, class F>
|
||||
void insert_or_cvisit(InputIterator first, InputIterator last, F f)
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
|
||||
for (; first != last; ++first) {
|
||||
table_.emplace_or_cvisit(*first, f);
|
||||
}
|
||||
}
|
||||
|
||||
template <class F>
|
||||
void insert_or_cvisit(std::initializer_list<value_type> ilist, F f)
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
|
||||
this->insert_or_cvisit(ilist.begin(), ilist.end(), f);
|
||||
}
|
||||
|
||||
template <class F>
|
||||
insert_return_type insert_or_cvisit(node_type&& nh, F f)
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
|
||||
using access = detail::foa::node_handle_access;
|
||||
|
||||
if (nh.empty()) {
|
||||
return {false, node_type{}};
|
||||
}
|
||||
|
||||
// Caveat: get_allocator() incurs synchronization (not cheap)
|
||||
BOOST_ASSERT(get_allocator() == nh.get_allocator());
|
||||
|
||||
if (table_.insert_or_cvisit(std::move(access::element(nh)), f)) {
|
||||
access::reset(nh);
|
||||
return {true, node_type{}};
|
||||
} else {
|
||||
return {false, std::move(nh)};
|
||||
}
|
||||
}
|
||||
|
||||
template <class... Args> BOOST_FORCEINLINE bool emplace(Args&&... args)
|
||||
{
|
||||
return table_.emplace(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template <class Arg, class... Args>
|
||||
BOOST_FORCEINLINE bool emplace_or_visit(Arg&& arg, Args&&... args)
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_LAST_ARG_CONST_INVOCABLE(Arg, Args...)
|
||||
return table_.emplace_or_visit(
|
||||
std::forward<Arg>(arg), std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template <class Arg, class... Args>
|
||||
BOOST_FORCEINLINE bool emplace_or_cvisit(Arg&& arg, Args&&... args)
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_LAST_ARG_CONST_INVOCABLE(Arg, Args...)
|
||||
return table_.emplace_or_cvisit(
|
||||
std::forward<Arg>(arg), std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
BOOST_FORCEINLINE size_type erase(key_type const& k)
|
||||
{
|
||||
return table_.erase(k);
|
||||
}
|
||||
|
||||
template <class K>
|
||||
BOOST_FORCEINLINE typename std::enable_if<
|
||||
detail::are_transparent<K, hasher, key_equal>::value, size_type>::type
|
||||
erase(K&& k)
|
||||
{
|
||||
return table_.erase(std::forward<K>(k));
|
||||
}
|
||||
|
||||
template <class F>
|
||||
BOOST_FORCEINLINE size_type erase_if(key_type const& k, F f)
|
||||
{
|
||||
return table_.erase_if(k, f);
|
||||
}
|
||||
|
||||
template <class K, class F>
|
||||
BOOST_FORCEINLINE typename std::enable_if<
|
||||
detail::are_transparent<K, hasher, key_equal>::value &&
|
||||
!detail::is_execution_policy<K>::value,
|
||||
size_type>::type
|
||||
erase_if(K&& k, F f)
|
||||
{
|
||||
return table_.erase_if(std::forward<K>(k), f);
|
||||
}
|
||||
|
||||
#if defined(BOOST_UNORDERED_PARALLEL_ALGORITHMS)
|
||||
template <class ExecPolicy, class F>
|
||||
typename std::enable_if<detail::is_execution_policy<ExecPolicy>::value,
|
||||
void>::type
|
||||
erase_if(ExecPolicy&& p, F f)
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_EXEC_POLICY(ExecPolicy)
|
||||
table_.erase_if(p, f);
|
||||
}
|
||||
#endif
|
||||
|
||||
template <class F> size_type erase_if(F f) { return table_.erase_if(f); }
|
||||
|
||||
void swap(concurrent_node_set& other) noexcept(
|
||||
boost::allocator_is_always_equal<Allocator>::type::value ||
|
||||
boost::allocator_propagate_on_container_swap<Allocator>::type::value)
|
||||
{
|
||||
return table_.swap(other.table_);
|
||||
}
|
||||
|
||||
node_type extract(key_type const& key)
|
||||
{
|
||||
node_type nh;
|
||||
table_.extract(key, detail::foa::node_handle_emplacer(nh));
|
||||
return nh;
|
||||
}
|
||||
|
||||
template <class K>
|
||||
typename std::enable_if<
|
||||
detail::are_transparent<K, hasher, key_equal>::value, node_type>::type
|
||||
extract(K const& key)
|
||||
{
|
||||
node_type nh;
|
||||
table_.extract(key, detail::foa::node_handle_emplacer(nh));
|
||||
return nh;
|
||||
}
|
||||
|
||||
template <class F>
|
||||
node_type extract_if(key_type const& key, F f)
|
||||
{
|
||||
node_type nh;
|
||||
table_.extract_if(key, f, detail::foa::node_handle_emplacer(nh));
|
||||
return nh;
|
||||
}
|
||||
|
||||
template <class K, class F>
|
||||
typename std::enable_if<
|
||||
detail::are_transparent<K, hasher, key_equal>::value, node_type>::type
|
||||
extract_if(K const& key, F f)
|
||||
{
|
||||
node_type nh;
|
||||
table_.extract_if(key, f, detail::foa::node_handle_emplacer(nh));
|
||||
return nh;
|
||||
}
|
||||
|
||||
void clear() noexcept { table_.clear(); }
|
||||
|
||||
template <typename H2, typename P2>
|
||||
size_type merge(concurrent_node_set<Key, H2, P2, Allocator>& x)
|
||||
{
|
||||
BOOST_ASSERT(get_allocator() == x.get_allocator());
|
||||
return table_.merge(x.table_);
|
||||
}
|
||||
|
||||
template <typename H2, typename P2>
|
||||
size_type merge(concurrent_node_set<Key, H2, P2, Allocator>&& x)
|
||||
{
|
||||
return merge(x);
|
||||
}
|
||||
|
||||
BOOST_FORCEINLINE size_type count(key_type const& k) const
|
||||
{
|
||||
return table_.count(k);
|
||||
}
|
||||
|
||||
template <class K>
|
||||
BOOST_FORCEINLINE typename std::enable_if<
|
||||
detail::are_transparent<K, hasher, key_equal>::value, size_type>::type
|
||||
count(K const& k)
|
||||
{
|
||||
return table_.count(k);
|
||||
}
|
||||
|
||||
BOOST_FORCEINLINE bool contains(key_type const& k) const
|
||||
{
|
||||
return table_.contains(k);
|
||||
}
|
||||
|
||||
template <class K>
|
||||
BOOST_FORCEINLINE typename std::enable_if<
|
||||
detail::are_transparent<K, hasher, key_equal>::value, bool>::type
|
||||
contains(K const& k) const
|
||||
{
|
||||
return table_.contains(k);
|
||||
}
|
||||
|
||||
/// Hash Policy
|
||||
///
|
||||
size_type bucket_count() const noexcept { return table_.capacity(); }
|
||||
|
||||
float load_factor() const noexcept { return table_.load_factor(); }
|
||||
float max_load_factor() const noexcept
|
||||
{
|
||||
return table_.max_load_factor();
|
||||
}
|
||||
void max_load_factor(float) {}
|
||||
size_type max_load() const noexcept { return table_.max_load(); }
|
||||
|
||||
void rehash(size_type n) { table_.rehash(n); }
|
||||
void reserve(size_type n) { table_.reserve(n); }
|
||||
|
||||
#if defined(BOOST_UNORDERED_ENABLE_STATS)
|
||||
/// Stats
|
||||
///
|
||||
stats get_stats() const { return table_.get_stats(); }
|
||||
|
||||
void reset_stats() noexcept { table_.reset_stats(); }
|
||||
#endif
|
||||
|
||||
/// Observers
|
||||
///
|
||||
allocator_type get_allocator() const noexcept
|
||||
{
|
||||
return table_.get_allocator();
|
||||
}
|
||||
|
||||
hasher hash_function() const { return table_.hash_function(); }
|
||||
key_equal key_eq() const { return table_.key_eq(); }
|
||||
};
|
||||
|
||||
template <class Key, class Hash, class KeyEqual, class Allocator>
|
||||
bool operator==(
|
||||
concurrent_node_set<Key, Hash, KeyEqual, Allocator> const& lhs,
|
||||
concurrent_node_set<Key, Hash, KeyEqual, Allocator> const& rhs)
|
||||
{
|
||||
return lhs.table_ == rhs.table_;
|
||||
}
|
||||
|
||||
template <class Key, class Hash, class KeyEqual, class Allocator>
|
||||
bool operator!=(
|
||||
concurrent_node_set<Key, Hash, KeyEqual, Allocator> const& lhs,
|
||||
concurrent_node_set<Key, Hash, KeyEqual, Allocator> const& rhs)
|
||||
{
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
|
||||
template <class Key, class Hash, class Pred, class Alloc>
|
||||
void swap(concurrent_node_set<Key, Hash, Pred, Alloc>& x,
|
||||
concurrent_node_set<Key, Hash, Pred, Alloc>& y)
|
||||
noexcept(noexcept(x.swap(y)))
|
||||
{
|
||||
x.swap(y);
|
||||
}
|
||||
|
||||
template <class K, class H, class P, class A, class Predicate>
|
||||
typename concurrent_node_set<K, H, P, A>::size_type erase_if(
|
||||
concurrent_node_set<K, H, P, A>& c, Predicate pred)
|
||||
{
|
||||
return c.table_.erase_if(pred);
|
||||
}
|
||||
|
||||
template<class Archive, class K, class H, class KE, class A>
|
||||
void serialize(
|
||||
Archive& ar, concurrent_node_set<K, H, KE, A>& c, unsigned int)
|
||||
{
|
||||
ar & core::make_nvp("table",c.table_);
|
||||
}
|
||||
|
||||
#if BOOST_UNORDERED_TEMPLATE_DEDUCTION_GUIDES
|
||||
|
||||
template <class InputIterator,
|
||||
class Hash =
|
||||
boost::hash<typename std::iterator_traits<InputIterator>::value_type>,
|
||||
class Pred =
|
||||
std::equal_to<typename std::iterator_traits<InputIterator>::value_type>,
|
||||
class Allocator = std::allocator<
|
||||
typename std::iterator_traits<InputIterator>::value_type>,
|
||||
class = std::enable_if_t<detail::is_input_iterator_v<InputIterator> >,
|
||||
class = std::enable_if_t<detail::is_hash_v<Hash> >,
|
||||
class = std::enable_if_t<detail::is_pred_v<Pred> >,
|
||||
class = std::enable_if_t<detail::is_allocator_v<Allocator> > >
|
||||
concurrent_node_set(InputIterator, InputIterator,
|
||||
std::size_t = boost::unordered::detail::foa::default_bucket_count,
|
||||
Hash = Hash(), Pred = Pred(), Allocator = Allocator())
|
||||
-> concurrent_node_set<
|
||||
typename std::iterator_traits<InputIterator>::value_type, Hash, Pred,
|
||||
Allocator>;
|
||||
|
||||
template <class T, class Hash = boost::hash<T>,
|
||||
class Pred = std::equal_to<T>, class Allocator = std::allocator<T>,
|
||||
class = std::enable_if_t<detail::is_hash_v<Hash> >,
|
||||
class = std::enable_if_t<detail::is_pred_v<Pred> >,
|
||||
class = std::enable_if_t<detail::is_allocator_v<Allocator> > >
|
||||
concurrent_node_set(std::initializer_list<T>,
|
||||
std::size_t = boost::unordered::detail::foa::default_bucket_count,
|
||||
Hash = Hash(), Pred = Pred(), Allocator = Allocator())
|
||||
-> concurrent_node_set< T, Hash, Pred, Allocator>;
|
||||
|
||||
template <class InputIterator, class Allocator,
|
||||
class = std::enable_if_t<detail::is_input_iterator_v<InputIterator> >,
|
||||
class = std::enable_if_t<detail::is_allocator_v<Allocator> > >
|
||||
concurrent_node_set(InputIterator, InputIterator, std::size_t, Allocator)
|
||||
-> concurrent_node_set<
|
||||
typename std::iterator_traits<InputIterator>::value_type,
|
||||
boost::hash<typename std::iterator_traits<InputIterator>::value_type>,
|
||||
std::equal_to<typename std::iterator_traits<InputIterator>::value_type>,
|
||||
Allocator>;
|
||||
|
||||
template <class InputIterator, class Allocator,
|
||||
class = std::enable_if_t<detail::is_input_iterator_v<InputIterator> >,
|
||||
class = std::enable_if_t<detail::is_allocator_v<Allocator> > >
|
||||
concurrent_node_set(InputIterator, InputIterator, Allocator)
|
||||
-> concurrent_node_set<
|
||||
typename std::iterator_traits<InputIterator>::value_type,
|
||||
boost::hash<typename std::iterator_traits<InputIterator>::value_type>,
|
||||
std::equal_to<typename std::iterator_traits<InputIterator>::value_type>,
|
||||
Allocator>;
|
||||
|
||||
template <class InputIterator, class Hash, class Allocator,
|
||||
class = std::enable_if_t<detail::is_hash_v<Hash> >,
|
||||
class = std::enable_if_t<detail::is_input_iterator_v<InputIterator> >,
|
||||
class = std::enable_if_t<detail::is_allocator_v<Allocator> > >
|
||||
concurrent_node_set(
|
||||
InputIterator, InputIterator, std::size_t, Hash, Allocator)
|
||||
-> concurrent_node_set<
|
||||
typename std::iterator_traits<InputIterator>::value_type, Hash,
|
||||
std::equal_to<typename std::iterator_traits<InputIterator>::value_type>,
|
||||
Allocator>;
|
||||
|
||||
template <class T, class Allocator,
|
||||
class = std::enable_if_t<detail::is_allocator_v<Allocator> > >
|
||||
concurrent_node_set(std::initializer_list<T>, std::size_t, Allocator)
|
||||
-> concurrent_node_set<T, boost::hash<T>,std::equal_to<T>, Allocator>;
|
||||
|
||||
template <class T, class Allocator,
|
||||
class = std::enable_if_t<detail::is_allocator_v<Allocator> > >
|
||||
concurrent_node_set(std::initializer_list<T >, Allocator)
|
||||
-> concurrent_node_set<T, boost::hash<T>, std::equal_to<T>, Allocator>;
|
||||
|
||||
template <class T, class Hash, class Allocator,
|
||||
class = std::enable_if_t<detail::is_hash_v<Hash> >,
|
||||
class = std::enable_if_t<detail::is_allocator_v<Allocator> > >
|
||||
concurrent_node_set(std::initializer_list<T >, std::size_t,Hash, Allocator)
|
||||
-> concurrent_node_set<T, Hash, std::equal_to<T>, Allocator>;
|
||||
|
||||
#endif
|
||||
|
||||
} // namespace unordered
|
||||
} // namespace boost
|
||||
|
||||
#endif // BOOST_UNORDERED_CONCURRENT_NODE_SET_HPP
|
67
include/boost/unordered/concurrent_node_set_fwd.hpp
Normal file
67
include/boost/unordered/concurrent_node_set_fwd.hpp
Normal file
@ -0,0 +1,67 @@
|
||||
/* Fast open-addressing, node-based concurrent hashset.
|
||||
*
|
||||
* Copyright 2023 Christian Mazakas.
|
||||
* Copyright 2023-2024 Joaquin M Lopez Munoz.
|
||||
* Copyright 2024 Braden Ganetsky.
|
||||
* Distributed under the Boost Software License, Version 1.0.
|
||||
* (See accompanying file LICENSE_1_0.txt or copy at
|
||||
* http://www.boost.org/LICENSE_1_0.txt)
|
||||
*
|
||||
* See https://www.boost.org/libs/unordered for library home page.
|
||||
*/
|
||||
|
||||
#ifndef BOOST_UNORDERED_CONCURRENT_NODE_SET_FWD_HPP
|
||||
#define BOOST_UNORDERED_CONCURRENT_NODE_SET_FWD_HPP
|
||||
|
||||
#include <boost/config.hpp>
|
||||
#include <boost/container_hash/hash_fwd.hpp>
|
||||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
|
||||
#ifndef BOOST_NO_CXX17_HDR_MEMORY_RESOURCE
|
||||
#include <memory_resource>
|
||||
#endif
|
||||
|
||||
namespace boost {
|
||||
namespace unordered {
|
||||
|
||||
template <class Key, class Hash = boost::hash<Key>,
|
||||
class Pred = std::equal_to<Key>,
|
||||
class Allocator = std::allocator<Key> >
|
||||
class concurrent_node_set;
|
||||
|
||||
template <class Key, class Hash, class KeyEqual, class Allocator>
|
||||
bool operator==(
|
||||
concurrent_node_set<Key, Hash, KeyEqual, Allocator> const& lhs,
|
||||
concurrent_node_set<Key, Hash, KeyEqual, Allocator> const& rhs);
|
||||
|
||||
template <class Key, class Hash, class KeyEqual, class Allocator>
|
||||
bool operator!=(
|
||||
concurrent_node_set<Key, Hash, KeyEqual, Allocator> const& lhs,
|
||||
concurrent_node_set<Key, Hash, KeyEqual, Allocator> const& rhs);
|
||||
|
||||
template <class Key, class Hash, class Pred, class Alloc>
|
||||
void swap(concurrent_node_set<Key, Hash, Pred, Alloc>& x,
|
||||
concurrent_node_set<Key, Hash, Pred, Alloc>& y)
|
||||
noexcept(noexcept(x.swap(y)));
|
||||
|
||||
template <class K, class H, class P, class A, class Predicate>
|
||||
typename concurrent_node_set<K, H, P, A>::size_type erase_if(
|
||||
concurrent_node_set<K, H, P, A>& c, Predicate pred);
|
||||
|
||||
#ifndef BOOST_NO_CXX17_HDR_MEMORY_RESOURCE
|
||||
namespace pmr {
|
||||
template <class Key, class Hash = boost::hash<Key>,
|
||||
class Pred = std::equal_to<Key> >
|
||||
using concurrent_node_set = boost::unordered::concurrent_node_set<Key,
|
||||
Hash, Pred, std::pmr::polymorphic_allocator<Key> >;
|
||||
} // namespace pmr
|
||||
#endif
|
||||
|
||||
} // namespace unordered
|
||||
|
||||
using boost::unordered::concurrent_node_set;
|
||||
} // namespace boost
|
||||
|
||||
#endif // BOOST_UNORDERED_CONCURRENT_NODE_SET_FWD_HPP
|
@ -397,10 +397,10 @@ inline void swap(atomic_size_control& x,atomic_size_control& y)
|
||||
* - Parallel versions of [c]visit_all(f) and erase_if(f) are provided based
|
||||
* on C++17 stdlib parallel algorithms.
|
||||
*
|
||||
* Consult boost::concurrent_flat_(map|set) docs for the full API reference.
|
||||
* Heterogeneous lookup is suported by default, that is, without checking for
|
||||
* any ::is_transparent typedefs --this checking is done by the wrapping
|
||||
* containers.
|
||||
* Consult boost::concurrent_(flat|node)_(map|set) docs for the full API
|
||||
* reference. Heterogeneous lookup is suported by default, that is, without
|
||||
* checking for any ::is_transparent typedefs --this checking is done by the
|
||||
* wrapping containers.
|
||||
*
|
||||
* Thread-safe concurrency is implemented using a two-level lock system:
|
||||
*
|
||||
@ -724,6 +724,14 @@ public:
|
||||
BOOST_FORCEINLINE bool
|
||||
insert(value_type&& x){return emplace_impl(std::move(x));}
|
||||
|
||||
template<typename T=element_type>
|
||||
BOOST_FORCEINLINE
|
||||
typename std::enable_if<
|
||||
!std::is_same<T,value_type>::value,
|
||||
bool
|
||||
>::type
|
||||
insert(element_type&& x){return emplace_impl(std::move(x));}
|
||||
|
||||
template<typename Key,typename... Args>
|
||||
BOOST_FORCEINLINE bool try_emplace(Key&& x,Args&&... args)
|
||||
{
|
||||
@ -819,6 +827,30 @@ public:
|
||||
group_shared{},std::forward<F>(f),std::move(x));
|
||||
}
|
||||
|
||||
template<typename F,typename T=element_type>
|
||||
BOOST_FORCEINLINE
|
||||
typename std::enable_if<
|
||||
!std::is_same<T,value_type>::value,
|
||||
bool
|
||||
>::type
|
||||
insert_or_visit(element_type&& x, F&& f)
|
||||
{
|
||||
return emplace_or_visit_impl(
|
||||
group_exclusive{},std::forward<F>(f),std::move(x));
|
||||
}
|
||||
|
||||
template<typename F,typename T=element_type>
|
||||
BOOST_FORCEINLINE
|
||||
typename std::enable_if<
|
||||
!std::is_same<T,value_type>::value,
|
||||
bool
|
||||
>::type
|
||||
insert_or_cvisit(element_type&& x, F&& f)
|
||||
{
|
||||
return emplace_or_visit_impl(
|
||||
group_shared{},std::forward<F>(f),std::move(x));
|
||||
}
|
||||
|
||||
template<typename Key>
|
||||
BOOST_FORCEINLINE std::size_t erase(const Key& x)
|
||||
{
|
||||
@ -889,6 +921,29 @@ public:
|
||||
super::clear();
|
||||
}
|
||||
|
||||
template<typename Key,typename Extractor>
|
||||
BOOST_FORCEINLINE void extract(const Key& x,Extractor&& ext)
|
||||
{
|
||||
extract_if(
|
||||
x,[](const value_type&){return true;},std::forward<Extractor>(ext));
|
||||
}
|
||||
|
||||
template<typename Key,typename F,typename Extractor>
|
||||
BOOST_FORCEINLINE void extract_if(const Key& x,F&& f,Extractor&& ext)
|
||||
{
|
||||
auto lck=shared_access();
|
||||
auto hash=this->hash_for(x);
|
||||
unprotected_internal_visit(
|
||||
group_exclusive{},x,this->position_for(hash),hash,
|
||||
[&,this](group_type* pg,unsigned int n,element_type* p)
|
||||
{
|
||||
if(f(cast_for(group_exclusive{},type_policy::value_from(*p)))){
|
||||
ext(std::move(*p),this->al());
|
||||
super::erase(pg,n,p);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// TODO: should we accept different allocator too?
|
||||
template<typename Hash2,typename Pred2>
|
||||
size_type merge(concurrent_table<TypePolicy,Hash2,Pred2,Allocator>& x)
|
||||
@ -1733,7 +1788,8 @@ private:
|
||||
|
||||
if(this->find(x,pos0,hash))throw_exception(bad_archive_exception());
|
||||
auto loc=this->unchecked_emplace_at(pos0,hash,std::move(x));
|
||||
ar.reset_object_address(std::addressof(*loc.p),std::addressof(x));
|
||||
ar.reset_object_address(
|
||||
std::addressof(type_policy::value_from(*loc.p)),std::addressof(x));
|
||||
}
|
||||
}
|
||||
|
||||
@ -1742,7 +1798,7 @@ private:
|
||||
{
|
||||
using raw_key_type=typename std::remove_const<key_type>::type;
|
||||
using raw_mapped_type=typename std::remove_const<
|
||||
typename TypePolicy::mapped_type>::type;
|
||||
typename type_policy::mapped_type>::type;
|
||||
|
||||
auto lck=exclusive_access();
|
||||
std::size_t s;
|
||||
@ -1766,8 +1822,12 @@ private:
|
||||
|
||||
if(this->find(k,pos0,hash))throw_exception(bad_archive_exception());
|
||||
auto loc=this->unchecked_emplace_at(pos0,hash,std::move(k),std::move(m));
|
||||
ar.reset_object_address(std::addressof(loc.p->first),std::addressof(k));
|
||||
ar.reset_object_address(std::addressof(loc.p->second),std::addressof(m));
|
||||
ar.reset_object_address(
|
||||
std::addressof(type_policy::value_from(*loc.p).first),
|
||||
std::addressof(k));
|
||||
ar.reset_object_address(
|
||||
std::addressof(type_policy::value_from(*loc.p).second),
|
||||
std::addressof(m));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1459,6 +1459,11 @@ public:
|
||||
using stats=table_core_stats;
|
||||
#endif
|
||||
|
||||
#if defined(BOOST_GCC)
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
|
||||
#endif
|
||||
|
||||
table_core(
|
||||
std::size_t n=default_bucket_count,const Hash& h_=Hash(),
|
||||
const Pred& pred_=Pred(),const Allocator& al_=Allocator()):
|
||||
@ -1467,6 +1472,10 @@ public:
|
||||
size_ctrl{initial_max_load(),0}
|
||||
{}
|
||||
|
||||
#if defined(BOOST_GCC)
|
||||
#pragma GCC diagnostic pop
|
||||
#endif
|
||||
|
||||
/* 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
|
||||
@ -2081,6 +2090,11 @@ private:
|
||||
using pred_base=empty_value<Pred,1>;
|
||||
using allocator_base=empty_value<Allocator,2>;
|
||||
|
||||
#if defined(BOOST_GCC)
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
|
||||
#endif
|
||||
|
||||
/* used by allocator-extended move ctor */
|
||||
|
||||
table_core(Hash&& h_,Pred&& pred_,const Allocator& al_):
|
||||
@ -2091,6 +2105,10 @@ private:
|
||||
{
|
||||
}
|
||||
|
||||
#if defined(BOOST_GCC)
|
||||
#pragma GCC diagnostic pop
|
||||
#endif
|
||||
|
||||
arrays_type new_arrays(std::size_t n)const
|
||||
{
|
||||
return arrays_type::new_(typename arrays_type::allocator_type(al()),n);
|
||||
|
@ -1,4 +1,5 @@
|
||||
/* Copyright 2023 Christian Mazakas.
|
||||
* Copyright 2024 Joaquin M Lopez Munoz.
|
||||
* 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)
|
||||
@ -11,8 +12,11 @@
|
||||
|
||||
#include <boost/unordered/detail/opt_storage.hpp>
|
||||
|
||||
#include <boost/assert.hpp>
|
||||
#include <boost/config.hpp>
|
||||
#include <boost/config/workaround.hpp>
|
||||
#include <boost/core/allocator_access.hpp>
|
||||
#include <type_traits>
|
||||
|
||||
namespace boost{
|
||||
namespace unordered{
|
||||
@ -27,6 +31,13 @@ struct insert_return_type
|
||||
NodeType node;
|
||||
};
|
||||
|
||||
template <class NodeType>
|
||||
struct iteratorless_insert_return_type
|
||||
{
|
||||
bool inserted;
|
||||
NodeType node;
|
||||
};
|
||||
|
||||
template <class TypePolicy,class Allocator>
|
||||
struct node_handle_base
|
||||
{
|
||||
@ -42,7 +53,27 @@ struct node_handle_base
|
||||
element_type p_;
|
||||
BOOST_ATTRIBUTE_NO_UNIQUE_ADDRESS opt_storage<Allocator> a_;
|
||||
|
||||
protected:
|
||||
friend struct node_handle_access;
|
||||
|
||||
template<bool B>
|
||||
void move_assign_allocator_if(node_handle_base&& nh)noexcept
|
||||
{
|
||||
move_assign_allocator_if(
|
||||
std::integral_constant<bool,B>{}, std::move(nh));
|
||||
}
|
||||
|
||||
void move_assign_allocator_if(
|
||||
std::true_type, node_handle_base&& nh)noexcept
|
||||
{
|
||||
al()=std::move(nh.al());
|
||||
}
|
||||
|
||||
void move_assign_allocator_if(
|
||||
std::false_type, node_handle_base&&)noexcept
|
||||
{
|
||||
}
|
||||
|
||||
protected:
|
||||
node_value_type& data()noexcept
|
||||
{
|
||||
return *(p_.p);
|
||||
@ -126,9 +157,7 @@ struct node_handle_base
|
||||
BOOST_ASSERT(pocma||al()==nh.al());
|
||||
|
||||
type_policy::destroy(al(),&p_);
|
||||
if(pocma){
|
||||
al()=std::move(nh.al());
|
||||
}
|
||||
move_assign_allocator_if<pocma>(std::move(nh));
|
||||
|
||||
p_=std::move(nh.p_);
|
||||
nh.reset();
|
||||
@ -153,7 +182,17 @@ struct node_handle_base
|
||||
}
|
||||
}
|
||||
|
||||
allocator_type get_allocator()const noexcept{return al();}
|
||||
allocator_type get_allocator()const
|
||||
{
|
||||
#if defined(BOOST_GCC)
|
||||
/* GCC lifetime analysis incorrectly warns about uninitialized
|
||||
* allocator object under some circumstances.
|
||||
*/
|
||||
if(empty())__builtin_unreachable();
|
||||
#endif
|
||||
return al();
|
||||
}
|
||||
|
||||
explicit operator bool()const noexcept{ return !empty();}
|
||||
BOOST_ATTRIBUTE_NODISCARD bool empty()const noexcept{return p_.p==nullptr;}
|
||||
|
||||
@ -196,6 +235,82 @@ struct node_handle_base
|
||||
}
|
||||
};
|
||||
|
||||
// Internal usage of node_handle_base protected API
|
||||
|
||||
struct node_handle_access
|
||||
{
|
||||
template <class TypePolicy, class Allocator>
|
||||
using node_type = node_handle_base<TypePolicy, Allocator>;
|
||||
|
||||
#if BOOST_WORKAROUND(BOOST_CLANG_VERSION,<190000)
|
||||
// https://github.com/llvm/llvm-project/issues/25708
|
||||
|
||||
template <class TypePolicy, class Allocator>
|
||||
struct element_type_impl
|
||||
{
|
||||
using type = typename node_type<TypePolicy, Allocator>::element_type;
|
||||
};
|
||||
template <class TypePolicy, class Allocator>
|
||||
using element_type = typename element_type_impl<TypePolicy, Allocator>::type;
|
||||
#else
|
||||
template <class TypePolicy, class Allocator>
|
||||
using element_type = typename node_type<TypePolicy, Allocator>::element_type;
|
||||
#endif
|
||||
|
||||
template <class TypePolicy, class Allocator>
|
||||
static element_type<TypePolicy, Allocator>&
|
||||
element(node_type<TypePolicy, Allocator>& nh)noexcept
|
||||
{
|
||||
return nh.element();
|
||||
}
|
||||
|
||||
template <class TypePolicy, class Allocator>
|
||||
static element_type<TypePolicy, Allocator>
|
||||
const& element(node_type<TypePolicy, Allocator> const& nh)noexcept
|
||||
{
|
||||
return nh.element();
|
||||
}
|
||||
|
||||
template <class TypePolicy, class Allocator>
|
||||
static void emplace(
|
||||
node_type<TypePolicy, Allocator>& nh,
|
||||
element_type<TypePolicy, Allocator>&& x, Allocator a)
|
||||
{
|
||||
nh.emplace(std::move(x), a);
|
||||
}
|
||||
|
||||
template <class TypePolicy,class Allocator>
|
||||
static void reset(node_type<TypePolicy, Allocator>& nh)
|
||||
{
|
||||
nh.reset();
|
||||
}
|
||||
};
|
||||
|
||||
template <class TypePolicy, class Allocator>
|
||||
class node_handle_emplacer_class
|
||||
{
|
||||
using access = node_handle_access;
|
||||
using node_type = access::node_type<TypePolicy, Allocator>;
|
||||
using element_type = access::element_type<TypePolicy, Allocator>;
|
||||
|
||||
node_type & nh;
|
||||
|
||||
public:
|
||||
node_handle_emplacer_class(node_type& nh_): nh(nh_) {}
|
||||
|
||||
void operator()(element_type&& x,Allocator a)
|
||||
{
|
||||
access::emplace(nh, std::move(x), a);
|
||||
}
|
||||
};
|
||||
|
||||
template <class TypePolicy, class Allocator>
|
||||
node_handle_emplacer_class<TypePolicy, Allocator>
|
||||
node_handle_emplacer(node_handle_base<TypePolicy, Allocator>& nh)
|
||||
{
|
||||
return {nh};
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
56
include/boost/unordered/detail/foa/node_map_handle.hpp
Normal file
56
include/boost/unordered/detail/foa/node_map_handle.hpp
Normal file
@ -0,0 +1,56 @@
|
||||
/* Copyright 2023 Christian Mazakas.
|
||||
* Copyright 2024 Joaquin M Lopez Munoz.
|
||||
* Distributed under the Boost Software License, Version 1.0.
|
||||
* (See accompanying file LICENSE_1_0.txt or copy at
|
||||
* http://www.boost.org/LICENSE_1_0.txt)
|
||||
*
|
||||
* See https://www.boost.org/libs/unordered for library home page.
|
||||
*/
|
||||
|
||||
#ifndef BOOST_UNORDERED_DETAIL_FOA_NODE_MAP_HANDLE_HPP
|
||||
#define BOOST_UNORDERED_DETAIL_FOA_NODE_MAP_HANDLE_HPP
|
||||
|
||||
#include <boost/unordered/detail/foa/node_handle.hpp>
|
||||
|
||||
namespace boost{
|
||||
namespace unordered{
|
||||
namespace detail{
|
||||
namespace foa{
|
||||
|
||||
template <class TypePolicy, class Allocator>
|
||||
struct node_map_handle
|
||||
: public node_handle_base<TypePolicy, Allocator>
|
||||
{
|
||||
private:
|
||||
using base_type = node_handle_base<TypePolicy, Allocator>;
|
||||
|
||||
using typename base_type::type_policy;
|
||||
|
||||
public:
|
||||
using key_type = typename TypePolicy::key_type;
|
||||
using mapped_type = typename TypePolicy::mapped_type;
|
||||
|
||||
constexpr node_map_handle() noexcept = default;
|
||||
node_map_handle(node_map_handle&& nh) noexcept = default;
|
||||
|
||||
node_map_handle& operator=(node_map_handle&&) noexcept = default;
|
||||
|
||||
key_type& key() const
|
||||
{
|
||||
BOOST_ASSERT(!this->empty());
|
||||
return const_cast<key_type&>(this->data().first);
|
||||
}
|
||||
|
||||
mapped_type& mapped() const
|
||||
{
|
||||
BOOST_ASSERT(!this->empty());
|
||||
return const_cast<mapped_type&>(this->data().second);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif // BOOST_UNORDERED_DETAIL_FOA_NODE_MAP_HANDLE_HPP
|
48
include/boost/unordered/detail/foa/node_set_handle.hpp
Normal file
48
include/boost/unordered/detail/foa/node_set_handle.hpp
Normal file
@ -0,0 +1,48 @@
|
||||
/* Copyright 2023 Christian Mazakas.
|
||||
* Copyright 2024 Joaquin M Lopez Munoz.
|
||||
* Distributed under the Boost Software License, Version 1.0.
|
||||
* (See accompanying file LICENSE_1_0.txt or copy at
|
||||
* http://www.boost.org/LICENSE_1_0.txt)
|
||||
*
|
||||
* See https://www.boost.org/libs/unordered for library home page.
|
||||
*/
|
||||
|
||||
#ifndef BOOST_UNORDERED_DETAIL_FOA_NODE_SET_HANDLE_HPP
|
||||
#define BOOST_UNORDERED_DETAIL_FOA_NODE_SET_HANDLE_HPP
|
||||
|
||||
#include <boost/unordered/detail/foa/node_handle.hpp>
|
||||
|
||||
namespace boost{
|
||||
namespace unordered{
|
||||
namespace detail{
|
||||
namespace foa{
|
||||
|
||||
template <class TypePolicy, class Allocator>
|
||||
struct node_set_handle
|
||||
: public detail::foa::node_handle_base<TypePolicy, Allocator>
|
||||
{
|
||||
private:
|
||||
using base_type = detail::foa::node_handle_base<TypePolicy, Allocator>;
|
||||
|
||||
using typename base_type::type_policy;
|
||||
|
||||
public:
|
||||
using value_type = typename TypePolicy::value_type;
|
||||
|
||||
constexpr node_set_handle() noexcept = default;
|
||||
node_set_handle(node_set_handle&& nh) noexcept = default;
|
||||
node_set_handle& operator=(node_set_handle&&) noexcept = default;
|
||||
|
||||
value_type& value() const
|
||||
{
|
||||
BOOST_ASSERT(!this->empty());
|
||||
return const_cast<value_type&>(this->data());
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif // BOOST_UNORDERED_DETAIL_FOA_NODE_SET_HANDLE_HPP
|
@ -1,4 +1,5 @@
|
||||
// Copyright (C) 2022-2023 Christian Mazakas
|
||||
// Copyright (C) 2024 Joaquin M Lopez Munoz
|
||||
// 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)
|
||||
|
||||
@ -10,8 +11,8 @@
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
#include <boost/unordered/detail/foa/element_type.hpp>
|
||||
#include <boost/unordered/detail/foa/node_handle.hpp>
|
||||
#include <boost/unordered/concurrent_node_map_fwd.hpp>
|
||||
#include <boost/unordered/detail/foa/node_map_handle.hpp>
|
||||
#include <boost/unordered/detail/foa/node_map_types.hpp>
|
||||
#include <boost/unordered/detail/foa/table.hpp>
|
||||
#include <boost/unordered/detail/serialize_container.hpp>
|
||||
@ -36,45 +37,13 @@ namespace boost {
|
||||
#pragma warning(disable : 4714) /* marked as __forceinline not inlined */
|
||||
#endif
|
||||
|
||||
namespace detail {
|
||||
template <class TypePolicy, class Allocator>
|
||||
struct node_map_handle
|
||||
: public detail::foa::node_handle_base<TypePolicy, Allocator>
|
||||
{
|
||||
private:
|
||||
using base_type = detail::foa::node_handle_base<TypePolicy, Allocator>;
|
||||
|
||||
using typename base_type::type_policy;
|
||||
|
||||
template <class Key, class T, class Hash, class Pred, class Alloc>
|
||||
friend class boost::unordered::unordered_node_map;
|
||||
|
||||
public:
|
||||
using key_type = typename TypePolicy::key_type;
|
||||
using mapped_type = typename TypePolicy::mapped_type;
|
||||
|
||||
constexpr node_map_handle() noexcept = default;
|
||||
node_map_handle(node_map_handle&& nh) noexcept = default;
|
||||
|
||||
node_map_handle& operator=(node_map_handle&&) noexcept = default;
|
||||
|
||||
key_type& key() const
|
||||
{
|
||||
BOOST_ASSERT(!this->empty());
|
||||
return const_cast<key_type&>(this->data().first);
|
||||
}
|
||||
|
||||
mapped_type& mapped() const
|
||||
{
|
||||
BOOST_ASSERT(!this->empty());
|
||||
return const_cast<mapped_type&>(this->data().second);
|
||||
}
|
||||
};
|
||||
} // namespace detail
|
||||
|
||||
template <class Key, class T, class Hash, class KeyEqual, class Allocator>
|
||||
class unordered_node_map
|
||||
{
|
||||
template <class Key2, class T2, class Hash2, class Pred2,
|
||||
class Allocator2>
|
||||
friend class concurrent_node_map;
|
||||
|
||||
using map_types = detail::foa::node_map_types<Key, T,
|
||||
typename boost::allocator_void_pointer<Allocator>::type>;
|
||||
|
||||
@ -109,7 +78,7 @@ namespace boost {
|
||||
typename boost::allocator_const_pointer<allocator_type>::type;
|
||||
using iterator = typename table_type::iterator;
|
||||
using const_iterator = typename table_type::const_iterator;
|
||||
using node_type = detail::node_map_handle<map_types,
|
||||
using node_type = detail::foa::node_map_handle<map_types,
|
||||
typename boost::allocator_rebind<Allocator,
|
||||
typename map_types::value_type>::type>;
|
||||
using insert_return_type =
|
||||
@ -220,6 +189,12 @@ namespace boost {
|
||||
{
|
||||
}
|
||||
|
||||
unordered_node_map(
|
||||
concurrent_node_map<Key, T, Hash, KeyEqual, Allocator>&& other)
|
||||
: table_(std::move(other.table_))
|
||||
{
|
||||
}
|
||||
|
||||
~unordered_node_map() = default;
|
||||
|
||||
unordered_node_map& operator=(unordered_node_map const& other)
|
||||
@ -307,15 +282,17 @@ namespace boost {
|
||||
|
||||
insert_return_type insert(node_type&& nh)
|
||||
{
|
||||
using access = detail::foa::node_handle_access;
|
||||
|
||||
if (nh.empty()) {
|
||||
return {end(), false, node_type{}};
|
||||
}
|
||||
|
||||
BOOST_ASSERT(get_allocator() == nh.get_allocator());
|
||||
|
||||
auto itp = table_.insert(std::move(nh.element()));
|
||||
auto itp = table_.insert(std::move(access::element(nh)));
|
||||
if (itp.second) {
|
||||
nh.reset();
|
||||
access::reset(nh);
|
||||
return {itp.first, true, node_type{}};
|
||||
} else {
|
||||
return {itp.first, false, std::move(nh)};
|
||||
@ -324,15 +301,17 @@ namespace boost {
|
||||
|
||||
iterator insert(const_iterator, node_type&& nh)
|
||||
{
|
||||
using access = detail::foa::node_handle_access;
|
||||
|
||||
if (nh.empty()) {
|
||||
return end();
|
||||
}
|
||||
|
||||
BOOST_ASSERT(get_allocator() == nh.get_allocator());
|
||||
|
||||
auto itp = table_.insert(std::move(nh.element()));
|
||||
auto itp = table_.insert(std::move(access::element(nh)));
|
||||
if (itp.second) {
|
||||
nh.reset();
|
||||
access::reset(nh);
|
||||
return itp.first;
|
||||
} else {
|
||||
return itp.first;
|
||||
@ -507,7 +486,8 @@ namespace boost {
|
||||
BOOST_ASSERT(pos != end());
|
||||
node_type nh;
|
||||
auto elem = table_.extract(pos);
|
||||
nh.emplace(std::move(elem), get_allocator());
|
||||
detail::foa::node_handle_emplacer(nh)(
|
||||
std::move(elem), get_allocator());
|
||||
return nh;
|
||||
}
|
||||
|
||||
|
@ -1,4 +1,5 @@
|
||||
// Copyright (C) 2022-2023 Christian Mazakas
|
||||
// Copyright (C) 2024 Joaquin M Lopez Munoz
|
||||
// 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)
|
||||
|
||||
@ -10,8 +11,9 @@
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
#include <boost/unordered/concurrent_node_set_fwd.hpp>
|
||||
#include <boost/unordered/detail/foa/element_type.hpp>
|
||||
#include <boost/unordered/detail/foa/node_handle.hpp>
|
||||
#include <boost/unordered/detail/foa/node_set_handle.hpp>
|
||||
#include <boost/unordered/detail/foa/node_set_types.hpp>
|
||||
#include <boost/unordered/detail/foa/table.hpp>
|
||||
#include <boost/unordered/detail/serialize_container.hpp>
|
||||
@ -35,37 +37,12 @@ namespace boost {
|
||||
#pragma warning(disable : 4714) /* marked as __forceinline not inlined */
|
||||
#endif
|
||||
|
||||
namespace detail {
|
||||
template <class TypePolicy, class Allocator>
|
||||
struct node_set_handle
|
||||
: public detail::foa::node_handle_base<TypePolicy, Allocator>
|
||||
{
|
||||
private:
|
||||
using base_type = detail::foa::node_handle_base<TypePolicy, Allocator>;
|
||||
|
||||
using typename base_type::type_policy;
|
||||
|
||||
template <class Key, class Hash, class Pred, class Alloc>
|
||||
friend class boost::unordered::unordered_node_set;
|
||||
|
||||
public:
|
||||
using value_type = typename TypePolicy::value_type;
|
||||
|
||||
constexpr node_set_handle() noexcept = default;
|
||||
node_set_handle(node_set_handle&& nh) noexcept = default;
|
||||
node_set_handle& operator=(node_set_handle&&) noexcept = default;
|
||||
|
||||
value_type& value() const
|
||||
{
|
||||
BOOST_ASSERT(!this->empty());
|
||||
return const_cast<value_type&>(this->data());
|
||||
}
|
||||
};
|
||||
} // namespace detail
|
||||
|
||||
template <class Key, class Hash, class KeyEqual, class Allocator>
|
||||
class unordered_node_set
|
||||
{
|
||||
template <class Key2, class Hash2, class Pred2, class Allocator2>
|
||||
friend class concurrent_node_set;
|
||||
|
||||
using set_types = detail::foa::node_set_types<Key,
|
||||
typename boost::allocator_void_pointer<Allocator>::type>;
|
||||
|
||||
@ -99,7 +76,7 @@ namespace boost {
|
||||
typename boost::allocator_const_pointer<allocator_type>::type;
|
||||
using iterator = typename table_type::iterator;
|
||||
using const_iterator = typename table_type::const_iterator;
|
||||
using node_type = detail::node_set_handle<set_types,
|
||||
using node_type = detail::foa::node_set_handle<set_types,
|
||||
typename boost::allocator_rebind<Allocator,
|
||||
typename set_types::value_type>::type>;
|
||||
using insert_return_type =
|
||||
@ -210,6 +187,12 @@ namespace boost {
|
||||
{
|
||||
}
|
||||
|
||||
unordered_node_set(
|
||||
concurrent_node_set<Key, Hash, KeyEqual, Allocator>&& other)
|
||||
: table_(std::move(other.table_))
|
||||
{
|
||||
}
|
||||
|
||||
~unordered_node_set() = default;
|
||||
|
||||
unordered_node_set& operator=(unordered_node_set const& other)
|
||||
@ -312,15 +295,17 @@ namespace boost {
|
||||
|
||||
insert_return_type insert(node_type&& nh)
|
||||
{
|
||||
using access = detail::foa::node_handle_access;
|
||||
|
||||
if (nh.empty()) {
|
||||
return {end(), false, node_type{}};
|
||||
}
|
||||
|
||||
BOOST_ASSERT(get_allocator() == nh.get_allocator());
|
||||
|
||||
auto itp = table_.insert(std::move(nh.element()));
|
||||
auto itp = table_.insert(std::move(access::element(nh)));
|
||||
if (itp.second) {
|
||||
nh.reset();
|
||||
access::reset(nh);
|
||||
return {itp.first, true, node_type{}};
|
||||
} else {
|
||||
return {itp.first, false, std::move(nh)};
|
||||
@ -329,15 +314,17 @@ namespace boost {
|
||||
|
||||
iterator insert(const_iterator, node_type&& nh)
|
||||
{
|
||||
using access = detail::foa::node_handle_access;
|
||||
|
||||
if (nh.empty()) {
|
||||
return end();
|
||||
}
|
||||
|
||||
BOOST_ASSERT(get_allocator() == nh.get_allocator());
|
||||
|
||||
auto itp = table_.insert(std::move(nh.element()));
|
||||
auto itp = table_.insert(std::move(access::element(nh)));
|
||||
if (itp.second) {
|
||||
nh.reset();
|
||||
access::reset(nh);
|
||||
return itp.first;
|
||||
} else {
|
||||
return itp.first;
|
||||
@ -395,7 +382,8 @@ namespace boost {
|
||||
BOOST_ASSERT(pos != end());
|
||||
node_type nh;
|
||||
auto elem = table_.extract(pos);
|
||||
nh.emplace(std::move(elem), get_allocator());
|
||||
detail::foa::node_handle_emplacer(nh)(
|
||||
std::move(elem), get_allocator());
|
||||
return nh;
|
||||
}
|
||||
|
||||
|
@ -135,7 +135,7 @@ foa_tests(SOURCES exception/merge_exception_tests.cpp)
|
||||
|
||||
# CFOA tests
|
||||
|
||||
cfoa_tests(SOURCES cfoa/insert_tests.cpp)
|
||||
cfoa_tests(SOURCES cfoa/insert_tests.cpp COMPILE_OPTIONS $<$<CXX_COMPILER_ID:MSVC>:/bigobj>)
|
||||
cfoa_tests(SOURCES cfoa/erase_tests.cpp)
|
||||
cfoa_tests(SOURCES cfoa/try_emplace_tests.cpp)
|
||||
cfoa_tests(SOURCES cfoa/emplace_tests.cpp)
|
||||
|
@ -110,6 +110,7 @@ local FCA_TESTS =
|
||||
move_tests
|
||||
narrow_cast_tests
|
||||
node_handle_tests
|
||||
node_handle_allocator_tests
|
||||
noexcept_tests
|
||||
post_move_tests
|
||||
prime_fmod_tests
|
||||
@ -232,6 +233,7 @@ local FOA_TESTS =
|
||||
fancy_pointer_noleak
|
||||
pmr_allocator_tests
|
||||
stats_tests
|
||||
node_handle_allocator_tests
|
||||
;
|
||||
|
||||
for local test in $(FOA_TESTS)
|
||||
@ -308,11 +310,10 @@ alias foa_tests :
|
||||
;
|
||||
|
||||
local CFOA_TESTS =
|
||||
insert_tests
|
||||
erase_tests
|
||||
try_emplace_tests
|
||||
emplace_tests
|
||||
visit_tests
|
||||
extract_insert_tests
|
||||
constructor_tests
|
||||
assign_tests
|
||||
clear_tests
|
||||
@ -338,6 +339,7 @@ local CFOA_TESTS =
|
||||
explicit_alloc_ctor_tests
|
||||
pmr_allocator_tests
|
||||
stats_tests
|
||||
node_handle_allocator_tests
|
||||
;
|
||||
|
||||
for local test in $(CFOA_TESTS)
|
||||
@ -348,6 +350,28 @@ for local test in $(CFOA_TESTS)
|
||||
;
|
||||
}
|
||||
|
||||
run cfoa/insert_tests.cpp
|
||||
:
|
||||
:
|
||||
: $(CPP11) <threading>multi
|
||||
<toolset>msvc:<cxxflags>/bigobj
|
||||
<toolset>gcc:<inlining>on
|
||||
<toolset>gcc:<optimization>space
|
||||
<toolset>clang:<inlining>on
|
||||
<toolset>clang:<optimization>space
|
||||
: cfoa_insert_tests ;
|
||||
|
||||
run cfoa/visit_tests.cpp
|
||||
:
|
||||
:
|
||||
: $(CPP11) <threading>multi
|
||||
<toolset>msvc:<cxxflags>/bigobj
|
||||
<toolset>gcc:<inlining>on
|
||||
<toolset>gcc:<optimization>space
|
||||
<toolset>clang:<inlining>on
|
||||
<toolset>clang:<optimization>space
|
||||
: cfoa_visit_tests ;
|
||||
|
||||
run cfoa/serialization_tests.cpp
|
||||
:
|
||||
:
|
||||
@ -383,6 +407,8 @@ make_cfoa_interprocess_concurrency_tests cfoa_interproc_conc_tests_stats
|
||||
|
||||
alias cfoa_tests :
|
||||
cfoa_$(CFOA_TESTS)
|
||||
cfoa_insert_tests
|
||||
cfoa_visit_tests
|
||||
cfoa_serialization_tests
|
||||
cfoa_interproc_conc_tests
|
||||
cfoa_interproc_conc_tests_stats ;
|
||||
|
@ -1,5 +1,5 @@
|
||||
// Copyright (C) 2023 Christian Mazakas
|
||||
// Copyright (C) 2023 Joaquin M Lopez Munoz
|
||||
// Copyright (C) 2023-2024 Joaquin M Lopez Munoz
|
||||
// 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)
|
||||
|
||||
@ -7,6 +7,8 @@
|
||||
|
||||
#include <boost/unordered/concurrent_flat_map.hpp>
|
||||
#include <boost/unordered/concurrent_flat_set.hpp>
|
||||
#include <boost/unordered/concurrent_node_map.hpp>
|
||||
#include <boost/unordered/concurrent_node_set.hpp>
|
||||
|
||||
#if defined(__clang__) && defined(__has_warning)
|
||||
|
||||
@ -37,19 +39,35 @@ using key_equal = stateful_key_equal;
|
||||
using map_type = boost::unordered::concurrent_flat_map<raii, raii, hasher,
|
||||
key_equal, stateful_allocator<std::pair<raii const, raii> > >;
|
||||
|
||||
using node_map_type = boost::unordered::concurrent_node_map<raii, raii, hasher,
|
||||
key_equal, stateful_allocator<std::pair<raii const, raii> > >;
|
||||
|
||||
using set_type = boost::unordered::concurrent_flat_set<raii, hasher,
|
||||
key_equal, stateful_allocator<raii> >;
|
||||
|
||||
using node_set_type = boost::unordered::concurrent_node_set<raii, hasher,
|
||||
key_equal, stateful_allocator<raii> >;
|
||||
|
||||
using fancy_map_type = boost::unordered::concurrent_flat_map<raii, raii, hasher,
|
||||
key_equal, stateful_allocator2<std::pair<raii const, raii> > >;
|
||||
|
||||
using fancy_node_map_type = boost::unordered::concurrent_node_map<raii, raii, hasher,
|
||||
key_equal, stateful_allocator2<std::pair<raii const, raii> > >;
|
||||
|
||||
using fancy_set_type = boost::unordered::concurrent_flat_set<raii, hasher,
|
||||
key_equal, stateful_allocator2<raii> >;
|
||||
|
||||
using fancy_node_set_type = boost::unordered::concurrent_node_set<raii, hasher,
|
||||
key_equal, stateful_allocator2<raii> >;
|
||||
|
||||
map_type* test_map;
|
||||
node_map_type* test_node_map;
|
||||
set_type* test_set;
|
||||
node_set_type* test_node_set;
|
||||
fancy_map_type* fancy_test_map;
|
||||
fancy_node_map_type* fancy_test_node_map;
|
||||
fancy_set_type* fancy_test_set;
|
||||
fancy_node_set_type* fancy_test_node_set;
|
||||
|
||||
std::initializer_list<map_type::value_type> map_init_list{
|
||||
{raii{0}, raii{0}},
|
||||
@ -102,7 +120,9 @@ std::initializer_list<set_type::value_type> set_init_list{
|
||||
};
|
||||
|
||||
auto test_map_and_init_list=std::make_pair(test_map,map_init_list);
|
||||
auto test_node_map_and_init_list=std::make_pair(test_node_map,map_init_list);
|
||||
auto test_set_and_init_list=std::make_pair(test_set,set_init_list);
|
||||
auto test_node_set_and_init_list=std::make_pair(test_node_set,set_init_list);
|
||||
|
||||
template <class T,bool POCCA, bool POCMA>
|
||||
struct poca_allocator: fancy_allocator<T>
|
||||
@ -928,7 +948,7 @@ namespace {
|
||||
}
|
||||
|
||||
template <class X, class GF>
|
||||
void flat_move_assign(X*, GF gen_factory, test::random_generator rg)
|
||||
void nonconcurrent_move_assign(X*, GF gen_factory, test::random_generator rg)
|
||||
{
|
||||
using value_type = typename X::value_type;
|
||||
static constexpr auto value_type_cardinality =
|
||||
@ -950,16 +970,17 @@ namespace {
|
||||
{
|
||||
raii::reset_counts();
|
||||
|
||||
flat_container<X> flat(values.begin(), values.end(), values.size(),
|
||||
nonconcurrent_container<X> nonc(
|
||||
values.begin(), values.end(), values.size(),
|
||||
hasher(1), key_equal(2), allocator_type(3));
|
||||
|
||||
X x(0, hasher(2), key_equal(1), allocator_type(3));
|
||||
|
||||
BOOST_TEST(flat.get_allocator() == x.get_allocator());
|
||||
BOOST_TEST(nonc.get_allocator() == x.get_allocator());
|
||||
|
||||
x = std::move(flat);
|
||||
x = std::move(nonc);
|
||||
|
||||
BOOST_TEST(flat.empty());
|
||||
BOOST_TEST(nonc.empty());
|
||||
BOOST_TEST_EQ(x.size(), reference_cont.size());
|
||||
|
||||
test_fuzzy_matches_reference(x, reference_cont, rg);
|
||||
@ -983,17 +1004,18 @@ namespace {
|
||||
X x(values.begin(), values.end(), values.size(), hasher(1),
|
||||
key_equal(2), allocator_type(3));
|
||||
|
||||
flat_container<X> flat(0, hasher(2), key_equal(1), allocator_type(3));
|
||||
nonconcurrent_container<X> nonc(
|
||||
0, hasher(2), key_equal(1), allocator_type(3));
|
||||
|
||||
BOOST_TEST(flat.get_allocator() == x.get_allocator());
|
||||
BOOST_TEST(nonc.get_allocator() == x.get_allocator());
|
||||
|
||||
flat = std::move(x);
|
||||
nonc = std::move(x);
|
||||
|
||||
BOOST_TEST(x.empty());
|
||||
BOOST_TEST_EQ(flat.size(), reference_cont.size());
|
||||
BOOST_TEST_EQ(nonc.size(), reference_cont.size());
|
||||
|
||||
BOOST_TEST_EQ(flat.hash_function(), hasher(1));
|
||||
BOOST_TEST_EQ(flat.key_eq(), key_equal(2));
|
||||
BOOST_TEST_EQ(nonc.hash_function(), hasher(1));
|
||||
BOOST_TEST_EQ(nonc.key_eq(), key_equal(2));
|
||||
|
||||
BOOST_TEST_EQ(
|
||||
raii::copy_constructor, value_type_cardinality * reference_cont.size());
|
||||
@ -1008,16 +1030,17 @@ namespace {
|
||||
{
|
||||
raii::reset_counts();
|
||||
|
||||
flat_container<X> flat(values.begin(), values.end(), values.size(),
|
||||
nonconcurrent_container<X> nonc(
|
||||
values.begin(), values.end(), values.size(),
|
||||
hasher(1), key_equal(2), allocator_type(3));
|
||||
|
||||
X x(0, hasher(2), key_equal(1), allocator_type(4));
|
||||
|
||||
BOOST_TEST(flat.get_allocator() != x.get_allocator());
|
||||
BOOST_TEST(nonc.get_allocator() != x.get_allocator());
|
||||
|
||||
x = std::move(flat);
|
||||
x = std::move(nonc);
|
||||
|
||||
BOOST_TEST(flat.empty());
|
||||
BOOST_TEST(nonc.empty());
|
||||
BOOST_TEST_EQ(x.size(), reference_cont.size());
|
||||
|
||||
test_fuzzy_matches_reference(x, reference_cont, rg);
|
||||
@ -1043,17 +1066,18 @@ namespace {
|
||||
X x(values.begin(), values.end(), values.size(), hasher(1),
|
||||
key_equal(2), allocator_type(3));
|
||||
|
||||
flat_container<X> flat(0, hasher(2), key_equal(1), allocator_type(4));
|
||||
nonconcurrent_container<X> nonc(
|
||||
0, hasher(2), key_equal(1), allocator_type(4));
|
||||
|
||||
BOOST_TEST(flat.get_allocator() != x.get_allocator());
|
||||
BOOST_TEST(nonc.get_allocator() != x.get_allocator());
|
||||
|
||||
flat = std::move(x);
|
||||
nonc = std::move(x);
|
||||
|
||||
BOOST_TEST(x.empty());
|
||||
BOOST_TEST_EQ(flat.size(), reference_cont.size());
|
||||
BOOST_TEST_EQ(nonc.size(), reference_cont.size());
|
||||
|
||||
BOOST_TEST_EQ(flat.hash_function(), hasher(1));
|
||||
BOOST_TEST_EQ(flat.key_eq(), key_equal(2));
|
||||
BOOST_TEST_EQ(nonc.hash_function(), hasher(1));
|
||||
BOOST_TEST_EQ(nonc.key_eq(), key_equal(2));
|
||||
|
||||
BOOST_TEST_EQ(
|
||||
raii::copy_constructor, value_type_cardinality * reference_cont.size());
|
||||
@ -1073,29 +1097,31 @@ namespace {
|
||||
// clang-format off
|
||||
UNORDERED_TEST(
|
||||
copy_assign,
|
||||
((test_map)(test_set))
|
||||
((test_map)(test_node_map)(test_set)(test_node_set))
|
||||
((value_type_generator_factory))
|
||||
((default_generator)(sequential)(limited_range)))
|
||||
|
||||
UNORDERED_TEST(
|
||||
move_assign,
|
||||
((test_map)(test_set))
|
||||
((test_map)(test_node_map)(test_set)(test_node_set))
|
||||
((value_type_generator_factory))
|
||||
((default_generator)(sequential)(limited_range)))
|
||||
|
||||
UNORDERED_TEST(
|
||||
initializer_list_assign,
|
||||
((test_map_and_init_list)(test_set_and_init_list)))
|
||||
((test_map_and_init_list)(test_node_map_and_init_list)
|
||||
(test_set_and_init_list)(test_node_set_and_init_list)))
|
||||
|
||||
UNORDERED_TEST(
|
||||
insert_and_assign,
|
||||
((test_map)(test_set))
|
||||
((test_map)(test_node_map)(test_set)(test_node_set))
|
||||
((init_type_generator_factory))
|
||||
((default_generator)(sequential)(limited_range)))
|
||||
|
||||
UNORDERED_TEST(
|
||||
flat_move_assign,
|
||||
((test_map)(test_set)(fancy_test_map)(fancy_test_set))
|
||||
nonconcurrent_move_assign,
|
||||
((test_map)(test_node_map)(test_set)(test_node_set)
|
||||
(fancy_test_map)(fancy_test_node_map)(fancy_test_set)(fancy_test_node_set))
|
||||
((init_type_generator_factory))
|
||||
((default_generator)(sequential)(limited_range)))
|
||||
// clang-format on
|
||||
|
@ -1,5 +1,5 @@
|
||||
// Copyright (C) 2023 Christian Mazakas
|
||||
// Copyright (C) 2023 Joaquin M Lopez Munoz
|
||||
// Copyright (C) 2023-2024 Joaquin M Lopez Munoz
|
||||
// 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)
|
||||
|
||||
@ -7,6 +7,8 @@
|
||||
|
||||
#include <boost/unordered/concurrent_flat_map.hpp>
|
||||
#include <boost/unordered/concurrent_flat_set.hpp>
|
||||
#include <boost/unordered/concurrent_node_map.hpp>
|
||||
#include <boost/unordered/concurrent_node_set.hpp>
|
||||
|
||||
test::seed_t initialize_seed{674140082};
|
||||
|
||||
@ -20,11 +22,19 @@ using key_equal = stateful_key_equal;
|
||||
using map_type = boost::unordered::concurrent_flat_map<raii, raii, hasher,
|
||||
key_equal, stateful_allocator<std::pair<raii const, raii> > >;
|
||||
|
||||
using node_map_type = boost::unordered::concurrent_node_map<raii, raii, hasher,
|
||||
key_equal, stateful_allocator<std::pair<raii const, raii> > >;
|
||||
|
||||
using set_type = boost::unordered::concurrent_flat_set<raii, hasher,
|
||||
key_equal, stateful_allocator<raii> >;
|
||||
|
||||
using node_set_type = boost::unordered::concurrent_node_set<raii, hasher,
|
||||
key_equal, stateful_allocator<raii> >;
|
||||
|
||||
map_type* test_map;
|
||||
node_map_type* test_node_map;
|
||||
set_type* test_set;
|
||||
node_set_type* test_node_set;
|
||||
|
||||
namespace {
|
||||
template <class X, class GF>
|
||||
@ -130,12 +140,12 @@ namespace {
|
||||
// clang-format off
|
||||
UNORDERED_TEST(
|
||||
clear_tests,
|
||||
((test_map)(test_set))
|
||||
((test_map)(test_node_map)(test_set)(test_node_set))
|
||||
((value_type_generator_factory))
|
||||
((default_generator)(sequential)(limited_range)))
|
||||
|
||||
UNORDERED_TEST(insert_and_clear,
|
||||
((test_map)(test_set))
|
||||
((test_map)(test_node_map)(test_set)(test_node_set))
|
||||
((value_type_generator_factory))
|
||||
((default_generator)(sequential)(limited_range)))
|
||||
// clang-format on
|
||||
|
@ -1,5 +1,5 @@
|
||||
// Copyright (C) 2023 Christian Mazakas
|
||||
// Copyright (C) 2023 Joaquin M Lopez Munoz
|
||||
// Copyright (C) 2023-2024 Joaquin M Lopez Munoz
|
||||
// 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)
|
||||
|
||||
@ -8,8 +8,12 @@
|
||||
|
||||
#include <boost/unordered/concurrent_flat_map_fwd.hpp>
|
||||
#include <boost/unordered/concurrent_flat_set_fwd.hpp>
|
||||
#include <boost/unordered/concurrent_node_map_fwd.hpp>
|
||||
#include <boost/unordered/concurrent_node_set_fwd.hpp>
|
||||
#include <boost/unordered/unordered_flat_map.hpp>
|
||||
#include <boost/unordered/unordered_flat_set.hpp>
|
||||
#include <boost/unordered/unordered_node_map.hpp>
|
||||
#include <boost/unordered/unordered_node_set.hpp>
|
||||
|
||||
#include <cstddef>
|
||||
#include <type_traits>
|
||||
@ -27,6 +31,31 @@ struct value_cardinality<std::pair<K, V> >
|
||||
static constexpr std::size_t value=2;
|
||||
};
|
||||
|
||||
template <typename K>
|
||||
struct value_nonconst_cardinality
|
||||
{
|
||||
static constexpr std::size_t value=1;
|
||||
};
|
||||
|
||||
template <typename K, typename V>
|
||||
struct value_nonconst_cardinality<std::pair<K, V> >
|
||||
{
|
||||
static constexpr std::size_t value=
|
||||
1 * !std::is_const<K>::value +
|
||||
1 * !std::is_const<V>::value ;
|
||||
};
|
||||
|
||||
template <class Container>
|
||||
struct is_container_node_based: std::false_type {};
|
||||
|
||||
template <typename K, typename V, typename H, typename P, typename A>
|
||||
struct is_container_node_based<boost::concurrent_node_map<K, V, H, P, A> >
|
||||
: std::true_type {};
|
||||
|
||||
template <typename K, typename H, typename P, typename A>
|
||||
struct is_container_node_based<boost::concurrent_node_set<K, H, P, A> >
|
||||
: std::true_type {};
|
||||
|
||||
template <class Container>
|
||||
struct reference_container_impl;
|
||||
|
||||
@ -39,30 +68,55 @@ struct reference_container_impl<boost::concurrent_flat_map<K, V, H, P, A> >
|
||||
using type = boost::unordered_flat_map<K, V>;
|
||||
};
|
||||
|
||||
template <typename K, typename V, typename H, typename P, typename A>
|
||||
struct reference_container_impl<boost::concurrent_node_map<K, V, H, P, A> >
|
||||
{
|
||||
using type = boost::unordered_node_map<K, V>;
|
||||
};
|
||||
|
||||
template <typename K, typename H, typename P, typename A>
|
||||
struct reference_container_impl<boost::concurrent_flat_set<K, H, P, A> >
|
||||
{
|
||||
using type = boost::unordered_flat_set<K>;
|
||||
};
|
||||
|
||||
template <class Container>
|
||||
struct flat_container_impl;
|
||||
template <typename K, typename H, typename P, typename A>
|
||||
struct reference_container_impl<boost::concurrent_node_set<K, H, P, A> >
|
||||
{
|
||||
using type = boost::unordered_node_set<K>;
|
||||
};
|
||||
|
||||
template <class Container>
|
||||
using flat_container = typename flat_container_impl<Container>::type;
|
||||
struct nonconcurrent_container_impl;
|
||||
|
||||
template <class Container>
|
||||
using nonconcurrent_container =
|
||||
typename nonconcurrent_container_impl<Container>::type;
|
||||
|
||||
template <typename K, typename V, typename H, typename P, typename A>
|
||||
struct flat_container_impl<boost::concurrent_flat_map<K, V, H, P, A> >
|
||||
struct nonconcurrent_container_impl<boost::concurrent_flat_map<K, V, H, P, A> >
|
||||
{
|
||||
using type = boost::unordered_flat_map<K, V, H, P, A>;
|
||||
};
|
||||
|
||||
template <typename K, typename V, typename H, typename P, typename A>
|
||||
struct nonconcurrent_container_impl<boost::concurrent_node_map<K, V, H, P, A> >
|
||||
{
|
||||
using type = boost::unordered_node_map<K, V, H, P, A>;
|
||||
};
|
||||
|
||||
template <typename K, typename H, typename P, typename A>
|
||||
struct flat_container_impl<boost::concurrent_flat_set<K, H, P, A> >
|
||||
struct nonconcurrent_container_impl<boost::concurrent_flat_set<K, H, P, A> >
|
||||
{
|
||||
using type = boost::unordered_flat_set<K, H, P, A>;
|
||||
};
|
||||
|
||||
template <typename K, typename H, typename P, typename A>
|
||||
struct nonconcurrent_container_impl<boost::concurrent_node_set<K, H, P, A> >
|
||||
{
|
||||
using type = boost::unordered_node_set<K, H, P, A>;
|
||||
};
|
||||
|
||||
template <typename Container, template <typename> class Allocator>
|
||||
struct replace_allocator_impl;
|
||||
|
||||
@ -96,6 +150,32 @@ struct replace_allocator_impl<
|
||||
boost::concurrent_flat_set<K, H, P, Allocator<value_type> >;
|
||||
};
|
||||
|
||||
template <
|
||||
typename K, typename V, typename H, typename P, typename A,
|
||||
template <typename> class Allocator
|
||||
>
|
||||
struct replace_allocator_impl<
|
||||
boost::concurrent_node_map<K, V, H, P, A>, Allocator>
|
||||
{
|
||||
using value_type =
|
||||
typename boost::concurrent_node_map<K, V, H, P, A>::value_type;
|
||||
using type =
|
||||
boost::concurrent_node_map<K, V, H, P, Allocator<value_type> >;
|
||||
};
|
||||
|
||||
template <
|
||||
typename K, typename H, typename P, typename A,
|
||||
template <typename> class Allocator
|
||||
>
|
||||
struct replace_allocator_impl<
|
||||
boost::concurrent_node_set<K, H, P, A>, Allocator>
|
||||
{
|
||||
using value_type =
|
||||
typename boost::concurrent_node_set<K, H, P, A>::value_type;
|
||||
using type =
|
||||
boost::concurrent_node_set<K, H, P, Allocator<value_type> >;
|
||||
};
|
||||
|
||||
template <typename K>
|
||||
K const& get_key(K const& x) { return x; }
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
// Copyright (C) 2023 Christian Mazakas
|
||||
// Copyright (C) 2023 Joaquin M Lopez Munoz
|
||||
// Copyright (C) 2023-2024 Joaquin M Lopez Munoz
|
||||
// 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)
|
||||
|
||||
@ -7,6 +7,8 @@
|
||||
|
||||
#include <boost/unordered/concurrent_flat_map.hpp>
|
||||
#include <boost/unordered/concurrent_flat_set.hpp>
|
||||
#include <boost/unordered/concurrent_node_map.hpp>
|
||||
#include <boost/unordered/concurrent_node_set.hpp>
|
||||
|
||||
test::seed_t initialize_seed(4122023);
|
||||
|
||||
@ -52,11 +54,19 @@ using key_equal = stateful_key_equal;
|
||||
using map_type = boost::unordered::concurrent_flat_map<raii, raii, hasher,
|
||||
key_equal, stateful_allocator<std::pair<raii const, raii> > >;
|
||||
|
||||
using node_map_type = boost::unordered::concurrent_node_map<raii, raii, hasher,
|
||||
key_equal, stateful_allocator<std::pair<raii const, raii> > >;
|
||||
|
||||
using set_type = boost::unordered::concurrent_flat_set<raii, hasher,
|
||||
key_equal, stateful_allocator<raii> >;
|
||||
|
||||
using node_set_type = boost::unordered::concurrent_node_set<raii, hasher,
|
||||
key_equal, stateful_allocator<raii> >;
|
||||
|
||||
map_type* test_map;
|
||||
node_map_type* test_node_map;
|
||||
set_type* test_set;
|
||||
node_set_type* test_node_set;
|
||||
|
||||
std::initializer_list<map_type::value_type> map_init_list{
|
||||
{raii{0}, raii{0}},
|
||||
@ -109,7 +119,9 @@ std::initializer_list<set_type::value_type> set_init_list{
|
||||
};
|
||||
|
||||
auto test_map_and_init_list=std::make_pair(test_map,map_init_list);
|
||||
auto test_node_map_and_init_list=std::make_pair(test_node_map,map_init_list);
|
||||
auto test_set_and_init_list=std::make_pair(test_set,set_init_list);
|
||||
auto test_node_set_and_init_list=std::make_pair(test_node_set,set_init_list);
|
||||
|
||||
namespace {
|
||||
template <class X>
|
||||
@ -865,7 +877,7 @@ namespace {
|
||||
}
|
||||
|
||||
template <class X, class GF>
|
||||
void flat_constructor(X*, GF gen_factory, test::random_generator rg)
|
||||
void nonconcurrent_constructor(X*, GF gen_factory, test::random_generator rg)
|
||||
{
|
||||
using value_type = typename X::value_type;
|
||||
static constexpr auto value_type_cardinality =
|
||||
@ -875,12 +887,13 @@ namespace {
|
||||
auto gen = gen_factory.template get<X>();
|
||||
auto values = make_random_values(1024 * 16, [&] { return gen(rg); });
|
||||
auto reference_cont = reference_container<X>(values.begin(), values.end());
|
||||
auto reference_flat= flat_container<X>(values.begin(), values.end());
|
||||
auto reference_nonc =
|
||||
nonconcurrent_container<X>(values.begin(), values.end());
|
||||
|
||||
raii::reset_counts();
|
||||
|
||||
{
|
||||
flat_container<X> flat(
|
||||
nonconcurrent_container<X> nonc(
|
||||
values.begin(), values.end(), reference_cont.size(), hasher(1),
|
||||
key_equal(2), allocator_type(3));
|
||||
|
||||
@ -890,9 +903,9 @@ namespace {
|
||||
|
||||
BOOST_TEST_EQ(old_dc, 0u);
|
||||
BOOST_TEST_EQ(old_mc, 0u);
|
||||
BOOST_TEST_EQ(old_cc, value_type_cardinality * flat.size());
|
||||
BOOST_TEST_EQ(old_cc, value_type_cardinality * nonc.size());
|
||||
|
||||
X x(std::move(flat));
|
||||
X x(std::move(nonc));
|
||||
|
||||
test_fuzzy_matches_reference(x, reference_cont, rg);
|
||||
|
||||
@ -904,15 +917,16 @@ namespace {
|
||||
BOOST_TEST_EQ(x.key_eq(), key_equal(2));
|
||||
BOOST_TEST(x.get_allocator() == allocator_type(3));
|
||||
|
||||
BOOST_TEST(flat.empty());
|
||||
BOOST_TEST(nonc.empty());
|
||||
}
|
||||
|
||||
check_raii_counts();
|
||||
|
||||
{
|
||||
flat_container<X> flat(0, hasher(1), key_equal(2), allocator_type(3));
|
||||
nonconcurrent_container<X> nonc(
|
||||
0, hasher(1), key_equal(2), allocator_type(3));
|
||||
|
||||
X x(std::move(flat));
|
||||
X x(std::move(nonc));
|
||||
|
||||
BOOST_TEST(x.empty());
|
||||
|
||||
@ -920,7 +934,7 @@ namespace {
|
||||
BOOST_TEST_EQ(x.key_eq(), key_equal(2));
|
||||
BOOST_TEST(x.get_allocator() == allocator_type(3));
|
||||
|
||||
BOOST_TEST(flat.empty());
|
||||
BOOST_TEST(nonc.empty());
|
||||
}
|
||||
|
||||
check_raii_counts();
|
||||
@ -937,17 +951,17 @@ namespace {
|
||||
BOOST_TEST_EQ(old_mc, 0u);
|
||||
BOOST_TEST_EQ(old_cc, 2u * value_type_cardinality * x.size());
|
||||
|
||||
flat_container<X> flat(std::move(x));
|
||||
nonconcurrent_container<X> nonc(std::move(x));
|
||||
|
||||
BOOST_TEST(flat == reference_flat);
|
||||
BOOST_TEST(nonc == reference_nonc);
|
||||
|
||||
BOOST_TEST_EQ(+raii::default_constructor, old_dc);
|
||||
BOOST_TEST_EQ(+raii::move_constructor, old_mc);
|
||||
BOOST_TEST_EQ(+raii::copy_constructor, old_cc);
|
||||
|
||||
BOOST_TEST_EQ(flat.hash_function(), hasher(1));
|
||||
BOOST_TEST_EQ(flat.key_eq(), key_equal(2));
|
||||
BOOST_TEST(flat.get_allocator() == allocator_type(3));
|
||||
BOOST_TEST_EQ(nonc.hash_function(), hasher(1));
|
||||
BOOST_TEST_EQ(nonc.key_eq(), key_equal(2));
|
||||
BOOST_TEST(nonc.get_allocator() == allocator_type(3));
|
||||
|
||||
BOOST_TEST(x.empty());
|
||||
}
|
||||
@ -957,13 +971,13 @@ namespace {
|
||||
{
|
||||
X x(0, hasher(1), key_equal(2), allocator_type(3));
|
||||
|
||||
flat_container<X> flat(std::move(x));
|
||||
nonconcurrent_container<X> nonc(std::move(x));
|
||||
|
||||
BOOST_TEST(flat.empty());
|
||||
BOOST_TEST(nonc.empty());
|
||||
|
||||
BOOST_TEST_EQ(flat.hash_function(), hasher(1));
|
||||
BOOST_TEST_EQ(flat.key_eq(), key_equal(2));
|
||||
BOOST_TEST(flat.get_allocator() == allocator_type(3));
|
||||
BOOST_TEST_EQ(nonc.hash_function(), hasher(1));
|
||||
BOOST_TEST_EQ(nonc.key_eq(), key_equal(2));
|
||||
BOOST_TEST(nonc.get_allocator() == allocator_type(3));
|
||||
|
||||
BOOST_TEST(x.empty());
|
||||
}
|
||||
@ -976,83 +990,84 @@ namespace {
|
||||
// clang-format off
|
||||
UNORDERED_TEST(
|
||||
default_constructor,
|
||||
((test_map)(test_set)))
|
||||
((test_map)(test_node_map)(test_set)(test_node_set)))
|
||||
|
||||
UNORDERED_TEST(
|
||||
bucket_count_with_hasher_key_equal_and_allocator,
|
||||
((test_map)(test_set)))
|
||||
((test_map)(test_node_map)(test_set)(test_node_set)))
|
||||
|
||||
UNORDERED_TEST(
|
||||
soccc,
|
||||
((test_map)(test_set)))
|
||||
((test_map)(test_node_map)(test_set)(test_node_set)))
|
||||
|
||||
UNORDERED_TEST(
|
||||
from_iterator_range,
|
||||
((test_map)(test_set))
|
||||
((test_map)(test_node_map)(test_set)(test_node_set))
|
||||
((value_type_generator_factory))
|
||||
((default_generator)(sequential)(limited_range)))
|
||||
|
||||
UNORDERED_TEST(
|
||||
copy_constructor,
|
||||
((test_map)(test_set))
|
||||
((test_map)(test_node_map)(test_set)(test_node_set))
|
||||
((value_type_generator_factory))
|
||||
((default_generator)(sequential)(limited_range)))
|
||||
|
||||
UNORDERED_TEST(
|
||||
copy_constructor_with_insertion,
|
||||
((test_map)(test_set))
|
||||
((test_map)(test_node_map)(test_set)(test_node_set))
|
||||
((value_type_generator_factory))
|
||||
((default_generator)(sequential)(limited_range)))
|
||||
|
||||
UNORDERED_TEST(
|
||||
move_constructor,
|
||||
((test_map)(test_set))
|
||||
((test_map)(test_node_map)(test_set)(test_node_set))
|
||||
((value_type_generator_factory))
|
||||
((default_generator)(sequential)(limited_range)))
|
||||
|
||||
UNORDERED_TEST(
|
||||
move_constructor_with_insertion,
|
||||
((test_map)(test_set))
|
||||
((test_map)(test_node_map)(test_set)(test_node_set))
|
||||
((value_type_generator_factory))
|
||||
((default_generator)(sequential)(limited_range)))
|
||||
|
||||
UNORDERED_TEST(
|
||||
iterator_range_with_allocator,
|
||||
((test_map)(test_set))
|
||||
((test_map)(test_node_map)(test_set)(test_node_set))
|
||||
((value_type_generator_factory))
|
||||
((default_generator)(sequential)(limited_range)))
|
||||
|
||||
UNORDERED_TEST(
|
||||
explicit_allocator,
|
||||
((test_map)(test_set)))
|
||||
((test_map)(test_node_map)(test_set)(test_node_set)))
|
||||
|
||||
UNORDERED_TEST(
|
||||
initializer_list_with_all_params,
|
||||
((test_map_and_init_list)(test_set_and_init_list)))
|
||||
((test_map_and_init_list)(test_node_map_and_init_list)
|
||||
(test_set_and_init_list)(test_node_set_and_init_list)))
|
||||
|
||||
UNORDERED_TEST(
|
||||
bucket_count_and_allocator,
|
||||
((test_map)(test_set)))
|
||||
((test_map)(test_node_map)(test_set)(test_node_set)))
|
||||
|
||||
UNORDERED_TEST(
|
||||
bucket_count_with_hasher_and_allocator,
|
||||
((test_map)(test_set)))
|
||||
((test_map)(test_node_map)(test_set)(test_node_set)))
|
||||
|
||||
UNORDERED_TEST(
|
||||
iterator_range_with_bucket_count_and_allocator,
|
||||
((test_map)(test_set))
|
||||
((test_map)(test_node_map)(test_set)(test_node_set))
|
||||
((value_type_generator_factory))
|
||||
((default_generator)(sequential)(limited_range)))
|
||||
|
||||
UNORDERED_TEST(
|
||||
iterator_range_with_bucket_count_hasher_and_allocator,
|
||||
((test_map)(test_set))
|
||||
((test_map)(test_node_map)(test_set)(test_node_set))
|
||||
((value_type_generator_factory))
|
||||
((default_generator)(sequential)(limited_range)))
|
||||
|
||||
UNORDERED_TEST(
|
||||
flat_constructor,
|
||||
((test_map)(test_set))
|
||||
nonconcurrent_constructor,
|
||||
((test_map)(test_node_map)(test_set)(test_node_set))
|
||||
((value_type_generator_factory))
|
||||
((default_generator)(sequential)(limited_range)))
|
||||
// clang-format on
|
||||
|
@ -1,5 +1,5 @@
|
||||
// Copyright (C) 2023 Christian Mazakas
|
||||
// Copyright (C) 2023 Joaquin M Lopez Munoz
|
||||
// Copyright (C) 2023-2024 Joaquin M Lopez Munoz
|
||||
// Copyright (C) 2024 Braden Ganetsky
|
||||
// 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)
|
||||
@ -9,6 +9,8 @@
|
||||
|
||||
#include <boost/unordered/concurrent_flat_map.hpp>
|
||||
#include <boost/unordered/concurrent_flat_set.hpp>
|
||||
#include <boost/unordered/concurrent_node_map.hpp>
|
||||
#include <boost/unordered/concurrent_node_set.hpp>
|
||||
|
||||
#include <boost/core/ignore_unused.hpp>
|
||||
|
||||
@ -80,11 +82,8 @@ namespace {
|
||||
}
|
||||
template <class T, class X> void operator()(std::vector<T>& values, X& x)
|
||||
{
|
||||
static constexpr auto value_type_cardinality =
|
||||
value_cardinality<typename X::value_type>::value;
|
||||
|
||||
call_impl(values, x);
|
||||
BOOST_TEST_GE(raii::move_constructor, value_type_cardinality * x.size());
|
||||
BOOST_TEST_GE(raii::move_constructor, x.size());
|
||||
}
|
||||
} lvalue_emplacer;
|
||||
|
||||
@ -197,7 +196,12 @@ namespace {
|
||||
BOOST_TEST_EQ(raii::default_constructor, 0u);
|
||||
|
||||
BOOST_TEST_EQ(raii::copy_constructor, value_type_cardinality * x.size());
|
||||
if (is_container_node_based<X>::value) {
|
||||
BOOST_TEST_EQ(raii::move_constructor, 0u);
|
||||
}
|
||||
else {
|
||||
BOOST_TEST_GT(raii::move_constructor, 0u);
|
||||
}
|
||||
|
||||
BOOST_TEST_EQ(raii::copy_assignment, 0u);
|
||||
BOOST_TEST_EQ(raii::move_assignment, 0u);
|
||||
@ -208,8 +212,8 @@ namespace {
|
||||
{
|
||||
template <class T, class X> void operator()(std::vector<T>& values, X& x)
|
||||
{
|
||||
static constexpr auto value_type_cardinality =
|
||||
value_cardinality<typename X::value_type>::value;
|
||||
static constexpr auto input_type_nonconst_cardinality =
|
||||
value_nonconst_cardinality<T>::value;
|
||||
|
||||
std::atomic<std::uint64_t> num_inserts{0};
|
||||
thread_runner(values, [&x, &num_inserts](boost::span<T> s) {
|
||||
@ -235,10 +239,18 @@ namespace {
|
||||
} else {
|
||||
BOOST_TEST_EQ(raii::copy_constructor, 0u);
|
||||
}
|
||||
|
||||
if (is_container_node_based<X>::value) {
|
||||
BOOST_TEST_EQ(
|
||||
raii::move_constructor, input_type_nonconst_cardinality * x.size());
|
||||
}
|
||||
else {
|
||||
BOOST_TEST_GT(
|
||||
raii::move_constructor, input_type_nonconst_cardinality * x.size());
|
||||
}
|
||||
#if defined(BOOST_MSVC)
|
||||
#pragma warning(pop) // C4127
|
||||
#endif
|
||||
BOOST_TEST_GT(raii::move_constructor, value_type_cardinality * x.size());
|
||||
|
||||
BOOST_TEST_EQ(raii::copy_assignment, 0u);
|
||||
BOOST_TEST_EQ(raii::move_assignment, 0u);
|
||||
@ -280,7 +292,9 @@ namespace {
|
||||
}
|
||||
|
||||
boost::unordered::concurrent_flat_map<raii, raii>* map;
|
||||
boost::unordered::concurrent_node_map<raii, raii>* node_map;
|
||||
boost::unordered::concurrent_flat_set<raii>* set;
|
||||
boost::unordered::concurrent_node_set<raii>* node_set;
|
||||
|
||||
} // namespace
|
||||
|
||||
@ -292,7 +306,7 @@ using test::sequential;
|
||||
|
||||
UNORDERED_TEST(
|
||||
emplace,
|
||||
((map)(set))
|
||||
((map)(node_map)(set)(node_set))
|
||||
((value_type_generator_factory)(init_type_generator_factory))
|
||||
((lvalue_emplacer)(norehash_lvalue_emplacer)
|
||||
(lvalue_emplace_or_cvisit)(lvalue_emplace_or_visit)(copy_emplacer)(move_emplacer))
|
||||
@ -455,6 +469,8 @@ namespace {
|
||||
|
||||
boost::unordered::concurrent_flat_map<counted_key_type, counted_value_type>*
|
||||
test_counted_flat_map = {};
|
||||
boost::unordered::concurrent_node_map<counted_key_type, counted_value_type>*
|
||||
test_counted_node_map = {};
|
||||
|
||||
} // namespace
|
||||
|
||||
@ -462,7 +478,7 @@ namespace {
|
||||
|
||||
UNORDERED_TEST(
|
||||
emplace_map_key_value,
|
||||
((test_counted_flat_map))
|
||||
((test_counted_flat_map)(test_counted_node_map))
|
||||
((copy)(move))
|
||||
((counted_key_checker)(converting_key_checker))
|
||||
((counted_value_checker)(converting_value_checker))
|
||||
|
@ -1,5 +1,5 @@
|
||||
// Copyright (C) 2023 Christian Mazakas
|
||||
// Copyright (C) 2023 Joaquin M Lopez Munoz
|
||||
// Copyright (C) 2023-2024 Joaquin M Lopez Munoz
|
||||
// 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)
|
||||
|
||||
@ -7,6 +7,8 @@
|
||||
|
||||
#include <boost/unordered/concurrent_flat_map.hpp>
|
||||
#include <boost/unordered/concurrent_flat_set.hpp>
|
||||
#include <boost/unordered/concurrent_node_map.hpp>
|
||||
#include <boost/unordered/concurrent_node_set.hpp>
|
||||
|
||||
test::seed_t initialize_seed{1634048962};
|
||||
|
||||
@ -20,28 +22,38 @@ using key_equal = stateful_key_equal;
|
||||
using map_type = boost::unordered::concurrent_flat_map<raii, raii, hasher,
|
||||
key_equal, stateful_allocator<std::pair<raii const, raii> > >;
|
||||
|
||||
using node_map_type = boost::unordered::concurrent_node_map<raii, raii, hasher,
|
||||
key_equal, stateful_allocator<std::pair<raii const, raii> > >;
|
||||
|
||||
using set_type = boost::unordered::concurrent_flat_set<raii, hasher,
|
||||
key_equal, stateful_allocator<raii> >;
|
||||
|
||||
using node_set_type = boost::unordered::concurrent_node_set<raii, hasher,
|
||||
key_equal, stateful_allocator<raii> >;
|
||||
|
||||
map_type* test_map;
|
||||
node_map_type* test_node_map;
|
||||
set_type* test_set;
|
||||
node_set_type* test_node_set;
|
||||
|
||||
namespace {
|
||||
|
||||
UNORDERED_AUTO_TEST (simple_map_equality) {
|
||||
using allocator_type = map_type::allocator_type;
|
||||
template <class X>
|
||||
void simple_map_equality(X*)
|
||||
{
|
||||
using allocator_type = typename X::allocator_type;
|
||||
|
||||
{
|
||||
map_type x1(
|
||||
X x1(
|
||||
{{1, 11}, {2, 22}}, 0, hasher(1), key_equal(2), allocator_type(3));
|
||||
|
||||
map_type x2(
|
||||
X x2(
|
||||
{{1, 11}, {2, 22}}, 0, hasher(2), key_equal(2), allocator_type(3));
|
||||
|
||||
map_type x3(
|
||||
X x3(
|
||||
{{1, 11}, {2, 23}}, 0, hasher(2), key_equal(2), allocator_type(3));
|
||||
|
||||
map_type x4({{1, 11}}, 0, hasher(2), key_equal(2), allocator_type(3));
|
||||
X x4({{1, 11}}, 0, hasher(2), key_equal(2), allocator_type(3));
|
||||
|
||||
BOOST_TEST_EQ(x1.size(), x2.size());
|
||||
BOOST_TEST(x1 == x2);
|
||||
@ -57,17 +69,19 @@ namespace {
|
||||
}
|
||||
}
|
||||
|
||||
UNORDERED_AUTO_TEST (simple_set_equality) {
|
||||
using allocator_type = set_type::allocator_type;
|
||||
template <class X>
|
||||
void simple_set_equality(X*)
|
||||
{
|
||||
using allocator_type = typename X::allocator_type;
|
||||
|
||||
{
|
||||
set_type x1(
|
||||
X x1(
|
||||
{1, 2}, 0, hasher(1), key_equal(2), allocator_type(3));
|
||||
|
||||
set_type x2(
|
||||
X x2(
|
||||
{1, 2}, 0, hasher(2), key_equal(2), allocator_type(3));
|
||||
|
||||
set_type x3({1}, 0, hasher(2), key_equal(2), allocator_type(3));
|
||||
X x3({1}, 0, hasher(2), key_equal(2), allocator_type(3));
|
||||
|
||||
BOOST_TEST_EQ(x1.size(), x2.size());
|
||||
BOOST_TEST(x1 == x2);
|
||||
@ -165,9 +179,17 @@ namespace {
|
||||
} // namespace
|
||||
|
||||
// clang-format off
|
||||
UNORDERED_TEST(
|
||||
simple_map_equality,
|
||||
((test_map)(test_node_map)))
|
||||
|
||||
UNORDERED_TEST(
|
||||
simple_set_equality,
|
||||
((test_set)(test_node_set)))
|
||||
|
||||
UNORDERED_TEST(
|
||||
insert_and_compare,
|
||||
((test_map)(test_set))
|
||||
((test_map)(test_node_map)(test_set)(test_node_set))
|
||||
((value_type_generator_factory))
|
||||
((default_generator)(sequential)(limited_range)))
|
||||
// clang-format on
|
||||
|
@ -1,5 +1,5 @@
|
||||
// Copyright (C) 2023 Christian Mazakas
|
||||
// Copyright (C) 2023 Joaquin M Lopez Munoz
|
||||
// Copyright (C) 2023-2024 Joaquin M Lopez Munoz
|
||||
// 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)
|
||||
|
||||
@ -7,6 +7,8 @@
|
||||
|
||||
#include <boost/unordered/concurrent_flat_map.hpp>
|
||||
#include <boost/unordered/concurrent_flat_set.hpp>
|
||||
#include <boost/unordered/concurrent_node_map.hpp>
|
||||
#include <boost/unordered/concurrent_node_set.hpp>
|
||||
|
||||
#include <boost/core/ignore_unused.hpp>
|
||||
|
||||
@ -439,11 +441,17 @@ namespace {
|
||||
}
|
||||
|
||||
boost::unordered::concurrent_flat_map<raii, raii>* map;
|
||||
boost::unordered::concurrent_node_map<raii, raii>* node_map;
|
||||
boost::unordered::concurrent_flat_set<raii>* set;
|
||||
boost::unordered::concurrent_node_set<raii>* node_set;
|
||||
boost::unordered::concurrent_flat_map<raii, raii, transp_hash,
|
||||
transp_key_equal>* transparent_map;
|
||||
boost::unordered::concurrent_node_map<raii, raii, transp_hash,
|
||||
transp_key_equal>* transparent_node_map;
|
||||
boost::unordered::concurrent_flat_set<raii, transp_hash,
|
||||
transp_key_equal>* transparent_set;
|
||||
boost::unordered::concurrent_node_set<raii, transp_hash,
|
||||
transp_key_equal>* transparent_node_set;
|
||||
|
||||
} // namespace
|
||||
|
||||
@ -454,14 +462,14 @@ using test::sequential;
|
||||
// clang-format off
|
||||
UNORDERED_TEST(
|
||||
erase,
|
||||
((map)(set))
|
||||
((map)(node_map)(set)(node_set))
|
||||
((value_type_generator_factory)(init_type_generator_factory))
|
||||
((lvalue_eraser)(lvalue_eraser_if)(erase_if)(free_fn_erase_if)(erase_if_exec_policy))
|
||||
((default_generator)(sequential)(limited_range)))
|
||||
|
||||
UNORDERED_TEST(
|
||||
erase,
|
||||
((transparent_map)(transparent_set))
|
||||
((transparent_map)(transparent_node_map)(transparent_set)(transparent_node_set))
|
||||
((value_type_generator_factory)(init_type_generator_factory))
|
||||
((transp_lvalue_eraser)(transp_lvalue_eraser_if)(erase_if_exec_policy))
|
||||
((default_generator)(sequential)(limited_range)))
|
||||
|
@ -1,5 +1,5 @@
|
||||
// Copyright (C) 2023 Christian Mazakas
|
||||
// Copyright (C) 2023 Joaquin M Lopez Munoz
|
||||
// Copyright (C) 2023-2024 Joaquin M Lopez Munoz
|
||||
// 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)
|
||||
|
||||
@ -7,6 +7,8 @@
|
||||
|
||||
#include <boost/unordered/concurrent_flat_map.hpp>
|
||||
#include <boost/unordered/concurrent_flat_set.hpp>
|
||||
#include <boost/unordered/concurrent_node_map.hpp>
|
||||
#include <boost/unordered/concurrent_node_set.hpp>
|
||||
|
||||
using hasher = stateful_hash;
|
||||
using key_equal = stateful_key_equal;
|
||||
@ -14,11 +16,19 @@ using key_equal = stateful_key_equal;
|
||||
using map_type = boost::unordered::concurrent_flat_map<raii, raii, hasher,
|
||||
key_equal, stateful_allocator<std::pair<raii const, raii> > >;
|
||||
|
||||
using node_map_type = boost::unordered::concurrent_node_map<raii, raii, hasher,
|
||||
key_equal, stateful_allocator<std::pair<raii const, raii> > >;
|
||||
|
||||
using set_type = boost::unordered::concurrent_flat_set<raii, hasher,
|
||||
key_equal, stateful_allocator<raii> >;
|
||||
|
||||
using node_set_type = boost::unordered::concurrent_node_set<raii, hasher,
|
||||
key_equal, stateful_allocator<raii> >;
|
||||
|
||||
map_type* test_map;
|
||||
node_map_type* test_node_map;
|
||||
set_type* test_set;
|
||||
node_set_type* test_node_set;
|
||||
|
||||
std::initializer_list<map_type::value_type> map_init_list{
|
||||
{raii{0}, raii{0}},
|
||||
@ -71,7 +81,9 @@ std::initializer_list<set_type::value_type> set_init_list{
|
||||
};
|
||||
|
||||
auto test_map_and_init_list=std::make_pair(test_map,map_init_list);
|
||||
auto test_node_map_and_init_list=std::make_pair(test_node_map,map_init_list);
|
||||
auto test_set_and_init_list=std::make_pair(test_set,set_init_list);
|
||||
auto test_node_set_and_init_list=std::make_pair(test_node_set,set_init_list);
|
||||
|
||||
namespace {
|
||||
test::seed_t initialize_seed(1794114520);
|
||||
@ -206,19 +218,20 @@ using test::sequential;
|
||||
// clang-format off
|
||||
UNORDERED_TEST(
|
||||
copy_assign,
|
||||
((test_map)(test_set))
|
||||
((test_map)(test_node_map)(test_set)(test_node_set))
|
||||
((exception_value_type_generator_factory))
|
||||
((default_generator)(sequential)(limited_range)))
|
||||
|
||||
UNORDERED_TEST(
|
||||
move_assign,
|
||||
((test_map)(test_set))
|
||||
((test_map)(test_node_map)(test_set)(test_node_set))
|
||||
((exception_value_type_generator_factory))
|
||||
((default_generator)(sequential)))
|
||||
|
||||
UNORDERED_TEST(
|
||||
intializer_list_assign,
|
||||
((test_map_and_init_list)(test_set_and_init_list)))
|
||||
((test_map_and_init_list)(test_node_map_and_init_list)
|
||||
(test_set_and_init_list)(test_node_set_and_init_list)))
|
||||
// clang-format on
|
||||
|
||||
RUN_TESTS()
|
||||
|
@ -1,5 +1,5 @@
|
||||
// Copyright (C) 2023 Christian Mazakas
|
||||
// Copyright (C) 2023 Joaquin M Lopez Munoz
|
||||
// Copyright (C) 2023-2024 Joaquin M Lopez Munoz
|
||||
// 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)
|
||||
|
||||
@ -7,6 +7,8 @@
|
||||
|
||||
#include <boost/unordered/concurrent_flat_map.hpp>
|
||||
#include <boost/unordered/concurrent_flat_set.hpp>
|
||||
#include <boost/unordered/concurrent_node_map.hpp>
|
||||
#include <boost/unordered/concurrent_node_set.hpp>
|
||||
|
||||
using hasher = stateful_hash;
|
||||
using key_equal = stateful_key_equal;
|
||||
@ -14,11 +16,19 @@ using key_equal = stateful_key_equal;
|
||||
using map_type = boost::unordered::concurrent_flat_map<raii, raii, hasher,
|
||||
key_equal, stateful_allocator<std::pair<raii const, raii> > >;
|
||||
|
||||
using node_map_type = boost::unordered::concurrent_node_map<raii, raii, hasher,
|
||||
key_equal, stateful_allocator<std::pair<raii const, raii> > >;
|
||||
|
||||
using set_type = boost::unordered::concurrent_flat_set<raii, hasher,
|
||||
key_equal, stateful_allocator<raii> >;
|
||||
|
||||
using node_set_type = boost::unordered::concurrent_node_set<raii, hasher,
|
||||
key_equal, stateful_allocator<raii> >;
|
||||
|
||||
map_type* test_map;
|
||||
node_map_type* test_node_map;
|
||||
set_type* test_set;
|
||||
node_set_type* test_node_set;
|
||||
|
||||
std::initializer_list<map_type::value_type> map_init_list{
|
||||
{raii{0}, raii{0}},
|
||||
@ -71,7 +81,9 @@ std::initializer_list<set_type::value_type> set_init_list{
|
||||
};
|
||||
|
||||
auto test_map_and_init_list=std::make_pair(test_map,map_init_list);
|
||||
auto test_node_map_and_init_list=std::make_pair(test_node_map,map_init_list);
|
||||
auto test_set_and_init_list=std::make_pair(test_set,set_init_list);
|
||||
auto test_node_set_and_init_list=std::make_pair(test_node_set,set_init_list);
|
||||
|
||||
namespace {
|
||||
test::seed_t initialize_seed(795610904);
|
||||
@ -339,29 +351,30 @@ using test::sequential;
|
||||
// clang-format off
|
||||
UNORDERED_TEST(
|
||||
bucket_constructor,
|
||||
((test_map)(test_set)))
|
||||
((test_map)(test_node_map)(test_set)(test_node_set)))
|
||||
|
||||
UNORDERED_TEST(
|
||||
iterator_range,
|
||||
((test_map)(test_set))
|
||||
((test_map)(test_node_map)(test_set)(test_node_set))
|
||||
((exception_value_type_generator_factory))
|
||||
((default_generator)(sequential)(limited_range)))
|
||||
|
||||
UNORDERED_TEST(
|
||||
copy_constructor,
|
||||
((test_map)(test_set))
|
||||
((test_map)(test_node_map)(test_set)(test_node_set))
|
||||
((exception_value_type_generator_factory))
|
||||
((default_generator)(sequential)))
|
||||
|
||||
UNORDERED_TEST(
|
||||
move_constructor,
|
||||
((test_map)(test_set))
|
||||
((test_map)(test_node_map)(test_set)(test_node_set))
|
||||
((exception_value_type_generator_factory))
|
||||
((default_generator)(sequential)))
|
||||
|
||||
UNORDERED_TEST(
|
||||
initializer_list_bucket_count,
|
||||
((test_map_and_init_list)(test_set_and_init_list)))
|
||||
((test_map_and_init_list)(test_node_map_and_init_list)
|
||||
(test_set_and_init_list)(test_node_set_and_init_list)))
|
||||
// clang-format on
|
||||
|
||||
RUN_TESTS()
|
||||
|
@ -1,5 +1,5 @@
|
||||
// Copyright (C) 2023 Christian Mazakas
|
||||
// Copyright (C) 2023 Joaquin M Lopez Munoz
|
||||
// Copyright (C) 2023-2024 Joaquin M Lopez Munoz
|
||||
// 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)
|
||||
|
||||
@ -7,6 +7,8 @@
|
||||
|
||||
#include <boost/unordered/concurrent_flat_map.hpp>
|
||||
#include <boost/unordered/concurrent_flat_set.hpp>
|
||||
#include <boost/unordered/concurrent_node_map.hpp>
|
||||
#include <boost/unordered/concurrent_node_set.hpp>
|
||||
|
||||
#include <boost/core/ignore_unused.hpp>
|
||||
|
||||
@ -283,8 +285,12 @@ namespace {
|
||||
|
||||
boost::unordered::concurrent_flat_map<raii, raii, stateful_hash,
|
||||
stateful_key_equal, stateful_allocator<std::pair<raii const, raii> > >* map;
|
||||
boost::unordered::concurrent_node_map<raii, raii, stateful_hash,
|
||||
stateful_key_equal, stateful_allocator<std::pair<raii const, raii> > >* node_map;
|
||||
boost::unordered::concurrent_flat_set<raii, stateful_hash,
|
||||
stateful_key_equal, stateful_allocator<raii> >* set;
|
||||
boost::unordered::concurrent_node_set<raii, stateful_hash,
|
||||
stateful_key_equal, stateful_allocator<raii> >* node_set;
|
||||
|
||||
} // namespace
|
||||
|
||||
@ -295,7 +301,7 @@ using test::sequential;
|
||||
// clang-format off
|
||||
UNORDERED_TEST(
|
||||
erase,
|
||||
((map)(set))
|
||||
((map)(node_map)(set)(node_set))
|
||||
((exception_value_type_generator_factory)
|
||||
(exception_init_type_generator_factory))
|
||||
((lvalue_eraser)(lvalue_eraser_if)(erase_if)(free_fn_erase_if))
|
||||
|
@ -1,5 +1,5 @@
|
||||
// Copyright (C) 2023 Christian Mazakas
|
||||
// Copyright (C) 2023 Joaquin M Lopez Munoz
|
||||
// Copyright (C) 2023-2024 Joaquin M Lopez Munoz
|
||||
// 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)
|
||||
|
||||
@ -7,6 +7,8 @@
|
||||
|
||||
#include <boost/unordered/concurrent_flat_map.hpp>
|
||||
#include <boost/unordered/concurrent_flat_set.hpp>
|
||||
#include <boost/unordered/concurrent_node_map.hpp>
|
||||
#include <boost/unordered/concurrent_node_set.hpp>
|
||||
|
||||
#include <boost/core/ignore_unused.hpp>
|
||||
|
||||
@ -153,7 +155,15 @@ namespace {
|
||||
|
||||
BOOST_TEST_EQ(raii::default_constructor, 0u);
|
||||
BOOST_TEST_GT(raii::copy_constructor, 0u);
|
||||
|
||||
if (is_container_node_based<X>::value) {
|
||||
BOOST_TEST_EQ(raii::move_constructor, 0u);
|
||||
}
|
||||
else{
|
||||
// don't check move construction count here because of rehashing
|
||||
BOOST_TEST_GT(raii::move_constructor, 0u);
|
||||
}
|
||||
|
||||
BOOST_TEST_EQ(raii::move_assignment, 0u);
|
||||
}
|
||||
} lvalue_insert_or_assign_copy_assign;
|
||||
@ -198,7 +208,7 @@ namespace {
|
||||
|
||||
BOOST_TEST_EQ(raii::default_constructor, 0u);
|
||||
BOOST_TEST_GT(raii::copy_constructor, 0u);
|
||||
BOOST_TEST_GT(raii::move_constructor, x.size()); // rehashing
|
||||
BOOST_TEST_GT(raii::move_constructor, 0u);
|
||||
BOOST_TEST_EQ(raii::move_assignment, 0u);
|
||||
}
|
||||
} rvalue_insert_or_assign_copy_assign;
|
||||
@ -249,8 +259,15 @@ namespace {
|
||||
|
||||
BOOST_TEST_GT(num_inserts, 0u);
|
||||
BOOST_TEST_EQ(raii::default_constructor, 0u);
|
||||
|
||||
if (is_container_node_based<X>::value) {
|
||||
BOOST_TEST_EQ(raii::move_constructor, 0u);
|
||||
}
|
||||
else{
|
||||
// don't check move construction count here because of rehashing
|
||||
BOOST_TEST_GT(raii::move_constructor, 0u);
|
||||
}
|
||||
|
||||
BOOST_TEST_EQ(raii::move_assignment, 0u);
|
||||
}
|
||||
} lvalue_insert_or_cvisit;
|
||||
@ -288,8 +305,14 @@ namespace {
|
||||
|
||||
BOOST_TEST_EQ(raii::default_constructor, 0u);
|
||||
|
||||
if (is_container_node_based<X>::value) {
|
||||
BOOST_TEST_EQ(raii::move_constructor, 0u);
|
||||
}
|
||||
else{
|
||||
// don't check move construction count here because of rehashing
|
||||
BOOST_TEST_GT(raii::move_constructor, 0u);
|
||||
}
|
||||
|
||||
BOOST_TEST_EQ(raii::move_assignment, 0u);
|
||||
}
|
||||
} lvalue_insert_or_visit;
|
||||
@ -426,8 +449,12 @@ namespace {
|
||||
|
||||
boost::unordered::concurrent_flat_map<raii, raii, stateful_hash,
|
||||
stateful_key_equal, stateful_allocator<std::pair<raii const, raii> > >* map;
|
||||
boost::unordered::concurrent_node_map<raii, raii, stateful_hash,
|
||||
stateful_key_equal, stateful_allocator<std::pair<raii const, raii> > >* node_map;
|
||||
boost::unordered::concurrent_flat_set<raii, stateful_hash,
|
||||
stateful_key_equal, stateful_allocator<raii> >* set;
|
||||
boost::unordered::concurrent_node_set<raii, stateful_hash,
|
||||
stateful_key_equal, stateful_allocator<raii> >* node_set;
|
||||
|
||||
} // namespace
|
||||
|
||||
@ -438,7 +465,7 @@ using test::sequential;
|
||||
// clang-format off
|
||||
UNORDERED_TEST(
|
||||
insert,
|
||||
((map)(set))
|
||||
((map)(node_map)(set)(node_set))
|
||||
((exception_value_type_generator_factory)
|
||||
(exception_init_type_generator_factory))
|
||||
((lvalue_inserter)(rvalue_inserter)(iterator_range_inserter)
|
||||
@ -450,7 +477,7 @@ UNORDERED_TEST(
|
||||
|
||||
UNORDERED_TEST(
|
||||
insert,
|
||||
((map))
|
||||
((map)(node_map))
|
||||
((exception_init_type_generator_factory))
|
||||
((lvalue_insert_or_assign_copy_assign)(lvalue_insert_or_assign_move_assign)
|
||||
(rvalue_insert_or_assign_copy_assign)(rvalue_insert_or_assign_move_assign))
|
||||
|
@ -1,5 +1,5 @@
|
||||
// Copyright (C) 2023 Christian Mazakas
|
||||
// Copyright (C) 2023 Joaquin M Lopez Munoz
|
||||
// Copyright (C) 2023-2024 Joaquin M Lopez Munoz
|
||||
// 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)
|
||||
|
||||
@ -7,6 +7,8 @@
|
||||
|
||||
#include <boost/unordered/concurrent_flat_map.hpp>
|
||||
#include <boost/unordered/concurrent_flat_set.hpp>
|
||||
#include <boost/unordered/concurrent_node_map.hpp>
|
||||
#include <boost/unordered/concurrent_node_set.hpp>
|
||||
|
||||
#include <boost/core/ignore_unused.hpp>
|
||||
|
||||
@ -16,11 +18,19 @@ using key_equal = stateful_key_equal;
|
||||
using map_type = boost::unordered::concurrent_flat_map<raii, raii, hasher,
|
||||
key_equal, stateful_allocator<std::pair<raii const, raii> > >;
|
||||
|
||||
using node_map_type = boost::unordered::concurrent_node_map<raii, raii, hasher,
|
||||
key_equal, stateful_allocator<std::pair<raii const, raii> > >;
|
||||
|
||||
using set_type = boost::unordered::concurrent_flat_set<raii, hasher,
|
||||
key_equal, stateful_allocator<raii> >;
|
||||
|
||||
using node_set_type = boost::unordered::concurrent_node_set<raii, hasher,
|
||||
key_equal, stateful_allocator<raii> >;
|
||||
|
||||
map_type* test_map;
|
||||
node_map_type* test_node_map;
|
||||
set_type* test_set;
|
||||
node_set_type* test_node_set;
|
||||
|
||||
namespace {
|
||||
test::seed_t initialize_seed(223333016);
|
||||
@ -79,7 +89,7 @@ using test::sequential;
|
||||
// clang-format off
|
||||
UNORDERED_TEST(
|
||||
merge,
|
||||
((test_map)(test_set))
|
||||
((test_map)(test_node_map)(test_set)(test_node_set))
|
||||
((exception_value_type_generator_factory))
|
||||
((default_generator)(sequential)(limited_range)))
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
// Copyright 2024 Joaquin M Lopez Muoz.
|
||||
// Copyright 2024 Joaquin M Lopez Munoz.
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at htT://www.boost.org/LICENSE_1_0.txt)
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#define BOOST_UNORDERED_CFOA_TESTS
|
||||
#include "../unordered/explicit_alloc_ctor_tests.cpp"
|
||||
|
162
test/cfoa/extract_insert_tests.cpp
Normal file
162
test/cfoa/extract_insert_tests.cpp
Normal file
@ -0,0 +1,162 @@
|
||||
// Copyright (C) 2024 Joaquin M Lopez Munoz
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#include "helpers.hpp"
|
||||
|
||||
#include <boost/assert.hpp>
|
||||
#include <boost/unordered/concurrent_node_map.hpp>
|
||||
#include <boost/unordered/concurrent_node_set.hpp>
|
||||
|
||||
using hasher = stateful_hash;
|
||||
using key_equal = stateful_key_equal;
|
||||
|
||||
using node_map_type = boost::unordered::concurrent_node_map<raii, raii, hasher,
|
||||
key_equal, stateful_allocator<std::pair<raii const, raii> > >;
|
||||
|
||||
using node_set_type = boost::unordered::concurrent_node_set<raii, hasher,
|
||||
key_equal, stateful_allocator<raii> >;
|
||||
|
||||
node_map_type* test_node_map;
|
||||
node_set_type* test_node_set;
|
||||
|
||||
namespace {
|
||||
|
||||
template <class X, class GF>
|
||||
void extract_insert_tests(X*, GF gen_factory)
|
||||
{
|
||||
using value_type = typename X::value_type;
|
||||
using allocator_type = typename X::allocator_type;
|
||||
|
||||
// set visit is always const access
|
||||
using arg_visit_type = typename std::conditional<
|
||||
std::is_same<typename X::key_type, typename X::value_type>::value,
|
||||
typename X::value_type const,
|
||||
typename X::value_type
|
||||
>::type;
|
||||
|
||||
test::random_generator rg = test::sequential;
|
||||
auto gen = gen_factory.template get<X>();
|
||||
auto values = make_random_values(1024 * 16, [&] { return gen(rg); });
|
||||
|
||||
|
||||
X in(0,hasher(1),key_equal(2), allocator_type(3));
|
||||
std::vector<X> out(2,in);
|
||||
for(std::size_t i = 0; i < values.size(); ++i) {
|
||||
in.insert(values[i]);
|
||||
out[i % 3 == 0? 0 : 1].insert(values[i]);
|
||||
}
|
||||
|
||||
raii::reset_counts();
|
||||
|
||||
thread_runner(values, [&](boost::span<value_type> s) {
|
||||
std::size_t br1 = 0, br2 = 0, br3 = 0;
|
||||
|
||||
for(auto const& v: s) {
|
||||
typename X::node_type nh;
|
||||
|
||||
while (nh.empty()) {
|
||||
switch (br1++ % 3) {
|
||||
case 0:
|
||||
nh = in.extract(test::get_key<X>(v));
|
||||
BOOST_ASSERT(!nh.empty());
|
||||
break;
|
||||
case 1:
|
||||
nh = in.extract_if(
|
||||
test::get_key<X>(v), [&](arg_visit_type& v2) {
|
||||
BOOST_ASSERT(test::get_key<X>(v) == test::get_key<X>(v2));
|
||||
(void)v2;
|
||||
return false;
|
||||
});
|
||||
BOOST_ASSERT(nh.empty());
|
||||
break;
|
||||
case 2: default:
|
||||
nh = in.extract_if(
|
||||
test::get_key<X>(v), [&](arg_visit_type& v2) {
|
||||
BOOST_ASSERT(test::get_key<X>(v) == test::get_key<X>(v2));
|
||||
(void)v2;
|
||||
return true;
|
||||
});
|
||||
BOOST_ASSERT(!nh.empty());
|
||||
break;
|
||||
}
|
||||
}
|
||||
BOOST_ASSERT(nh.get_allocator() == in.get_allocator());
|
||||
|
||||
while (!nh.empty()) {
|
||||
auto& o = out[br2++ % out.size()];
|
||||
typename X::insert_return_type r;
|
||||
switch (br3++ % 3) {
|
||||
case 0:
|
||||
r = o.insert(std::move(nh));
|
||||
break;
|
||||
case 1:
|
||||
r = o.insert_or_visit(
|
||||
std::move(nh), [&](arg_visit_type& v2) {
|
||||
BOOST_ASSERT(test::get_key<X>(v) == test::get_key<X>(v2));
|
||||
(void)v2;
|
||||
});
|
||||
break;
|
||||
case 2: default:
|
||||
r = o.insert_or_cvisit(
|
||||
std::move(nh), [&](arg_visit_type const& v2) {
|
||||
BOOST_ASSERT(test::get_key<X>(v) == test::get_key<X>(v2));
|
||||
(void)v2;
|
||||
});
|
||||
break;
|
||||
}
|
||||
BOOST_ASSERT(r.inserted || !r.node.empty());
|
||||
nh = std::move(r.node);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
BOOST_TEST_EQ(in.size(), 0u);
|
||||
BOOST_TEST_EQ(out[0].size() + out[1].size(), 2 * values.size());
|
||||
BOOST_TEST_EQ(raii::default_constructor, 0u);
|
||||
BOOST_TEST_EQ(raii::copy_constructor, 0u);
|
||||
BOOST_TEST_EQ(raii::move_constructor, 0u);
|
||||
BOOST_TEST_EQ(raii::destructor, 0u);
|
||||
}
|
||||
|
||||
template <class X>
|
||||
void insert_empty_node_tests(X*)
|
||||
{
|
||||
using value_type = typename X::value_type;
|
||||
using node_type = typename X::node_type ;
|
||||
|
||||
X x;
|
||||
{
|
||||
node_type nh;
|
||||
auto r = x.insert(std::move(nh));
|
||||
BOOST_TEST(!r.inserted);
|
||||
BOOST_TEST(r.node.empty());
|
||||
}
|
||||
{
|
||||
node_type nh;
|
||||
auto r = x.insert_or_visit(std::move(nh), [](value_type const&) {});
|
||||
BOOST_TEST(!r.inserted);
|
||||
BOOST_TEST(r.node.empty());
|
||||
}
|
||||
{
|
||||
node_type nh;
|
||||
auto r = x.insert_or_cvisit(std::move(nh), [](value_type const&) {});
|
||||
BOOST_TEST(!r.inserted);
|
||||
BOOST_TEST(r.node.empty());
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
// clang-format off
|
||||
UNORDERED_TEST(
|
||||
extract_insert_tests,
|
||||
((test_node_map)(test_node_set))
|
||||
((value_type_generator_factory)))
|
||||
|
||||
UNORDERED_TEST(
|
||||
insert_empty_node_tests,
|
||||
((test_node_map)(test_node_set)))
|
||||
// clang-format on
|
||||
|
||||
RUN_TESTS()
|
@ -1,5 +1,5 @@
|
||||
// Copyright (C) 2023 Christian Mazakas
|
||||
// Copyright (C) 2023 Joaquin M Lopez Munoz
|
||||
// Copyright (C) 2023-2024 Joaquin M Lopez Munoz
|
||||
// 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)
|
||||
|
||||
@ -7,6 +7,8 @@
|
||||
#include <boost/config/workaround.hpp>
|
||||
#include <boost/unordered/concurrent_flat_map_fwd.hpp>
|
||||
#include <boost/unordered/concurrent_flat_set_fwd.hpp>
|
||||
#include <boost/unordered/concurrent_node_map_fwd.hpp>
|
||||
#include <boost/unordered/concurrent_node_set_fwd.hpp>
|
||||
#include <limits>
|
||||
|
||||
test::seed_t initialize_seed{32304628};
|
||||
@ -36,6 +38,27 @@ bool unequal_call(boost::unordered::concurrent_flat_map<T, T>& x1,
|
||||
return x1 != x2;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
void swap_call(boost::unordered::concurrent_node_map<T, T>& x1,
|
||||
boost::unordered::concurrent_node_map<T, T>& x2)
|
||||
{
|
||||
swap(x1, x2);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
bool equal_call(boost::unordered::concurrent_node_map<T, T>& x1,
|
||||
boost::unordered::concurrent_node_map<T, T>& x2)
|
||||
{
|
||||
return x1 == x2;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
bool unequal_call(boost::unordered::concurrent_node_map<T, T>& x1,
|
||||
boost::unordered::concurrent_node_map<T, T>& x2)
|
||||
{
|
||||
return x1 != x2;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
void swap_call(boost::unordered::concurrent_flat_set<T>& x1,
|
||||
boost::unordered::concurrent_flat_set<T>& x2)
|
||||
@ -57,14 +80,41 @@ bool unequal_call(boost::unordered::concurrent_flat_set<T>& x1,
|
||||
return x1 != x2;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
void swap_call(boost::unordered::concurrent_node_set<T>& x1,
|
||||
boost::unordered::concurrent_node_set<T>& x2)
|
||||
{
|
||||
swap(x1, x2);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
bool equal_call(boost::unordered::concurrent_node_set<T>& x1,
|
||||
boost::unordered::concurrent_node_set<T>& x2)
|
||||
{
|
||||
return x1 == x2;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
bool unequal_call(boost::unordered::concurrent_node_set<T>& x1,
|
||||
boost::unordered::concurrent_node_set<T>& x2)
|
||||
{
|
||||
return x1 != x2;
|
||||
}
|
||||
|
||||
#include <boost/unordered/concurrent_flat_map.hpp>
|
||||
#include <boost/unordered/concurrent_flat_set.hpp>
|
||||
#include <boost/unordered/concurrent_node_map.hpp>
|
||||
#include <boost/unordered/concurrent_node_set.hpp>
|
||||
|
||||
using map_type = boost::unordered::concurrent_flat_map<int, int>;
|
||||
using set_type = boost::unordered::concurrent_flat_map<int, int>;
|
||||
using node_map_type = boost::unordered::concurrent_node_map<int, int>;
|
||||
using set_type = boost::unordered::concurrent_flat_set<int>;
|
||||
using node_set_type = boost::unordered::concurrent_node_set<int>;
|
||||
|
||||
map_type* test_map;
|
||||
node_map_type* test_node_map;
|
||||
set_type* test_set;
|
||||
node_set_type* test_node_set;
|
||||
|
||||
template <typename X>
|
||||
void fwd_swap_call(X*)
|
||||
@ -106,19 +156,19 @@ void max_size(X*)
|
||||
// clang-format off
|
||||
UNORDERED_TEST(
|
||||
fwd_swap_call,
|
||||
((test_map)(test_set)))
|
||||
((test_map)(test_node_map)(test_set)(test_node_set)))
|
||||
|
||||
UNORDERED_TEST(
|
||||
fwd_equal_call,
|
||||
((test_map)(test_set)))
|
||||
((test_map)(test_node_map)(test_set)(test_node_set)))
|
||||
|
||||
UNORDERED_TEST(
|
||||
fwd_unequal_call,
|
||||
((test_map)(test_set)))
|
||||
((test_map)(test_node_map)(test_set)(test_node_set)))
|
||||
|
||||
UNORDERED_TEST(
|
||||
max_size,
|
||||
((test_map)(test_set)))
|
||||
((test_map)(test_node_map)(test_set)(test_node_set)))
|
||||
// clang-format on
|
||||
|
||||
RUN_TESTS()
|
||||
|
@ -1,5 +1,5 @@
|
||||
// Copyright (C) 2023 Christian Mazakas
|
||||
// Copyright (C) 2023 Joaquin M Lopez Munoz
|
||||
// Copyright (C) 2023-2024 Joaquin M Lopez Munoz
|
||||
// Copyright (C) 2024 Braden Ganetsky
|
||||
// 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)
|
||||
@ -147,9 +147,15 @@ public:
|
||||
cfoa_ptr(std::nullptr_t) : p_(nullptr){};
|
||||
template <class U> using rebind = cfoa_ptr<U>;
|
||||
|
||||
operator bool() const { return !!p_; }
|
||||
|
||||
template <typename Q = T>
|
||||
Q& operator*() const noexcept { return *p_; }
|
||||
|
||||
T* operator->() const noexcept { return p_; }
|
||||
|
||||
static cfoa_ptr<T> pointer_to(element_type& r) { return {std::addressof(r)}; }
|
||||
template<typename Q = T>
|
||||
static cfoa_ptr<Q> pointer_to(Q& r) { return {std::addressof(r)}; }
|
||||
};
|
||||
|
||||
template <class T> struct stateful_allocator
|
||||
@ -670,4 +676,17 @@ public:
|
||||
fancy_allocator& operator=(fancy_allocator const&) { return *this; }
|
||||
};
|
||||
|
||||
namespace boost {
|
||||
template <> struct pointer_traits<void_ptr>
|
||||
{
|
||||
template <class U> struct rebind_to
|
||||
{
|
||||
typedef ptr<U> type;
|
||||
};
|
||||
|
||||
template<class U>
|
||||
using rebind=typename rebind_to<U>::type;
|
||||
};
|
||||
} // namespace boost
|
||||
|
||||
#endif // BOOST_UNORDERED_TEST_CFOA_HELPERS_HPP
|
||||
|
@ -1,5 +1,5 @@
|
||||
// Copyright (C) 2023 Christian Mazakas
|
||||
// Copyright (C) 2023 Joaquin M Lopez Munoz
|
||||
// Copyright (C) 2023-2024 Joaquin M Lopez Munoz
|
||||
// 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)
|
||||
|
||||
@ -8,6 +8,8 @@
|
||||
#include <boost/config.hpp>
|
||||
#include <boost/unordered/concurrent_flat_map.hpp>
|
||||
#include <boost/unordered/concurrent_flat_set.hpp>
|
||||
#include <boost/unordered/concurrent_node_map.hpp>
|
||||
#include <boost/unordered/concurrent_node_set.hpp>
|
||||
|
||||
#include <boost/core/ignore_unused.hpp>
|
||||
|
||||
@ -179,8 +181,15 @@ namespace {
|
||||
|
||||
BOOST_TEST_EQ(raii::default_constructor, 0u);
|
||||
BOOST_TEST_EQ(raii::copy_constructor, 2 * x.size());
|
||||
|
||||
if (is_container_node_based<X>::value) {
|
||||
BOOST_TEST_EQ(raii::move_constructor, 0u);
|
||||
}
|
||||
else{
|
||||
// don't check move construction count here because of rehashing
|
||||
BOOST_TEST_GT(raii::move_constructor, 0u);
|
||||
}
|
||||
|
||||
BOOST_TEST_EQ(raii::copy_assignment, values.size() - x.size());
|
||||
BOOST_TEST_EQ(raii::move_assignment, 0u);
|
||||
}
|
||||
@ -198,7 +207,14 @@ namespace {
|
||||
|
||||
BOOST_TEST_EQ(raii::default_constructor, 0u);
|
||||
BOOST_TEST_EQ(raii::copy_constructor, x.size());
|
||||
|
||||
if (is_container_node_based<X>::value) {
|
||||
BOOST_TEST_EQ(raii::move_constructor, x.size());
|
||||
}
|
||||
else{
|
||||
BOOST_TEST_GT(raii::move_constructor, x.size()); // rehashing
|
||||
}
|
||||
|
||||
BOOST_TEST_EQ(raii::copy_assignment, 0u);
|
||||
BOOST_TEST_EQ(raii::move_assignment, values.size() - x.size());
|
||||
}
|
||||
@ -216,7 +232,14 @@ namespace {
|
||||
|
||||
BOOST_TEST_EQ(raii::default_constructor, 0u);
|
||||
BOOST_TEST_EQ(raii::copy_constructor, x.size());
|
||||
|
||||
if (is_container_node_based<X>::value) {
|
||||
BOOST_TEST_EQ(raii::move_constructor, x.size());
|
||||
}
|
||||
else{
|
||||
BOOST_TEST_GT(raii::move_constructor, x.size()); // rehashing
|
||||
}
|
||||
|
||||
BOOST_TEST_EQ(raii::copy_assignment, values.size() - x.size());
|
||||
BOOST_TEST_EQ(raii::move_assignment, 0u);
|
||||
}
|
||||
@ -234,7 +257,14 @@ namespace {
|
||||
|
||||
BOOST_TEST_EQ(raii::default_constructor, 0u);
|
||||
BOOST_TEST_EQ(raii::copy_constructor, 0u);
|
||||
BOOST_TEST_GE(raii::move_constructor, 2 * x.size());
|
||||
|
||||
if (is_container_node_based<X>::value) {
|
||||
BOOST_TEST_EQ(raii::move_constructor, 2 * x.size());
|
||||
}
|
||||
else{
|
||||
BOOST_TEST_GE(raii::move_constructor, 2 * x.size()); // rehashing
|
||||
}
|
||||
|
||||
BOOST_TEST_EQ(raii::copy_assignment, 0u);
|
||||
BOOST_TEST_EQ(raii::move_assignment, values.size() - x.size());
|
||||
}
|
||||
@ -260,7 +290,14 @@ namespace {
|
||||
|
||||
BOOST_TEST_EQ(raii::default_constructor, x.size());
|
||||
BOOST_TEST_EQ(raii::copy_constructor, x.size());
|
||||
BOOST_TEST_GT(raii::move_constructor, x.size()); // rehashing
|
||||
|
||||
if (is_container_node_based<X>::value) {
|
||||
BOOST_TEST_EQ(raii::move_constructor, 0u);
|
||||
}
|
||||
else{
|
||||
BOOST_TEST_GT(raii::move_constructor, 0u); // rehashing
|
||||
}
|
||||
|
||||
BOOST_TEST_EQ(raii::copy_assignment, values.size() - x.size());
|
||||
BOOST_TEST_EQ(raii::move_assignment, 0u);
|
||||
}
|
||||
@ -284,7 +321,14 @@ namespace {
|
||||
|
||||
BOOST_TEST_EQ(raii::default_constructor, x.size());
|
||||
BOOST_TEST_EQ(raii::copy_constructor, 0u);
|
||||
BOOST_TEST_GT(raii::move_constructor, 2 * x.size()); // rehashing
|
||||
|
||||
if (is_container_node_based<X>::value) {
|
||||
BOOST_TEST_EQ(raii::move_constructor, x.size());
|
||||
}
|
||||
else{
|
||||
BOOST_TEST_GT(raii::move_constructor, x.size()); // rehashing
|
||||
}
|
||||
|
||||
BOOST_TEST_EQ(raii::copy_assignment, 0u);
|
||||
BOOST_TEST_EQ(raii::move_assignment, values.size() - x.size());
|
||||
}
|
||||
@ -319,8 +363,15 @@ namespace {
|
||||
BOOST_TEST_EQ(raii::default_constructor, 0u);
|
||||
BOOST_TEST_EQ(
|
||||
raii::copy_constructor, value_type_cardinality * x.size());
|
||||
|
||||
if (is_container_node_based<X>::value) {
|
||||
BOOST_TEST_EQ(raii::move_constructor, 0u);
|
||||
}
|
||||
else{
|
||||
// don't check move construction count here because of rehashing
|
||||
BOOST_TEST_GT(raii::move_constructor, 0u);
|
||||
}
|
||||
|
||||
BOOST_TEST_EQ(raii::move_assignment, 0u);
|
||||
}
|
||||
} lvalue_insert_or_cvisit;
|
||||
@ -360,8 +411,15 @@ namespace {
|
||||
|
||||
BOOST_TEST_EQ(raii::default_constructor, 0u);
|
||||
BOOST_TEST_EQ(raii::copy_constructor, value_type_cardinality * x.size());
|
||||
|
||||
if (is_container_node_based<X>::value) {
|
||||
BOOST_TEST_EQ(raii::move_constructor, 0u);
|
||||
}
|
||||
else{
|
||||
// don't check move construction count here because of rehashing
|
||||
BOOST_TEST_GT(raii::move_constructor, 0u);
|
||||
}
|
||||
|
||||
BOOST_TEST_EQ(raii::move_assignment, 0u);
|
||||
}
|
||||
} lvalue_insert_or_visit;
|
||||
@ -665,12 +723,14 @@ namespace {
|
||||
}
|
||||
}
|
||||
|
||||
UNORDERED_AUTO_TEST (insert_sfinae_test) {
|
||||
|
||||
template <class X>
|
||||
void insert_map_sfinae_test(X*)
|
||||
{
|
||||
// mostly a compile-time tests to ensure that there's no ambiguity when a
|
||||
// user does this
|
||||
using value_type =
|
||||
typename boost::unordered::concurrent_flat_map<raii, raii>::value_type;
|
||||
boost::unordered::concurrent_flat_map<raii, raii> x;
|
||||
using value_type = typename X::value_type;
|
||||
X x;
|
||||
x.insert({1, 2});
|
||||
|
||||
x.insert_or_visit({2, 3}, [](value_type&) {});
|
||||
@ -684,11 +744,23 @@ namespace {
|
||||
std::equal_to<raii>, fancy_allocator<std::pair<raii const, raii> > >*
|
||||
fancy_map;
|
||||
|
||||
boost::unordered::concurrent_node_map<raii, raii>* node_map;
|
||||
boost::unordered::concurrent_node_map<raii, raii, transp_hash,
|
||||
transp_key_equal>* trans_node_map;
|
||||
boost::unordered::concurrent_node_map<raii, raii, boost::hash<raii>,
|
||||
std::equal_to<raii>, fancy_allocator<std::pair<raii const, raii> > >*
|
||||
fancy_node_map;
|
||||
|
||||
boost::unordered::concurrent_flat_set<raii>* set;
|
||||
boost::unordered::concurrent_flat_set<raii, boost::hash<raii>,
|
||||
std::equal_to<raii>, fancy_allocator<std::pair<raii const, raii> > >*
|
||||
std::equal_to<raii>, fancy_allocator<raii> >*
|
||||
fancy_set;
|
||||
|
||||
boost::unordered::concurrent_node_set<raii>* node_set;
|
||||
boost::unordered::concurrent_node_set<raii, boost::hash<raii>,
|
||||
std::equal_to<raii>, fancy_allocator<raii> >*
|
||||
fancy_node_set;
|
||||
|
||||
std::initializer_list<std::pair<raii const, raii> > map_init_list{
|
||||
{raii{0}, raii{0}},
|
||||
{raii{1}, raii{1}},
|
||||
@ -740,7 +812,9 @@ namespace {
|
||||
};
|
||||
|
||||
auto map_and_init_list=std::make_pair(map,map_init_list);
|
||||
auto node_map_and_init_list=std::make_pair(node_map,map_init_list);
|
||||
auto set_and_init_list=std::make_pair(set,set_init_list);
|
||||
auto node_set_and_init_list=std::make_pair(node_set,set_init_list);
|
||||
|
||||
} // namespace
|
||||
|
||||
@ -751,11 +825,12 @@ using test::sequential;
|
||||
// clang-format off
|
||||
UNORDERED_TEST(
|
||||
insert_initializer_list,
|
||||
((map_and_init_list)(set_and_init_list)))
|
||||
((map_and_init_list)(node_map_and_init_list)(set_and_init_list)))
|
||||
|
||||
UNORDERED_TEST(
|
||||
insert,
|
||||
((map)(fancy_map)(set)(fancy_set))
|
||||
((map)(fancy_map)(node_map)(fancy_node_map)
|
||||
(set)(fancy_set)(node_set)(fancy_node_set))
|
||||
((value_type_generator_factory)(init_type_generator_factory))
|
||||
((lvalue_inserter)(rvalue_inserter)(iterator_range_inserter)
|
||||
(norehash_lvalue_inserter)(norehash_rvalue_inserter)
|
||||
@ -766,7 +841,7 @@ UNORDERED_TEST(
|
||||
|
||||
UNORDERED_TEST(
|
||||
insert,
|
||||
((map))
|
||||
((map)(node_map))
|
||||
((init_type_generator_factory))
|
||||
((lvalue_insert_or_assign_copy_assign)(lvalue_insert_or_assign_move_assign)
|
||||
(rvalue_insert_or_assign_copy_assign)(rvalue_insert_or_assign_move_assign))
|
||||
@ -774,10 +849,14 @@ UNORDERED_TEST(
|
||||
|
||||
UNORDERED_TEST(
|
||||
insert,
|
||||
((trans_map))
|
||||
((trans_map)(trans_node_map))
|
||||
((init_type_generator_factory))
|
||||
((trans_insert_or_assign_copy_assign)(trans_insert_or_assign_move_assign))
|
||||
((default_generator)(sequential)(limited_range)))
|
||||
|
||||
UNORDERED_TEST(
|
||||
insert_map_sfinae_test,
|
||||
((map)(node_map)))
|
||||
// clang-format on
|
||||
|
||||
RUN_TESTS()
|
||||
|
@ -1,5 +1,5 @@
|
||||
// Copyright (C) 2023 Christian Mazakas
|
||||
// Copyright (C) 2023 Joaquin M Lopez Munoz
|
||||
// Copyright (C) 2023-2024 Joaquin M Lopez Munoz
|
||||
// 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)
|
||||
|
||||
@ -7,6 +7,8 @@
|
||||
|
||||
#include <boost/unordered/concurrent_flat_map.hpp>
|
||||
#include <boost/unordered/concurrent_flat_set.hpp>
|
||||
#include <boost/unordered/concurrent_node_map.hpp>
|
||||
#include <boost/unordered/concurrent_node_set.hpp>
|
||||
|
||||
test::seed_t initialize_seed{402031699};
|
||||
|
||||
@ -23,19 +25,38 @@ using map2_type = boost::unordered::concurrent_flat_map<raii, raii,
|
||||
std::hash<raii>, std::equal_to<raii>,
|
||||
stateful_allocator<std::pair<raii const, raii> > >;
|
||||
|
||||
using node_map_type = boost::unordered::concurrent_node_map<raii, raii,
|
||||
hasher, key_equal, stateful_allocator<std::pair<raii const, raii> > >;
|
||||
using node_map2_type = boost::unordered::concurrent_node_map<raii, raii,
|
||||
std::hash<raii>, std::equal_to<raii>,
|
||||
stateful_allocator<std::pair<raii const, raii> > >;
|
||||
|
||||
using set_type = boost::unordered::concurrent_flat_set<raii, hasher,
|
||||
key_equal, stateful_allocator<raii> >;
|
||||
using set2_type = boost::unordered::concurrent_flat_set<raii, std::hash<raii>,
|
||||
std::equal_to<raii>, stateful_allocator<raii> >;
|
||||
|
||||
using node_set_type = boost::unordered::concurrent_node_set<raii, hasher,
|
||||
key_equal, stateful_allocator<raii> >;
|
||||
using node_set2_type = boost::unordered::concurrent_node_set<raii, std::hash<raii>,
|
||||
std::equal_to<raii>, stateful_allocator<raii> >;
|
||||
|
||||
map_type* test_map;
|
||||
map2_type* test_map2;
|
||||
auto test_maps=std::make_pair(test_map,test_map2);
|
||||
|
||||
node_map_type* test_node_map;
|
||||
node_map2_type* test_node_map2;
|
||||
auto test_node_maps=std::make_pair(test_node_map,test_node_map2);
|
||||
|
||||
set_type* test_set;
|
||||
set2_type* test_set2;
|
||||
auto test_sets=std::make_pair(test_set,test_set2);
|
||||
|
||||
node_set_type* test_node_set;
|
||||
node_set2_type* test_node_set2;
|
||||
auto test_node_sets=std::make_pair(test_node_set,test_node_set2);
|
||||
|
||||
struct
|
||||
{
|
||||
template <class X1, class X2>
|
||||
@ -91,9 +112,16 @@ namespace {
|
||||
});
|
||||
|
||||
BOOST_TEST_EQ(raii::copy_constructor, old_cc + expected_copies);
|
||||
|
||||
if (is_container_node_based<X>::value) {
|
||||
BOOST_TEST_EQ(raii::move_constructor, 0u);
|
||||
}
|
||||
else{
|
||||
BOOST_TEST_EQ(
|
||||
raii::move_constructor,
|
||||
value_type_cardinality * reference_cont.size());
|
||||
}
|
||||
|
||||
BOOST_TEST_EQ(+num_merged, reference_cont.size());
|
||||
|
||||
test_fuzzy_matches_reference(x, reference_cont, rg);
|
||||
@ -210,10 +238,17 @@ namespace {
|
||||
t3.join();
|
||||
|
||||
if (num_merges > 0) {
|
||||
|
||||
if (is_container_node_based<X>::value) {
|
||||
BOOST_TEST_EQ(raii::move_constructor, 0u);
|
||||
}
|
||||
else{
|
||||
// num merges is 0 most commonly in the cast of the limited_range
|
||||
// generator as both maps will contains keys from 0 to 99
|
||||
BOOST_TEST_EQ(
|
||||
+raii::move_constructor, value_type_cardinality * num_merges);
|
||||
raii::move_constructor, value_type_cardinality * num_merges);
|
||||
}
|
||||
|
||||
BOOST_TEST_GE(call_count, 1u);
|
||||
}
|
||||
|
||||
@ -229,14 +264,14 @@ namespace {
|
||||
// clang-format off
|
||||
UNORDERED_TEST(
|
||||
merge_tests,
|
||||
((test_maps)(test_sets))
|
||||
((test_maps)(test_node_maps)(test_sets)(test_node_sets))
|
||||
((lvalue_merge)(rvalue_merge))
|
||||
((value_type_generator_factory))
|
||||
((default_generator)(sequential)(limited_range)))
|
||||
|
||||
UNORDERED_TEST(
|
||||
insert_and_merge_tests,
|
||||
((test_maps)(test_sets))
|
||||
((test_maps)(test_node_maps)(test_sets)(test_node_sets))
|
||||
((value_type_generator_factory))
|
||||
((default_generator)(sequential)(limited_range)))
|
||||
// clang-format on
|
||||
|
6
test/cfoa/node_handle_allocator_tests.cpp
Normal file
6
test/cfoa/node_handle_allocator_tests.cpp
Normal file
@ -0,0 +1,6 @@
|
||||
// Copyright 2024 Joaquin M Lopez Munoz.
|
||||
// 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)
|
||||
|
||||
#define BOOST_UNORDERED_CFOA_TESTS
|
||||
#include "../unordered/node_handle_allocator_tests.cpp"
|
@ -5,4 +5,6 @@
|
||||
#define BOOST_UNORDERED_CFOA_TESTS
|
||||
#include <boost/unordered/concurrent_flat_map.hpp>
|
||||
#include <boost/unordered/concurrent_flat_set.hpp>
|
||||
#include <boost/unordered/concurrent_node_map.hpp>
|
||||
#include <boost/unordered/concurrent_node_set.hpp>
|
||||
#include "../unordered/pmr_allocator_tests.cpp"
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Copyright 2023 Joaquin M Lopez Munoz
|
||||
// Copyright 2023-2024 Joaquin M Lopez Munoz
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// https://www.boost.org/LICENSE_1_0.txt
|
||||
|
||||
@ -32,15 +32,21 @@ namespace boost {
|
||||
|
||||
#include <boost/unordered/concurrent_flat_map.hpp>
|
||||
#include <boost/unordered/concurrent_flat_set.hpp>
|
||||
#include <boost/unordered/concurrent_node_map.hpp>
|
||||
#include <boost/unordered/concurrent_node_set.hpp>
|
||||
#include <boost/core/lightweight_test.hpp>
|
||||
|
||||
using test::default_generator;
|
||||
|
||||
using map_type = boost::unordered::concurrent_flat_map<raii, raii>;
|
||||
using node_map_type = boost::unordered::concurrent_node_map<raii, raii>;
|
||||
using set_type = boost::unordered::concurrent_flat_set<raii>;
|
||||
using node_set_type = boost::unordered::concurrent_node_set<raii>;
|
||||
|
||||
map_type* test_map;
|
||||
map_type* test_node_map;
|
||||
set_type* test_set;
|
||||
node_set_type* test_node_set;
|
||||
|
||||
template<typename F>
|
||||
void detect_reentrancy(F f)
|
||||
@ -105,7 +111,7 @@ namespace {
|
||||
// clang-format off
|
||||
UNORDERED_TEST(
|
||||
reentrancy_tests,
|
||||
((test_map)(test_set))
|
||||
((test_map)(test_node_map)(test_set)(test_node_set))
|
||||
((value_type_generator_factory))
|
||||
((default_generator)))
|
||||
// clang-format on
|
||||
|
@ -1,5 +1,5 @@
|
||||
// Copyright (C) 2023 Christian Mazakas
|
||||
// Copyright (C) 2023 Joaquin M Lopez Munoz
|
||||
// Copyright (C) 2023-2024 Joaquin M Lopez Munoz
|
||||
// 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)
|
||||
|
||||
@ -7,6 +7,8 @@
|
||||
|
||||
#include <boost/unordered/concurrent_flat_map.hpp>
|
||||
#include <boost/unordered/concurrent_flat_set.hpp>
|
||||
#include <boost/unordered/concurrent_node_map.hpp>
|
||||
#include <boost/unordered/concurrent_node_set.hpp>
|
||||
|
||||
using test::default_generator;
|
||||
using test::limited_range;
|
||||
@ -18,11 +20,19 @@ using key_equal = stateful_key_equal;
|
||||
using map_type = boost::unordered::concurrent_flat_map<raii, raii, hasher,
|
||||
key_equal, stateful_allocator<std::pair<raii const, raii> > >;
|
||||
|
||||
using node_map_type = boost::unordered::concurrent_node_map<raii, raii, hasher,
|
||||
key_equal, stateful_allocator<std::pair<raii const, raii> > >;
|
||||
|
||||
using set_type = boost::unordered::concurrent_flat_set<raii, hasher,
|
||||
key_equal, stateful_allocator<raii> >;
|
||||
|
||||
using node_set_type = boost::unordered::concurrent_node_set<raii, hasher,
|
||||
key_equal, stateful_allocator<raii> >;
|
||||
|
||||
map_type* test_map;
|
||||
node_map_type* test_node_map;
|
||||
set_type* test_set;
|
||||
node_set_type* test_node_set;
|
||||
|
||||
namespace {
|
||||
test::seed_t initialize_seed{748775921};
|
||||
@ -187,15 +197,15 @@ namespace {
|
||||
// clang-format off
|
||||
UNORDERED_TEST(
|
||||
rehash_no_insert,
|
||||
((test_map)(test_set)))
|
||||
((test_map)(test_node_map)(test_set)(test_node_set)))
|
||||
|
||||
UNORDERED_TEST(
|
||||
reserve_no_insert,
|
||||
((test_map)(test_set)))
|
||||
((test_map)(test_node_map)(test_set)(test_node_set)))
|
||||
|
||||
UNORDERED_TEST(
|
||||
insert_and_erase_with_rehash,
|
||||
((test_map)(test_set))
|
||||
((test_map)(test_node_map)(test_set)(test_node_set))
|
||||
((value_type_generator_factory))
|
||||
((default_generator)(sequential)(limited_range)))
|
||||
// clang-format on
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Copyright (C) 2023 Joaquin M Lopez Munoz
|
||||
// Copyright (C) 2023-2024 Joaquin M Lopez Munoz
|
||||
// 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)
|
||||
|
||||
@ -12,6 +12,8 @@
|
||||
#include <boost/serialization/nvp.hpp>
|
||||
#include <boost/unordered/concurrent_flat_map.hpp>
|
||||
#include <boost/unordered/concurrent_flat_set.hpp>
|
||||
#include <boost/unordered/concurrent_node_map.hpp>
|
||||
#include <boost/unordered/concurrent_node_set.hpp>
|
||||
|
||||
namespace {
|
||||
|
||||
@ -70,11 +72,15 @@ namespace {
|
||||
|
||||
boost::concurrent_flat_map<
|
||||
test::object, test::object, test::hash, test::equal_to>* test_flat_map;
|
||||
boost::concurrent_node_map<
|
||||
test::object, test::object, test::hash, test::equal_to>* test_node_map;
|
||||
boost::concurrent_flat_set<
|
||||
test::object, test::hash, test::equal_to>* test_flat_set;
|
||||
boost::concurrent_node_set<
|
||||
test::object, test::hash, test::equal_to>* test_node_set;
|
||||
|
||||
UNORDERED_TEST(serialization_tests,
|
||||
((test_flat_map)(test_flat_set))
|
||||
((test_flat_map)(test_node_map)(test_flat_set)(test_node_set))
|
||||
((text_archive)(xml_archive))
|
||||
((default_generator)))
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Copyright 2024 Joaquin M Lopez Muoz.
|
||||
// Copyright 2024 Joaquin M Lopez Munoz.
|
||||
// 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)
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
// Copyright (C) 2023 Christian Mazakas
|
||||
// Copyright (C) 2023 Joaquin M Lopez Munoz
|
||||
// Copyright (C) 2023-2024 Joaquin M Lopez Munoz
|
||||
// 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)
|
||||
|
||||
@ -7,6 +7,8 @@
|
||||
|
||||
#include <boost/unordered/concurrent_flat_map.hpp>
|
||||
#include <boost/unordered/concurrent_flat_set.hpp>
|
||||
#include <boost/unordered/concurrent_node_map.hpp>
|
||||
#include <boost/unordered/concurrent_node_set.hpp>
|
||||
|
||||
test::seed_t initialize_seed{996130204};
|
||||
|
||||
@ -62,9 +64,15 @@ using key_equal = stateful_key_equal;
|
||||
using map_type = boost::unordered::concurrent_flat_map<raii, raii, hasher,
|
||||
key_equal, stateful_allocator<std::pair<raii const, raii> > >;
|
||||
|
||||
using node_map_type = boost::unordered::concurrent_node_map<raii, raii, hasher,
|
||||
key_equal, stateful_allocator<std::pair<raii const, raii> > >;
|
||||
|
||||
using set_type = boost::unordered::concurrent_flat_set<raii, hasher,
|
||||
key_equal, stateful_allocator<raii> >;
|
||||
|
||||
using node_set_type = boost::unordered::concurrent_node_set<raii, hasher,
|
||||
key_equal, stateful_allocator<raii> >;
|
||||
|
||||
template <class T> struct is_nothrow_member_swappable
|
||||
{
|
||||
static bool const value =
|
||||
@ -293,21 +301,28 @@ namespace {
|
||||
map_type* map;
|
||||
replace_allocator<map_type, pocs_allocator>* pocs_map;
|
||||
|
||||
node_map_type* node_map;
|
||||
replace_allocator<node_map_type, pocs_allocator>* pocs_node_map;
|
||||
|
||||
set_type* set;
|
||||
replace_allocator<set_type, pocs_allocator>* pocs_set;
|
||||
|
||||
node_set_type* node_set;
|
||||
replace_allocator<node_set_type, pocs_allocator>* pocs_node_set;
|
||||
|
||||
} // namespace
|
||||
|
||||
// clang-format off
|
||||
UNORDERED_TEST(
|
||||
swap_tests,
|
||||
((map)(pocs_map)(set)(pocs_set))
|
||||
((map)(pocs_map)(node_map)(pocs_node_map)
|
||||
(set)(pocs_set)(node_set)(pocs_node_set))
|
||||
((member_fn_swap)(free_fn_swap))
|
||||
((value_type_generator_factory))
|
||||
((default_generator)(sequential)(limited_range)))
|
||||
|
||||
UNORDERED_TEST(insert_and_swap,
|
||||
((map)(set))
|
||||
((map)(node_map)(set)(node_set))
|
||||
((member_fn_swap)(free_fn_swap))
|
||||
((value_type_generator_factory))
|
||||
((default_generator)(sequential)(limited_range)))
|
||||
|
@ -1,10 +1,12 @@
|
||||
// Copyright (C) 2023 Christian Mazakas
|
||||
// Copyright (C) 2024 Joaquin M Lopez Munoz
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#include "helpers.hpp"
|
||||
|
||||
#include <boost/unordered/concurrent_flat_map.hpp>
|
||||
#include <boost/unordered/concurrent_node_map.hpp>
|
||||
|
||||
#include <boost/core/ignore_unused.hpp>
|
||||
|
||||
@ -161,8 +163,15 @@ namespace {
|
||||
|
||||
BOOST_TEST_EQ(raii::default_constructor, x.size());
|
||||
BOOST_TEST_EQ(raii::copy_constructor, x.size());
|
||||
|
||||
if (is_container_node_based<X>::value) {
|
||||
BOOST_TEST_EQ(raii::move_constructor, 0u);
|
||||
}
|
||||
else{
|
||||
// don't check move construction count here because of rehashing
|
||||
BOOST_TEST_GT(raii::move_constructor, 0u);
|
||||
}
|
||||
|
||||
BOOST_TEST_EQ(raii::move_assignment, 0u);
|
||||
BOOST_TEST_EQ(raii::copy_assignment, 0u);
|
||||
}
|
||||
@ -194,8 +203,15 @@ namespace {
|
||||
|
||||
BOOST_TEST_EQ(raii::default_constructor, x.size());
|
||||
BOOST_TEST_EQ(raii::copy_constructor, x.size());
|
||||
|
||||
if (is_container_node_based<X>::value) {
|
||||
BOOST_TEST_EQ(raii::move_constructor, 0u);
|
||||
}
|
||||
else{
|
||||
// don't check move construction count here because of rehashing
|
||||
BOOST_TEST_GT(raii::move_constructor, 0u);
|
||||
}
|
||||
|
||||
BOOST_TEST_EQ(raii::move_assignment, 0u);
|
||||
BOOST_TEST_EQ(raii::copy_assignment, 0u);
|
||||
}
|
||||
@ -229,7 +245,12 @@ namespace {
|
||||
|
||||
if (std::is_same<T, typename X::value_type>::value) {
|
||||
BOOST_TEST_EQ(raii::copy_constructor, x.size());
|
||||
if (is_container_node_based<X>::value) {
|
||||
BOOST_TEST_EQ(raii::move_constructor, 0u);
|
||||
}
|
||||
else{
|
||||
BOOST_TEST_GE(raii::move_constructor, x.size());
|
||||
}
|
||||
} else {
|
||||
BOOST_TEST_EQ(raii::copy_constructor, 0u);
|
||||
BOOST_TEST_GE(raii::move_constructor, x.size());
|
||||
@ -264,7 +285,12 @@ namespace {
|
||||
BOOST_TEST_EQ(raii::default_constructor, x.size());
|
||||
if (std::is_same<T, typename X::value_type>::value) {
|
||||
BOOST_TEST_EQ(raii::copy_constructor, x.size());
|
||||
if (is_container_node_based<X>::value) {
|
||||
BOOST_TEST_EQ(raii::move_constructor, 0u);
|
||||
}
|
||||
else{
|
||||
BOOST_TEST_GE(raii::move_constructor, x.size());
|
||||
}
|
||||
} else {
|
||||
BOOST_TEST_EQ(raii::copy_constructor, 0u);
|
||||
BOOST_TEST_GE(raii::move_constructor, x.size());
|
||||
@ -367,6 +393,10 @@ namespace {
|
||||
boost::unordered::concurrent_flat_map<raii, raii, transp_hash,
|
||||
transp_key_equal>* transp_map;
|
||||
|
||||
boost::unordered::concurrent_node_map<raii, raii>* node_map;
|
||||
boost::unordered::concurrent_node_map<raii, raii, transp_hash,
|
||||
transp_key_equal>* transp_node_map;
|
||||
|
||||
} // namespace
|
||||
|
||||
using test::default_generator;
|
||||
@ -379,7 +409,7 @@ value_generator<std::pair<raii, raii> > init_type_generator;
|
||||
// clang-format off
|
||||
UNORDERED_TEST(
|
||||
try_emplace,
|
||||
((map))
|
||||
((map)(node_map))
|
||||
((value_type_generator)(init_type_generator))
|
||||
((lvalue_try_emplacer)(norehash_lvalue_try_emplacer)
|
||||
(rvalue_try_emplacer)(norehash_rvalue_try_emplacer)
|
||||
@ -389,7 +419,7 @@ UNORDERED_TEST(
|
||||
|
||||
UNORDERED_TEST(
|
||||
try_emplace,
|
||||
((transp_map))
|
||||
((transp_map)(transp_node_map))
|
||||
((init_type_generator))
|
||||
((transp_try_emplace)(norehash_transp_try_emplace)
|
||||
(transp_try_emplace_or_cvisit)(transp_try_emplace_or_visit))
|
||||
|
@ -17,6 +17,8 @@
|
||||
|
||||
#include <boost/unordered/concurrent_flat_map.hpp>
|
||||
#include <boost/unordered/concurrent_flat_set.hpp>
|
||||
#include <boost/unordered/concurrent_node_map.hpp>
|
||||
#include <boost/unordered/concurrent_node_set.hpp>
|
||||
|
||||
#include <boost/compat/latch.hpp>
|
||||
#include <boost/core/ignore_unused.hpp>
|
||||
@ -971,9 +973,15 @@ namespace {
|
||||
boost::unordered::concurrent_flat_map<raii, raii>* map;
|
||||
boost::unordered::concurrent_flat_map<raii, raii, transp_hash,
|
||||
transp_key_equal>* transp_map;
|
||||
boost::unordered::concurrent_node_map<raii, raii>* node_map;
|
||||
boost::unordered::concurrent_node_map<raii, raii, transp_hash,
|
||||
transp_key_equal>* transp_node_map;
|
||||
boost::unordered::concurrent_flat_set<raii>* set;
|
||||
boost::unordered::concurrent_flat_set<raii, transp_hash,
|
||||
transp_key_equal>* transp_set;
|
||||
boost::unordered::concurrent_node_set<raii>* node_set;
|
||||
boost::unordered::concurrent_node_set<raii, transp_hash,
|
||||
transp_key_equal>* transp_node_set;
|
||||
|
||||
struct mutable_pair
|
||||
{
|
||||
@ -1115,6 +1123,8 @@ namespace {
|
||||
|
||||
boost::concurrent_flat_set<
|
||||
mutable_pair, mutable_pair_hash, mutable_pair_equal_to>* mutable_set;
|
||||
boost::concurrent_node_set<
|
||||
mutable_pair, mutable_pair_hash, mutable_pair_equal_to>* mutable_node_set;
|
||||
|
||||
} // namespace
|
||||
|
||||
@ -1126,7 +1136,7 @@ using test::sequential;
|
||||
|
||||
UNORDERED_TEST(
|
||||
visit,
|
||||
((map)(set))
|
||||
((map)(node_map)(set)(node_set))
|
||||
((value_type_generator_factory)(init_type_generator_factory))
|
||||
((lvalue_visitor)(visit_all)(visit_while)(exec_policy_visit_all)
|
||||
(exec_policy_visit_while))
|
||||
@ -1134,28 +1144,29 @@ UNORDERED_TEST(
|
||||
|
||||
UNORDERED_TEST(
|
||||
visit,
|
||||
((transp_map)(transp_set))
|
||||
((transp_map)(transp_node_map)(transp_set)(transp_node_set))
|
||||
((value_type_generator_factory)(init_type_generator_factory))
|
||||
((transp_visitor))
|
||||
((default_generator)(sequential)(limited_range)))
|
||||
|
||||
UNORDERED_TEST(
|
||||
empty_visit,
|
||||
((map)(transp_map)(set)(transp_set))
|
||||
((map)(transp_map)(node_map)(transp_node_map)
|
||||
(set)(transp_set)(node_set)(transp_node_set))
|
||||
((value_type_generator_factory)(init_type_generator_factory))
|
||||
((default_generator)(sequential)(limited_range))
|
||||
)
|
||||
|
||||
UNORDERED_TEST(
|
||||
insert_and_visit,
|
||||
((map)(set))
|
||||
((map)(node_map)(set)(node_set))
|
||||
((value_type_generator_factory))
|
||||
((sequential))
|
||||
)
|
||||
|
||||
UNORDERED_TEST(
|
||||
bulk_visit,
|
||||
((map)(set))
|
||||
((map)(node_map)(set)(node_set))
|
||||
((regular_key_extract))
|
||||
((value_type_generator_factory))
|
||||
((sequential))
|
||||
@ -1163,7 +1174,7 @@ UNORDERED_TEST(
|
||||
|
||||
UNORDERED_TEST(
|
||||
bulk_visit,
|
||||
((transp_map)(transp_set))
|
||||
((transp_map)(transp_node_map)(transp_set)(transp_node_set))
|
||||
((transp_key_extract))
|
||||
((value_type_generator_factory))
|
||||
((sequential))
|
||||
@ -1173,7 +1184,7 @@ UNORDERED_TEST(
|
||||
|
||||
UNORDERED_TEST(
|
||||
exclusive_access_set_visit,
|
||||
((mutable_set))
|
||||
((mutable_set)(mutable_node_set))
|
||||
)
|
||||
|
||||
// clang-format on
|
||||
|
@ -20,6 +20,8 @@
|
||||
#include <boost/interprocess/managed_shared_memory.hpp>
|
||||
#include <boost/unordered/concurrent_flat_map.hpp>
|
||||
#include <boost/unordered/concurrent_flat_set.hpp>
|
||||
#include <boost/unordered/concurrent_node_map.hpp>
|
||||
#include <boost/unordered/concurrent_node_set.hpp>
|
||||
#include <boost/unordered/unordered_flat_map.hpp>
|
||||
#include <boost/unordered/unordered_flat_set.hpp>
|
||||
#include <boost/unordered/unordered_map.hpp>
|
||||
@ -61,8 +63,12 @@ template <class Tester> void visualization_test(Tester& tester)
|
||||
|
||||
auto cfoa_flat_map_ptr = tester.template construct_map<boost::concurrent_flat_map>();
|
||||
auto cfoa_flat_set_ptr = tester.template construct_set<boost::concurrent_flat_set>();
|
||||
auto cfoa_node_map_ptr = tester.template construct_map<boost::concurrent_node_map>();
|
||||
auto cfoa_node_set_ptr = tester.template construct_set<boost::concurrent_node_set>();
|
||||
auto& cfoa_flat_map = *cfoa_flat_map_ptr;
|
||||
auto& cfoa_flat_set = *cfoa_flat_set_ptr;
|
||||
auto& cfoa_node_map = *cfoa_node_map_ptr;
|
||||
auto& cfoa_node_set = *cfoa_node_set_ptr;
|
||||
// clang-format on
|
||||
|
||||
for (int i = 0; i < 5; ++i) {
|
||||
@ -75,6 +81,7 @@ template <class Tester> void visualization_test(Tester& tester)
|
||||
foa_flat_map.emplace(str, num);
|
||||
foa_node_map.emplace(str, num);
|
||||
cfoa_flat_map.emplace(str, num);
|
||||
cfoa_node_map.emplace(str, num);
|
||||
|
||||
fca_set.emplace(str);
|
||||
fca_multiset.emplace(str);
|
||||
@ -82,6 +89,7 @@ template <class Tester> void visualization_test(Tester& tester)
|
||||
foa_flat_set.emplace(str);
|
||||
foa_node_set.emplace(str);
|
||||
cfoa_flat_set.emplace(str);
|
||||
cfoa_node_set.emplace(str);
|
||||
}
|
||||
|
||||
auto fca_map_begin = fca_map.begin();
|
||||
@ -102,7 +110,7 @@ template <class Tester> void visualization_test(Tester& tester)
|
||||
auto foa_node_set_begin = foa_node_set.begin();
|
||||
auto foa_node_set_end = foa_node_set.end();
|
||||
|
||||
use(cfoa_flat_map, cfoa_flat_set);
|
||||
use(cfoa_flat_map, cfoa_flat_set, cfoa_node_map, cfoa_node_set);
|
||||
use(fca_map_begin, fca_map_end, fca_multimap_begin, fca_multimap_end,
|
||||
fca_set_begin, fca_set_end, fca_multiset_begin, fca_multiset_end);
|
||||
use(foa_flat_map_begin, foa_flat_map_end, foa_flat_set_begin,
|
||||
|
@ -1,14 +1,17 @@
|
||||
// Copyright 2024 Joaquin M Lopez Muoz.
|
||||
// Copyright 2024 Joaquin M Lopez Munoz.
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at htT://www.boost.org/LICENSE_1_0.txt)
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#ifdef BOOST_UNORDERED_CFOA_TESTS
|
||||
#include <boost/unordered/concurrent_flat_map.hpp>
|
||||
#include <boost/unordered/concurrent_flat_set.hpp>
|
||||
#include <boost/unordered/concurrent_node_map.hpp>
|
||||
#include <boost/unordered/concurrent_node_set.hpp>
|
||||
#else
|
||||
#include "../helpers/unordered.hpp"
|
||||
#endif
|
||||
|
||||
#include "../helpers/helpers.hpp"
|
||||
#include "../helpers/test.hpp"
|
||||
#include <boost/type_traits/make_void.hpp>
|
||||
#include <memory>
|
||||
@ -62,7 +65,16 @@ template <class Container> void test_explicit_alloc_ctor_extract(
|
||||
template <class Container> void test_explicit_alloc_ctor_extract(
|
||||
Container& c, std::true_type)
|
||||
{
|
||||
#ifdef BOOST_UNORDERED_CFOA_TESTS
|
||||
typename Container::key_type k;
|
||||
c.cvisit_while([&](typename Container::value_type const & x) {
|
||||
k = test::get_key<Container>(x);
|
||||
return false;
|
||||
});
|
||||
auto n = c.extract(k);
|
||||
#else
|
||||
auto n = c.extract(c.begin());
|
||||
#endif
|
||||
c.insert(std::move(n));
|
||||
n = c.extract(typename Container::key_type());
|
||||
c.insert(std::move(n));
|
||||
@ -122,8 +134,13 @@ UNORDERED_AUTO_TEST (explicit_alloc_ctor) {
|
||||
test_explicit_alloc_ctor<boost::concurrent_flat_map<int, int,
|
||||
boost::hash<int>, std::equal_to<int>,
|
||||
explicit_allocator<std::pair<const int, int> > > >();
|
||||
test_explicit_alloc_ctor<boost::concurrent_node_map<int, int,
|
||||
boost::hash<int>, std::equal_to<int>,
|
||||
explicit_allocator<std::pair<const int, int> > > >();
|
||||
test_explicit_alloc_ctor<boost::concurrent_flat_set<
|
||||
int, boost::hash<int>, std::equal_to<int>, explicit_allocator<int> > >();
|
||||
test_explicit_alloc_ctor<boost::concurrent_node_set<
|
||||
int, boost::hash<int>, std::equal_to<int>, explicit_allocator<int> > >();
|
||||
#elif defined(BOOST_UNORDERED_FOA_TESTS)
|
||||
test_explicit_alloc_ctor<boost::unordered_flat_map<int, int,
|
||||
boost::hash<int>, std::equal_to<int>,
|
||||
|
213
test/unordered/node_handle_allocator_tests.cpp
Normal file
213
test/unordered/node_handle_allocator_tests.cpp
Normal file
@ -0,0 +1,213 @@
|
||||
// Copyright (C) 2024 Joaquin M Lopez Munoz
|
||||
// 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_GCC)
|
||||
// Spurious maybe-uninitialized warnings with allocators contained
|
||||
// in node handles.
|
||||
// Maybe related to https://gcc.gnu.org/bugzilla/show_bug.cgi?id=108230
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
|
||||
#endif
|
||||
|
||||
#ifdef BOOST_UNORDERED_CFOA_TESTS
|
||||
#include <boost/unordered/concurrent_node_map.hpp>
|
||||
#include <boost/unordered/concurrent_node_set.hpp>
|
||||
#else
|
||||
#include "../helpers/unordered.hpp"
|
||||
#endif
|
||||
|
||||
#include "../helpers/test.hpp"
|
||||
|
||||
#include <boost/config/workaround.hpp>
|
||||
#include <boost/core/allocator_access.hpp>
|
||||
#include <memory>
|
||||
#include <type_traits>
|
||||
|
||||
|
||||
namespace {
|
||||
template <class T> struct nonassignable_allocator
|
||||
{
|
||||
using value_type = T;
|
||||
|
||||
nonassignable_allocator() = default;
|
||||
nonassignable_allocator(nonassignable_allocator const&) = default;
|
||||
|
||||
template <class U>
|
||||
nonassignable_allocator(nonassignable_allocator<U> const&) {}
|
||||
|
||||
nonassignable_allocator& operator=(nonassignable_allocator const&) = delete;
|
||||
|
||||
T* allocate(std::size_t n)
|
||||
{
|
||||
return static_cast<T*>(::operator new(n * sizeof(T)));
|
||||
}
|
||||
|
||||
void deallocate(T* p, std::size_t) { ::operator delete(p); }
|
||||
|
||||
bool operator==(nonassignable_allocator const&) const { return true; }
|
||||
bool operator!=(nonassignable_allocator const&) const { return false; }
|
||||
};
|
||||
|
||||
template <class T> struct pocx_allocator
|
||||
{
|
||||
int x_;
|
||||
|
||||
using value_type = T;
|
||||
using propagate_on_container_copy_assignment = std::true_type;
|
||||
using propagate_on_container_move_assignment = std::true_type;
|
||||
using propagate_on_container_swap = std::true_type;
|
||||
|
||||
pocx_allocator() : x_{-1} {}
|
||||
pocx_allocator(pocx_allocator const&) = default;
|
||||
pocx_allocator(int const x) : x_{x} {}
|
||||
|
||||
template <class U>
|
||||
pocx_allocator(pocx_allocator<U> const& rhs) : x_{rhs.x_}
|
||||
{
|
||||
}
|
||||
|
||||
pocx_allocator& operator=(pocx_allocator const&) = default;
|
||||
|
||||
T* allocate(std::size_t n)
|
||||
{
|
||||
return static_cast<T*>(::operator new(n * sizeof(T)));
|
||||
}
|
||||
|
||||
void deallocate(T* p, std::size_t) { ::operator delete(p); }
|
||||
|
||||
bool operator==(pocx_allocator const& rhs) const { return x_ == rhs.x_; }
|
||||
bool operator!=(pocx_allocator const& rhs) const { return x_ != rhs.x_; }
|
||||
};
|
||||
|
||||
template <typename Container, typename Allocator>
|
||||
struct replace_allocator_impl;
|
||||
|
||||
template <typename Container, typename Allocator>
|
||||
using replace_allocator =
|
||||
typename replace_allocator_impl<Container, Allocator>::type;
|
||||
|
||||
template <
|
||||
typename K, typename H, typename P, typename A,
|
||||
template <typename, typename, typename, typename> class Set,
|
||||
typename Allocator
|
||||
>
|
||||
struct replace_allocator_impl<Set<K, H, P, A>, Allocator>
|
||||
{
|
||||
using type = Set<
|
||||
K, H, P, boost::allocator_rebind_t<Allocator, K> >;
|
||||
};
|
||||
|
||||
template <
|
||||
typename K, typename H, typename T, typename P, typename A,
|
||||
template <typename, typename, typename, typename, typename> class Map,
|
||||
typename Allocator
|
||||
>
|
||||
struct replace_allocator_impl<Map<K, T, H, P, A>, Allocator>
|
||||
{
|
||||
using type = Map<
|
||||
K, T, H, P,
|
||||
boost::allocator_rebind_t<Allocator, std::pair<K const, T> > >;
|
||||
};
|
||||
|
||||
template<typename X, typename Allocator>
|
||||
void node_handle_allocator_tests(
|
||||
X*, std::pair<Allocator, Allocator> allocators)
|
||||
{
|
||||
using value_type = typename X::value_type;
|
||||
using replaced_allocator_container = replace_allocator<X, Allocator>;
|
||||
using node_type = typename replaced_allocator_container::node_type;
|
||||
|
||||
replaced_allocator_container x1(allocators.first);
|
||||
node_type nh;
|
||||
|
||||
x1.emplace(value_type());
|
||||
nh = x1.extract(0);
|
||||
|
||||
BOOST_TEST(!nh.empty());
|
||||
BOOST_TEST(nh.get_allocator() == x1.get_allocator());
|
||||
|
||||
replaced_allocator_container x2(allocators.second);
|
||||
|
||||
x2.emplace(value_type());
|
||||
nh = x2.extract(0);
|
||||
|
||||
BOOST_TEST(!nh.empty());
|
||||
BOOST_TEST(nh.get_allocator() == x2.get_allocator());
|
||||
}
|
||||
|
||||
template<typename X, typename Allocator>
|
||||
void node_handle_allocator_swap_tests(
|
||||
X*, std::pair<Allocator, Allocator> allocators)
|
||||
{
|
||||
using value_type = typename X::value_type;
|
||||
using replaced_allocator_container = replace_allocator<X, Allocator>;
|
||||
using node_type = typename replaced_allocator_container::node_type;
|
||||
|
||||
replaced_allocator_container x1(allocators.first), x2(allocators.second);
|
||||
x1.emplace(value_type());
|
||||
x2.emplace(value_type());
|
||||
|
||||
node_type nh1, nh2;
|
||||
|
||||
nh1 = x1.extract(0);
|
||||
swap(nh1, nh2);
|
||||
|
||||
BOOST_TEST(nh1.empty());
|
||||
BOOST_TEST(!nh2.empty());
|
||||
BOOST_TEST(nh2.get_allocator() == x1.get_allocator());
|
||||
|
||||
nh1 = x2.extract(0);
|
||||
swap(nh1, nh2);
|
||||
|
||||
BOOST_TEST(!nh1.empty());
|
||||
BOOST_TEST(nh1.get_allocator() == x1.get_allocator());
|
||||
BOOST_TEST(!nh2.empty());
|
||||
BOOST_TEST(nh2.get_allocator() == x2.get_allocator());
|
||||
}
|
||||
|
||||
#if BOOST_WORKAROUND(BOOST_MSVC, <= 1900)
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable : 4592) // symbol will be dynamically initialized
|
||||
#endif
|
||||
|
||||
std::pair<
|
||||
std::allocator<int>, std::allocator<int> > test_std_allocators({},{});
|
||||
std::pair<
|
||||
nonassignable_allocator<int>,
|
||||
nonassignable_allocator<int> > test_nonassignable_allocators({},{});
|
||||
std::pair<
|
||||
pocx_allocator<int>, pocx_allocator<int> > test_pocx_allocators(5,6);
|
||||
|
||||
#if BOOST_WORKAROUND(BOOST_MSVC, <= 1900)
|
||||
#pragma warning(pop) // C4592
|
||||
#endif
|
||||
|
||||
#if defined(BOOST_UNORDERED_FOA_TESTS)
|
||||
boost::unordered_node_map<int, int>* test_map;
|
||||
boost::unordered_node_set<int>* test_set;
|
||||
#elif defined(BOOST_UNORDERED_CFOA_TESTS)
|
||||
boost::concurrent_node_map<int, int>* test_map;
|
||||
boost::concurrent_node_set<int>* test_set;
|
||||
#else
|
||||
boost::unordered_map<int, int>* test_map;
|
||||
boost::unordered_set<int>* test_set;
|
||||
#endif
|
||||
} // namespace
|
||||
|
||||
// clang-format off
|
||||
UNORDERED_TEST(
|
||||
node_handle_allocator_tests,
|
||||
((test_map)(test_set))
|
||||
((test_std_allocators)(test_nonassignable_allocators)
|
||||
(test_pocx_allocators)))
|
||||
|
||||
UNORDERED_TEST(
|
||||
node_handle_allocator_swap_tests,
|
||||
((test_map)(test_set))
|
||||
((test_std_allocators)(test_pocx_allocators)))
|
||||
// clang-format on
|
||||
|
||||
RUN_TESTS()
|
@ -33,12 +33,23 @@ namespace pmr_allocator_tests {
|
||||
test_string_flat_map;
|
||||
static boost::unordered::pmr::concurrent_flat_map<pmr_string, pmr_string>*
|
||||
test_pmr_string_flat_map;
|
||||
static boost::unordered::pmr::concurrent_node_map<std::string, std::string>*
|
||||
test_string_node_map;
|
||||
static boost::unordered::pmr::concurrent_node_map<pmr_string, pmr_string>*
|
||||
test_pmr_string_node_map;
|
||||
static boost::unordered::pmr::concurrent_flat_set<std::string>*
|
||||
test_string_flat_set;
|
||||
static boost::unordered::pmr::concurrent_flat_set<pmr_string>*
|
||||
test_pmr_string_flat_set;
|
||||
static boost::unordered::pmr::concurrent_node_set<std::string>*
|
||||
test_string_node_set;
|
||||
static boost::unordered::pmr::concurrent_node_set<pmr_string>*
|
||||
test_pmr_string_node_set;
|
||||
#define PMR_ALLOCATOR_TESTS_ARGS \
|
||||
((test_string_flat_map)(test_pmr_string_flat_map)(test_string_flat_set)(test_pmr_string_flat_set))
|
||||
((test_string_flat_map)(test_pmr_string_flat_map) \
|
||||
(test_string_node_map)(test_pmr_string_node_map) \
|
||||
(test_string_flat_set)(test_pmr_string_flat_set) \
|
||||
(test_string_node_set)(test_pmr_string_node_set))
|
||||
#elif defined(BOOST_UNORDERED_FOA_TESTS)
|
||||
static boost::unordered::pmr::unordered_flat_map<std::string, std::string>*
|
||||
test_string_flat_map;
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Copyright 2024 Joaquin M Lopez Muoz.
|
||||
// Copyright 2024 Joaquin M Lopez Munoz.
|
||||
// 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)
|
||||
|
||||
@ -7,8 +7,12 @@
|
||||
#ifdef BOOST_UNORDERED_CFOA_TESTS
|
||||
#include <boost/unordered/concurrent_flat_map.hpp>
|
||||
#include <boost/unordered/concurrent_flat_set.hpp>
|
||||
#include <boost/unordered/concurrent_node_map.hpp>
|
||||
#include <boost/unordered/concurrent_node_set.hpp>
|
||||
#include <boost/unordered/unordered_flat_map.hpp>
|
||||
#include <boost/unordered/unordered_flat_set.hpp>
|
||||
#include <boost/unordered/unordered_node_map.hpp>
|
||||
#include <boost/unordered/unordered_node_set.hpp>
|
||||
#include "../cfoa/helpers.hpp"
|
||||
#else
|
||||
#include "../helpers/unordered.hpp"
|
||||
@ -19,6 +23,7 @@
|
||||
#include "../helpers/test.hpp"
|
||||
#include <boost/assert.hpp>
|
||||
#include <boost/core/make_span.hpp>
|
||||
#include <cmath>
|
||||
#include <cstring>
|
||||
|
||||
template <class T> struct unequal_allocator
|
||||
@ -45,16 +50,20 @@ template <class T> struct unequal_allocator
|
||||
int n_;
|
||||
};
|
||||
|
||||
bool exact_same(double x, double y)
|
||||
bool esentially_same(double x, double y)
|
||||
{
|
||||
return std::memcmp(
|
||||
reinterpret_cast<void*>(&x), reinterpret_cast<void*>(&y),
|
||||
sizeof(double))==0;
|
||||
// Some optimizer-related issues in GCC X86 result in last-bit differences
|
||||
// on doubles that should otherwise be identical.
|
||||
|
||||
// https://stackoverflow.com/a/253874/213114
|
||||
|
||||
static constexpr double epsilon = 1.0E-6;
|
||||
return fabs(x - y) <= ( (fabs(x) > fabs(y) ? fabs(x) : fabs(y)) * epsilon);
|
||||
}
|
||||
|
||||
bool not_exact_same(double x, double y)
|
||||
bool not_esentially_same(double x, double y)
|
||||
{
|
||||
return !exact_same(x, y);
|
||||
return !esentially_same(x, y);
|
||||
}
|
||||
|
||||
enum check_stats_contition
|
||||
@ -69,19 +78,19 @@ void check_stat(const Stats& s, check_stats_contition cond)
|
||||
{
|
||||
switch (cond) {
|
||||
case stats_empty:
|
||||
BOOST_TEST(exact_same(s.average, 0.0));
|
||||
BOOST_TEST(exact_same(s.variance, 0.0));
|
||||
BOOST_TEST(exact_same(s.deviation, 0.0));
|
||||
BOOST_TEST(esentially_same(s.average, 0.0));
|
||||
BOOST_TEST(esentially_same(s.variance, 0.0));
|
||||
BOOST_TEST(esentially_same(s.deviation, 0.0));
|
||||
break;
|
||||
case stats_full:
|
||||
BOOST_TEST_GT(s.average, 0.0);
|
||||
if(not_exact_same(s.variance, 0.0)) {
|
||||
if(not_esentially_same(s.variance, 0.0)) {
|
||||
BOOST_TEST_GT(s.variance, 0.0);
|
||||
BOOST_TEST_GT(s.deviation, 0.0);
|
||||
}
|
||||
break;
|
||||
case stats_mostly_full:
|
||||
if(not_exact_same(s.variance, 0.0)) {
|
||||
if(not_esentially_same(s.variance, 0.0)) {
|
||||
BOOST_TEST_GT(s.average, 0.0);
|
||||
BOOST_TEST_GT(s.variance, 0.0);
|
||||
BOOST_TEST_GT(s.deviation, 0.0);
|
||||
@ -94,9 +103,9 @@ void check_stat(const Stats& s, check_stats_contition cond)
|
||||
|
||||
template <class Stats> void check_stat(const Stats& s1, const Stats& s2)
|
||||
{
|
||||
BOOST_TEST(exact_same(s1.average, s2.average));
|
||||
BOOST_TEST(exact_same(s1.variance, s2.variance));
|
||||
BOOST_TEST(exact_same(s1.deviation, s2.deviation));
|
||||
BOOST_TEST(esentially_same(s1.average, s2.average));
|
||||
BOOST_TEST(esentially_same(s1.variance, s2.variance));
|
||||
BOOST_TEST(esentially_same(s1.deviation, s2.deviation));
|
||||
}
|
||||
|
||||
template <class Stats>
|
||||
@ -345,15 +354,28 @@ UNORDERED_AUTO_TEST (stats_) {
|
||||
boost::concurrent_flat_map<
|
||||
int, int, boost::hash<int>, std::equal_to<int>,
|
||||
unequal_allocator< std::pair< const int, int> >>>();
|
||||
test_stats<
|
||||
boost::concurrent_node_map<
|
||||
int, int, boost::hash<int>, std::equal_to<int>,
|
||||
unequal_allocator< std::pair< const int, int> >>>();
|
||||
test_stats<
|
||||
boost::concurrent_flat_set<
|
||||
int, boost::hash<int>, std::equal_to<int>, unequal_allocator<int>>>();
|
||||
test_stats<
|
||||
boost::concurrent_node_set<
|
||||
int, boost::hash<int>, std::equal_to<int>, unequal_allocator<int>>>();
|
||||
test_stats_concurrent_unordered_interop<
|
||||
boost::unordered_flat_map<int, int>,
|
||||
boost::concurrent_flat_map<int, int>>();
|
||||
test_stats_concurrent_unordered_interop<
|
||||
boost::unordered_node_map<int, int>,
|
||||
boost::concurrent_node_map<int, int>>();
|
||||
test_stats_concurrent_unordered_interop<
|
||||
boost::unordered_flat_set<int>,
|
||||
boost::concurrent_flat_set<int>>();
|
||||
test_stats_concurrent_unordered_interop<
|
||||
boost::unordered_node_set<int>,
|
||||
boost::concurrent_node_set<int>>();
|
||||
#elif defined(BOOST_UNORDERED_FOA_TESTS)
|
||||
test_stats<
|
||||
boost::unordered_flat_map<
|
||||
|
Loading…
x
Reference in New Issue
Block a user