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:
joaquintides 2024-08-25 18:34:58 +02:00 committed by GitHub
parent 35bdabf259
commit f734e399e3
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
58 changed files with 7007 additions and 338 deletions

View File

@ -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.

View File

@ -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^]).

View File

@ -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`].

View File

@ -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:

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -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
}
----

View File

@ -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

View File

@ -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[]

View File

@ -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.

View File

@ -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.
---

View File

@ -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.
---

View File

@ -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&lt;*&gt;" />
<AlternativeType Name="boost::unordered::concurrent_flat_map&lt;*&gt;" />
<AlternativeType Name="boost::unordered::concurrent_flat_set&lt;*&gt;" />
<AlternativeType Name="boost::unordered::concurrent_node_map&lt;*&gt;" />
<AlternativeType Name="boost::unordered::concurrent_node_set&lt;*&gt;" />
<DisplayString>{{ size={table_.size_ctrl.size} }}</DisplayString>
<Expand>
<Item Name="[hash_function]" ExcludeView="simple">*reinterpret_cast&lt;hasher*&gt;(static_cast&lt;table_type::super::hash_base*&gt;(&amp;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&lt;*&gt;" Priority="MediumHigh" ExcludeView="ShowElementsByIndex">
<AlternativeType Name="boost::unordered::unordered_node_map&lt;*&gt;" />
<AlternativeType Name="boost::unordered::concurrent_flat_map&lt;*&gt;" />
<AlternativeType Name="boost::unordered::concurrent_node_map&lt;*&gt;" />
<DisplayString>{{ size={table_.size_ctrl.size} }}</DisplayString>
<Expand>
<Item Name="[hash_function]" ExcludeView="simple">*reinterpret_cast&lt;hasher*&gt;(static_cast&lt;table_type::super::hash_base*&gt;(&amp;table_))</Item>

View 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

View 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

View 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

View 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

View File

@ -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));
}
}

View File

@ -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);

View File

@ -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};
}
}
}
}

View 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

View 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

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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)

View File

@ -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 ;

View File

@ -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

View File

@ -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

View File

@ -1,15 +1,19 @@
// 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)
#ifndef BOOST_UNORDERED_TEST_CFOA_COMMON_HELPERS_HPP
#define BOOST_UNORDERED_TEST_CFOA_COMMON_HELPERS_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 <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;
@ -95,7 +149,33 @@ struct replace_allocator_impl<
using type =
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; }

View File

@ -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

View File

@ -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());
BOOST_TEST_GT(raii::move_constructor, 0u);
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))

View File

@ -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

View File

@ -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)))

View File

@ -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()

View File

@ -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()

View File

@ -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))

View File

@ -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);
BOOST_TEST_GT(raii::move_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);
// don't check move construction count here because of rehashing
BOOST_TEST_GT(raii::move_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);
// don't check move construction count here because of rehashing
BOOST_TEST_GT(raii::move_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))

View File

@ -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)))

View File

@ -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"

View 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()

View File

@ -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()

View File

@ -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

View File

@ -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());
// don't check move construction count here because of rehashing
BOOST_TEST_GT(raii::move_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::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());
BOOST_TEST_GT(raii::move_constructor, 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());
}
@ -216,7 +232,14 @@ namespace {
BOOST_TEST_EQ(raii::default_constructor, 0u);
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, 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());
// don't check move construction count here because of rehashing
BOOST_TEST_GT(raii::move_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;
@ -360,8 +411,15 @@ namespace {
BOOST_TEST_EQ(raii::default_constructor, 0u);
BOOST_TEST_EQ(raii::copy_constructor, value_type_cardinality * x.size());
// don't check move construction count here because of rehashing
BOOST_TEST_GT(raii::move_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;
@ -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()

View File

@ -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);
BOOST_TEST_EQ(
raii::move_constructor,
value_type_cardinality * reference_cont.size());
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) {
// 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);
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);
}
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

View 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"

View File

@ -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"

View File

@ -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

View File

@ -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

View File

@ -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)))
}

View File

@ -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)

View File

@ -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)))

View File

@ -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());
// don't check move construction count here because of rehashing
BOOST_TEST_GT(raii::move_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);
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());
// don't check move construction count here because of rehashing
BOOST_TEST_GT(raii::move_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);
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());
BOOST_TEST_GE(raii::move_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());
BOOST_TEST_GE(raii::move_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))

View File

@ -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
{
@ -1114,7 +1122,9 @@ namespace {
}
boost::concurrent_flat_set<
mutable_pair, mutable_pair_hash, mutable_pair_equal_to>* mutable_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

View File

@ -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,

View File

@ -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>,

View 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()

View File

@ -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;

View File

@ -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<