forked from boostorg/unordered
Compare commits
63 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 57546ed7e3 | |||
| 6067d9e515 | |||
| ad4f00a825 | |||
| d1c166558a | |||
| d8a75accff | |||
| cdb31c8309 | |||
| f48ae7f7f4 | |||
| 843d6df58c | |||
| 95f36f334d | |||
| 3f4d5fc567 | |||
| 9a2483dd2c | |||
| cd9a592f00 | |||
| aa67315922 | |||
| 21937249c4 | |||
| 5dd2068b62 | |||
| 85098f0506 | |||
| 1b0037135c | |||
| f734e399e3 | |||
| 2afef71107 | |||
| 35bdabf259 | |||
| 1ebe692957 | |||
| 111a503195 | |||
| 5206ac46a3 | |||
| 0bca47c690 | |||
| 7f9aa8505f | |||
| ae364ea311 | |||
| f415d17e58 | |||
| 10fd6c8647 | |||
| 45e50f6cd2 | |||
| 600dd0a8e2 | |||
| a6a0cd0a13 | |||
| a39cf60e93 | |||
| a14b159665 | |||
| 793fad5620 | |||
| c117f4448f | |||
| 65f3ea60dd | |||
| 7645e4f20d | |||
| cd4c1f3713 | |||
| 32ce058ebe | |||
| 4c6d0211fb | |||
| 1edbe3fdbe | |||
| 42579dee10 | |||
| 0928b3d016 | |||
| 5d7786b47e | |||
| 228fede498 | |||
| 1d1f0d306d | |||
| 7ddd562532 | |||
| 3ad8ac4c34 | |||
| ac1a2b4d23 | |||
| e354fb432b | |||
| 88535dca1f | |||
| 3d9b24019d | |||
| 0387791dc1 | |||
| 4126a56528 | |||
| 38acf9c619 | |||
| e5d89f1a15 | |||
| 8630a588bd | |||
| 390efc275f | |||
| 3868496ac9 | |||
| a1f9097f2b | |||
| f177f437b9 | |||
| a3873dc19b | |||
| 89515ee604 |
+4
-4
@@ -107,14 +107,14 @@ environment:
|
||||
B2_TOOLSET: gcc
|
||||
B2_VARIANT: release
|
||||
|
||||
- FLAVOR: Cygwin (64 bit, C++11, Release)
|
||||
- FLAVOR: Cygwin (64 bit, C++11, Debug)
|
||||
APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2022
|
||||
ADDPATH: C:\cygwin64\bin;
|
||||
B2_ADDRESS_MODEL: 64
|
||||
B2_CXXSTD: 11
|
||||
B2_TOOLSET: gcc
|
||||
B2_FLAGS: "include=libs/unordered/test/unordered include=libs/unordered/test/exception"
|
||||
B2_VARIANT: release
|
||||
B2_VARIANT: debug
|
||||
|
||||
- FLAVOR: Cygwin (64 bit, C++14, Debug)
|
||||
APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2022
|
||||
@@ -125,14 +125,14 @@ environment:
|
||||
B2_FLAGS: "include=libs/unordered/test/unordered include=libs/unordered/test/exception"
|
||||
B2_VARIANT: debug
|
||||
|
||||
- FLAVOR: Cygwin (64 bit, C++17, Release)
|
||||
- FLAVOR: Cygwin (64 bit, C++17, Debug)
|
||||
APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2022
|
||||
ADDPATH: C:\cygwin64\bin;
|
||||
B2_ADDRESS_MODEL: 64
|
||||
B2_CXXSTD: 17
|
||||
B2_TOOLSET: gcc
|
||||
B2_FLAGS: "include=libs/unordered/test/unordered include=libs/unordered/test/exception"
|
||||
B2_VARIANT: release
|
||||
B2_VARIANT: debug
|
||||
|
||||
- FLAVOR: Cygwin (64 bit, C++2a, Debug)
|
||||
APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2022
|
||||
|
||||
@@ -30,6 +30,7 @@ local linux_pipeline(name, image, environment, packages = "", sources = [], arch
|
||||
name: "everything",
|
||||
image: image,
|
||||
environment: environment,
|
||||
privileged: true,
|
||||
commands:
|
||||
[
|
||||
'set -e',
|
||||
|
||||
@@ -14,6 +14,11 @@ DRONE_BUILD_DIR=$(pwd)
|
||||
BOOST_BRANCH=develop
|
||||
if [ "$DRONE_BRANCH" = "master" ]; then BOOST_BRANCH=master; fi
|
||||
|
||||
if [[ $(uname) == "Linux" && ( "$TSAN" == 1 || "$ASAN" == 1 ) ]]; then
|
||||
echo 0 | sudo tee /proc/sys/kernel/randomize_va_space
|
||||
sudo sysctl vm.mmap_rnd_bits=28
|
||||
fi
|
||||
|
||||
cd ..
|
||||
git clone -b $BOOST_BRANCH --depth 1 https://github.com/boostorg/boost.git boost-root
|
||||
cd boost-root
|
||||
|
||||
@@ -25,6 +25,10 @@ deviate from the standard in exchange for top performance.</ul>
|
||||
|
||||
<ul>High performance for multithreaded scenarios. Introducing a new non-standard, iterator-free API.</ul>
|
||||
|
||||
**`boost::concurrent_node_set` `boost::concurrent_node_map`**
|
||||
|
||||
<ul>Variations of <code>boost::concurrent_flat_(set|map)</code> providing pointer stability.</ul>
|
||||
|
||||
## Learn about Boost.Unordered
|
||||
|
||||
* [Online documentation](https://boost.org/libs/unordered)
|
||||
@@ -34,16 +38,18 @@ deviate from the standard in exchange for top performance.</ul>
|
||||
* [Inside `boost::unordered_flat_map`](https://bannalia.blogspot.com/2022/11/inside-boostunorderedflatmap.html)
|
||||
* [Inside `boost::concurrent_flat_map`](https://bannalia.blogspot.com/2023/07/inside-boostconcurrentflatmap.html)
|
||||
* [Bulk visitation in `boost::concurrent_flat_map`](https://bannalia.blogspot.com/2023/10/bulk-visitation-in-boostconcurrentflatm.html)
|
||||
|
||||
## Get the library
|
||||
* Debugging visualizers for Boost.Unordered:
|
||||
* [Natvis for boost::unordered_map, and how to use <Intrinsic> elements](https://blog.ganets.ky/NatvisForUnordered/)
|
||||
* [Natvis for boost::concurrent_flat_map, and why fancy pointers are hard](https://blog.ganets.ky/NatvisForUnordered2/)
|
||||
* [Visualizing boost::unordered_map in GDB, with pretty-printer customization points](https://blog.ganets.ky/PrettyPrinter/)
|
||||
|
||||
Boost.Unordered can be installed in a number of ways:
|
||||
|
||||
* [Download Boost](https://www.boost.org/users/download/) and you're ready to go (this is a header-only library requiring no building).
|
||||
* Using Conan 2: In case you don't have it yet, add an entry for Boost in your `conanfile.txt` (the example requires at least Boost 1.83):
|
||||
* Using Conan 2: In case you don't have it yet, add an entry for Boost in your `conanfile.txt` (the example requires at least Boost 1.86):
|
||||
```
|
||||
[requires]
|
||||
boost/[>=1.83.0]
|
||||
boost/[>=1.86.0]
|
||||
```
|
||||
<ul>If you're not using any compiled Boost library, the following will skip building altogether:</ul>
|
||||
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
# Copyright 2023-2024 René Ferdinand Rivera Morell
|
||||
# Copyright 2024 Peter Dimov
|
||||
# Distributed under the Boost Software License, Version 1.0.
|
||||
# https://www.boost.org/LICENSE_1_0.txt
|
||||
|
||||
require-b2 5.2 ;
|
||||
|
||||
constant boost_dependencies :
|
||||
/boost/assert//boost_assert
|
||||
/boost/config//boost_config
|
||||
/boost/container_hash//boost_container_hash
|
||||
/boost/core//boost_core
|
||||
/boost/mp11//boost_mp11
|
||||
/boost/predef//boost_predef
|
||||
/boost/throw_exception//boost_throw_exception
|
||||
;
|
||||
|
||||
project /boost/unordered ;
|
||||
|
||||
explicit
|
||||
[ alias boost_unordered : : : : <include>include <library>$(boost_dependencies) ]
|
||||
[ alias all : boost_unordered test ]
|
||||
;
|
||||
|
||||
call-if : boost-library unordered
|
||||
;
|
||||
@@ -126,7 +126,7 @@ h|*Method* h|*Description*
|
||||
|
||||
|`float max_load_factor(float z)`
|
||||
|Changes the container's maximum load factor, using `z` as a hint. +
|
||||
**Open-addressing containers:** this function does nothing: users are not allowed to change the maximum load factor.
|
||||
**Open-addressing and concurrent containers:** this function does nothing: users are not allowed to change the maximum load factor.
|
||||
|
||||
|`void rehash(size_type n)`
|
||||
|Changes the number of buckets so that there at least `n` buckets, and so that the load factor is less than the maximum load factor.
|
||||
|
||||
@@ -6,6 +6,18 @@
|
||||
:github-pr-url: https://github.com/boostorg/unordered/pull
|
||||
:cpp: C++
|
||||
|
||||
== 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^]).
|
||||
* In Visual Studio Natvis, supported any container with an allocator that uses fancy pointers. This applies to any fancy pointer type, as long as the proper Natvis customization point "Intrinsic" functions are written for the fancy pointer type.
|
||||
* Added GDB pretty-printers for all containers and iterators. For a container with an allocator that uses fancy pointers, these only work if the proper pretty-printer is written for the fancy pointer type itself.
|
||||
* Fixed `std::initializer_list` assignment issues for open-addressing containers
|
||||
({github-pr-url}/277[PR#277^]).
|
||||
|
||||
|
||||
== Release 1.86.0
|
||||
|
||||
* Added container `pmr` aliases when header `<memory_resource>` is available. The alias `boost::unordered::pmr::[container]` refers to `boost::unordered::[container]` with a `std::pmr::polymorphic_allocator` allocator type.
|
||||
|
||||
@@ -89,7 +89,8 @@ The main differences with C++ unordered associative containers are:
|
||||
== Concurrent Containers
|
||||
|
||||
There is currently no specification in the C++ standard for this or any other type of concurrent
|
||||
data structure. The APIs of `boost::concurrent_flat_set` and `boost::concurrent_flat_map`
|
||||
data structure. The APIs of `boost::concurrent_flat_set`/`boost::concurrent_node_set` and
|
||||
`boost::concurrent_flat_map`/`boost::concurrent_node_map`
|
||||
are modelled after `std::unordered_flat_set` and `std::unordered_flat_map`, respectively,
|
||||
with the crucial difference that iterators are not provided
|
||||
due to their inherent problems in concurrent scenarios (high contention, prone to deadlocking):
|
||||
@@ -105,7 +106,7 @@ In a non-concurrent unordered container, iterators serve two main purposes:
|
||||
* Access to an element previously located via lookup.
|
||||
* Container traversal.
|
||||
|
||||
In place of iterators, `boost::concurrent_flat_set` and `boost::concurrent_flat_map` use _internal visitation_
|
||||
In place of iterators, Boost.Unordered concurrent containers use _internal visitation_
|
||||
facilities as a thread-safe substitute. Classical operations returning an iterator to an
|
||||
element already existing in the container, like for instance:
|
||||
|
||||
@@ -141,7 +142,8 @@ respectively, here visitation is granted mutable or const access depending on
|
||||
the constness of the member function used (there are also `*cvisit` overloads for
|
||||
explicit const visitation); In the case of `boost::concurrent_flat_set`, visitation is always const.
|
||||
|
||||
One notable operation not provided by `boost::concurrent_flat_map` is `operator[]`/`at`, which can be
|
||||
One notable operation not provided by `boost::concurrent_flat_map`/`boost::concurrent_node_map`
|
||||
is `operator[]`/`at`, which can be
|
||||
replaced, if in a more convoluted manner, by
|
||||
xref:#concurrent_flat_map_try_emplace_or_cvisit[`try_emplace_or_visit`].
|
||||
|
||||
|
||||
@@ -3,7 +3,8 @@
|
||||
|
||||
:idprefix: concurrent_
|
||||
|
||||
Boost.Unordered provides `boost::concurrent_flat_set` and `boost::concurrent_flat_map`,
|
||||
Boost.Unordered provides `boost::concurrent_node_set`, `boost::concurrent_node_map`,
|
||||
`boost::concurrent_flat_set` and `boost::concurrent_flat_map`,
|
||||
hash tables that allow concurrent write/read access from
|
||||
different threads without having to implement any synchronzation mechanism on the user's side.
|
||||
|
||||
@@ -43,7 +44,7 @@ logical cores in the CPU).
|
||||
|
||||
== Visitation-based API
|
||||
|
||||
The first thing a new user of `boost::concurrent_flat_set` or `boost::concurrent_flat_map`
|
||||
The first thing a new user of Boost.Unordered concurrent containers
|
||||
will notice is that these classes _do not provide iterators_ (which makes them technically
|
||||
not https://en.cppreference.com/w/cpp/named_req/Container[Containers^]
|
||||
in the C++ standard sense). The reason for this is that iterators are inherently
|
||||
@@ -62,7 +63,7 @@ thread issues an `m.erase(k)` operation between A and B. There are designs that
|
||||
can remedy this by making iterators lock the element they point to, but this
|
||||
approach lends itself to high contention and can easily produce deadlocks in a program.
|
||||
`operator[]` has similar concurrency issues, and is not provided by
|
||||
`boost::concurrent_flat_map` either. Instead, element access is done through
|
||||
`boost::concurrent_flat_map`/`boost::concurrent_node_map` either. Instead, element access is done through
|
||||
so-called _visitation functions_:
|
||||
|
||||
[source,c++]
|
||||
@@ -112,7 +113,7 @@ if (found) {
|
||||
}
|
||||
----
|
||||
|
||||
Visitation is prominent in the API provided by `boost::concurrent_flat_set` and `boost::concurrent_flat_map`, and
|
||||
Visitation is prominent in the API provided by concurrent containers, and
|
||||
many classical operations have visitation-enabled variations:
|
||||
|
||||
[source,c++]
|
||||
@@ -125,13 +126,15 @@ m.insert_or_visit(x, [](auto& y) {
|
||||
----
|
||||
|
||||
Note that in this last example the visitation function could actually _modify_
|
||||
the element: as a general rule, operations on a `boost::concurrent_flat_map` `m`
|
||||
the element: as a general rule, operations on a concurrent map `m`
|
||||
will grant visitation functions const/non-const access to the element depending on whether
|
||||
`m` is const/non-const. Const access can be always be explicitly requested
|
||||
by using `cvisit` overloads (for instance, `insert_or_cvisit`) and may result
|
||||
in higher parallelization. For `boost::concurrent_flat_set`, on the other hand,
|
||||
in higher parallelization. For concurrent sets, on the other hand,
|
||||
visitation is always const access.
|
||||
Consult the references of
|
||||
xref:#concurrent_node_set[`boost::concurrent_node_set`],
|
||||
xref:#concurrent_flat_map[`boost::concurrent_node_map`],
|
||||
xref:#concurrent_flat_set[`boost::concurrent_flat_set`] and
|
||||
xref:#concurrent_flat_map[`boost::concurrent_flat_map`]
|
||||
for the complete list of visitation-enabled operations.
|
||||
@@ -245,7 +248,7 @@ may yield worse performance.
|
||||
|
||||
== Blocking Operations
|
||||
|
||||
``boost::concurrent_flat_set``s and ``boost::concurrent_flat_map``s can be copied, assigned, cleared and merged just like any
|
||||
Concurrent containers can be copied, assigned, cleared and merged just like any other
|
||||
Boost.Unordered container. Unlike most other operations, these are _blocking_,
|
||||
that is, all other threads are prevented from accesing the tables involved while a copy, assignment,
|
||||
clear or merge operation is in progress. Blocking is taken care of automatically by the library
|
||||
@@ -258,9 +261,25 @@ reserving space in advance of bulk insertions will generally speed up the proces
|
||||
== Interoperability with non-concurrent containers
|
||||
|
||||
As open-addressing and concurrent containers are based on the same internal data structure,
|
||||
`boost::unordered_flat_set` and `boost::unordered_flat_map` can
|
||||
be efficiently move-constructed from `boost::concurrent_flat_set` and `boost::concurrent_flat_map`,
|
||||
respectively, and vice versa.
|
||||
they can be efficiently move-constructed from their non-concurrent counterpart, and vice versa.
|
||||
|
||||
[caption=, title='Table {counter:table-counter}. Concurrent/non-concurrent interoperatibility']
|
||||
[cols="1,1", frame=all, grid=all]
|
||||
|===
|
||||
^|`boost::concurrent_node_set`
|
||||
^|`boost::unordered_node_set`
|
||||
|
||||
^|`boost::concurrent_node_map`
|
||||
^|`boost::unordered_node_map`
|
||||
|
||||
^|`boost::concurrent_flat_set`
|
||||
^|`boost::unordered_flat_set`
|
||||
|
||||
^|`boost::concurrent_flat_map`
|
||||
^|`boost::unordered_flat_map`
|
||||
|
||||
|===
|
||||
|
||||
This interoperability comes handy in multistage scenarios where parts of the data processing happen
|
||||
in parallel whereas other steps are non-concurrent (or non-modifying). In the following example,
|
||||
we want to construct a histogram from a huge input vector of words:
|
||||
|
||||
@@ -389,9 +389,13 @@ prior blocking operations on `x` synchronize with *op*. So, blocking operations
|
||||
An operation is said to be _blocking on rehashing of_ ``__x__`` if it blocks on `x`
|
||||
only when an internal rehashing is issued.
|
||||
|
||||
Access or modification of an element of a `boost::concurrent_flat_map` passed by reference to a
|
||||
user-provided visitation function do not introduce data races when the visitation function
|
||||
is executed internally by the `boost::concurrent_flat_map`.
|
||||
When executed internally by a `boost::concurrent_flat_map`, the following operations by a
|
||||
user-provided visitation function on the element passed do not introduce data races:
|
||||
|
||||
* Read access to the element.
|
||||
* Non-mutable modification of the element.
|
||||
* Mutable modification of the element (if the container operation executing the visitation function is not const
|
||||
and its name does not contain `cvisit`.)
|
||||
|
||||
Any `boost::concurrent_flat_map operation` that inserts or modifies an element `e`
|
||||
synchronizes with the internal invocation of a visitation function on `e`.
|
||||
|
||||
@@ -98,25 +98,35 @@ namespace boost {
|
||||
|
||||
|
||||
// visitation
|
||||
template<class F> size_t xref:#concurrent_flat_set_cvisit[visit](const key_type& k, F f);
|
||||
template<class F> size_t xref:#concurrent_flat_set_cvisit[visit](const key_type& k, F f) const;
|
||||
template<class F> size_t xref:#concurrent_flat_set_cvisit[cvisit](const key_type& k, F f) const;
|
||||
template<class K, class F> size_t xref:#concurrent_flat_set_cvisit[visit](const K& k, F f);
|
||||
template<class K, class F> size_t xref:#concurrent_flat_set_cvisit[visit](const K& k, F f) const;
|
||||
template<class K, class F> size_t xref:#concurrent_flat_set_cvisit[cvisit](const K& k, F f) const;
|
||||
|
||||
template<class FwdIterator, class F>
|
||||
size_t xref:concurrent_flat_set_bulk_visit[visit](FwdIterator first, FwdIterator last, F f);
|
||||
template<class FwdIterator, class F>
|
||||
size_t xref:concurrent_flat_set_bulk_visit[visit](FwdIterator first, FwdIterator last, F f) const;
|
||||
template<class FwdIterator, class F>
|
||||
size_t xref:concurrent_flat_set_bulk_visit[cvisit](FwdIterator first, FwdIterator last, F f) const;
|
||||
|
||||
template<class F> size_t xref:#concurrent_flat_set_cvisit_all[visit_all](F f);
|
||||
template<class F> size_t xref:#concurrent_flat_set_cvisit_all[visit_all](F f) const;
|
||||
template<class F> size_t xref:#concurrent_flat_set_cvisit_all[cvisit_all](F f) const;
|
||||
template<class ExecutionPolicy, class F>
|
||||
void xref:#concurrent_flat_set_parallel_cvisit_all[visit_all](ExecutionPolicy&& policy, F f);
|
||||
template<class ExecutionPolicy, class F>
|
||||
void xref:#concurrent_flat_set_parallel_cvisit_all[visit_all](ExecutionPolicy&& policy, F f) const;
|
||||
template<class ExecutionPolicy, class F>
|
||||
void xref:#concurrent_flat_set_parallel_cvisit_all[cvisit_all](ExecutionPolicy&& policy, F f) const;
|
||||
|
||||
template<class F> bool xref:#concurrent_flat_set_cvisit_while[visit_while](F f);
|
||||
template<class F> bool xref:#concurrent_flat_set_cvisit_while[visit_while](F f) const;
|
||||
template<class F> bool xref:#concurrent_flat_set_cvisit_while[cvisit_while](F f) const;
|
||||
template<class ExecutionPolicy, class F>
|
||||
bool xref:#concurrent_flat_set_parallel_cvisit_while[visit_while](ExecutionPolicy&& policy, F f);
|
||||
template<class ExecutionPolicy, class F>
|
||||
bool xref:#concurrent_flat_set_parallel_cvisit_while[visit_while](ExecutionPolicy&& policy, F f) const;
|
||||
template<class ExecutionPolicy, class F>
|
||||
@@ -340,9 +350,13 @@ prior blocking operations on `x` synchronize with *op*. So, blocking operations
|
||||
An operation is said to be _blocking on rehashing of_ ``__x__`` if it blocks on `x`
|
||||
only when an internal rehashing is issued.
|
||||
|
||||
Access or modification of an element of a `boost::concurrent_flat_set` passed by reference to a
|
||||
user-provided visitation function do not introduce data races when the visitation function
|
||||
is executed internally by the `boost::concurrent_flat_set`.
|
||||
When executed internally by a `boost::concurrent_flat_set`, the following operations by a
|
||||
user-provided visitation function on the element passed do not introduce data races:
|
||||
|
||||
* Read access to the element.
|
||||
* Non-mutable modification of the element.
|
||||
* Mutable modification of the element (if the container operation executing the visitation function is not const
|
||||
and its name does not contain `cvisit`.)
|
||||
|
||||
Any `boost::concurrent_flat_set operation` that inserts or modifies an element `e`
|
||||
synchronizes with the internal invocation of a visitation function on `e`.
|
||||
@@ -708,8 +722,10 @@ Concurrency:;; Blocking on `*this`.
|
||||
==== [c]visit
|
||||
|
||||
```c++
|
||||
template<class F> size_t visit(const key_type& k, F f);
|
||||
template<class F> size_t visit(const key_type& k, F f) const;
|
||||
template<class F> size_t cvisit(const key_type& k, F f) const;
|
||||
template<class K, class F> size_t visit(const K& k, F f);
|
||||
template<class K, class F> size_t visit(const K& k, F f) const;
|
||||
template<class K, class F> size_t cvisit(const K& k, F f) const;
|
||||
```
|
||||
@@ -725,6 +741,8 @@ Notes:;; The `template<class K, class F>` overloads only participate in overload
|
||||
==== Bulk visit
|
||||
|
||||
```c++
|
||||
template<class FwdIterator, class F>
|
||||
size_t visit(FwdIterator first, FwdIterator last, F f);
|
||||
template<class FwdIterator, class F>
|
||||
size_t visit(FwdIterator first, FwdIterator last, F f) const;
|
||||
template<class FwdIterator, class F>
|
||||
@@ -758,6 +776,7 @@ Returns:;; The number of elements visited.
|
||||
==== [c]visit_all
|
||||
|
||||
```c++
|
||||
template<class F> size_t visit_all(F f);
|
||||
template<class F> size_t visit_all(F f) const;
|
||||
template<class F> size_t cvisit_all(F f) const;
|
||||
```
|
||||
@@ -772,6 +791,7 @@ Returns:;; The number of elements visited.
|
||||
==== Parallel [c]visit_all
|
||||
|
||||
```c++
|
||||
template<class ExecutionPolicy, class F> void visit_all(ExecutionPolicy&& policy, F f);
|
||||
template<class ExecutionPolicy, class F> void visit_all(ExecutionPolicy&& policy, F f) const;
|
||||
template<class ExecutionPolicy, class F> void cvisit_all(ExecutionPolicy&& policy, F f) const;
|
||||
```
|
||||
@@ -792,6 +812,7 @@ Unsequenced execution policies are not allowed.
|
||||
==== [c]visit_while
|
||||
|
||||
```c++
|
||||
template<class F> bool visit_while(F f);
|
||||
template<class F> bool visit_while(F f) const;
|
||||
template<class F> bool cvisit_while(F f) const;
|
||||
```
|
||||
@@ -807,6 +828,7 @@ Returns:;; `false` iff `f` ever returns `false`.
|
||||
==== Parallel [c]visit_while
|
||||
|
||||
```c++
|
||||
template<class ExecutionPolicy, class F> bool visit_while(ExecutionPolicy&& policy, F f);
|
||||
template<class ExecutionPolicy, class F> bool visit_while(ExecutionPolicy&& policy, F f) const;
|
||||
template<class ExecutionPolicy, class F> bool cvisit_while(ExecutionPolicy&& policy, F f) const;
|
||||
```
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -5,7 +5,13 @@
|
||||
|
||||
== Visual Studio Natvis
|
||||
|
||||
All containers and iterators have custom visualizations in the Natvis framework, as long as their allocator uses regular raw pointers. Any container or iterator with an allocator using fancy pointers does not have a custom visualization right now.
|
||||
All containers and iterators have custom visualizations in the Natvis framework.
|
||||
|
||||
=== Using in your project
|
||||
|
||||
To visualize Boost.Unordered containers in the Natvis framework in your project, simply add the file link:https://github.com/boostorg/unordered/blob/develop/extra/boost_unordered.natvis[/extra/boost_unordered.natvis] to your Visual Studio project as an "Existing Item".
|
||||
|
||||
=== Visualization structure
|
||||
|
||||
The visualizations mirror those for the standard unordered containers. A container has a maximum of 100 elements displayed at once. Each set element has its item name listed as `[i]`, where `i` is the index in the display, starting at `0`. Each map element has its item name listed as `[\{key-display}]` by default. For example, if the first element is the pair `("abc", 1)`, the item name will be `["abc"]`. This behaviour can be overridden by using the view "ShowElementsByIndex", which switches the map display behaviour to name the elements by index. This same view name is used in the standard unordered containers.
|
||||
|
||||
@@ -13,4 +19,72 @@ By default, the closed-addressing containers will show the `[hash_function]` and
|
||||
|
||||
By default, the open-addressing containers will show the `[hash_function]`, `[key_eq]`, `[allocator]`, and the elements. Using the view "simple" shows only the elements, with no other items present. Both the SIMD and the non-SIMD implementations are viewable through the Natvis framework.
|
||||
|
||||
Iterators are displayed similarly to their standard counterparts. An iterator is displayed as though it were the element that it points to. An end iterator is simply displayed as `\{ end iterator }`.
|
||||
Iterators are displayed similarly to their standard counterparts. An iterator is displayed as though it were the element that it points to. An end iterator is simply displayed as `{ end iterator }`.
|
||||
|
||||
=== Fancy pointers
|
||||
|
||||
The container visualizations also work if you are using fancy pointers in your allocator, such as `boost::interprocess::offset_ptr`. While this is rare, Boost.Unordered has natvis customization points to support any type of fancy pointer. `boost::interprocess::offset_ptr` has support already defined in the Boost.Interprocess library, and you can add support to your own type by following the instructions contained in a comment near the end of the file link:https://github.com/boostorg/unordered/blob/develop/extra/boost_unordered.natvis[/extra/boost_unordered.natvis].
|
||||
|
||||
== GDB Pretty-Printers
|
||||
|
||||
All containers and iterators have a custom GDB pretty-printer.
|
||||
|
||||
=== Using in your project
|
||||
|
||||
Always, when using pretty-printers, you must enable pretty-printing like below. This is typically a one-time setup.
|
||||
|
||||
```plaintext
|
||||
(gdb) set print pretty on
|
||||
```
|
||||
|
||||
By default, if you compile into an ELF binary format, your binary will contain the Boost.Unordered pretty-printers. To use the embedded pretty-printers, ensure you allow auto-loading like below. This must be done every time you load GDB, or add it to a ".gdbinit" file.
|
||||
|
||||
```plaintext
|
||||
(gdb) add-auto-load-safe-path [/path/to/executable]
|
||||
```
|
||||
|
||||
You can choose to compile your binary _without_ embedding the pretty-printers by defining `BOOST_ALL_NO_EMBEDDED_GDB_SCRIPTS`, which disables the embedded GDB pretty-printers for all Boost libraries that have this feature.
|
||||
|
||||
You can load the pretty-printers externally from the non-embedded Python script. Add the script, link:https://github.com/boostorg/unordered/blob/develop/extra/boost_unordered_printers.py[/extra/boost_unordered_printers.py], using the `source` command as shown below.
|
||||
|
||||
```plaintext
|
||||
(gdb) source [/path/to/boost]/libs/unordered/extra/boost_unordered_printers.py
|
||||
```
|
||||
|
||||
=== Visualization structure
|
||||
|
||||
The visualizations mirror the standard unordered containers. The map containers display an association from key to mapped value. The set containers display an association from index to value. An iterator is either displayed with its item, or as an end iterator. Here is what may be shown for an example `boost::unordered_map`, an example `boost::unordered_set`, and their respective begin and end iterators.
|
||||
|
||||
```plaintext
|
||||
(gdb) print example_unordered_map
|
||||
$1 = boost::unordered_map with 3 elements = {["C"] = "c", ["B"] = "b", ["A"] = "a"}
|
||||
(gdb) print example_unordered_map_begin
|
||||
$2 = iterator = { {first = "C", second = "c"} }
|
||||
(gdb) print example_unordered_map_end
|
||||
$3 = iterator = { end iterator }
|
||||
(gdb) print example_unordered_set
|
||||
$4 = boost::unordered_set with 3 elements = {[0] = "c", [1] = "b", [2] = "a"}
|
||||
(gdb) print example_unordered_set_begin
|
||||
$5 = iterator = { "c" }
|
||||
(gdb) print example_unordered_set_end
|
||||
$6 = iterator = { end iterator }
|
||||
```
|
||||
|
||||
The other containers are identical other than replacing "`boost::unordered_{map|set}`" with the appropriate template name when displaying the container itself. Note that each sub-element (i.e. the key, the mapped value, or the value) is displayed based on its own printing settings which may include its own pretty-printer.
|
||||
|
||||
Both the SIMD and the non-SIMD implementations are viewable through the GDB pretty-printers.
|
||||
|
||||
For open-addressing containers where xref:#hash_quality_container_statistics[container statistics] are enabled, you can obtain these statistics by calling `get_stats()` on the container, from within GDB. This is overridden in GDB as an link:https://sourceware.org/gdb/current/onlinedocs/gdb.html/Xmethod-API.html[xmethod], so it will not invoke any C++ synchronization code. See the following printout as an example for the expected format.
|
||||
|
||||
```plaintext
|
||||
(gdb) print example_flat_map.get_stats()
|
||||
$1 = [stats] = {[insertion] = {[count] = 5, [probe_length] = {avg = 1.0, var = 0.0, dev = 0.0}},
|
||||
[successful_lookup] = {[count] = 0, [probe_length] = {avg = 0.0, var = 0.0, dev = 0.0},
|
||||
[num_comparisons] = {avg = 0.0, var = 0.0, dev = 0.0}}, [unsuccessful_lookup] = {[count] = 5,
|
||||
[probe_length] = {avg = 1.0, var = 0.0, dev = 0.0},
|
||||
[num_comparisons] = {avg = 0.0, var = 0.0, dev = 0.0}}}
|
||||
```
|
||||
|
||||
=== Fancy pointers
|
||||
|
||||
The pretty-printers also work if you are using fancy pointers in your allocator, such as `boost::interprocess::offset_ptr`. While this is rare, Boost.Unordered has GDB pretty-printer customization points to support any type of fancy pointer. `boost::interprocess::offset_ptr` has support already defined in the Boost.Interprocess library, and you can add support to your own type by following the instructions contained in a comment near the end of the file link:https://github.com/boostorg/unordered/blob/develop/extra/boost_unordered_printers.py[/extra/boost_unordered_printers.py].
|
||||
|
||||
@@ -43,7 +43,8 @@ boost::unordered_node_map
|
||||
boost::unordered_flat_map
|
||||
|
||||
^.^h|*Concurrent*
|
||||
^|
|
||||
^| `boost::concurrent_node_set` +
|
||||
`boost::concurrent_node_map`
|
||||
^| `boost::concurrent_flat_set` +
|
||||
`boost::concurrent_flat_map`
|
||||
|
||||
@@ -59,6 +60,7 @@ There are two variants: **flat** (the fastest) and **node-based**, which
|
||||
provide pointer stability under rehashing at the expense of being slower.
|
||||
* Finally, **concurrent containers** are designed and implemented to be used in high-performance
|
||||
multithreaded scenarios. Their interface is radically different from that of regular C++ containers.
|
||||
Flat and node-based variants are provided.
|
||||
|
||||
All sets and maps in Boost.Unordered are instantiatied similarly as
|
||||
`std::unordered_set` and `std::unordered_map`, respectively:
|
||||
@@ -72,8 +74,8 @@ namespace boost {
|
||||
class Pred = std::equal_to<Key>,
|
||||
class Alloc = std::allocator<Key> >
|
||||
class unordered_set;
|
||||
// same for unordered_multiset, unordered_flat_set, unordered_node_set
|
||||
// and concurrent_flat_set
|
||||
// same for unordered_multiset, unordered_flat_set, unordered_node_set,
|
||||
// concurrent_flat_set and concurrent_node_set
|
||||
|
||||
template <
|
||||
class Key, class Mapped,
|
||||
@@ -81,8 +83,8 @@ namespace boost {
|
||||
class Pred = std::equal_to<Key>,
|
||||
class Alloc = std::allocator<std::pair<Key const, Mapped> > >
|
||||
class unordered_map;
|
||||
// same for unordered_multimap, unordered_flat_map, unordered_node_map
|
||||
// and concurrent_flat_map
|
||||
// same for unordered_multimap, unordered_flat_map, unordered_node_map,
|
||||
// concurrent_flat_map and concurrent_node_map
|
||||
}
|
||||
----
|
||||
|
||||
|
||||
@@ -121,7 +121,8 @@ for Visual Studio on an x64-mode Intel CPU with SSE2 and for GCC on an IBM s390x
|
||||
== Concurrent Containers
|
||||
|
||||
The same data structure used by Boost.Unordered open-addressing containers has been chosen
|
||||
also as the foundation of `boost::concurrent_flat_set` and `boost::concurrent_flat_map`:
|
||||
also as the foundation of `boost::concurrent_flat_set`/`boost::concurrent_node_set` and
|
||||
`boost::concurrent_flat_map`/`boost::concurrent_node_map`:
|
||||
|
||||
* Open-addressing is faster than closed-addressing alternatives, both in non-concurrent and
|
||||
concurrent scenarios.
|
||||
@@ -130,7 +131,7 @@ with minimal locking. In particular, the metadata array can be used for implemen
|
||||
lookup that are lock-free up to the last step of actual element comparison.
|
||||
* Layout compatibility with Boost.Unordered flat containers allows for
|
||||
xref:#concurrent_interoperability_with_non_concurrent_containers[fast transfer]
|
||||
of all elements between `boost::concurrent_flat_map` and `boost::unordered_flat_map`,
|
||||
of all elements between a concurrent container and its non-concurrent counterpart,
|
||||
and vice versa.
|
||||
|
||||
=== Hash Function and Platform Interoperability
|
||||
|
||||
@@ -13,3 +13,5 @@ include::unordered_node_map.adoc[]
|
||||
include::unordered_node_set.adoc[]
|
||||
include::concurrent_flat_map.adoc[]
|
||||
include::concurrent_flat_set.adoc[]
|
||||
include::concurrent_node_map.adoc[]
|
||||
include::concurrent_node_set.adoc[]
|
||||
|
||||
@@ -129,7 +129,8 @@ xref:#rationale_open_addresing_containers[corresponding section].
|
||||
|
||||
== Concurrent Containers
|
||||
|
||||
`boost::concurrent_flat_set` and `boost::concurrent_flat_map` use the basic
|
||||
`boost::concurrent_flat_set`/`boost::concurrent_node_set` and
|
||||
`boost::concurrent_flat_map`/`boost::concurrent_node_map` use the basic
|
||||
xref:#structures_open_addressing_containers[open-addressing layout] described above
|
||||
augmented with synchronization mechanisms.
|
||||
|
||||
|
||||
@@ -78,6 +78,7 @@ namespace boost {
|
||||
explicit xref:#unordered_node_map_allocator_constructor[unordered_node_map](const Allocator& a);
|
||||
xref:#unordered_node_map_copy_constructor_with_allocator[unordered_node_map](const unordered_node_map& other, const Allocator& a);
|
||||
xref:#unordered_node_map_move_constructor_with_allocator[unordered_node_map](unordered_node_map&& other, const Allocator& a);
|
||||
xref:#unordered_node_map_move_constructor_from_concurrent_node_map[unordered_node_map](concurrent_node_map<Key, T, Hash, Pred, Allocator>&& other);
|
||||
xref:#unordered_node_map_initializer_list_constructor[unordered_node_map](std::initializer_list<value_type> il,
|
||||
size_type n = _implementation-defined_
|
||||
const hasher& hf = hasher(),
|
||||
@@ -537,6 +538,24 @@ and always calls `other.reset_stats()`.
|
||||
|
||||
---
|
||||
|
||||
==== Move Constructor from concurrent_node_map
|
||||
|
||||
```c++
|
||||
unordered_node_map(concurrent_node_map<Key, T, Hash, Pred, Allocator>&& other);
|
||||
```
|
||||
|
||||
Move construction from a xref:#concurrent_node_map[`concurrent_node_map`].
|
||||
The internal bucket array of `other` is transferred directly to the new container.
|
||||
The hash function, predicate and allocator are moved-constructed from `other`.
|
||||
If statistics are xref:unordered_node_map_boost_unordered_enable_stats[enabled],
|
||||
transfers the internal statistical information from `other` and calls `other.reset_stats()`.
|
||||
|
||||
[horizontal]
|
||||
Complexity:;; Constant time.
|
||||
Concurrency:;; Blocking on `other`.
|
||||
|
||||
---
|
||||
|
||||
==== Initializer List Constructor
|
||||
[source,c++,subs="+quotes"]
|
||||
----
|
||||
@@ -1219,8 +1238,8 @@ Throws:;; Nothing.
|
||||
|
||||
==== Extract by Key
|
||||
```c++
|
||||
node_type erase(const key_type& k);
|
||||
template<class K> node_type erase(K&& k);
|
||||
node_type extract(const key_type& k);
|
||||
template<class K> node_type extract(K&& k);
|
||||
```
|
||||
|
||||
Extracts the element with key equivalent to `k`, if it exists.
|
||||
@@ -1228,7 +1247,7 @@ Extracts the element with key equivalent to `k`, if it exists.
|
||||
[horizontal]
|
||||
Returns:;; A `node_type` object holding the extracted element, or empty if no element was extracted.
|
||||
Throws:;; Only throws an exception if it is thrown by `hasher` or `key_equal`.
|
||||
Notes:;; The `template<class K>` overload only participates in overload resolution if `Hash::is_transparent` and `Pred::is_transparent` are valid member typedefs and neither `iterator` nor `const_iterator` are implicitly convertible from `K`. The library assumes that `Hash` is callable with both `K` and `Key` and that `Pred` is transparent. This enables heterogeneous lookup which avoids the cost of instantiating an instance of the `Key` type.
|
||||
Notes:;; The `template<class K>` overload only participates in overload resolution if `Hash::is_transparent` and `Pred::is_transparent` are valid member typedefs. The library assumes that `Hash` is callable with both `K` and `Key` and that `Pred` is transparent. This enables heterogeneous lookup which avoids the cost of instantiating an instance of the `Key` type.
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -73,6 +73,7 @@ namespace boost {
|
||||
explicit xref:#unordered_node_set_allocator_constructor[unordered_node_set](const Allocator& a);
|
||||
xref:#unordered_node_set_copy_constructor_with_allocator[unordered_node_set](const unordered_node_set& other, const Allocator& a);
|
||||
xref:#unordered_node_set_move_constructor_with_allocator[unordered_node_set](unordered_node_set&& other, const Allocator& a);
|
||||
xref:#unordered_node_set_move_constructor_from_concurrent_node_set[unordered_node_set](concurrent_node_set<Key, Hash, Pred, Allocator>&& other);
|
||||
xref:#unordered_node_set_initializer_list_constructor[unordered_node_set](std::initializer_list<value_type> il,
|
||||
size_type n = _implementation-defined_
|
||||
const hasher& hf = hasher(),
|
||||
@@ -489,6 +490,24 @@ and always calls `other.reset_stats()`.
|
||||
|
||||
---
|
||||
|
||||
==== Move Constructor from concurrent_node_set
|
||||
|
||||
```c++
|
||||
unordered_node_set(concurrent_node_set<Key, Hash, Pred, Allocator>&& other);
|
||||
```
|
||||
|
||||
Move construction from a xref:#concurrent_node_set[`concurrent_node_set`].
|
||||
The internal bucket array of `other` is transferred directly to the new container.
|
||||
The hash function, predicate and allocator are moved-constructed from `other`.
|
||||
If statistics are xref:unordered_node_set_boost_unordered_enable_stats[enabled],
|
||||
transfers the internal statistical information from `other` and calls `other.reset_stats()`.
|
||||
|
||||
[horizontal]
|
||||
Complexity:;; Constant time.
|
||||
Concurrency:;; Blocking on `other`.
|
||||
|
||||
---
|
||||
|
||||
==== Initializer List Constructor
|
||||
[source,c++,subs="+quotes"]
|
||||
----
|
||||
@@ -1028,8 +1047,8 @@ Throws:;; Nothing.
|
||||
|
||||
==== Extract by Key
|
||||
```c++
|
||||
node_type erase(const key_type& k);
|
||||
template<class K> node_type erase(K&& k);
|
||||
node_type extract(const key_type& k);
|
||||
template<class K> node_type extract(K&& k);
|
||||
```
|
||||
|
||||
Extracts the element with key equivalent to `k`, if it exists.
|
||||
@@ -1037,7 +1056,7 @@ Extracts the element with key equivalent to `k`, if it exists.
|
||||
[horizontal]
|
||||
Returns:;; A `node_type` object holding the extracted element, or empty if no element was extracted.
|
||||
Throws:;; Only throws an exception if it is thrown by `hasher` or `key_equal`.
|
||||
Notes:;; The `template<class K>` overload only participates in overload resolution if `Hash::is_transparent` and `Pred::is_transparent` are valid member typedefs and neither `iterator` nor `const_iterator` are implicitly convertible from `K`. The library assumes that `Hash` is callable with both `K` and `Key` and that `Pred` is transparent. This enables heterogeneous lookup which avoids the cost of instantiating an instance of the `Key` type.
|
||||
Notes:;; The `template<class K>` overload only participates in overload resolution if `Hash::is_transparent` and `Pred::is_transparent` are valid member typedefs. The library assumes that `Hash` is callable with both `K` and `Key` and that `Pred` is transparent. This enables heterogeneous lookup which avoids the cost of instantiating an instance of the `Key` type.
|
||||
|
||||
---
|
||||
|
||||
|
||||
+175
-46
@@ -30,18 +30,50 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
</Type>
|
||||
|
||||
<Type Name="boost::unordered::detail::grouped_bucket_array<*>" Inheritable="false">
|
||||
<!--
|
||||
The expression `&**p` is used so the Intrinsic fails to parse for a fancy pointer type.
|
||||
Only one of the definitions can exist at any given time, so the other must always fail to parse, similar to SFINAE in C++.
|
||||
For a raw pointer, this expression is exactly equivalent to `*p`.
|
||||
For a fancy pointer, this expression will try to call a user-defined `operator*()`, which is not allowed in Natvis, and it will fail.
|
||||
-->
|
||||
<Intrinsic Name="to_address" Optional="true" Expression="&**p">
|
||||
<Parameter Name="p" Type="bucket_pointer*" />
|
||||
</Intrinsic>
|
||||
<Intrinsic Name="to_address" Optional="true" Expression="&**p">
|
||||
<Parameter Name="p" Type="node_pointer*" />
|
||||
</Intrinsic>
|
||||
<Intrinsic Name="to_address" Optional="true" Expression="p->boost_to_address()">
|
||||
<Parameter Name="p" Type="bucket_pointer*" />
|
||||
</Intrinsic>
|
||||
<Intrinsic Name="to_address" Optional="true" Expression="p->boost_to_address()">
|
||||
<Parameter Name="p" Type="node_pointer*" />
|
||||
</Intrinsic>
|
||||
<!--
|
||||
The casting expression `(xyz_pointer)p` is used so the Intrinsic fails to parse for a fancy pointer type.
|
||||
Only one of the definitions can exist at any given time, so the other must always fail to parse, similar to SFINAE in C++.
|
||||
In this case, `(xyz_pointer)p` is either a no-op for a raw pointer type, or it's an invalid expression.
|
||||
-->
|
||||
<Intrinsic Name="next" Optional="true" Expression="((bucket_pointer)p) + n">
|
||||
<Parameter Name="p" Type="bucket_type*" />
|
||||
<Parameter Name="n" Type="ptrdiff_t" />
|
||||
</Intrinsic>
|
||||
<Intrinsic Name="next" Optional="true" Expression="((bucket_pointer*)nullptr)->boost_next(p, n)">
|
||||
<Parameter Name="p" Type="bucket_type*" />
|
||||
<Parameter Name="n" Type="ptrdiff_t" />
|
||||
</Intrinsic>
|
||||
|
||||
<Expand>
|
||||
<CustomListItems MaxItemsPerView="100">
|
||||
<Variable Name="size" InitialValue="size_" />
|
||||
<Variable Name="bucket_index" InitialValue="0" />
|
||||
<Variable Name="current_bucket" InitialValue="&buckets[bucket_index]" />
|
||||
<Variable Name="node" InitialValue="current_bucket->next" />
|
||||
<Variable Name="current_bucket" InitialValue="to_address(&buckets)" />
|
||||
<Variable Name="node" InitialValue="to_address(&current_bucket->next)" />
|
||||
<Loop Condition="bucket_index != size">
|
||||
<Exec>current_bucket = &buckets[bucket_index]</Exec>
|
||||
<Exec>node = current_bucket->next</Exec>
|
||||
<Exec>current_bucket = next(to_address(&buckets), bucket_index)</Exec>
|
||||
<Exec>node = to_address(&current_bucket->next)</Exec>
|
||||
<Loop Condition="node != nullptr">
|
||||
<Item>node->buf.t_</Item>
|
||||
<Exec>node = node->next</Exec>
|
||||
<Exec>node = to_address(&node->next)</Exec>
|
||||
</Loop>
|
||||
<Exec>++bucket_index</Exec>
|
||||
</Loop>
|
||||
@@ -54,14 +86,14 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
<CustomListItems MaxItemsPerView="100">
|
||||
<Variable Name="size" InitialValue="size_" />
|
||||
<Variable Name="bucket_index" InitialValue="0" />
|
||||
<Variable Name="current_bucket" InitialValue="&buckets[bucket_index]" />
|
||||
<Variable Name="node" InitialValue="current_bucket->next" />
|
||||
<Variable Name="current_bucket" InitialValue="to_address(&buckets)" />
|
||||
<Variable Name="node" InitialValue="to_address(&current_bucket->next)" />
|
||||
<Loop Condition="bucket_index != size">
|
||||
<Exec>current_bucket = &buckets[bucket_index]</Exec>
|
||||
<Exec>node = current_bucket->next</Exec>
|
||||
<Exec>current_bucket = next(to_address(&buckets), bucket_index)</Exec>
|
||||
<Exec>node = to_address(&current_bucket->next)</Exec>
|
||||
<Loop Condition="node != nullptr">
|
||||
<Item Name="[{node->buf.t_.first}]">node->buf.t_</Item>
|
||||
<Exec>node = node->next</Exec>
|
||||
<Exec>node = to_address(&node->next)</Exec>
|
||||
</Loop>
|
||||
<Exec>++bucket_index</Exec>
|
||||
</Loop>
|
||||
@@ -99,29 +131,46 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
<Type Name="boost::unordered::detail::iterator_detail::iterator<*>" Inheritable="false">
|
||||
<AlternativeType Name="boost::unordered::detail::iterator_detail::c_iterator<*>" />
|
||||
<Intrinsic Name="valid" Expression="p != nullptr && itb.p != nullptr && itb.pbg != nullptr" />
|
||||
<DisplayString Condition="valid()">{p->buf.t_}</DisplayString>
|
||||
<Intrinsic Name="to_address" Optional="true" Expression="&**p">
|
||||
<Parameter Name="p" Type="node_pointer*" />
|
||||
</Intrinsic>
|
||||
<Intrinsic Name="to_address" Optional="true" Expression="&**p">
|
||||
<Parameter Name="p" Type="bucket_iterator::bucket_pointer*" />
|
||||
</Intrinsic>
|
||||
<Intrinsic Name="to_address" Optional="true" Expression="&**p">
|
||||
<Parameter Name="p" Type="bucket_iterator::bucket_group_pointer*" />
|
||||
</Intrinsic>
|
||||
<Intrinsic Name="to_address" Optional="true" Expression="p->boost_to_address()">
|
||||
<Parameter Name="p" Type="node_pointer*" />
|
||||
</Intrinsic>
|
||||
<Intrinsic Name="to_address" Optional="true" Expression="p->boost_to_address()">
|
||||
<Parameter Name="p" Type="bucket_iterator::bucket_pointer*" />
|
||||
</Intrinsic>
|
||||
<Intrinsic Name="to_address" Optional="true" Expression="p->boost_to_address()">
|
||||
<Parameter Name="p" Type="bucket_iterator::bucket_group_pointer*" />
|
||||
</Intrinsic>
|
||||
<Intrinsic Name="valid" Expression="to_address(&p) && to_address(&itb.p) && to_address(&itb.pbg)" />
|
||||
<DisplayString Condition="valid()">{to_address(&p)->buf.t_}</DisplayString>
|
||||
<DisplayString Condition="!valid()">{{ end iterator }}</DisplayString>
|
||||
<Expand>
|
||||
<ExpandedItem Condition="valid()">p->buf.t_</ExpandedItem>
|
||||
<ExpandedItem Condition="valid()">to_address(&p)->buf.t_</ExpandedItem>
|
||||
</Expand>
|
||||
</Type>
|
||||
|
||||
<!-- FOA and CFOA helpers -->
|
||||
|
||||
<Type Name="boost::unordered::detail::foa::element_type<*>" Priority="Medium" Inheritable="false">
|
||||
<DisplayString>{*p}</DisplayString>
|
||||
<Intrinsic Name="to_address" Optional="true" Expression="&**p">
|
||||
<Parameter Name="p" Type="pointer*" />
|
||||
</Intrinsic>
|
||||
<Intrinsic Name="to_address" Optional="true" Expression="p->boost_to_address()">
|
||||
<Parameter Name="p" Type="pointer*" />
|
||||
</Intrinsic>
|
||||
<Intrinsic Name="get" Expression="to_address(&p)" />
|
||||
|
||||
<DisplayString>{*get()}</DisplayString>
|
||||
<Expand>
|
||||
<ExpandedItem>*p</ExpandedItem>
|
||||
</Expand>
|
||||
</Type>
|
||||
|
||||
<Type Name="boost::unordered::detail::foa::element_type<std::pair<*>,*>" Priority="MediumHigh" Inheritable="false">
|
||||
<!-- Manually expand when holding a `std::pair`, otherwise the debugger complains about recursion-->
|
||||
<DisplayString>({p->first}, {p->second})</DisplayString>
|
||||
<Expand>
|
||||
<Item Name="first">p->first</Item>
|
||||
<Item Name="second">p->second</Item>
|
||||
<ExpandedItem>*get()</ExpandedItem>
|
||||
</Expand>
|
||||
</Type>
|
||||
|
||||
@@ -227,10 +276,39 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
<Type Name="boost::unordered::detail::foa::table<*>" Inheritable="false">
|
||||
<AlternativeType Name="boost::unordered::detail::foa::concurrent_table<*>" />
|
||||
|
||||
<Intrinsic Name="to_address" Optional="true" Expression="&**p">
|
||||
<Parameter Name="p" Type="arrays_type::value_type_pointer*" />
|
||||
</Intrinsic>
|
||||
<Intrinsic Name="to_address" Optional="true" Expression="&**p">
|
||||
<Parameter Name="p" Type="arrays_type::group_type_pointer*" />
|
||||
</Intrinsic>
|
||||
<Intrinsic Name="to_address" Optional="true" Expression="p->boost_to_address()">
|
||||
<Parameter Name="p" Type="arrays_type::value_type_pointer*" />
|
||||
</Intrinsic>
|
||||
<Intrinsic Name="to_address" Optional="true" Expression="p->boost_to_address()">
|
||||
<Parameter Name="p" Type="arrays_type::group_type_pointer*" />
|
||||
</Intrinsic>
|
||||
<Intrinsic Name="next" Optional="true" Expression="((arrays_type::value_type_pointer)p) + n">
|
||||
<Parameter Name="p" Type="arrays_type::value_type*" />
|
||||
<Parameter Name="n" Type="ptrdiff_t" />
|
||||
</Intrinsic>
|
||||
<Intrinsic Name="next" Optional="true" Expression="((arrays_type::char_pointer)p) + n">
|
||||
<Parameter Name="p" Type="unsigned char*" />
|
||||
<Parameter Name="n" Type="ptrdiff_t" />
|
||||
</Intrinsic>
|
||||
<Intrinsic Name="next" Optional="true" Expression="((arrays_type::value_type_pointer*)nullptr)->boost_next(p, n)">
|
||||
<Parameter Name="p" Type="arrays_type::value_type*" />
|
||||
<Parameter Name="n" Type="ptrdiff_t" />
|
||||
</Intrinsic>
|
||||
<Intrinsic Name="next" Optional="true" Expression="((arrays_type::char_pointer*)nullptr)->boost_next(p, n)">
|
||||
<Parameter Name="p" Type="unsigned char*" />
|
||||
<Parameter Name="n" Type="ptrdiff_t" />
|
||||
</Intrinsic>
|
||||
|
||||
<Intrinsic Optional="true" Name="get_value" ReturnType="value_type*" Expression="e">
|
||||
<Parameter Name="e" Type="value_type*" />
|
||||
</Intrinsic>
|
||||
<Intrinsic Optional="true" Name="get_value" ReturnType="value_type*" Expression="e->p">
|
||||
<Intrinsic Optional="true" Name="get_value" ReturnType="value_type*" Expression="e->get()">
|
||||
<Parameter Name="e" Type="element_type*" />
|
||||
</Intrinsic>
|
||||
|
||||
@@ -254,8 +332,8 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
<Expand>
|
||||
<Item Name="[stats]" Optional="true">cstats</Item>
|
||||
<CustomListItems MaxItemsPerView="100">
|
||||
<Variable Name="pc_" InitialValue="reinterpret_cast<unsigned char*>(arrays.groups_)" />
|
||||
<Variable Name="p_" InitialValue="arrays.elements_" />
|
||||
<Variable Name="pc_" InitialValue="reinterpret_cast<unsigned char*>(to_address(&arrays.groups_))" />
|
||||
<Variable Name="p_" InitialValue="to_address(&arrays.elements_)" />
|
||||
<Variable Name="first_time" InitialValue="true" />
|
||||
<Variable Name="mask" InitialValue="(int)0" />
|
||||
<Variable Name="n0" InitialValue="(size_t)0" />
|
||||
@@ -264,18 +342,18 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
<Loop Condition="p_ != nullptr">
|
||||
|
||||
<!-- This if block mirrors the condition in the begin() call -->
|
||||
<If Condition="!first_time || !(arrays.elements_ && !(arrays.groups_[0].match_occupied() & 0x1))">
|
||||
<If Condition="!first_time || !(p_ && !(to_address(&arrays.groups_)[0].match_occupied() & 0x1))">
|
||||
<Item>*p_</Item>
|
||||
</If>
|
||||
<Exec>first_time = false</Exec>
|
||||
|
||||
<Exec>n0 = reinterpret_cast<uintptr_t>(pc_) % sizeof(group_type)</Exec>
|
||||
<Exec>pc_ -= (ptrdiff_t)n0</Exec>
|
||||
<Exec>pc_ = next(pc_, -(ptrdiff_t)n0)</Exec>
|
||||
|
||||
<Exec>mask = (reinterpret_cast<group_type*>(pc_)->match_occupied() >> (n0+1)) << (n0+1)</Exec>
|
||||
<Loop Condition="mask == 0">
|
||||
<Exec>pc_ += sizeof(group_type)</Exec>
|
||||
<Exec>p_ += group_type::N</Exec>
|
||||
<Exec>pc_ = next(pc_, sizeof(group_type))</Exec>
|
||||
<Exec>p_ = next(p_, group_type::N)</Exec>
|
||||
<Exec>mask = reinterpret_cast<group_type*>(pc_)->match_occupied()</Exec>
|
||||
</Loop>
|
||||
|
||||
@@ -284,9 +362,8 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
<Exec>p_ = nullptr</Exec>
|
||||
</If>
|
||||
<Else>
|
||||
<Exec>pc_ += (ptrdiff_t)n</Exec>
|
||||
<Exec>p_ -= (ptrdiff_t)n0</Exec>
|
||||
<Exec>p_ += (ptrdiff_t)n</Exec>
|
||||
<Exec>pc_ = next(pc_, (ptrdiff_t)n)</Exec>
|
||||
<Exec>p_ = next(p_, (ptrdiff_t)n - (ptrdiff_t)n0)</Exec>
|
||||
</Else>
|
||||
|
||||
</Loop>
|
||||
@@ -299,8 +376,8 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
<Expand>
|
||||
<Item Name="[stats]" Optional="true">cstats</Item>
|
||||
<CustomListItems MaxItemsPerView="100">
|
||||
<Variable Name="pc_" InitialValue="reinterpret_cast<unsigned char*>(arrays.groups_)" />
|
||||
<Variable Name="p_" InitialValue="arrays.elements_" />
|
||||
<Variable Name="pc_" InitialValue="reinterpret_cast<unsigned char*>(to_address(&arrays.groups_))" />
|
||||
<Variable Name="p_" InitialValue="to_address(&arrays.elements_)" />
|
||||
<Variable Name="first_time" InitialValue="true" />
|
||||
<Variable Name="mask" InitialValue="(int)0" />
|
||||
<Variable Name="n0" InitialValue="(size_t)0" />
|
||||
@@ -309,18 +386,18 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
<Loop Condition="p_ != nullptr">
|
||||
|
||||
<!-- This if block mirrors the condition in the begin() call -->
|
||||
<If Condition="!first_time || !(arrays.elements_ && !(arrays.groups_[0].match_occupied() & 0x1))">
|
||||
<If Condition="!first_time || !(p_ && !(to_address(&arrays.groups_)[0].match_occupied() & 0x1))">
|
||||
<Item Name="[{get_value(p_)->first}]">*p_</Item>
|
||||
</If>
|
||||
<Exec>first_time = false</Exec>
|
||||
|
||||
<Exec>n0 = reinterpret_cast<uintptr_t>(pc_) % sizeof(group_type)</Exec>
|
||||
<Exec>pc_ -= (ptrdiff_t)n0</Exec>
|
||||
<Exec>pc_ = next(pc_, -(ptrdiff_t)n0)</Exec>
|
||||
|
||||
<Exec>mask = (reinterpret_cast<group_type*>(pc_)->match_occupied() >> (n0+1)) << (n0+1)</Exec>
|
||||
<Loop Condition="mask == 0">
|
||||
<Exec>pc_ += sizeof(group_type)</Exec>
|
||||
<Exec>p_ += group_type::N</Exec>
|
||||
<Exec>pc_ = next(pc_, sizeof(group_type))</Exec>
|
||||
<Exec>p_ = next(p_, group_type::N)</Exec>
|
||||
<Exec>mask = reinterpret_cast<group_type*>(pc_)->match_occupied()</Exec>
|
||||
</Loop>
|
||||
|
||||
@@ -329,9 +406,8 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
<Exec>p_ = nullptr</Exec>
|
||||
</If>
|
||||
<Else>
|
||||
<Exec>pc_ += (ptrdiff_t)n</Exec>
|
||||
<Exec>p_ -= (ptrdiff_t)n0</Exec>
|
||||
<Exec>p_ += (ptrdiff_t)n</Exec>
|
||||
<Exec>pc_ = next(pc_, (ptrdiff_t)n)</Exec>
|
||||
<Exec>p_ = next(p_, (ptrdiff_t)n - (ptrdiff_t)n0)</Exec>
|
||||
</Else>
|
||||
|
||||
</Loop>
|
||||
@@ -345,6 +421,8 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
<AlternativeType Name="boost::unordered::unordered_node_set<*>" />
|
||||
<AlternativeType Name="boost::unordered::concurrent_flat_map<*>" />
|
||||
<AlternativeType Name="boost::unordered::concurrent_flat_set<*>" />
|
||||
<AlternativeType Name="boost::unordered::concurrent_node_map<*>" />
|
||||
<AlternativeType Name="boost::unordered::concurrent_node_set<*>" />
|
||||
<DisplayString>{{ size={table_.size_ctrl.size} }}</DisplayString>
|
||||
<Expand>
|
||||
<Item Name="[hash_function]" ExcludeView="simple">*reinterpret_cast<hasher*>(static_cast<table_type::super::hash_base*>(&table_))</Item>
|
||||
@@ -357,6 +435,7 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
<Type Name="boost::unordered::unordered_flat_map<*>" Priority="MediumHigh" ExcludeView="ShowElementsByIndex">
|
||||
<AlternativeType Name="boost::unordered::unordered_node_map<*>" />
|
||||
<AlternativeType Name="boost::unordered::concurrent_flat_map<*>" />
|
||||
<AlternativeType Name="boost::unordered::concurrent_node_map<*>" />
|
||||
<DisplayString>{{ size={table_.size_ctrl.size} }}</DisplayString>
|
||||
<Expand>
|
||||
<Item Name="[hash_function]" ExcludeView="simple">*reinterpret_cast<hasher*>(static_cast<table_type::super::hash_base*>(&table_))</Item>
|
||||
@@ -369,12 +448,62 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
<!-- FOA iterators -->
|
||||
|
||||
<Type Name="boost::unordered::detail::foa::table_iterator<*>" Inheritable="false">
|
||||
<Intrinsic Name="valid" Expression="p_ != nullptr && pc_ != nullptr" />
|
||||
<DisplayString Condition="valid()">{*p_}</DisplayString>
|
||||
<Intrinsic Name="to_address" Optional="true" Expression="&**p">
|
||||
<Parameter Name="p" Type="table_element_pointer*" />
|
||||
</Intrinsic>
|
||||
<Intrinsic Name="to_address" Optional="true" Expression="p->boost_to_address()">
|
||||
<Parameter Name="p" Type="table_element_pointer*" />
|
||||
</Intrinsic>
|
||||
<Intrinsic Name="to_address" Optional="true" Expression="&**p">
|
||||
<Parameter Name="p" Type="char_pointer*" />
|
||||
</Intrinsic>
|
||||
<Intrinsic Name="to_address" Optional="true" Expression="p->boost_to_address()">
|
||||
<Parameter Name="p" Type="char_pointer*" />
|
||||
</Intrinsic>
|
||||
|
||||
<Intrinsic Name="valid" Expression="to_address(&p_) != nullptr && to_address(&pc_) != nullptr" />
|
||||
<DisplayString Condition="valid()">{*to_address(&p_)}</DisplayString>
|
||||
<DisplayString Condition="!valid()">{{ end iterator }}</DisplayString>
|
||||
<Expand>
|
||||
<ExpandedItem Condition="valid()">*p_</ExpandedItem>
|
||||
<ExpandedItem Condition="valid()">*to_address(&p_)</ExpandedItem>
|
||||
</Expand>
|
||||
</Type>
|
||||
|
||||
<!-- Fancy pointer support -->
|
||||
|
||||
<!--
|
||||
To allow your own fancy pointer type to interact with Boost.Unordered Natvis,
|
||||
add the following intrinsics to your type, with the following conditions.
|
||||
|
||||
(Note, this is assuming the presence of a type alias `pointer` for the underlying
|
||||
raw pointer type, and a type alias `difference_type` for your fancy pointer
|
||||
difference type. Substitute whichever names are applicable in your case.)
|
||||
|
||||
`boost_to_address`
|
||||
* Takes no parameters
|
||||
* Returns the raw pointer equivalent to your fancy pointer
|
||||
|
||||
`boost_next`
|
||||
* Parameter 1, an underlying raw pointer of type `pointer`
|
||||
* Parameter 2, an offset of type `difference_type`
|
||||
* Returns the raw pointer equivalent to your fancy pointer, as if you did the following operations
|
||||
1. Convert the incoming raw pointer to your fancy pointer
|
||||
2. Use operator+= to add the offset to the fancy pointer
|
||||
3. Convert back to the raw pointer
|
||||
* Note, you will not actually do these operations as stated. You will do equivalent lower-level operations that emulate having done the above.
|
||||
|
||||
Example
|
||||
```
|
||||
<Type Name="my_fancy_ptr<*>">
|
||||
...
|
||||
<Intrinsic Name="boost_to_address" ReturnType="pointer" Expression="..." />
|
||||
<Intrinsic Name="boost_next" ReturnType="pointer" Expression="...">
|
||||
<Parameter Name="ptr" Type="pointer" />
|
||||
<Parameter Name="offset" Type="difference_type" />
|
||||
</Intrinsic>
|
||||
...
|
||||
</Type>
|
||||
```
|
||||
-->
|
||||
|
||||
</AutoVisualizer>
|
||||
|
||||
@@ -0,0 +1,391 @@
|
||||
# Copyright 2024 Braden Ganetsky
|
||||
# Distributed under the Boost Software License, Version 1.0.
|
||||
# https://www.boost.org/LICENSE_1_0.txt
|
||||
|
||||
import gdb.printing
|
||||
import gdb.xmethod
|
||||
import re
|
||||
import math
|
||||
|
||||
class BoostUnorderedHelpers:
|
||||
def maybe_unwrap_atomic(n):
|
||||
if f"{n.type.strip_typedefs()}".startswith("std::atomic<"):
|
||||
underlying_type = n.type.template_argument(0)
|
||||
return n.cast(underlying_type)
|
||||
else:
|
||||
return n
|
||||
|
||||
def maybe_unwrap_foa_element(e):
|
||||
if f"{e.type.strip_typedefs()}".startswith("boost::unordered::detail::foa::element_type<"):
|
||||
return e["p"]
|
||||
else:
|
||||
return e
|
||||
|
||||
def maybe_unwrap_reference(value):
|
||||
if value.type.code == gdb.TYPE_CODE_REF:
|
||||
return value.referenced_value()
|
||||
else:
|
||||
return value
|
||||
|
||||
def countr_zero(n):
|
||||
for i in range(32):
|
||||
if (n & (1 << i)) != 0:
|
||||
return i
|
||||
return 32
|
||||
|
||||
class BoostUnorderedPointerCustomizationPoint:
|
||||
def __init__(self, any_ptr):
|
||||
vis = gdb.default_visualizer(any_ptr)
|
||||
if vis is None:
|
||||
self.to_address = lambda ptr: ptr
|
||||
self.next = lambda ptr, offset: ptr + offset
|
||||
else:
|
||||
self.to_address = lambda ptr: ptr if (ptr.type.code == gdb.TYPE_CODE_PTR) else type(vis).boost_to_address(ptr)
|
||||
self.next = lambda ptr, offset: type(vis).boost_next(ptr, offset)
|
||||
|
||||
class BoostUnorderedFcaPrinter:
|
||||
def __init__(self, val):
|
||||
self.val = BoostUnorderedHelpers.maybe_unwrap_reference(val)
|
||||
self.name = f"{self.val.type.strip_typedefs()}".split("<")[0]
|
||||
self.name = self.name.replace("boost::unordered::", "boost::")
|
||||
self.is_map = self.name.endswith("map")
|
||||
self.cpo = BoostUnorderedPointerCustomizationPoint(self.val["table_"]["buckets_"]["buckets"])
|
||||
|
||||
def to_string(self):
|
||||
size = self.val["table_"]["size_"]
|
||||
return f"{self.name} with {size} elements"
|
||||
|
||||
def display_hint(self):
|
||||
return "map"
|
||||
|
||||
def children(self):
|
||||
def generator():
|
||||
grouped_buckets = self.val["table_"]["buckets_"]
|
||||
|
||||
size = grouped_buckets["size_"]
|
||||
buckets = grouped_buckets["buckets"]
|
||||
bucket_index = 0
|
||||
|
||||
count = 0
|
||||
while bucket_index != size:
|
||||
current_bucket = self.cpo.next(self.cpo.to_address(buckets), bucket_index)
|
||||
node = self.cpo.to_address(current_bucket.dereference()["next"])
|
||||
while node != 0:
|
||||
value = node.dereference()["buf"]["t_"]
|
||||
if self.is_map:
|
||||
first = value["first"]
|
||||
second = value["second"]
|
||||
yield "", first
|
||||
yield "", second
|
||||
else:
|
||||
yield "", count
|
||||
yield "", value
|
||||
count += 1
|
||||
node = self.cpo.to_address(node.dereference()["next"])
|
||||
bucket_index += 1
|
||||
|
||||
return generator()
|
||||
|
||||
class BoostUnorderedFcaIteratorPrinter:
|
||||
def __init__(self, val):
|
||||
self.val = val
|
||||
self.cpo = BoostUnorderedPointerCustomizationPoint(self.val["p"])
|
||||
|
||||
def to_string(self):
|
||||
if self.valid():
|
||||
value = self.cpo.to_address(self.val["p"]).dereference()["buf"]["t_"]
|
||||
return f"iterator = {{ {value} }}"
|
||||
else:
|
||||
return "iterator = { end iterator }"
|
||||
|
||||
def valid(self):
|
||||
return (self.cpo.to_address(self.val["p"]) != 0) and (self.cpo.to_address(self.val["itb"]["p"]) != 0)
|
||||
|
||||
class BoostUnorderedFoaTableCoreCumulativeStatsPrinter:
|
||||
def __init__(self, val):
|
||||
self.val = val
|
||||
|
||||
def to_string(self):
|
||||
return "[stats]"
|
||||
|
||||
def display_hint(self):
|
||||
return "map"
|
||||
|
||||
def children(self):
|
||||
def generator():
|
||||
members = ["insertion", "successful_lookup", "unsuccessful_lookup"]
|
||||
for member in members:
|
||||
yield "", member
|
||||
yield "", self.val[member]
|
||||
return generator()
|
||||
|
||||
class BoostUnorderedFoaCumulativeStatsPrinter:
|
||||
def __init__(self, val):
|
||||
self.val = val
|
||||
self.n = self.val["n"]
|
||||
self.N = self.val.type.template_argument(0)
|
||||
|
||||
def display_hint(self):
|
||||
return "map"
|
||||
|
||||
def children(self):
|
||||
def generator():
|
||||
yield "", "count"
|
||||
yield "", self.n
|
||||
|
||||
sequence_stats_data = gdb.lookup_type("boost::unordered::detail::foa::sequence_stats_data")
|
||||
data = self.val["data"]
|
||||
arr = data.address.reinterpret_cast(sequence_stats_data.pointer())
|
||||
def build_string(idx):
|
||||
entry = arr[idx]
|
||||
avg = float(entry["m"])
|
||||
var = float(entry["s"] / self.n) if (self.n != 0) else 0.0
|
||||
dev = math.sqrt(var)
|
||||
return f"{{avg = {avg}, var = {var}, dev = {dev}}}"
|
||||
|
||||
if self.N > 0:
|
||||
yield "", "probe_length"
|
||||
yield "", build_string(0)
|
||||
if self.N > 1:
|
||||
yield "", "num_comparisons"
|
||||
yield "", build_string(1)
|
||||
|
||||
return generator()
|
||||
|
||||
class BoostUnorderedFoaPrinter:
|
||||
def __init__(self, val):
|
||||
self.val = BoostUnorderedHelpers.maybe_unwrap_reference(val)
|
||||
self.name = f"{self.val.type.strip_typedefs()}".split("<")[0]
|
||||
self.name = self.name.replace("boost::unordered::", "boost::")
|
||||
self.is_map = self.name.endswith("map")
|
||||
self.cpo = BoostUnorderedPointerCustomizationPoint(self.val["table_"]["arrays"]["groups_"])
|
||||
|
||||
def to_string(self):
|
||||
size = BoostUnorderedHelpers.maybe_unwrap_atomic(self.val["table_"]["size_ctrl"]["size"])
|
||||
return f"{self.name} with {size} elements"
|
||||
|
||||
def display_hint(self):
|
||||
return "map"
|
||||
|
||||
def is_regular_layout(self, group):
|
||||
typename = group["m"].type.strip_typedefs()
|
||||
array_size = typename.sizeof // typename.target().sizeof
|
||||
if array_size == 16:
|
||||
return True
|
||||
elif array_size == 2:
|
||||
return False
|
||||
|
||||
def match_occupied(self, group):
|
||||
m = group["m"]
|
||||
at = lambda b: BoostUnorderedHelpers.maybe_unwrap_atomic(m[b]["n"])
|
||||
|
||||
if self.is_regular_layout(group):
|
||||
bits = [1 << b for b in range(16) if at(b) == 0]
|
||||
return 0x7FFF & ~sum(bits)
|
||||
else:
|
||||
xx = at(0) | at(1)
|
||||
yy = xx | (xx >> 32)
|
||||
return 0x7FFF & (yy | (yy >> 16))
|
||||
|
||||
def is_sentinel(self, group, pos):
|
||||
m = group["m"]
|
||||
at = lambda b: BoostUnorderedHelpers.maybe_unwrap_atomic(m[b]["n"])
|
||||
|
||||
N = group["N"]
|
||||
sentinel_ = group["sentinel_"]
|
||||
if self.is_regular_layout(group):
|
||||
return pos == N-1 and at(N-1) == sentinel_
|
||||
else:
|
||||
return pos == N-1 and (at(0) & 0x4000400040004000) == 0x4000 and (at(1) & 0x4000400040004000) == 0
|
||||
|
||||
def children(self):
|
||||
def generator():
|
||||
table = self.val["table_"]
|
||||
groups = self.cpo.to_address(table["arrays"]["groups_"])
|
||||
elements = self.cpo.to_address(table["arrays"]["elements_"])
|
||||
|
||||
pc_ = groups.cast(gdb.lookup_type("unsigned char").pointer())
|
||||
p_ = elements
|
||||
first_time = True
|
||||
mask = 0
|
||||
n0 = 0
|
||||
n = 0
|
||||
|
||||
count = 0
|
||||
while p_ != 0:
|
||||
# This if block mirrors the condition in the begin() call
|
||||
if (not first_time) or (self.match_occupied(groups.dereference()) & 1):
|
||||
pointer = BoostUnorderedHelpers.maybe_unwrap_foa_element(p_)
|
||||
value = self.cpo.to_address(pointer).dereference()
|
||||
if self.is_map:
|
||||
first = value["first"]
|
||||
second = value["second"]
|
||||
yield "", first
|
||||
yield "", second
|
||||
else:
|
||||
yield "", count
|
||||
yield "", value
|
||||
count += 1
|
||||
first_time = False
|
||||
|
||||
n0 = pc_.cast(gdb.lookup_type("uintptr_t")) % groups.dereference().type.sizeof
|
||||
pc_ = self.cpo.next(pc_, -n0)
|
||||
|
||||
mask = (self.match_occupied(pc_.cast(groups.type).dereference()) >> (n0+1)) << (n0+1)
|
||||
while mask == 0:
|
||||
pc_ = self.cpo.next(pc_, groups.dereference().type.sizeof)
|
||||
p_ = self.cpo.next(p_, groups.dereference()["N"])
|
||||
mask = self.match_occupied(pc_.cast(groups.type).dereference())
|
||||
|
||||
n = BoostUnorderedHelpers.countr_zero(mask)
|
||||
if self.is_sentinel(pc_.cast(groups.type).dereference(), n):
|
||||
p_ = 0
|
||||
else:
|
||||
pc_ = self.cpo.next(pc_, n)
|
||||
p_ = self.cpo.next(p_, n - n0)
|
||||
|
||||
return generator()
|
||||
|
||||
class BoostUnorderedFoaIteratorPrinter:
|
||||
def __init__(self, val):
|
||||
self.val = val
|
||||
self.cpo = BoostUnorderedPointerCustomizationPoint(self.val["p_"])
|
||||
|
||||
def to_string(self):
|
||||
if self.valid():
|
||||
element = self.cpo.to_address(self.val["p_"])
|
||||
pointer = BoostUnorderedHelpers.maybe_unwrap_foa_element(element)
|
||||
value = self.cpo.to_address(pointer).dereference()
|
||||
return f"iterator = {{ {value} }}"
|
||||
else:
|
||||
return "iterator = { end iterator }"
|
||||
|
||||
def valid(self):
|
||||
return (self.cpo.to_address(self.val["p_"]) != 0) and (self.cpo.to_address(self.val["pc_"]) != 0)
|
||||
|
||||
def boost_unordered_build_pretty_printer():
|
||||
pp = gdb.printing.RegexpCollectionPrettyPrinter("boost_unordered")
|
||||
add_template_printer = lambda name, printer: pp.add_printer(name, f"^{name}<.*>$", printer)
|
||||
add_concrete_printer = lambda name, printer: pp.add_printer(name, f"^{name}$", printer)
|
||||
|
||||
add_template_printer("boost::unordered::unordered_map", BoostUnorderedFcaPrinter)
|
||||
add_template_printer("boost::unordered::unordered_multimap", BoostUnorderedFcaPrinter)
|
||||
add_template_printer("boost::unordered::unordered_set", BoostUnorderedFcaPrinter)
|
||||
add_template_printer("boost::unordered::unordered_multiset", BoostUnorderedFcaPrinter)
|
||||
|
||||
add_template_printer("boost::unordered::detail::iterator_detail::iterator", BoostUnorderedFcaIteratorPrinter)
|
||||
add_template_printer("boost::unordered::detail::iterator_detail::c_iterator", BoostUnorderedFcaIteratorPrinter)
|
||||
|
||||
add_template_printer("boost::unordered::unordered_flat_map", BoostUnorderedFoaPrinter)
|
||||
add_template_printer("boost::unordered::unordered_flat_set", BoostUnorderedFoaPrinter)
|
||||
add_template_printer("boost::unordered::unordered_node_map", BoostUnorderedFoaPrinter)
|
||||
add_template_printer("boost::unordered::unordered_node_set", BoostUnorderedFoaPrinter)
|
||||
add_template_printer("boost::unordered::concurrent_flat_map", BoostUnorderedFoaPrinter)
|
||||
add_template_printer("boost::unordered::concurrent_flat_set", BoostUnorderedFoaPrinter)
|
||||
add_template_printer("boost::unordered::concurrent_node_map", BoostUnorderedFoaPrinter)
|
||||
add_template_printer("boost::unordered::concurrent_node_set", BoostUnorderedFoaPrinter)
|
||||
|
||||
add_template_printer("boost::unordered::detail::foa::table_iterator", BoostUnorderedFoaIteratorPrinter)
|
||||
|
||||
add_concrete_printer("boost::unordered::detail::foa::table_core_cumulative_stats", BoostUnorderedFoaTableCoreCumulativeStatsPrinter)
|
||||
add_template_printer("boost::unordered::detail::foa::cumulative_stats", BoostUnorderedFoaCumulativeStatsPrinter)
|
||||
add_template_printer("boost::unordered::detail::foa::concurrent_cumulative_stats", BoostUnorderedFoaCumulativeStatsPrinter)
|
||||
|
||||
return pp
|
||||
|
||||
gdb.printing.register_pretty_printer(gdb.current_objfile(), boost_unordered_build_pretty_printer())
|
||||
|
||||
|
||||
|
||||
# https://sourceware.org/gdb/current/onlinedocs/gdb.html/Writing-an-Xmethod.html
|
||||
class BoostUnorderedFoaGetStatsMethod(gdb.xmethod.XMethod):
|
||||
def __init__(self):
|
||||
gdb.xmethod.XMethod.__init__(self, "get_stats")
|
||||
|
||||
def get_worker(self, method_name):
|
||||
if method_name == "get_stats":
|
||||
return BoostUnorderedFoaGetStatsWorker()
|
||||
|
||||
class BoostUnorderedFoaGetStatsWorker(gdb.xmethod.XMethodWorker):
|
||||
def get_arg_types(self):
|
||||
return None
|
||||
|
||||
def get_result_type(self, obj):
|
||||
return gdb.lookup_type("boost::unordered::detail::foa::table_core_cumulative_stats")
|
||||
|
||||
def __call__(self, obj):
|
||||
try:
|
||||
return obj["table_"]["cstats"]
|
||||
except gdb.error:
|
||||
print("Error: Binary was compiled without stats. Recompile with `BOOST_UNORDERED_ENABLE_STATS` defined.")
|
||||
return
|
||||
|
||||
class BoostUnorderedFoaMatcher(gdb.xmethod.XMethodMatcher):
|
||||
def __init__(self):
|
||||
gdb.xmethod.XMethodMatcher.__init__(self, 'BoostUnorderedFoaMatcher')
|
||||
self.methods = [BoostUnorderedFoaGetStatsMethod()]
|
||||
|
||||
def match(self, class_type, method_name):
|
||||
template_name = f"{class_type.strip_typedefs()}".split("<")[0]
|
||||
regex = "^boost::unordered::(unordered|concurrent)_(flat|node)_(map|set)$"
|
||||
if not re.match(regex, template_name):
|
||||
return None
|
||||
|
||||
workers = []
|
||||
for method in self.methods:
|
||||
if method.enabled:
|
||||
worker = method.get_worker(method_name)
|
||||
if worker:
|
||||
workers.append(worker)
|
||||
return workers
|
||||
|
||||
gdb.xmethod.register_xmethod_matcher(None, BoostUnorderedFoaMatcher())
|
||||
|
||||
|
||||
|
||||
""" Fancy pointer support """
|
||||
|
||||
"""
|
||||
To allow your own fancy pointer type to interact with Boost.Unordered GDB pretty-printers,
|
||||
create a pretty-printer for your own type with the following additional methods.
|
||||
|
||||
(Note, this is assuming the presence of a type alias `pointer` for the underlying
|
||||
raw pointer type, Substitute whichever name is applicable in your case.)
|
||||
|
||||
`boost_to_address(fancy_ptr)`
|
||||
* A static method, but `@staticmethod` is not required
|
||||
* Parameter `fancy_ptr` of type `gdb.Value`
|
||||
* Its `.type` will be your fancy pointer type
|
||||
* Returns a `gdb.Value` with the raw pointer equivalent to your fancy pointer
|
||||
* This method should be equivalent to calling `operator->()` on your fancy pointer in C++
|
||||
|
||||
`boost_next(raw_ptr, offset)`
|
||||
* Parameter `raw_ptr` of type `gdb.Value`
|
||||
* Its `.type` will be `pointer`
|
||||
* Parameter `offset`
|
||||
* Either has integer type, or is of type `gdb.Value` with an underlying integer
|
||||
* Returns a `gdb.Value` with the raw pointer equivalent to your fancy pointer, as if you did the following operations
|
||||
1. Convert the incoming raw pointer to your fancy pointer
|
||||
2. Use operator+= to add the offset to the fancy pointer
|
||||
3. Convert back to the raw pointer
|
||||
* Note, you will not actually do these operations as stated. You will do equivalent lower-level operations that emulate having done the above
|
||||
* Ultimately, it will be as if you called `operator+()` on your fancy pointer in C++, but using only raw pointers
|
||||
|
||||
Example
|
||||
```
|
||||
class MyFancyPtrPrinter:
|
||||
...
|
||||
|
||||
# Equivalent to `operator->()`
|
||||
def boost_to_address(fancy_ptr):
|
||||
...
|
||||
return ...
|
||||
|
||||
# Equivalent to `operator+()`
|
||||
def boost_next(raw_ptr, offset):
|
||||
...
|
||||
return ...
|
||||
|
||||
...
|
||||
```
|
||||
"""
|
||||
@@ -227,6 +227,13 @@ namespace boost {
|
||||
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
|
||||
{
|
||||
@@ -241,6 +248,15 @@ namespace boost {
|
||||
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
|
||||
@@ -259,6 +275,15 @@ namespace boost {
|
||||
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
|
||||
@@ -277,6 +302,12 @@ namespace boost {
|
||||
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)
|
||||
@@ -290,6 +321,16 @@ namespace boost {
|
||||
}
|
||||
|
||||
#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
|
||||
@@ -311,6 +352,12 @@ namespace boost {
|
||||
}
|
||||
#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)
|
||||
@@ -324,6 +371,16 @@ namespace boost {
|
||||
}
|
||||
|
||||
#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
|
||||
@@ -384,14 +441,14 @@ namespace boost {
|
||||
BOOST_FORCEINLINE bool insert_or_visit(value_type const& obj, F f)
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
|
||||
return table_.insert_or_cvisit(obj, 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_cvisit(std::move(obj), f);
|
||||
return table_.insert_or_visit(std::move(obj), f);
|
||||
}
|
||||
|
||||
template <class K, class F>
|
||||
@@ -401,7 +458,7 @@ namespace boost {
|
||||
insert_or_visit(K&& k, F f)
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
|
||||
return table_.try_emplace_or_cvisit(std::forward<K>(k), f);
|
||||
return table_.try_emplace_or_visit(std::forward<K>(k), f);
|
||||
}
|
||||
|
||||
template <class InputIterator, class F>
|
||||
@@ -409,7 +466,7 @@ namespace boost {
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
|
||||
for (; first != last; ++first) {
|
||||
table_.emplace_or_cvisit(*first, f);
|
||||
table_.emplace_or_visit(*first, f);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -417,7 +474,7 @@ namespace boost {
|
||||
void insert_or_visit(std::initializer_list<value_type> ilist, F f)
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
|
||||
this->insert_or_cvisit(ilist.begin(), ilist.end(), f);
|
||||
this->insert_or_visit(ilist.begin(), ilist.end(), f);
|
||||
}
|
||||
|
||||
template <class F>
|
||||
@@ -469,7 +526,7 @@ namespace boost {
|
||||
BOOST_FORCEINLINE bool emplace_or_visit(Arg&& arg, Args&&... args)
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_LAST_ARG_CONST_INVOCABLE(Arg, Args...)
|
||||
return table_.emplace_or_cvisit(
|
||||
return table_.emplace_or_visit(
|
||||
std::forward<Arg>(arg), std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -0,0 +1,67 @@
|
||||
/* Fast open-addressing, node-based concurrent hashset.
|
||||
*
|
||||
* Copyright 2023 Christian Mazakas.
|
||||
* Copyright 2023-2024 Joaquin M Lopez Munoz.
|
||||
* Copyright 2024 Braden Ganetsky.
|
||||
* Distributed under the Boost Software License, Version 1.0.
|
||||
* (See accompanying file LICENSE_1_0.txt or copy at
|
||||
* http://www.boost.org/LICENSE_1_0.txt)
|
||||
*
|
||||
* See https://www.boost.org/libs/unordered for library home page.
|
||||
*/
|
||||
|
||||
#ifndef BOOST_UNORDERED_CONCURRENT_NODE_SET_FWD_HPP
|
||||
#define BOOST_UNORDERED_CONCURRENT_NODE_SET_FWD_HPP
|
||||
|
||||
#include <boost/config.hpp>
|
||||
#include <boost/container_hash/hash_fwd.hpp>
|
||||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
|
||||
#ifndef BOOST_NO_CXX17_HDR_MEMORY_RESOURCE
|
||||
#include <memory_resource>
|
||||
#endif
|
||||
|
||||
namespace boost {
|
||||
namespace unordered {
|
||||
|
||||
template <class Key, class Hash = boost::hash<Key>,
|
||||
class Pred = std::equal_to<Key>,
|
||||
class Allocator = std::allocator<Key> >
|
||||
class concurrent_node_set;
|
||||
|
||||
template <class Key, class Hash, class KeyEqual, class Allocator>
|
||||
bool operator==(
|
||||
concurrent_node_set<Key, Hash, KeyEqual, Allocator> const& lhs,
|
||||
concurrent_node_set<Key, Hash, KeyEqual, Allocator> const& rhs);
|
||||
|
||||
template <class Key, class Hash, class KeyEqual, class Allocator>
|
||||
bool operator!=(
|
||||
concurrent_node_set<Key, Hash, KeyEqual, Allocator> const& lhs,
|
||||
concurrent_node_set<Key, Hash, KeyEqual, Allocator> const& rhs);
|
||||
|
||||
template <class Key, class Hash, class Pred, class Alloc>
|
||||
void swap(concurrent_node_set<Key, Hash, Pred, Alloc>& x,
|
||||
concurrent_node_set<Key, Hash, Pred, Alloc>& y)
|
||||
noexcept(noexcept(x.swap(y)));
|
||||
|
||||
template <class K, class H, class P, class A, class Predicate>
|
||||
typename concurrent_node_set<K, H, P, A>::size_type erase_if(
|
||||
concurrent_node_set<K, H, P, A>& c, Predicate pred);
|
||||
|
||||
#ifndef BOOST_NO_CXX17_HDR_MEMORY_RESOURCE
|
||||
namespace pmr {
|
||||
template <class Key, class Hash = boost::hash<Key>,
|
||||
class Pred = std::equal_to<Key> >
|
||||
using concurrent_node_set = boost::unordered::concurrent_node_set<Key,
|
||||
Hash, Pred, std::pmr::polymorphic_allocator<Key> >;
|
||||
} // namespace pmr
|
||||
#endif
|
||||
|
||||
} // namespace unordered
|
||||
|
||||
using boost::unordered::concurrent_node_set;
|
||||
} // namespace boost
|
||||
|
||||
#endif // BOOST_UNORDERED_CONCURRENT_NODE_SET_FWD_HPP
|
||||
@@ -397,10 +397,10 @@ inline void swap(atomic_size_control& x,atomic_size_control& y)
|
||||
* - Parallel versions of [c]visit_all(f) and erase_if(f) are provided based
|
||||
* on C++17 stdlib parallel algorithms.
|
||||
*
|
||||
* Consult boost::concurrent_flat_(map|set) docs for the full API reference.
|
||||
* Heterogeneous lookup is suported by default, that is, without checking for
|
||||
* any ::is_transparent typedefs --this checking is done by the wrapping
|
||||
* containers.
|
||||
* Consult boost::concurrent_(flat|node)_(map|set) docs for the full API
|
||||
* reference. Heterogeneous lookup is suported by default, that is, without
|
||||
* checking for any ::is_transparent typedefs --this checking is done by the
|
||||
* wrapping containers.
|
||||
*
|
||||
* Thread-safe concurrency is implemented using a two-level lock system:
|
||||
*
|
||||
@@ -724,6 +724,14 @@ public:
|
||||
BOOST_FORCEINLINE bool
|
||||
insert(value_type&& x){return emplace_impl(std::move(x));}
|
||||
|
||||
template<typename T=element_type>
|
||||
BOOST_FORCEINLINE
|
||||
typename std::enable_if<
|
||||
!std::is_same<T,value_type>::value,
|
||||
bool
|
||||
>::type
|
||||
insert(element_type&& x){return emplace_impl(std::move(x));}
|
||||
|
||||
template<typename Key,typename... Args>
|
||||
BOOST_FORCEINLINE bool try_emplace(Key&& x,Args&&... args)
|
||||
{
|
||||
@@ -819,6 +827,30 @@ public:
|
||||
group_shared{},std::forward<F>(f),std::move(x));
|
||||
}
|
||||
|
||||
template<typename F,typename T=element_type>
|
||||
BOOST_FORCEINLINE
|
||||
typename std::enable_if<
|
||||
!std::is_same<T,value_type>::value,
|
||||
bool
|
||||
>::type
|
||||
insert_or_visit(element_type&& x, F&& f)
|
||||
{
|
||||
return emplace_or_visit_impl(
|
||||
group_exclusive{},std::forward<F>(f),std::move(x));
|
||||
}
|
||||
|
||||
template<typename F,typename T=element_type>
|
||||
BOOST_FORCEINLINE
|
||||
typename std::enable_if<
|
||||
!std::is_same<T,value_type>::value,
|
||||
bool
|
||||
>::type
|
||||
insert_or_cvisit(element_type&& x, F&& f)
|
||||
{
|
||||
return emplace_or_visit_impl(
|
||||
group_shared{},std::forward<F>(f),std::move(x));
|
||||
}
|
||||
|
||||
template<typename Key>
|
||||
BOOST_FORCEINLINE std::size_t erase(const Key& x)
|
||||
{
|
||||
@@ -889,6 +921,29 @@ public:
|
||||
super::clear();
|
||||
}
|
||||
|
||||
template<typename Key,typename Extractor>
|
||||
BOOST_FORCEINLINE void extract(const Key& x,Extractor&& ext)
|
||||
{
|
||||
extract_if(
|
||||
x,[](const value_type&){return true;},std::forward<Extractor>(ext));
|
||||
}
|
||||
|
||||
template<typename Key,typename F,typename Extractor>
|
||||
BOOST_FORCEINLINE void extract_if(const Key& x,F&& f,Extractor&& ext)
|
||||
{
|
||||
auto lck=shared_access();
|
||||
auto hash=this->hash_for(x);
|
||||
unprotected_internal_visit(
|
||||
group_exclusive{},x,this->position_for(hash),hash,
|
||||
[&,this](group_type* pg,unsigned int n,element_type* p)
|
||||
{
|
||||
if(f(cast_for(group_exclusive{},type_policy::value_from(*p)))){
|
||||
ext(std::move(*p),this->al());
|
||||
super::erase(pg,n,p);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// TODO: should we accept different allocator too?
|
||||
template<typename Hash2,typename Pred2>
|
||||
size_type merge(concurrent_table<TypePolicy,Hash2,Pred2,Allocator>& x)
|
||||
@@ -1733,7 +1788,8 @@ private:
|
||||
|
||||
if(this->find(x,pos0,hash))throw_exception(bad_archive_exception());
|
||||
auto loc=this->unchecked_emplace_at(pos0,hash,std::move(x));
|
||||
ar.reset_object_address(std::addressof(*loc.p),std::addressof(x));
|
||||
ar.reset_object_address(
|
||||
std::addressof(type_policy::value_from(*loc.p)),std::addressof(x));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1742,7 +1798,7 @@ private:
|
||||
{
|
||||
using raw_key_type=typename std::remove_const<key_type>::type;
|
||||
using raw_mapped_type=typename std::remove_const<
|
||||
typename TypePolicy::mapped_type>::type;
|
||||
typename type_policy::mapped_type>::type;
|
||||
|
||||
auto lck=exclusive_access();
|
||||
std::size_t s;
|
||||
@@ -1766,8 +1822,12 @@ private:
|
||||
|
||||
if(this->find(k,pos0,hash))throw_exception(bad_archive_exception());
|
||||
auto loc=this->unchecked_emplace_at(pos0,hash,std::move(k),std::move(m));
|
||||
ar.reset_object_address(std::addressof(loc.p->first),std::addressof(k));
|
||||
ar.reset_object_address(std::addressof(loc.p->second),std::addressof(m));
|
||||
ar.reset_object_address(
|
||||
std::addressof(type_policy::value_from(*loc.p).first),
|
||||
std::addressof(k));
|
||||
ar.reset_object_address(
|
||||
std::addressof(type_policy::value_from(*loc.p).second),
|
||||
std::addressof(m));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
#include <boost/unordered/detail/static_assert.hpp>
|
||||
#include <boost/unordered/detail/type_traits.hpp>
|
||||
#include <boost/unordered/hash_traits.hpp>
|
||||
#include <boost/unordered/unordered_printers.hpp>
|
||||
#include <climits>
|
||||
#include <cmath>
|
||||
#include <cstddef>
|
||||
@@ -1017,6 +1018,11 @@ struct table_arrays
|
||||
rebind<group_type>;
|
||||
using group_type_pointer_traits=boost::pointer_traits<group_type_pointer>;
|
||||
|
||||
// For natvis purposes
|
||||
using char_pointer=
|
||||
typename boost::pointer_traits<value_type_pointer>::template
|
||||
rebind<unsigned char>;
|
||||
|
||||
table_arrays(
|
||||
std::size_t gsi,std::size_t gsm,
|
||||
group_type_pointer pg,value_type_pointer pe):
|
||||
@@ -1453,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()):
|
||||
@@ -1461,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
|
||||
@@ -2075,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_):
|
||||
@@ -2085,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);
|
||||
|
||||
@@ -6,6 +6,8 @@
|
||||
#ifndef BOOST_UNORDERED_DETAIL_FOA_FLAT_MAP_TYPES_HPP
|
||||
#define BOOST_UNORDERED_DETAIL_FOA_FLAT_MAP_TYPES_HPP
|
||||
|
||||
#include <boost/unordered/detail/foa/types_constructibility.hpp>
|
||||
|
||||
#include <boost/core/allocator_access.hpp>
|
||||
|
||||
namespace boost {
|
||||
@@ -25,6 +27,9 @@ namespace boost {
|
||||
|
||||
using element_type = value_type;
|
||||
|
||||
using types = flat_map_types<Key, T>;
|
||||
using constructibility_checker = map_types_constructibility<types>;
|
||||
|
||||
static value_type& value_from(element_type& x) { return x; }
|
||||
|
||||
template <class K, class V>
|
||||
@@ -48,18 +53,21 @@ namespace boost {
|
||||
template <class A, class... Args>
|
||||
static void construct(A& al, init_type* p, Args&&... args)
|
||||
{
|
||||
constructibility_checker::check(al, p, std::forward<Args>(args)...);
|
||||
boost::allocator_construct(al, p, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template <class A, class... Args>
|
||||
static void construct(A& al, value_type* p, Args&&... args)
|
||||
{
|
||||
constructibility_checker::check(al, p, std::forward<Args>(args)...);
|
||||
boost::allocator_construct(al, p, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template <class A, class... Args>
|
||||
static void construct(A& al, key_type* p, Args&&... args)
|
||||
{
|
||||
constructibility_checker::check(al, p, std::forward<Args>(args)...);
|
||||
boost::allocator_construct(al, p, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
@@ -79,8 +87,8 @@ namespace boost {
|
||||
}
|
||||
};
|
||||
} // namespace foa
|
||||
} // namespace detail
|
||||
} // namespace unordered
|
||||
} // namespace detail
|
||||
} // namespace unordered
|
||||
} // namespace boost
|
||||
|
||||
#endif // BOOST_UNORDERED_DETAIL_FOA_FLAT_MAP_TYPES_HPP
|
||||
|
||||
@@ -5,6 +5,8 @@
|
||||
#ifndef BOOST_UNORDERED_DETAIL_FOA_FLAT_SET_TYPES_HPP
|
||||
#define BOOST_UNORDERED_DETAIL_FOA_FLAT_SET_TYPES_HPP
|
||||
|
||||
#include <boost/unordered/detail/foa/types_constructibility.hpp>
|
||||
|
||||
#include <boost/core/allocator_access.hpp>
|
||||
|
||||
namespace boost {
|
||||
@@ -21,6 +23,9 @@ namespace boost {
|
||||
|
||||
using element_type = value_type;
|
||||
|
||||
using types = flat_set_types<Key>;
|
||||
using constructibility_checker = set_types_constructibility<types>;
|
||||
|
||||
static Key& value_from(element_type& x) { return x; }
|
||||
|
||||
static element_type&& move(element_type& x) { return std::move(x); }
|
||||
@@ -28,6 +33,7 @@ namespace boost {
|
||||
template <class A, class... Args>
|
||||
static void construct(A& al, value_type* p, Args&&... args)
|
||||
{
|
||||
constructibility_checker::check(al, p, std::forward<Args>(args)...);
|
||||
boost::allocator_construct(al, p, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
@@ -37,8 +43,8 @@ namespace boost {
|
||||
}
|
||||
};
|
||||
} // namespace foa
|
||||
} // namespace detail
|
||||
} // namespace unordered
|
||||
} // namespace detail
|
||||
} // namespace unordered
|
||||
} // namespace boost
|
||||
|
||||
#endif // BOOST_UNORDERED_DETAIL_FOA_FLAT_SET_TYPES_HPP
|
||||
|
||||
@@ -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};
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
@@ -7,6 +7,8 @@
|
||||
#define BOOST_UNORDERED_DETAIL_FOA_NODE_MAP_TYPES_HPP
|
||||
|
||||
#include <boost/unordered/detail/foa/element_type.hpp>
|
||||
#include <boost/unordered/detail/foa/types_constructibility.hpp>
|
||||
#include <boost/unordered/detail/type_traits.hpp>
|
||||
|
||||
#include <boost/core/allocator_access.hpp>
|
||||
#include <boost/core/no_exceptions_support.hpp>
|
||||
@@ -29,6 +31,9 @@ namespace boost {
|
||||
|
||||
using element_type = foa::element_type<value_type, VoidPtr>;
|
||||
|
||||
using types = node_map_types<Key, T, VoidPtr>;
|
||||
using constructibility_checker = map_types_constructibility<types>;
|
||||
|
||||
static value_type& value_from(element_type const& x)
|
||||
{
|
||||
return *(x.p);
|
||||
@@ -68,24 +73,27 @@ namespace boost {
|
||||
static void construct(
|
||||
A& al, element_type* p, element_type const& copy)
|
||||
{
|
||||
construct(al, p, *copy.p);
|
||||
construct(al, p, detail::as_const(*copy.p));
|
||||
}
|
||||
|
||||
template <class A, class... Args>
|
||||
static void construct(A& al, init_type* p, Args&&... args)
|
||||
{
|
||||
constructibility_checker::check(al, p, std::forward<Args>(args)...);
|
||||
boost::allocator_construct(al, p, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template <class A, class... Args>
|
||||
static void construct(A& al, value_type* p, Args&&... args)
|
||||
{
|
||||
constructibility_checker::check(al, p, std::forward<Args>(args)...);
|
||||
boost::allocator_construct(al, p, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template <class A, class... Args>
|
||||
static void construct(A& al, key_type* p, Args&&... args)
|
||||
{
|
||||
constructibility_checker::check(al, p, std::forward<Args>(args)...);
|
||||
boost::allocator_construct(al, p, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
@@ -95,8 +103,11 @@ namespace boost {
|
||||
p->p = boost::allocator_allocate(al, 1);
|
||||
BOOST_TRY
|
||||
{
|
||||
auto address = boost::to_address(p->p);
|
||||
constructibility_checker::check(
|
||||
al, address, std::forward<Args>(args)...);
|
||||
boost::allocator_construct(
|
||||
al, boost::to_address(p->p), std::forward<Args>(args)...);
|
||||
al, address, std::forward<Args>(args)...);
|
||||
}
|
||||
BOOST_CATCH(...)
|
||||
{
|
||||
@@ -132,8 +143,8 @@ namespace boost {
|
||||
};
|
||||
|
||||
} // namespace foa
|
||||
} // namespace detail
|
||||
} // namespace unordered
|
||||
} // namespace detail
|
||||
} // namespace unordered
|
||||
} // namespace boost
|
||||
|
||||
#endif // BOOST_UNORDERED_DETAIL_FOA_NODE_MAP_TYPES_HPP
|
||||
|
||||
@@ -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
|
||||
@@ -6,6 +6,8 @@
|
||||
#define BOOST_UNORDERED_DETAIL_FOA_NODE_SET_TYPES_HPP
|
||||
|
||||
#include <boost/unordered/detail/foa/element_type.hpp>
|
||||
#include <boost/unordered/detail/foa/types_constructibility.hpp>
|
||||
#include <boost/unordered/detail/type_traits.hpp>
|
||||
|
||||
#include <boost/core/allocator_access.hpp>
|
||||
#include <boost/core/no_exceptions_support.hpp>
|
||||
@@ -26,6 +28,9 @@ namespace boost {
|
||||
|
||||
using element_type = foa::element_type<value_type, VoidPtr>;
|
||||
|
||||
using types = node_set_types<Key, VoidPtr>;
|
||||
using constructibility_checker = set_types_constructibility<types>;
|
||||
|
||||
static value_type& value_from(element_type const& x) { return *x.p; }
|
||||
static Key const& extract(element_type const& k) { return *k.p; }
|
||||
static element_type&& move(element_type& x) { return std::move(x); }
|
||||
@@ -35,7 +40,7 @@ namespace boost {
|
||||
static void construct(
|
||||
A& al, element_type* p, element_type const& copy)
|
||||
{
|
||||
construct(al, p, *copy.p);
|
||||
construct(al, p, detail::as_const(*copy.p));
|
||||
}
|
||||
|
||||
template <typename Allocator>
|
||||
@@ -49,6 +54,7 @@ namespace boost {
|
||||
template <class A, class... Args>
|
||||
static void construct(A& al, value_type* p, Args&&... args)
|
||||
{
|
||||
constructibility_checker::check(al, p, std::forward<Args>(args)...);
|
||||
boost::allocator_construct(al, p, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
@@ -58,8 +64,11 @@ namespace boost {
|
||||
p->p = boost::allocator_allocate(al, 1);
|
||||
BOOST_TRY
|
||||
{
|
||||
auto address = boost::to_address(p->p);
|
||||
constructibility_checker::check(
|
||||
al, address, std::forward<Args>(args)...);
|
||||
boost::allocator_construct(
|
||||
al, boost::to_address(p->p), std::forward<Args>(args)...);
|
||||
al, address, std::forward<Args>(args)...);
|
||||
}
|
||||
BOOST_CATCH(...)
|
||||
{
|
||||
@@ -85,8 +94,8 @@ namespace boost {
|
||||
};
|
||||
|
||||
} // namespace foa
|
||||
} // namespace detail
|
||||
} // namespace unordered
|
||||
} // namespace detail
|
||||
} // namespace unordered
|
||||
} // namespace boost
|
||||
|
||||
#endif // BOOST_UNORDERED_DETAIL_FOA_NODE_SET_TYPES_HPP
|
||||
|
||||
@@ -0,0 +1,172 @@
|
||||
// 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)
|
||||
|
||||
#ifndef BOOST_UNORDERED_DETAIL_FOA_TYPES_CONSTRUCTIBILITY_HPP
|
||||
#define BOOST_UNORDERED_DETAIL_FOA_TYPES_CONSTRUCTIBILITY_HPP
|
||||
|
||||
#include <memory>
|
||||
#include <tuple>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
namespace boost {
|
||||
namespace unordered {
|
||||
namespace detail {
|
||||
namespace foa {
|
||||
template <class Key, class... Args> struct check_key_type_t
|
||||
{
|
||||
static_assert(std::is_constructible<Key, Args...>::value,
|
||||
"key_type must be constructible from Args");
|
||||
};
|
||||
template <class Key> struct check_key_type_t<Key>
|
||||
{
|
||||
static_assert(std::is_constructible<Key>::value,
|
||||
"key_type must be default constructible");
|
||||
};
|
||||
template <class Key> struct check_key_type_t<Key, const Key&>
|
||||
{
|
||||
static_assert(std::is_constructible<Key, const Key&>::value,
|
||||
"key_type must be copy constructible");
|
||||
};
|
||||
template <class Key> struct check_key_type_t<Key, Key&&>
|
||||
{
|
||||
static_assert(std::is_constructible<Key, Key&&>::value,
|
||||
"key_type must be move constructible");
|
||||
};
|
||||
|
||||
template <class Mapped, class... Args> struct check_mapped_type_t
|
||||
{
|
||||
static_assert(std::is_constructible<Mapped, Args...>::value,
|
||||
"mapped_type must be constructible from Args");
|
||||
};
|
||||
template <class Mapped> struct check_mapped_type_t<Mapped>
|
||||
{
|
||||
static_assert(std::is_constructible<Mapped>::value,
|
||||
"mapped_type must be default constructible");
|
||||
};
|
||||
template <class Mapped>
|
||||
struct check_mapped_type_t<Mapped, const Mapped&>
|
||||
{
|
||||
static_assert(std::is_constructible<Mapped, const Mapped&>::value,
|
||||
"mapped_type must be copy constructible");
|
||||
};
|
||||
template <class Mapped> struct check_mapped_type_t<Mapped, Mapped&&>
|
||||
{
|
||||
static_assert(std::is_constructible<Mapped, Mapped&&>::value,
|
||||
"mapped_type must be move constructible");
|
||||
};
|
||||
|
||||
template <class TypePolicy> struct map_types_constructibility
|
||||
{
|
||||
using key_type = typename TypePolicy::key_type;
|
||||
using mapped_type = typename TypePolicy::mapped_type;
|
||||
using init_type = typename TypePolicy::init_type;
|
||||
using value_type = typename TypePolicy::value_type;
|
||||
|
||||
template <class A, class X, class... Args>
|
||||
static void check(A&, X*, Args&&...)
|
||||
{
|
||||
// Pass through, as we cannot say anything about a general allocator
|
||||
}
|
||||
|
||||
template <class... Args> static void check_key_type()
|
||||
{
|
||||
(void)check_key_type_t<key_type, Args...>{};
|
||||
}
|
||||
template <class... Args> static void check_mapped_type()
|
||||
{
|
||||
(void)check_mapped_type_t<mapped_type, Args...>{};
|
||||
}
|
||||
|
||||
template <class Arg>
|
||||
static void check(std::allocator<value_type>&, key_type*, Arg&&)
|
||||
{
|
||||
check_key_type<Arg&&>();
|
||||
}
|
||||
|
||||
template <class Arg1, class Arg2>
|
||||
static void check(
|
||||
std::allocator<value_type>&, value_type*, Arg1&&, Arg2&&)
|
||||
{
|
||||
check_key_type<Arg1&&>();
|
||||
check_mapped_type<Arg2&&>();
|
||||
}
|
||||
template <class Arg1, class Arg2>
|
||||
static void check(std::allocator<value_type>&, value_type*,
|
||||
const std::pair<Arg1, Arg2>&)
|
||||
{
|
||||
check_key_type<const Arg1&>();
|
||||
check_mapped_type<const Arg2&>();
|
||||
}
|
||||
template <class Arg1, class Arg2>
|
||||
static void check(
|
||||
std::allocator<value_type>&, value_type*, std::pair<Arg1, Arg2>&&)
|
||||
{
|
||||
check_key_type<Arg1&&>();
|
||||
check_mapped_type<Arg2&&>();
|
||||
}
|
||||
template <class... Args1, class... Args2>
|
||||
static void check(std::allocator<value_type>&, value_type*,
|
||||
std::piecewise_construct_t, std::tuple<Args1...>&&,
|
||||
std::tuple<Args2...>&&)
|
||||
{
|
||||
check_key_type<Args1&&...>();
|
||||
check_mapped_type<Args2&&...>();
|
||||
}
|
||||
|
||||
template <class Arg1, class Arg2>
|
||||
static void check(
|
||||
std::allocator<value_type>&, init_type*, Arg1&&, Arg2&&)
|
||||
{
|
||||
check_key_type<Arg1&&>();
|
||||
check_mapped_type<Arg2&&>();
|
||||
}
|
||||
template <class Arg1, class Arg2>
|
||||
static void check(std::allocator<value_type>&, init_type*,
|
||||
const std::pair<Arg1, Arg2>&)
|
||||
{
|
||||
check_key_type<const Arg1&>();
|
||||
check_mapped_type<const Arg2&>();
|
||||
}
|
||||
template <class Arg1, class Arg2>
|
||||
static void check(
|
||||
std::allocator<value_type>&, init_type*, std::pair<Arg1, Arg2>&&)
|
||||
{
|
||||
check_key_type<Arg1&&>();
|
||||
check_mapped_type<Arg2&&>();
|
||||
}
|
||||
template <class... Args1, class... Args2>
|
||||
static void check(std::allocator<value_type>&, init_type*,
|
||||
std::piecewise_construct_t, std::tuple<Args1...>&&,
|
||||
std::tuple<Args2...>&&)
|
||||
{
|
||||
check_key_type<Args1&&...>();
|
||||
check_mapped_type<Args2&&...>();
|
||||
}
|
||||
};
|
||||
|
||||
template <class TypePolicy> struct set_types_constructibility
|
||||
{
|
||||
using key_type = typename TypePolicy::key_type;
|
||||
using value_type = typename TypePolicy::value_type;
|
||||
static_assert(std::is_same<key_type, value_type>::value, "");
|
||||
|
||||
template <class A, class X, class... Args>
|
||||
static void check(A&, X*, Args&&...)
|
||||
{
|
||||
// Pass through, as we cannot say anything about a general allocator
|
||||
}
|
||||
|
||||
template <class... Args>
|
||||
static void check(std::allocator<value_type>&, key_type*, Args&&...)
|
||||
{
|
||||
(void)check_key_type_t<key_type, Args&&...>{};
|
||||
}
|
||||
};
|
||||
} // namespace foa
|
||||
} // namespace detail
|
||||
} // namespace unordered
|
||||
} // namespace boost
|
||||
|
||||
#endif // BOOST_UNORDERED_DETAIL_FOA_TYPES_CONSTRUCTIBILITY_HPP
|
||||
@@ -41,23 +41,6 @@ namespace boost {
|
||||
typedef typename table::iterator iterator;
|
||||
typedef boost::unordered::insert_return_type_map<iterator, node_type> insert_return_type;
|
||||
};
|
||||
|
||||
template <typename K, typename M, typename H, typename P, typename A>
|
||||
class instantiate_map
|
||||
{
|
||||
typedef boost::unordered_map<K, M, H, P, A> container;
|
||||
container x;
|
||||
typename container::node_type node_type;
|
||||
typename container::insert_return_type insert_return_type;
|
||||
};
|
||||
|
||||
template <typename K, typename M, typename H, typename P, typename A>
|
||||
class instantiate_multimap
|
||||
{
|
||||
typedef boost::unordered_multimap<K, M, H, P, A> container;
|
||||
container x;
|
||||
typename container::node_type node_type;
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,23 +39,6 @@ namespace boost {
|
||||
typedef boost::unordered::insert_return_type_set<iterator, node_type>
|
||||
insert_return_type;
|
||||
};
|
||||
|
||||
template <typename T, typename H, typename P, typename A>
|
||||
class instantiate_set
|
||||
{
|
||||
typedef boost::unordered_set<T, H, P, A> container;
|
||||
container x;
|
||||
typename container::node_type node_type;
|
||||
typename container::insert_return_type insert_return_type;
|
||||
};
|
||||
|
||||
template <typename T, typename H, typename P, typename A>
|
||||
class instantiate_multiset
|
||||
{
|
||||
typedef boost::unordered_multiset<T, H, P, A> container;
|
||||
container x;
|
||||
typename container::node_type node_type;
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -219,6 +219,17 @@ namespace boost {
|
||||
using iter_to_alloc_t =
|
||||
typename std::pair<iter_key_t<T> const, iter_val_t<T> >;
|
||||
#endif
|
||||
|
||||
#if BOOST_CXX_VERSION < 201703L
|
||||
template <class T>
|
||||
constexpr typename std::add_const<T>::type& as_const(T& t) noexcept
|
||||
{
|
||||
return t;
|
||||
}
|
||||
template <class T> void as_const(const T&&) = delete;
|
||||
#else
|
||||
using std::as_const;
|
||||
#endif
|
||||
} // namespace detail
|
||||
} // namespace unordered
|
||||
} // namespace boost
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -202,6 +203,13 @@ namespace boost {
|
||||
return *this;
|
||||
}
|
||||
|
||||
unordered_flat_map& operator=(std::initializer_list<value_type> il)
|
||||
{
|
||||
this->clear();
|
||||
this->insert(il.begin(), il.end());
|
||||
return *this;
|
||||
}
|
||||
|
||||
allocator_type get_allocator() const noexcept
|
||||
{
|
||||
return table_.get_allocator();
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -198,6 +199,13 @@ namespace boost {
|
||||
return *this;
|
||||
}
|
||||
|
||||
unordered_flat_set& operator=(std::initializer_list<value_type> il)
|
||||
{
|
||||
this->clear();
|
||||
this->insert(il.begin(), il.end());
|
||||
return *this;
|
||||
}
|
||||
|
||||
allocator_type get_allocator() const noexcept
|
||||
{
|
||||
return table_.get_allocator();
|
||||
|
||||
@@ -198,7 +198,7 @@ namespace boost {
|
||||
template <class... Args> std::pair<iterator, bool> emplace(Args&&... args)
|
||||
{
|
||||
return table_.emplace_unique(
|
||||
table::extractor::extract(std::forward<Args>(args)...),
|
||||
table::extractor::extract(detail::as_const(args)...),
|
||||
std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
@@ -206,7 +206,7 @@ namespace boost {
|
||||
iterator emplace_hint(const_iterator hint, Args&&... args)
|
||||
{
|
||||
return table_.emplace_hint_unique(hint,
|
||||
table::extractor::extract(std::forward<Args>(args)...),
|
||||
table::extractor::extract(detail::as_const(args)...),
|
||||
std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
@@ -235,6 +210,13 @@ namespace boost {
|
||||
return *this;
|
||||
}
|
||||
|
||||
unordered_node_map& operator=(std::initializer_list<value_type> il)
|
||||
{
|
||||
this->clear();
|
||||
this->insert(il.begin(), il.end());
|
||||
return *this;
|
||||
}
|
||||
|
||||
allocator_type get_allocator() const noexcept
|
||||
{
|
||||
return table_.get_allocator();
|
||||
@@ -307,15 +289,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 +308,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 +493,8 @@ namespace boost {
|
||||
BOOST_ASSERT(pos != end());
|
||||
node_type nh;
|
||||
auto elem = table_.extract(pos);
|
||||
nh.emplace(std::move(elem), get_allocator());
|
||||
detail::foa::node_handle_emplacer(nh)(
|
||||
std::move(elem), get_allocator());
|
||||
return nh;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
// Copyright (C) 2022-2023 Christian Mazakas
|
||||
// Copyright (C) 2024 Joaquin M Lopez Munoz
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
@@ -10,8 +11,9 @@
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
#include <boost/unordered/concurrent_node_set_fwd.hpp>
|
||||
#include <boost/unordered/detail/foa/element_type.hpp>
|
||||
#include <boost/unordered/detail/foa/node_handle.hpp>
|
||||
#include <boost/unordered/detail/foa/node_set_handle.hpp>
|
||||
#include <boost/unordered/detail/foa/node_set_types.hpp>
|
||||
#include <boost/unordered/detail/foa/table.hpp>
|
||||
#include <boost/unordered/detail/serialize_container.hpp>
|
||||
@@ -35,37 +37,12 @@ namespace boost {
|
||||
#pragma warning(disable : 4714) /* marked as __forceinline not inlined */
|
||||
#endif
|
||||
|
||||
namespace detail {
|
||||
template <class TypePolicy, class Allocator>
|
||||
struct node_set_handle
|
||||
: public detail::foa::node_handle_base<TypePolicy, Allocator>
|
||||
{
|
||||
private:
|
||||
using base_type = detail::foa::node_handle_base<TypePolicy, Allocator>;
|
||||
|
||||
using typename base_type::type_policy;
|
||||
|
||||
template <class Key, class Hash, class Pred, class Alloc>
|
||||
friend class boost::unordered::unordered_node_set;
|
||||
|
||||
public:
|
||||
using value_type = typename TypePolicy::value_type;
|
||||
|
||||
constexpr node_set_handle() noexcept = default;
|
||||
node_set_handle(node_set_handle&& nh) noexcept = default;
|
||||
node_set_handle& operator=(node_set_handle&&) noexcept = default;
|
||||
|
||||
value_type& value() const
|
||||
{
|
||||
BOOST_ASSERT(!this->empty());
|
||||
return const_cast<value_type&>(this->data());
|
||||
}
|
||||
};
|
||||
} // namespace detail
|
||||
|
||||
template <class Key, class Hash, class KeyEqual, class Allocator>
|
||||
class unordered_node_set
|
||||
{
|
||||
template <class Key2, class Hash2, class Pred2, class Allocator2>
|
||||
friend class concurrent_node_set;
|
||||
|
||||
using set_types = detail::foa::node_set_types<Key,
|
||||
typename boost::allocator_void_pointer<Allocator>::type>;
|
||||
|
||||
@@ -99,7 +76,7 @@ namespace boost {
|
||||
typename boost::allocator_const_pointer<allocator_type>::type;
|
||||
using iterator = typename table_type::iterator;
|
||||
using const_iterator = typename table_type::const_iterator;
|
||||
using node_type = detail::node_set_handle<set_types,
|
||||
using node_type = detail::foa::node_set_handle<set_types,
|
||||
typename boost::allocator_rebind<Allocator,
|
||||
typename set_types::value_type>::type>;
|
||||
using insert_return_type =
|
||||
@@ -210,6 +187,12 @@ namespace boost {
|
||||
{
|
||||
}
|
||||
|
||||
unordered_node_set(
|
||||
concurrent_node_set<Key, Hash, KeyEqual, Allocator>&& other)
|
||||
: table_(std::move(other.table_))
|
||||
{
|
||||
}
|
||||
|
||||
~unordered_node_set() = default;
|
||||
|
||||
unordered_node_set& operator=(unordered_node_set const& other)
|
||||
@@ -225,6 +208,13 @@ namespace boost {
|
||||
return *this;
|
||||
}
|
||||
|
||||
unordered_node_set& operator=(std::initializer_list<value_type> il)
|
||||
{
|
||||
this->clear();
|
||||
this->insert(il.begin(), il.end());
|
||||
return *this;
|
||||
}
|
||||
|
||||
allocator_type get_allocator() const noexcept
|
||||
{
|
||||
return table_.get_allocator();
|
||||
@@ -312,15 +302,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 +321,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 +389,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;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,414 @@
|
||||
// Copyright 2024 Braden Ganetsky
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// https://www.boost.org/LICENSE_1_0.txt
|
||||
|
||||
// Generated on 2024-08-25T17:48:54
|
||||
|
||||
#ifndef BOOST_UNORDERED_UNORDERED_PRINTERS_HPP
|
||||
#define BOOST_UNORDERED_UNORDERED_PRINTERS_HPP
|
||||
|
||||
#ifndef BOOST_ALL_NO_EMBEDDED_GDB_SCRIPTS
|
||||
#if defined(__ELF__)
|
||||
#ifdef __clang__
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Woverlength-strings"
|
||||
#endif
|
||||
__asm__(".pushsection \".debug_gdb_scripts\", \"MS\",@progbits,1\n"
|
||||
".ascii \"\\4gdb.inlined-script.BOOST_UNORDERED_UNORDERED_PRINTERS_HPP\\n\"\n"
|
||||
".ascii \"import gdb.printing\\n\"\n"
|
||||
".ascii \"import gdb.xmethod\\n\"\n"
|
||||
".ascii \"import re\\n\"\n"
|
||||
".ascii \"import math\\n\"\n"
|
||||
|
||||
".ascii \"class BoostUnorderedHelpers:\\n\"\n"
|
||||
".ascii \" def maybe_unwrap_atomic(n):\\n\"\n"
|
||||
".ascii \" if f\\\"{n.type.strip_typedefs()}\\\".startswith(\\\"std::atomic<\\\"):\\n\"\n"
|
||||
".ascii \" underlying_type = n.type.template_argument(0)\\n\"\n"
|
||||
".ascii \" return n.cast(underlying_type)\\n\"\n"
|
||||
".ascii \" else:\\n\"\n"
|
||||
".ascii \" return n\\n\"\n"
|
||||
|
||||
".ascii \" def maybe_unwrap_foa_element(e):\\n\"\n"
|
||||
".ascii \" if f\\\"{e.type.strip_typedefs()}\\\".startswith(\\\"boost::unordered::detail::foa::element_type<\\\"):\\n\"\n"
|
||||
".ascii \" return e[\\\"p\\\"]\\n\"\n"
|
||||
".ascii \" else:\\n\"\n"
|
||||
".ascii \" return e\\n\"\n"
|
||||
|
||||
".ascii \" def maybe_unwrap_reference(value):\\n\"\n"
|
||||
".ascii \" if value.type.code == gdb.TYPE_CODE_REF:\\n\"\n"
|
||||
".ascii \" return value.referenced_value()\\n\"\n"
|
||||
".ascii \" else:\\n\"\n"
|
||||
".ascii \" return value\\n\"\n"
|
||||
|
||||
".ascii \" def countr_zero(n):\\n\"\n"
|
||||
".ascii \" for i in range(32):\\n\"\n"
|
||||
".ascii \" if (n & (1 << i)) != 0:\\n\"\n"
|
||||
".ascii \" return i\\n\"\n"
|
||||
".ascii \" return 32\\n\"\n"
|
||||
|
||||
".ascii \"class BoostUnorderedPointerCustomizationPoint:\\n\"\n"
|
||||
".ascii \" def __init__(self, any_ptr):\\n\"\n"
|
||||
".ascii \" vis = gdb.default_visualizer(any_ptr)\\n\"\n"
|
||||
".ascii \" if vis is None:\\n\"\n"
|
||||
".ascii \" self.to_address = lambda ptr: ptr\\n\"\n"
|
||||
".ascii \" self.next = lambda ptr, offset: ptr + offset\\n\"\n"
|
||||
".ascii \" else:\\n\"\n"
|
||||
".ascii \" self.to_address = lambda ptr: ptr if (ptr.type.code == gdb.TYPE_CODE_PTR) else type(vis).boost_to_address(ptr)\\n\"\n"
|
||||
".ascii \" self.next = lambda ptr, offset: type(vis).boost_next(ptr, offset)\\n\"\n"
|
||||
|
||||
".ascii \"class BoostUnorderedFcaPrinter:\\n\"\n"
|
||||
".ascii \" def __init__(self, val):\\n\"\n"
|
||||
".ascii \" self.val = BoostUnorderedHelpers.maybe_unwrap_reference(val)\\n\"\n"
|
||||
".ascii \" self.name = f\\\"{self.val.type.strip_typedefs()}\\\".split(\\\"<\\\")[0]\\n\"\n"
|
||||
".ascii \" self.name = self.name.replace(\\\"boost::unordered::\\\", \\\"boost::\\\")\\n\"\n"
|
||||
".ascii \" self.is_map = self.name.endswith(\\\"map\\\")\\n\"\n"
|
||||
".ascii \" self.cpo = BoostUnorderedPointerCustomizationPoint(self.val[\\\"table_\\\"][\\\"buckets_\\\"][\\\"buckets\\\"])\\n\"\n"
|
||||
|
||||
".ascii \" def to_string(self):\\n\"\n"
|
||||
".ascii \" size = self.val[\\\"table_\\\"][\\\"size_\\\"]\\n\"\n"
|
||||
".ascii \" return f\\\"{self.name} with {size} elements\\\"\\n\"\n"
|
||||
|
||||
".ascii \" def display_hint(self):\\n\"\n"
|
||||
".ascii \" return \\\"map\\\"\\n\"\n"
|
||||
|
||||
".ascii \" def children(self):\\n\"\n"
|
||||
".ascii \" def generator():\\n\"\n"
|
||||
".ascii \" grouped_buckets = self.val[\\\"table_\\\"][\\\"buckets_\\\"]\\n\"\n"
|
||||
|
||||
".ascii \" size = grouped_buckets[\\\"size_\\\"]\\n\"\n"
|
||||
".ascii \" buckets = grouped_buckets[\\\"buckets\\\"]\\n\"\n"
|
||||
".ascii \" bucket_index = 0\\n\"\n"
|
||||
|
||||
".ascii \" count = 0\\n\"\n"
|
||||
".ascii \" while bucket_index != size:\\n\"\n"
|
||||
".ascii \" current_bucket = self.cpo.next(self.cpo.to_address(buckets), bucket_index)\\n\"\n"
|
||||
".ascii \" node = self.cpo.to_address(current_bucket.dereference()[\\\"next\\\"])\\n\"\n"
|
||||
".ascii \" while node != 0:\\n\"\n"
|
||||
".ascii \" value = node.dereference()[\\\"buf\\\"][\\\"t_\\\"]\\n\"\n"
|
||||
".ascii \" if self.is_map:\\n\"\n"
|
||||
".ascii \" first = value[\\\"first\\\"]\\n\"\n"
|
||||
".ascii \" second = value[\\\"second\\\"]\\n\"\n"
|
||||
".ascii \" yield \\\"\\\", first\\n\"\n"
|
||||
".ascii \" yield \\\"\\\", second\\n\"\n"
|
||||
".ascii \" else:\\n\"\n"
|
||||
".ascii \" yield \\\"\\\", count\\n\"\n"
|
||||
".ascii \" yield \\\"\\\", value\\n\"\n"
|
||||
".ascii \" count += 1\\n\"\n"
|
||||
".ascii \" node = self.cpo.to_address(node.dereference()[\\\"next\\\"])\\n\"\n"
|
||||
".ascii \" bucket_index += 1\\n\"\n"
|
||||
|
||||
".ascii \" return generator()\\n\"\n"
|
||||
|
||||
".ascii \"class BoostUnorderedFcaIteratorPrinter:\\n\"\n"
|
||||
".ascii \" def __init__(self, val):\\n\"\n"
|
||||
".ascii \" self.val = val\\n\"\n"
|
||||
".ascii \" self.cpo = BoostUnorderedPointerCustomizationPoint(self.val[\\\"p\\\"])\\n\"\n"
|
||||
|
||||
".ascii \" def to_string(self):\\n\"\n"
|
||||
".ascii \" if self.valid():\\n\"\n"
|
||||
".ascii \" value = self.cpo.to_address(self.val[\\\"p\\\"]).dereference()[\\\"buf\\\"][\\\"t_\\\"]\\n\"\n"
|
||||
".ascii \" return f\\\"iterator = {{ {value} }}\\\"\\n\"\n"
|
||||
".ascii \" else:\\n\"\n"
|
||||
".ascii \" return \\\"iterator = { end iterator }\\\"\\n\"\n"
|
||||
|
||||
".ascii \" def valid(self):\\n\"\n"
|
||||
".ascii \" return (self.cpo.to_address(self.val[\\\"p\\\"]) != 0) and (self.cpo.to_address(self.val[\\\"itb\\\"][\\\"p\\\"]) != 0)\\n\"\n"
|
||||
|
||||
".ascii \"class BoostUnorderedFoaTableCoreCumulativeStatsPrinter:\\n\"\n"
|
||||
".ascii \" def __init__(self, val):\\n\"\n"
|
||||
".ascii \" self.val = val\\n\"\n"
|
||||
|
||||
".ascii \" def to_string(self):\\n\"\n"
|
||||
".ascii \" return \\\"[stats]\\\"\\n\"\n"
|
||||
|
||||
".ascii \" def display_hint(self):\\n\"\n"
|
||||
".ascii \" return \\\"map\\\"\\n\"\n"
|
||||
|
||||
".ascii \" def children(self):\\n\"\n"
|
||||
".ascii \" def generator():\\n\"\n"
|
||||
".ascii \" members = [\\\"insertion\\\", \\\"successful_lookup\\\", \\\"unsuccessful_lookup\\\"]\\n\"\n"
|
||||
".ascii \" for member in members:\\n\"\n"
|
||||
".ascii \" yield \\\"\\\", member\\n\"\n"
|
||||
".ascii \" yield \\\"\\\", self.val[member]\\n\"\n"
|
||||
".ascii \" return generator()\\n\"\n"
|
||||
|
||||
".ascii \"class BoostUnorderedFoaCumulativeStatsPrinter:\\n\"\n"
|
||||
".ascii \" def __init__(self, val):\\n\"\n"
|
||||
".ascii \" self.val = val\\n\"\n"
|
||||
".ascii \" self.n = self.val[\\\"n\\\"]\\n\"\n"
|
||||
".ascii \" self.N = self.val.type.template_argument(0)\\n\"\n"
|
||||
|
||||
".ascii \" def display_hint(self):\\n\"\n"
|
||||
".ascii \" return \\\"map\\\"\\n\"\n"
|
||||
|
||||
".ascii \" def children(self):\\n\"\n"
|
||||
".ascii \" def generator():\\n\"\n"
|
||||
".ascii \" yield \\\"\\\", \\\"count\\\"\\n\"\n"
|
||||
".ascii \" yield \\\"\\\", self.n\\n\"\n"
|
||||
|
||||
".ascii \" sequence_stats_data = gdb.lookup_type(\\\"boost::unordered::detail::foa::sequence_stats_data\\\")\\n\"\n"
|
||||
".ascii \" data = self.val[\\\"data\\\"]\\n\"\n"
|
||||
".ascii \" arr = data.address.reinterpret_cast(sequence_stats_data.pointer())\\n\"\n"
|
||||
".ascii \" def build_string(idx):\\n\"\n"
|
||||
".ascii \" entry = arr[idx]\\n\"\n"
|
||||
".ascii \" avg = float(entry[\\\"m\\\"])\\n\"\n"
|
||||
".ascii \" var = float(entry[\\\"s\\\"] / self.n) if (self.n != 0) else 0.0\\n\"\n"
|
||||
".ascii \" dev = math.sqrt(var)\\n\"\n"
|
||||
".ascii \" return f\\\"{{avg = {avg}, var = {var}, dev = {dev}}}\\\"\\n\"\n"
|
||||
|
||||
".ascii \" if self.N > 0:\\n\"\n"
|
||||
".ascii \" yield \\\"\\\", \\\"probe_length\\\"\\n\"\n"
|
||||
".ascii \" yield \\\"\\\", build_string(0)\\n\"\n"
|
||||
".ascii \" if self.N > 1:\\n\"\n"
|
||||
".ascii \" yield \\\"\\\", \\\"num_comparisons\\\"\\n\"\n"
|
||||
".ascii \" yield \\\"\\\", build_string(1)\\n\"\n"
|
||||
|
||||
".ascii \" return generator()\\n\"\n"
|
||||
|
||||
".ascii \"class BoostUnorderedFoaPrinter:\\n\"\n"
|
||||
".ascii \" def __init__(self, val):\\n\"\n"
|
||||
".ascii \" self.val = BoostUnorderedHelpers.maybe_unwrap_reference(val)\\n\"\n"
|
||||
".ascii \" self.name = f\\\"{self.val.type.strip_typedefs()}\\\".split(\\\"<\\\")[0]\\n\"\n"
|
||||
".ascii \" self.name = self.name.replace(\\\"boost::unordered::\\\", \\\"boost::\\\")\\n\"\n"
|
||||
".ascii \" self.is_map = self.name.endswith(\\\"map\\\")\\n\"\n"
|
||||
".ascii \" self.cpo = BoostUnorderedPointerCustomizationPoint(self.val[\\\"table_\\\"][\\\"arrays\\\"][\\\"groups_\\\"])\\n\"\n"
|
||||
|
||||
".ascii \" def to_string(self):\\n\"\n"
|
||||
".ascii \" size = BoostUnorderedHelpers.maybe_unwrap_atomic(self.val[\\\"table_\\\"][\\\"size_ctrl\\\"][\\\"size\\\"])\\n\"\n"
|
||||
".ascii \" return f\\\"{self.name} with {size} elements\\\"\\n\"\n"
|
||||
|
||||
".ascii \" def display_hint(self):\\n\"\n"
|
||||
".ascii \" return \\\"map\\\"\\n\"\n"
|
||||
|
||||
".ascii \" def is_regular_layout(self, group):\\n\"\n"
|
||||
".ascii \" typename = group[\\\"m\\\"].type.strip_typedefs()\\n\"\n"
|
||||
".ascii \" array_size = typename.sizeof // typename.target().sizeof\\n\"\n"
|
||||
".ascii \" if array_size == 16:\\n\"\n"
|
||||
".ascii \" return True\\n\"\n"
|
||||
".ascii \" elif array_size == 2:\\n\"\n"
|
||||
".ascii \" return False\\n\"\n"
|
||||
|
||||
".ascii \" def match_occupied(self, group):\\n\"\n"
|
||||
".ascii \" m = group[\\\"m\\\"]\\n\"\n"
|
||||
".ascii \" at = lambda b: BoostUnorderedHelpers.maybe_unwrap_atomic(m[b][\\\"n\\\"])\\n\"\n"
|
||||
|
||||
".ascii \" if self.is_regular_layout(group):\\n\"\n"
|
||||
".ascii \" bits = [1 << b for b in range(16) if at(b) == 0]\\n\"\n"
|
||||
".ascii \" return 0x7FFF & ~sum(bits)\\n\"\n"
|
||||
".ascii \" else:\\n\"\n"
|
||||
".ascii \" xx = at(0) | at(1)\\n\"\n"
|
||||
".ascii \" yy = xx | (xx >> 32)\\n\"\n"
|
||||
".ascii \" return 0x7FFF & (yy | (yy >> 16))\\n\"\n"
|
||||
|
||||
".ascii \" def is_sentinel(self, group, pos):\\n\"\n"
|
||||
".ascii \" m = group[\\\"m\\\"]\\n\"\n"
|
||||
".ascii \" at = lambda b: BoostUnorderedHelpers.maybe_unwrap_atomic(m[b][\\\"n\\\"])\\n\"\n"
|
||||
|
||||
".ascii \" N = group[\\\"N\\\"]\\n\"\n"
|
||||
".ascii \" sentinel_ = group[\\\"sentinel_\\\"]\\n\"\n"
|
||||
".ascii \" if self.is_regular_layout(group):\\n\"\n"
|
||||
".ascii \" return pos == N-1 and at(N-1) == sentinel_\\n\"\n"
|
||||
".ascii \" else:\\n\"\n"
|
||||
".ascii \" return pos == N-1 and (at(0) & 0x4000400040004000) == 0x4000 and (at(1) & 0x4000400040004000) == 0\\n\"\n"
|
||||
|
||||
".ascii \" def children(self):\\n\"\n"
|
||||
".ascii \" def generator():\\n\"\n"
|
||||
".ascii \" table = self.val[\\\"table_\\\"]\\n\"\n"
|
||||
".ascii \" groups = self.cpo.to_address(table[\\\"arrays\\\"][\\\"groups_\\\"])\\n\"\n"
|
||||
".ascii \" elements = self.cpo.to_address(table[\\\"arrays\\\"][\\\"elements_\\\"])\\n\"\n"
|
||||
|
||||
".ascii \" pc_ = groups.cast(gdb.lookup_type(\\\"unsigned char\\\").pointer())\\n\"\n"
|
||||
".ascii \" p_ = elements\\n\"\n"
|
||||
".ascii \" first_time = True\\n\"\n"
|
||||
".ascii \" mask = 0\\n\"\n"
|
||||
".ascii \" n0 = 0\\n\"\n"
|
||||
".ascii \" n = 0\\n\"\n"
|
||||
|
||||
".ascii \" count = 0\\n\"\n"
|
||||
".ascii \" while p_ != 0:\\n\"\n"
|
||||
".ascii \" # This if block mirrors the condition in the begin() call\\n\"\n"
|
||||
".ascii \" if (not first_time) or (self.match_occupied(groups.dereference()) & 1):\\n\"\n"
|
||||
".ascii \" pointer = BoostUnorderedHelpers.maybe_unwrap_foa_element(p_)\\n\"\n"
|
||||
".ascii \" value = self.cpo.to_address(pointer).dereference()\\n\"\n"
|
||||
".ascii \" if self.is_map:\\n\"\n"
|
||||
".ascii \" first = value[\\\"first\\\"]\\n\"\n"
|
||||
".ascii \" second = value[\\\"second\\\"]\\n\"\n"
|
||||
".ascii \" yield \\\"\\\", first\\n\"\n"
|
||||
".ascii \" yield \\\"\\\", second\\n\"\n"
|
||||
".ascii \" else:\\n\"\n"
|
||||
".ascii \" yield \\\"\\\", count\\n\"\n"
|
||||
".ascii \" yield \\\"\\\", value\\n\"\n"
|
||||
".ascii \" count += 1\\n\"\n"
|
||||
".ascii \" first_time = False\\n\"\n"
|
||||
|
||||
".ascii \" n0 = pc_.cast(gdb.lookup_type(\\\"uintptr_t\\\")) % groups.dereference().type.sizeof\\n\"\n"
|
||||
".ascii \" pc_ = self.cpo.next(pc_, -n0)\\n\"\n"
|
||||
|
||||
".ascii \" mask = (self.match_occupied(pc_.cast(groups.type).dereference()) >> (n0+1)) << (n0+1)\\n\"\n"
|
||||
".ascii \" while mask == 0:\\n\"\n"
|
||||
".ascii \" pc_ = self.cpo.next(pc_, groups.dereference().type.sizeof)\\n\"\n"
|
||||
".ascii \" p_ = self.cpo.next(p_, groups.dereference()[\\\"N\\\"])\\n\"\n"
|
||||
".ascii \" mask = self.match_occupied(pc_.cast(groups.type).dereference())\\n\"\n"
|
||||
|
||||
".ascii \" n = BoostUnorderedHelpers.countr_zero(mask)\\n\"\n"
|
||||
".ascii \" if self.is_sentinel(pc_.cast(groups.type).dereference(), n):\\n\"\n"
|
||||
".ascii \" p_ = 0\\n\"\n"
|
||||
".ascii \" else:\\n\"\n"
|
||||
".ascii \" pc_ = self.cpo.next(pc_, n)\\n\"\n"
|
||||
".ascii \" p_ = self.cpo.next(p_, n - n0)\\n\"\n"
|
||||
|
||||
".ascii \" return generator()\\n\"\n"
|
||||
|
||||
".ascii \"class BoostUnorderedFoaIteratorPrinter:\\n\"\n"
|
||||
".ascii \" def __init__(self, val):\\n\"\n"
|
||||
".ascii \" self.val = val\\n\"\n"
|
||||
".ascii \" self.cpo = BoostUnorderedPointerCustomizationPoint(self.val[\\\"p_\\\"])\\n\"\n"
|
||||
|
||||
".ascii \" def to_string(self):\\n\"\n"
|
||||
".ascii \" if self.valid():\\n\"\n"
|
||||
".ascii \" element = self.cpo.to_address(self.val[\\\"p_\\\"])\\n\"\n"
|
||||
".ascii \" pointer = BoostUnorderedHelpers.maybe_unwrap_foa_element(element)\\n\"\n"
|
||||
".ascii \" value = self.cpo.to_address(pointer).dereference()\\n\"\n"
|
||||
".ascii \" return f\\\"iterator = {{ {value} }}\\\"\\n\"\n"
|
||||
".ascii \" else:\\n\"\n"
|
||||
".ascii \" return \\\"iterator = { end iterator }\\\"\\n\"\n"
|
||||
|
||||
".ascii \" def valid(self):\\n\"\n"
|
||||
".ascii \" return (self.cpo.to_address(self.val[\\\"p_\\\"]) != 0) and (self.cpo.to_address(self.val[\\\"pc_\\\"]) != 0)\\n\"\n"
|
||||
|
||||
".ascii \"def boost_unordered_build_pretty_printer():\\n\"\n"
|
||||
".ascii \" pp = gdb.printing.RegexpCollectionPrettyPrinter(\\\"boost_unordered\\\")\\n\"\n"
|
||||
".ascii \" add_template_printer = lambda name, printer: pp.add_printer(name, f\\\"^{name}<.*>$\\\", printer)\\n\"\n"
|
||||
".ascii \" add_concrete_printer = lambda name, printer: pp.add_printer(name, f\\\"^{name}$\\\", printer)\\n\"\n"
|
||||
|
||||
".ascii \" add_template_printer(\\\"boost::unordered::unordered_map\\\", BoostUnorderedFcaPrinter)\\n\"\n"
|
||||
".ascii \" add_template_printer(\\\"boost::unordered::unordered_multimap\\\", BoostUnorderedFcaPrinter)\\n\"\n"
|
||||
".ascii \" add_template_printer(\\\"boost::unordered::unordered_set\\\", BoostUnorderedFcaPrinter)\\n\"\n"
|
||||
".ascii \" add_template_printer(\\\"boost::unordered::unordered_multiset\\\", BoostUnorderedFcaPrinter)\\n\"\n"
|
||||
|
||||
".ascii \" add_template_printer(\\\"boost::unordered::detail::iterator_detail::iterator\\\", BoostUnorderedFcaIteratorPrinter)\\n\"\n"
|
||||
".ascii \" add_template_printer(\\\"boost::unordered::detail::iterator_detail::c_iterator\\\", BoostUnorderedFcaIteratorPrinter)\\n\"\n"
|
||||
|
||||
".ascii \" add_template_printer(\\\"boost::unordered::unordered_flat_map\\\", BoostUnorderedFoaPrinter)\\n\"\n"
|
||||
".ascii \" add_template_printer(\\\"boost::unordered::unordered_flat_set\\\", BoostUnorderedFoaPrinter)\\n\"\n"
|
||||
".ascii \" add_template_printer(\\\"boost::unordered::unordered_node_map\\\", BoostUnorderedFoaPrinter)\\n\"\n"
|
||||
".ascii \" add_template_printer(\\\"boost::unordered::unordered_node_set\\\", BoostUnorderedFoaPrinter)\\n\"\n"
|
||||
".ascii \" add_template_printer(\\\"boost::unordered::concurrent_flat_map\\\", BoostUnorderedFoaPrinter)\\n\"\n"
|
||||
".ascii \" add_template_printer(\\\"boost::unordered::concurrent_flat_set\\\", BoostUnorderedFoaPrinter)\\n\"\n"
|
||||
".ascii \" add_template_printer(\\\"boost::unordered::concurrent_node_map\\\", BoostUnorderedFoaPrinter)\\n\"\n"
|
||||
".ascii \" add_template_printer(\\\"boost::unordered::concurrent_node_set\\\", BoostUnorderedFoaPrinter)\\n\"\n"
|
||||
|
||||
".ascii \" add_template_printer(\\\"boost::unordered::detail::foa::table_iterator\\\", BoostUnorderedFoaIteratorPrinter)\\n\"\n"
|
||||
|
||||
".ascii \" add_concrete_printer(\\\"boost::unordered::detail::foa::table_core_cumulative_stats\\\", BoostUnorderedFoaTableCoreCumulativeStatsPrinter)\\n\"\n"
|
||||
".ascii \" add_template_printer(\\\"boost::unordered::detail::foa::cumulative_stats\\\", BoostUnorderedFoaCumulativeStatsPrinter)\\n\"\n"
|
||||
".ascii \" add_template_printer(\\\"boost::unordered::detail::foa::concurrent_cumulative_stats\\\", BoostUnorderedFoaCumulativeStatsPrinter)\\n\"\n"
|
||||
|
||||
".ascii \" return pp\\n\"\n"
|
||||
|
||||
".ascii \"gdb.printing.register_pretty_printer(gdb.current_objfile(), boost_unordered_build_pretty_printer())\\n\"\n"
|
||||
|
||||
|
||||
|
||||
".ascii \"# https://sourceware.org/gdb/current/onlinedocs/gdb.html/Writing-an-Xmethod.html\\n\"\n"
|
||||
".ascii \"class BoostUnorderedFoaGetStatsMethod(gdb.xmethod.XMethod):\\n\"\n"
|
||||
".ascii \" def __init__(self):\\n\"\n"
|
||||
".ascii \" gdb.xmethod.XMethod.__init__(self, \\\"get_stats\\\")\\n\"\n"
|
||||
|
||||
".ascii \" def get_worker(self, method_name):\\n\"\n"
|
||||
".ascii \" if method_name == \\\"get_stats\\\":\\n\"\n"
|
||||
".ascii \" return BoostUnorderedFoaGetStatsWorker()\\n\"\n"
|
||||
|
||||
".ascii \"class BoostUnorderedFoaGetStatsWorker(gdb.xmethod.XMethodWorker):\\n\"\n"
|
||||
".ascii \" def get_arg_types(self):\\n\"\n"
|
||||
".ascii \" return None\\n\"\n"
|
||||
|
||||
".ascii \" def get_result_type(self, obj):\\n\"\n"
|
||||
".ascii \" return gdb.lookup_type(\\\"boost::unordered::detail::foa::table_core_cumulative_stats\\\")\\n\"\n"
|
||||
|
||||
".ascii \" def __call__(self, obj):\\n\"\n"
|
||||
".ascii \" try:\\n\"\n"
|
||||
".ascii \" return obj[\\\"table_\\\"][\\\"cstats\\\"]\\n\"\n"
|
||||
".ascii \" except gdb.error:\\n\"\n"
|
||||
".ascii \" print(\\\"Error: Binary was compiled without stats. Recompile with `BOOST_UNORDERED_ENABLE_STATS` defined.\\\")\\n\"\n"
|
||||
".ascii \" return\\n\"\n"
|
||||
|
||||
".ascii \"class BoostUnorderedFoaMatcher(gdb.xmethod.XMethodMatcher):\\n\"\n"
|
||||
".ascii \" def __init__(self):\\n\"\n"
|
||||
".ascii \" gdb.xmethod.XMethodMatcher.__init__(self, 'BoostUnorderedFoaMatcher')\\n\"\n"
|
||||
".ascii \" self.methods = [BoostUnorderedFoaGetStatsMethod()]\\n\"\n"
|
||||
|
||||
".ascii \" def match(self, class_type, method_name):\\n\"\n"
|
||||
".ascii \" template_name = f\\\"{class_type.strip_typedefs()}\\\".split(\\\"<\\\")[0]\\n\"\n"
|
||||
".ascii \" regex = \\\"^boost::unordered::(unordered|concurrent)_(flat|node)_(map|set)$\\\"\\n\"\n"
|
||||
".ascii \" if not re.match(regex, template_name):\\n\"\n"
|
||||
".ascii \" return None\\n\"\n"
|
||||
|
||||
".ascii \" workers = []\\n\"\n"
|
||||
".ascii \" for method in self.methods:\\n\"\n"
|
||||
".ascii \" if method.enabled:\\n\"\n"
|
||||
".ascii \" worker = method.get_worker(method_name)\\n\"\n"
|
||||
".ascii \" if worker:\\n\"\n"
|
||||
".ascii \" workers.append(worker)\\n\"\n"
|
||||
".ascii \" return workers\\n\"\n"
|
||||
|
||||
".ascii \"gdb.xmethod.register_xmethod_matcher(None, BoostUnorderedFoaMatcher())\\n\"\n"
|
||||
|
||||
|
||||
|
||||
".ascii \"\\\"\\\"\\\" Fancy pointer support \\\"\\\"\\\"\\n\"\n"
|
||||
|
||||
".ascii \"\\\"\\\"\\\"\\n\"\n"
|
||||
".ascii \"To allow your own fancy pointer type to interact with Boost.Unordered GDB pretty-printers,\\n\"\n"
|
||||
".ascii \"create a pretty-printer for your own type with the following additional methods.\\n\"\n"
|
||||
|
||||
".ascii \"(Note, this is assuming the presence of a type alias `pointer` for the underlying\\n\"\n"
|
||||
".ascii \"raw pointer type, Substitute whichever name is applicable in your case.)\\n\"\n"
|
||||
|
||||
".ascii \"`boost_to_address(fancy_ptr)`\\n\"\n"
|
||||
".ascii \" * A static method, but `@staticmethod` is not required\\n\"\n"
|
||||
".ascii \" * Parameter `fancy_ptr` of type `gdb.Value`\\n\"\n"
|
||||
".ascii \" * Its `.type` will be your fancy pointer type\\n\"\n"
|
||||
".ascii \" * Returns a `gdb.Value` with the raw pointer equivalent to your fancy pointer\\n\"\n"
|
||||
".ascii \" * This method should be equivalent to calling `operator->()` on your fancy pointer in C++\\n\"\n"
|
||||
|
||||
".ascii \"`boost_next(raw_ptr, offset)`\\n\"\n"
|
||||
".ascii \" * Parameter `raw_ptr` of type `gdb.Value`\\n\"\n"
|
||||
".ascii \" * Its `.type` will be `pointer`\\n\"\n"
|
||||
".ascii \" * Parameter `offset`\\n\"\n"
|
||||
".ascii \" * Either has integer type, or is of type `gdb.Value` with an underlying integer\\n\"\n"
|
||||
".ascii \" * Returns a `gdb.Value` with the raw pointer equivalent to your fancy pointer, as if you did the following operations\\n\"\n"
|
||||
".ascii \" 1. Convert the incoming raw pointer to your fancy pointer\\n\"\n"
|
||||
".ascii \" 2. Use operator+= to add the offset to the fancy pointer\\n\"\n"
|
||||
".ascii \" 3. Convert back to the raw pointer\\n\"\n"
|
||||
".ascii \" * Note, you will not actually do these operations as stated. You will do equivalent lower-level operations that emulate having done the above\\n\"\n"
|
||||
".ascii \" * Ultimately, it will be as if you called `operator+()` on your fancy pointer in C++, but using only raw pointers\\n\"\n"
|
||||
|
||||
".ascii \"Example\\n\"\n"
|
||||
".ascii \"```\\n\"\n"
|
||||
".ascii \"class MyFancyPtrPrinter:\\n\"\n"
|
||||
".ascii \" ...\\n\"\n"
|
||||
|
||||
".ascii \" # Equivalent to `operator->()`\\n\"\n"
|
||||
".ascii \" def boost_to_address(fancy_ptr):\\n\"\n"
|
||||
".ascii \" ...\\n\"\n"
|
||||
".ascii \" return ...\\n\"\n"
|
||||
|
||||
".ascii \" # Equivalent to `operator+()`\\n\"\n"
|
||||
".ascii \" def boost_next(raw_ptr, offset):\\n\"\n"
|
||||
".ascii \" ...\\n\"\n"
|
||||
".ascii \" return ...\\n\"\n"
|
||||
|
||||
".ascii \" ...\\n\"\n"
|
||||
".ascii \"```\\n\"\n"
|
||||
".ascii \"\\\"\\\"\\\"\\n\"\n"
|
||||
|
||||
".byte 0\n"
|
||||
".popsection\n");
|
||||
#ifdef __clang__
|
||||
#pragma clang diagnostic pop
|
||||
#endif
|
||||
#endif // defined(__ELF__)
|
||||
#endif // !defined(BOOST_ALL_NO_EMBEDDED_GDB_SCRIPTS)
|
||||
|
||||
#endif // !defined(BOOST_UNORDERED_UNORDERED_PRINTERS_HPP)
|
||||
@@ -194,7 +194,7 @@ namespace boost {
|
||||
template <class... Args> std::pair<iterator, bool> emplace(Args&&... args)
|
||||
{
|
||||
return table_.emplace_unique(
|
||||
table::extractor::extract(std::forward<Args>(args)...),
|
||||
table::extractor::extract(detail::as_const(args)...),
|
||||
std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
@@ -202,7 +202,7 @@ namespace boost {
|
||||
iterator emplace_hint(const_iterator hint, Args&&... args)
|
||||
{
|
||||
return table_.emplace_hint_unique(hint,
|
||||
table::extractor::extract(std::forward<Args>(args)...),
|
||||
table::extractor::extract(detail::as_const(args)...),
|
||||
std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -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)
|
||||
|
||||
+56
-8
@@ -8,7 +8,7 @@
|
||||
import path ;
|
||||
import regex ;
|
||||
import testing ;
|
||||
import ../../config/checks/config : requires ;
|
||||
import config : requires ;
|
||||
|
||||
path-constant TOP : . ;
|
||||
|
||||
@@ -21,6 +21,9 @@ local msvc-flags = /wd4494 ;
|
||||
|
||||
project
|
||||
: requirements
|
||||
<library>/boost/concept_check//boost_concept_check
|
||||
<library>/boost/compat//boost_compat
|
||||
<library>/boost/iterator//boost_iterator
|
||||
<warnings>pedantic
|
||||
<toolset>intel:<warnings>on
|
||||
|
||||
@@ -42,6 +45,10 @@ path-constant BOOST_UNORDERED_TEST_DIR : . ;
|
||||
|
||||
run quick.cpp ;
|
||||
|
||||
compile debuggability/visualization_tests.cpp
|
||||
: <target-os>cygwin:<define>_XOPEN_SOURCE=600
|
||||
;
|
||||
|
||||
compile unordered/self_include_tests_obj.cpp
|
||||
: <define>BOOST_UNORDERED_HEADER="boost/unordered_map.hpp"
|
||||
: tl_unordered_map_hpp ;
|
||||
@@ -108,6 +115,7 @@ local FCA_TESTS =
|
||||
move_tests
|
||||
narrow_cast_tests
|
||||
node_handle_tests
|
||||
node_handle_allocator_tests
|
||||
noexcept_tests
|
||||
post_move_tests
|
||||
prime_fmod_tests
|
||||
@@ -148,7 +156,7 @@ run unordered/serialization_tests.cpp
|
||||
<toolset>gcc:<optimization>space
|
||||
<toolset>clang:<inlining>on
|
||||
<toolset>clang:<optimization>space
|
||||
<library>/boost//serialization/<warnings>off ;
|
||||
<library>/boost/serialization//boost_serialization/<warnings>off ;
|
||||
|
||||
compile-fail unordered/insert_node_type_fail.cpp : <define>UNORDERED_TEST_MAP : insert_node_type_fail_map ;
|
||||
compile-fail unordered/insert_node_type_fail.cpp : <define>UNORDERED_TEST_MULTIMAP : insert_node_type_fail_multimap ;
|
||||
@@ -230,6 +238,7 @@ local FOA_TESTS =
|
||||
fancy_pointer_noleak
|
||||
pmr_allocator_tests
|
||||
stats_tests
|
||||
node_handle_allocator_tests
|
||||
;
|
||||
|
||||
for local test in $(FOA_TESTS)
|
||||
@@ -251,7 +260,7 @@ run unordered/serialization_tests.cpp
|
||||
<toolset>gcc:<optimization>space
|
||||
<toolset>clang:<inlining>on
|
||||
<toolset>clang:<optimization>space
|
||||
<library>/boost//serialization/<warnings>off
|
||||
<library>/boost/serialization//boost_serialization/<warnings>off
|
||||
: foa_serialization_tests ;
|
||||
|
||||
local FOA_EXCEPTION_TESTS =
|
||||
@@ -286,11 +295,19 @@ local MMAP_CONTAINERS =
|
||||
|
||||
for local container in $(MMAP_CONTAINERS)
|
||||
{
|
||||
run unordered/mmap_tests.cpp /boost/filesystem//boost_filesystem : :
|
||||
run unordered/mmap_tests.cpp
|
||||
/boost/filesystem//boost_filesystem
|
||||
/boost/interprocess//boost_interprocess
|
||||
/boost/process//boost_process
|
||||
/boost/uuid//boost_uuid
|
||||
: :
|
||||
: <define>BOOST_UNORDERED_FOA_MMAP_MAP_TYPE="boost::$(container)"
|
||||
<warnings>off
|
||||
<link>static
|
||||
<toolset>gcc-4.8:<build>no # Boost.Process does not compile
|
||||
<toolset>gcc-4.9:<build>no # ditto
|
||||
<target-os>cygwin:<build>no
|
||||
<toolset>gcc,<target-os>windows:<build>no # Process on MinGW on Appveyor doesn't compile
|
||||
: foa_mmap_$(container)_tests ;
|
||||
}
|
||||
|
||||
@@ -306,11 +323,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
|
||||
@@ -336,6 +352,7 @@ local CFOA_TESTS =
|
||||
explicit_alloc_ctor_tests
|
||||
pmr_allocator_tests
|
||||
stats_tests
|
||||
node_handle_allocator_tests
|
||||
;
|
||||
|
||||
for local test in $(CFOA_TESTS)
|
||||
@@ -346,6 +363,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
|
||||
:
|
||||
:
|
||||
@@ -357,12 +396,12 @@ run cfoa/serialization_tests.cpp
|
||||
<toolset>gcc:<optimization>space
|
||||
<toolset>clang:<inlining>on
|
||||
<toolset>clang:<optimization>space
|
||||
<library>/boost//serialization/<warnings>off
|
||||
<library>/boost/serialization//boost_serialization/<warnings>off
|
||||
: cfoa_serialization_tests ;
|
||||
|
||||
rule make_cfoa_interprocess_concurrency_tests ( name : defines ? )
|
||||
{
|
||||
run cfoa/interprocess_concurrency_tests.cpp /boost/filesystem//boost_filesystem : :
|
||||
run cfoa/interprocess_concurrency_tests.cpp : :
|
||||
: <define>$(defines)
|
||||
<warnings>off
|
||||
<link>static
|
||||
@@ -370,7 +409,14 @@ rule make_cfoa_interprocess_concurrency_tests ( name : defines ? )
|
||||
<toolset>clang-3.6:<build>no # idem
|
||||
<toolset>clang-3.7:<build>no # idem
|
||||
<toolset>clang-3.8:<build>no # idem
|
||||
<toolset>gcc-4.8:<build>no # idem
|
||||
<toolset>gcc-4.9:<build>no # idem
|
||||
<target-os>cygwin:<build>no
|
||||
<toolset>gcc,<target-os>windows:<build>no # Process on MinGW on Appveyor doesn't compile
|
||||
<library>/boost/filesystem//boost_filesystem
|
||||
<library>/boost/interprocess//boost_interprocess
|
||||
<library>/boost/process//boost_process
|
||||
<library>/boost/uuid//boost_uuid
|
||||
: $(name) ;
|
||||
}
|
||||
|
||||
@@ -381,6 +427,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 ;
|
||||
|
||||
+87
-28
@@ -1,12 +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)
|
||||
|
||||
#include "helpers.hpp"
|
||||
|
||||
#include "../helpers/replace_allocator.hpp"
|
||||
#include "../objects/non_default_ctble_allocator.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 <vector>
|
||||
|
||||
#if defined(__clang__) && defined(__has_warning)
|
||||
|
||||
@@ -37,19 +44,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 +125,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>
|
||||
@@ -862,6 +887,28 @@ namespace {
|
||||
check_raii_counts();
|
||||
}
|
||||
|
||||
template <class X, class GF>
|
||||
void initializer_list_assign_gh276(
|
||||
X*, GF gen_factory, test::random_generator rg)
|
||||
{
|
||||
// https://github.com/boostorg/unordered/issues/276
|
||||
|
||||
using replaced_allocator_container = test::replace_allocator<
|
||||
X, test::non_default_ctble_allocator<int> >;
|
||||
using replaced_allocator_type =
|
||||
typename replaced_allocator_container::allocator_type;
|
||||
|
||||
auto gen = gen_factory.template get<X>();
|
||||
auto values = make_random_values(4, [&] { return gen(rg); });
|
||||
|
||||
replaced_allocator_container
|
||||
x(replaced_allocator_type(0)),
|
||||
y(values.begin(), values.end(), replaced_allocator_type(0));
|
||||
|
||||
x = {values[0], values[1], values[2], values[3]};
|
||||
BOOST_TEST(x == y);
|
||||
}
|
||||
|
||||
template <class X, class GF>
|
||||
void insert_and_assign(X*, GF gen_factory, test::random_generator rg)
|
||||
{
|
||||
@@ -928,7 +975,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 +997,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 +1031,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 +1057,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 +1093,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 +1124,37 @@ 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(
|
||||
initializer_list_assign_gh276,
|
||||
((test_map)(test_node_map)(test_set)(test_node_set))
|
||||
((value_type_generator_factory))
|
||||
((default_generator)))
|
||||
|
||||
UNORDERED_TEST(
|
||||
insert_and_assign,
|
||||
((test_map)(test_set))
|
||||
((test_map)(test_node_map)(test_set)(test_node_set))
|
||||
((init_type_generator_factory))
|
||||
((default_generator)(sequential)(limited_range)))
|
||||
|
||||
UNORDERED_TEST(
|
||||
flat_move_assign,
|
||||
((test_map)(test_set)(fancy_test_map)(fancy_test_set))
|
||||
nonconcurrent_move_assign,
|
||||
((test_map)(test_node_map)(test_set)(test_node_set)
|
||||
(fancy_test_map)(fancy_test_node_map)(fancy_test_set)(fancy_test_node_set))
|
||||
((init_type_generator_factory))
|
||||
((default_generator)(sequential)(limited_range)))
|
||||
// clang-format on
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// Copyright (C) 2023 Christian Mazakas
|
||||
// Copyright (C) 2023 Joaquin M Lopez Munoz
|
||||
// Copyright (C) 2023-2024 Joaquin M Lopez Munoz
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
@@ -7,6 +7,8 @@
|
||||
|
||||
#include <boost/unordered/concurrent_flat_map.hpp>
|
||||
#include <boost/unordered/concurrent_flat_set.hpp>
|
||||
#include <boost/unordered/concurrent_node_map.hpp>
|
||||
#include <boost/unordered/concurrent_node_set.hpp>
|
||||
|
||||
test::seed_t initialize_seed{674140082};
|
||||
|
||||
@@ -20,11 +22,19 @@ using key_equal = stateful_key_equal;
|
||||
using map_type = boost::unordered::concurrent_flat_map<raii, raii, hasher,
|
||||
key_equal, stateful_allocator<std::pair<raii const, raii> > >;
|
||||
|
||||
using node_map_type = boost::unordered::concurrent_node_map<raii, raii, hasher,
|
||||
key_equal, stateful_allocator<std::pair<raii const, raii> > >;
|
||||
|
||||
using set_type = boost::unordered::concurrent_flat_set<raii, hasher,
|
||||
key_equal, stateful_allocator<raii> >;
|
||||
|
||||
using node_set_type = boost::unordered::concurrent_node_set<raii, hasher,
|
||||
key_equal, stateful_allocator<raii> >;
|
||||
|
||||
map_type* test_map;
|
||||
node_map_type* test_node_map;
|
||||
set_type* test_set;
|
||||
node_set_type* test_node_set;
|
||||
|
||||
namespace {
|
||||
template <class X, class GF>
|
||||
@@ -130,12 +140,12 @@ namespace {
|
||||
// clang-format off
|
||||
UNORDERED_TEST(
|
||||
clear_tests,
|
||||
((test_map)(test_set))
|
||||
((test_map)(test_node_map)(test_set)(test_node_set))
|
||||
((value_type_generator_factory))
|
||||
((default_generator)(sequential)(limited_range)))
|
||||
|
||||
UNORDERED_TEST(insert_and_clear,
|
||||
((test_map)(test_set))
|
||||
((test_map)(test_node_map)(test_set)(test_node_set))
|
||||
((value_type_generator_factory))
|
||||
((default_generator)(sequential)(limited_range)))
|
||||
// clang-format on
|
||||
|
||||
@@ -1,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; }
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
+27
-11
@@ -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))
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// Copyright (C) 2023 Christian Mazakas
|
||||
// Copyright (C) 2023 Joaquin M Lopez Munoz
|
||||
// Copyright (C) 2023-2024 Joaquin M Lopez Munoz
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
@@ -7,6 +7,8 @@
|
||||
|
||||
#include <boost/unordered/concurrent_flat_map.hpp>
|
||||
#include <boost/unordered/concurrent_flat_set.hpp>
|
||||
#include <boost/unordered/concurrent_node_map.hpp>
|
||||
#include <boost/unordered/concurrent_node_set.hpp>
|
||||
|
||||
test::seed_t initialize_seed{1634048962};
|
||||
|
||||
@@ -20,28 +22,38 @@ using key_equal = stateful_key_equal;
|
||||
using map_type = boost::unordered::concurrent_flat_map<raii, raii, hasher,
|
||||
key_equal, stateful_allocator<std::pair<raii const, raii> > >;
|
||||
|
||||
using node_map_type = boost::unordered::concurrent_node_map<raii, raii, hasher,
|
||||
key_equal, stateful_allocator<std::pair<raii const, raii> > >;
|
||||
|
||||
using set_type = boost::unordered::concurrent_flat_set<raii, hasher,
|
||||
key_equal, stateful_allocator<raii> >;
|
||||
|
||||
using node_set_type = boost::unordered::concurrent_node_set<raii, hasher,
|
||||
key_equal, stateful_allocator<raii> >;
|
||||
|
||||
map_type* test_map;
|
||||
node_map_type* test_node_map;
|
||||
set_type* test_set;
|
||||
node_set_type* test_node_set;
|
||||
|
||||
namespace {
|
||||
|
||||
UNORDERED_AUTO_TEST (simple_map_equality) {
|
||||
using allocator_type = map_type::allocator_type;
|
||||
template <class X>
|
||||
void simple_map_equality(X*)
|
||||
{
|
||||
using allocator_type = typename X::allocator_type;
|
||||
|
||||
{
|
||||
map_type x1(
|
||||
X x1(
|
||||
{{1, 11}, {2, 22}}, 0, hasher(1), key_equal(2), allocator_type(3));
|
||||
|
||||
map_type x2(
|
||||
X x2(
|
||||
{{1, 11}, {2, 22}}, 0, hasher(2), key_equal(2), allocator_type(3));
|
||||
|
||||
map_type x3(
|
||||
X x3(
|
||||
{{1, 11}, {2, 23}}, 0, hasher(2), key_equal(2), allocator_type(3));
|
||||
|
||||
map_type x4({{1, 11}}, 0, hasher(2), key_equal(2), allocator_type(3));
|
||||
X x4({{1, 11}}, 0, hasher(2), key_equal(2), allocator_type(3));
|
||||
|
||||
BOOST_TEST_EQ(x1.size(), x2.size());
|
||||
BOOST_TEST(x1 == x2);
|
||||
@@ -57,17 +69,19 @@ namespace {
|
||||
}
|
||||
}
|
||||
|
||||
UNORDERED_AUTO_TEST (simple_set_equality) {
|
||||
using allocator_type = set_type::allocator_type;
|
||||
template <class X>
|
||||
void simple_set_equality(X*)
|
||||
{
|
||||
using allocator_type = typename X::allocator_type;
|
||||
|
||||
{
|
||||
set_type x1(
|
||||
X x1(
|
||||
{1, 2}, 0, hasher(1), key_equal(2), allocator_type(3));
|
||||
|
||||
set_type x2(
|
||||
X x2(
|
||||
{1, 2}, 0, hasher(2), key_equal(2), allocator_type(3));
|
||||
|
||||
set_type x3({1}, 0, hasher(2), key_equal(2), allocator_type(3));
|
||||
X x3({1}, 0, hasher(2), key_equal(2), allocator_type(3));
|
||||
|
||||
BOOST_TEST_EQ(x1.size(), x2.size());
|
||||
BOOST_TEST(x1 == x2);
|
||||
@@ -165,9 +179,17 @@ namespace {
|
||||
} // namespace
|
||||
|
||||
// clang-format off
|
||||
UNORDERED_TEST(
|
||||
simple_map_equality,
|
||||
((test_map)(test_node_map)))
|
||||
|
||||
UNORDERED_TEST(
|
||||
simple_set_equality,
|
||||
((test_set)(test_node_set)))
|
||||
|
||||
UNORDERED_TEST(
|
||||
insert_and_compare,
|
||||
((test_map)(test_set))
|
||||
((test_map)(test_node_map)(test_set)(test_node_set))
|
||||
((value_type_generator_factory))
|
||||
((default_generator)(sequential)(limited_range)))
|
||||
// clang-format on
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// Copyright (C) 2023 Christian Mazakas
|
||||
// Copyright (C) 2023 Joaquin M Lopez Munoz
|
||||
// Copyright (C) 2023-2024 Joaquin M Lopez Munoz
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
@@ -7,6 +7,8 @@
|
||||
|
||||
#include <boost/unordered/concurrent_flat_map.hpp>
|
||||
#include <boost/unordered/concurrent_flat_set.hpp>
|
||||
#include <boost/unordered/concurrent_node_map.hpp>
|
||||
#include <boost/unordered/concurrent_node_set.hpp>
|
||||
|
||||
#include <boost/core/ignore_unused.hpp>
|
||||
|
||||
@@ -439,11 +441,17 @@ namespace {
|
||||
}
|
||||
|
||||
boost::unordered::concurrent_flat_map<raii, raii>* map;
|
||||
boost::unordered::concurrent_node_map<raii, raii>* node_map;
|
||||
boost::unordered::concurrent_flat_set<raii>* set;
|
||||
boost::unordered::concurrent_node_set<raii>* node_set;
|
||||
boost::unordered::concurrent_flat_map<raii, raii, transp_hash,
|
||||
transp_key_equal>* transparent_map;
|
||||
boost::unordered::concurrent_node_map<raii, raii, transp_hash,
|
||||
transp_key_equal>* transparent_node_map;
|
||||
boost::unordered::concurrent_flat_set<raii, transp_hash,
|
||||
transp_key_equal>* transparent_set;
|
||||
boost::unordered::concurrent_node_set<raii, transp_hash,
|
||||
transp_key_equal>* transparent_node_set;
|
||||
|
||||
} // namespace
|
||||
|
||||
@@ -454,14 +462,14 @@ using test::sequential;
|
||||
// clang-format off
|
||||
UNORDERED_TEST(
|
||||
erase,
|
||||
((map)(set))
|
||||
((map)(node_map)(set)(node_set))
|
||||
((value_type_generator_factory)(init_type_generator_factory))
|
||||
((lvalue_eraser)(lvalue_eraser_if)(erase_if)(free_fn_erase_if)(erase_if_exec_policy))
|
||||
((default_generator)(sequential)(limited_range)))
|
||||
|
||||
UNORDERED_TEST(
|
||||
erase,
|
||||
((transparent_map)(transparent_set))
|
||||
((transparent_map)(transparent_node_map)(transparent_set)(transparent_node_set))
|
||||
((value_type_generator_factory)(init_type_generator_factory))
|
||||
((transp_lvalue_eraser)(transp_lvalue_eraser_if)(erase_if_exec_policy))
|
||||
((default_generator)(sequential)(limited_range)))
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// Copyright (C) 2023 Christian Mazakas
|
||||
// Copyright (C) 2023 Joaquin M Lopez Munoz
|
||||
// Copyright (C) 2023-2024 Joaquin M Lopez Munoz
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
@@ -7,6 +7,8 @@
|
||||
|
||||
#include <boost/unordered/concurrent_flat_map.hpp>
|
||||
#include <boost/unordered/concurrent_flat_set.hpp>
|
||||
#include <boost/unordered/concurrent_node_map.hpp>
|
||||
#include <boost/unordered/concurrent_node_set.hpp>
|
||||
|
||||
using hasher = stateful_hash;
|
||||
using key_equal = stateful_key_equal;
|
||||
@@ -14,11 +16,19 @@ using key_equal = stateful_key_equal;
|
||||
using map_type = boost::unordered::concurrent_flat_map<raii, raii, hasher,
|
||||
key_equal, stateful_allocator<std::pair<raii const, raii> > >;
|
||||
|
||||
using node_map_type = boost::unordered::concurrent_node_map<raii, raii, hasher,
|
||||
key_equal, stateful_allocator<std::pair<raii const, raii> > >;
|
||||
|
||||
using set_type = boost::unordered::concurrent_flat_set<raii, hasher,
|
||||
key_equal, stateful_allocator<raii> >;
|
||||
|
||||
using node_set_type = boost::unordered::concurrent_node_set<raii, hasher,
|
||||
key_equal, stateful_allocator<raii> >;
|
||||
|
||||
map_type* test_map;
|
||||
node_map_type* test_node_map;
|
||||
set_type* test_set;
|
||||
node_set_type* test_node_set;
|
||||
|
||||
std::initializer_list<map_type::value_type> map_init_list{
|
||||
{raii{0}, raii{0}},
|
||||
@@ -71,7 +81,9 @@ std::initializer_list<set_type::value_type> set_init_list{
|
||||
};
|
||||
|
||||
auto test_map_and_init_list=std::make_pair(test_map,map_init_list);
|
||||
auto test_node_map_and_init_list=std::make_pair(test_node_map,map_init_list);
|
||||
auto test_set_and_init_list=std::make_pair(test_set,set_init_list);
|
||||
auto test_node_set_and_init_list=std::make_pair(test_node_set,set_init_list);
|
||||
|
||||
namespace {
|
||||
test::seed_t initialize_seed(1794114520);
|
||||
@@ -206,19 +218,20 @@ using test::sequential;
|
||||
// clang-format off
|
||||
UNORDERED_TEST(
|
||||
copy_assign,
|
||||
((test_map)(test_set))
|
||||
((test_map)(test_node_map)(test_set)(test_node_set))
|
||||
((exception_value_type_generator_factory))
|
||||
((default_generator)(sequential)(limited_range)))
|
||||
|
||||
UNORDERED_TEST(
|
||||
move_assign,
|
||||
((test_map)(test_set))
|
||||
((test_map)(test_node_map)(test_set)(test_node_set))
|
||||
((exception_value_type_generator_factory))
|
||||
((default_generator)(sequential)))
|
||||
|
||||
UNORDERED_TEST(
|
||||
intializer_list_assign,
|
||||
((test_map_and_init_list)(test_set_and_init_list)))
|
||||
((test_map_and_init_list)(test_node_map_and_init_list)
|
||||
(test_set_and_init_list)(test_node_set_and_init_list)))
|
||||
// clang-format on
|
||||
|
||||
RUN_TESTS()
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// Copyright (C) 2023 Christian Mazakas
|
||||
// Copyright (C) 2023 Joaquin M Lopez Munoz
|
||||
// Copyright (C) 2023-2024 Joaquin M Lopez Munoz
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
@@ -7,6 +7,8 @@
|
||||
|
||||
#include <boost/unordered/concurrent_flat_map.hpp>
|
||||
#include <boost/unordered/concurrent_flat_set.hpp>
|
||||
#include <boost/unordered/concurrent_node_map.hpp>
|
||||
#include <boost/unordered/concurrent_node_set.hpp>
|
||||
|
||||
using hasher = stateful_hash;
|
||||
using key_equal = stateful_key_equal;
|
||||
@@ -14,11 +16,19 @@ using key_equal = stateful_key_equal;
|
||||
using map_type = boost::unordered::concurrent_flat_map<raii, raii, hasher,
|
||||
key_equal, stateful_allocator<std::pair<raii const, raii> > >;
|
||||
|
||||
using node_map_type = boost::unordered::concurrent_node_map<raii, raii, hasher,
|
||||
key_equal, stateful_allocator<std::pair<raii const, raii> > >;
|
||||
|
||||
using set_type = boost::unordered::concurrent_flat_set<raii, hasher,
|
||||
key_equal, stateful_allocator<raii> >;
|
||||
|
||||
using node_set_type = boost::unordered::concurrent_node_set<raii, hasher,
|
||||
key_equal, stateful_allocator<raii> >;
|
||||
|
||||
map_type* test_map;
|
||||
node_map_type* test_node_map;
|
||||
set_type* test_set;
|
||||
node_set_type* test_node_set;
|
||||
|
||||
std::initializer_list<map_type::value_type> map_init_list{
|
||||
{raii{0}, raii{0}},
|
||||
@@ -71,7 +81,9 @@ std::initializer_list<set_type::value_type> set_init_list{
|
||||
};
|
||||
|
||||
auto test_map_and_init_list=std::make_pair(test_map,map_init_list);
|
||||
auto test_node_map_and_init_list=std::make_pair(test_node_map,map_init_list);
|
||||
auto test_set_and_init_list=std::make_pair(test_set,set_init_list);
|
||||
auto test_node_set_and_init_list=std::make_pair(test_node_set,set_init_list);
|
||||
|
||||
namespace {
|
||||
test::seed_t initialize_seed(795610904);
|
||||
@@ -339,29 +351,30 @@ using test::sequential;
|
||||
// clang-format off
|
||||
UNORDERED_TEST(
|
||||
bucket_constructor,
|
||||
((test_map)(test_set)))
|
||||
((test_map)(test_node_map)(test_set)(test_node_set)))
|
||||
|
||||
UNORDERED_TEST(
|
||||
iterator_range,
|
||||
((test_map)(test_set))
|
||||
((test_map)(test_node_map)(test_set)(test_node_set))
|
||||
((exception_value_type_generator_factory))
|
||||
((default_generator)(sequential)(limited_range)))
|
||||
|
||||
UNORDERED_TEST(
|
||||
copy_constructor,
|
||||
((test_map)(test_set))
|
||||
((test_map)(test_node_map)(test_set)(test_node_set))
|
||||
((exception_value_type_generator_factory))
|
||||
((default_generator)(sequential)))
|
||||
|
||||
UNORDERED_TEST(
|
||||
move_constructor,
|
||||
((test_map)(test_set))
|
||||
((test_map)(test_node_map)(test_set)(test_node_set))
|
||||
((exception_value_type_generator_factory))
|
||||
((default_generator)(sequential)))
|
||||
|
||||
UNORDERED_TEST(
|
||||
initializer_list_bucket_count,
|
||||
((test_map_and_init_list)(test_set_and_init_list)))
|
||||
((test_map_and_init_list)(test_node_map_and_init_list)
|
||||
(test_set_and_init_list)(test_node_set_and_init_list)))
|
||||
// clang-format on
|
||||
|
||||
RUN_TESTS()
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// Copyright (C) 2023 Christian Mazakas
|
||||
// Copyright (C) 2023 Joaquin M Lopez Munoz
|
||||
// Copyright (C) 2023-2024 Joaquin M Lopez Munoz
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
@@ -7,6 +7,8 @@
|
||||
|
||||
#include <boost/unordered/concurrent_flat_map.hpp>
|
||||
#include <boost/unordered/concurrent_flat_set.hpp>
|
||||
#include <boost/unordered/concurrent_node_map.hpp>
|
||||
#include <boost/unordered/concurrent_node_set.hpp>
|
||||
|
||||
#include <boost/core/ignore_unused.hpp>
|
||||
|
||||
@@ -283,8 +285,12 @@ namespace {
|
||||
|
||||
boost::unordered::concurrent_flat_map<raii, raii, stateful_hash,
|
||||
stateful_key_equal, stateful_allocator<std::pair<raii const, raii> > >* map;
|
||||
boost::unordered::concurrent_node_map<raii, raii, stateful_hash,
|
||||
stateful_key_equal, stateful_allocator<std::pair<raii const, raii> > >* node_map;
|
||||
boost::unordered::concurrent_flat_set<raii, stateful_hash,
|
||||
stateful_key_equal, stateful_allocator<raii> >* set;
|
||||
boost::unordered::concurrent_node_set<raii, stateful_hash,
|
||||
stateful_key_equal, stateful_allocator<raii> >* node_set;
|
||||
|
||||
} // namespace
|
||||
|
||||
@@ -295,7 +301,7 @@ using test::sequential;
|
||||
// clang-format off
|
||||
UNORDERED_TEST(
|
||||
erase,
|
||||
((map)(set))
|
||||
((map)(node_map)(set)(node_set))
|
||||
((exception_value_type_generator_factory)
|
||||
(exception_init_type_generator_factory))
|
||||
((lvalue_eraser)(lvalue_eraser_if)(erase_if)(free_fn_erase_if))
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// Copyright (C) 2023 Christian Mazakas
|
||||
// Copyright (C) 2023 Joaquin M Lopez Munoz
|
||||
// Copyright (C) 2023-2024 Joaquin M Lopez Munoz
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
@@ -7,6 +7,8 @@
|
||||
|
||||
#include <boost/unordered/concurrent_flat_map.hpp>
|
||||
#include <boost/unordered/concurrent_flat_set.hpp>
|
||||
#include <boost/unordered/concurrent_node_map.hpp>
|
||||
#include <boost/unordered/concurrent_node_set.hpp>
|
||||
|
||||
#include <boost/core/ignore_unused.hpp>
|
||||
|
||||
@@ -153,7 +155,15 @@ namespace {
|
||||
|
||||
BOOST_TEST_EQ(raii::default_constructor, 0u);
|
||||
BOOST_TEST_GT(raii::copy_constructor, 0u);
|
||||
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))
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// Copyright (C) 2023 Christian Mazakas
|
||||
// Copyright (C) 2023 Joaquin M Lopez Munoz
|
||||
// Copyright (C) 2023-2024 Joaquin M Lopez Munoz
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
@@ -7,6 +7,8 @@
|
||||
|
||||
#include <boost/unordered/concurrent_flat_map.hpp>
|
||||
#include <boost/unordered/concurrent_flat_set.hpp>
|
||||
#include <boost/unordered/concurrent_node_map.hpp>
|
||||
#include <boost/unordered/concurrent_node_set.hpp>
|
||||
|
||||
#include <boost/core/ignore_unused.hpp>
|
||||
|
||||
@@ -16,11 +18,19 @@ using key_equal = stateful_key_equal;
|
||||
using map_type = boost::unordered::concurrent_flat_map<raii, raii, hasher,
|
||||
key_equal, stateful_allocator<std::pair<raii const, raii> > >;
|
||||
|
||||
using node_map_type = boost::unordered::concurrent_node_map<raii, raii, hasher,
|
||||
key_equal, stateful_allocator<std::pair<raii const, raii> > >;
|
||||
|
||||
using set_type = boost::unordered::concurrent_flat_set<raii, hasher,
|
||||
key_equal, stateful_allocator<raii> >;
|
||||
|
||||
using node_set_type = boost::unordered::concurrent_node_set<raii, hasher,
|
||||
key_equal, stateful_allocator<raii> >;
|
||||
|
||||
map_type* test_map;
|
||||
node_map_type* test_node_map;
|
||||
set_type* test_set;
|
||||
node_set_type* test_node_set;
|
||||
|
||||
namespace {
|
||||
test::seed_t initialize_seed(223333016);
|
||||
@@ -79,7 +89,7 @@ using test::sequential;
|
||||
// clang-format off
|
||||
UNORDERED_TEST(
|
||||
merge,
|
||||
((test_map)(test_set))
|
||||
((test_map)(test_node_map)(test_set)(test_node_set))
|
||||
((exception_value_type_generator_factory))
|
||||
((default_generator)(sequential)(limited_range)))
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// Copyright 2024 Joaquin M Lopez Muoz.
|
||||
// Copyright 2024 Joaquin M Lopez Munoz.
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at htT://www.boost.org/LICENSE_1_0.txt)
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#define BOOST_UNORDERED_CFOA_TESTS
|
||||
#include "../unordered/explicit_alloc_ctor_tests.cpp"
|
||||
|
||||
@@ -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()
|
||||
+56
-6
@@ -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()
|
||||
|
||||
+21
-2
@@ -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
|
||||
|
||||
+100
-21
@@ -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()
|
||||
|
||||
+45
-10
@@ -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
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
// Copyright 2024 Joaquin M Lopez Munoz.
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#define BOOST_UNORDERED_CFOA_TESTS
|
||||
#include "../unordered/node_handle_allocator_tests.cpp"
|
||||
@@ -5,4 +5,6 @@
|
||||
#define BOOST_UNORDERED_CFOA_TESTS
|
||||
#include <boost/unordered/concurrent_flat_map.hpp>
|
||||
#include <boost/unordered/concurrent_flat_set.hpp>
|
||||
#include <boost/unordered/concurrent_node_map.hpp>
|
||||
#include <boost/unordered/concurrent_node_set.hpp>
|
||||
#include "../unordered/pmr_allocator_tests.cpp"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2023 Joaquin M Lopez Munoz
|
||||
// Copyright 2023-2024 Joaquin M Lopez Munoz
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// https://www.boost.org/LICENSE_1_0.txt
|
||||
|
||||
@@ -32,15 +32,21 @@ namespace boost {
|
||||
|
||||
#include <boost/unordered/concurrent_flat_map.hpp>
|
||||
#include <boost/unordered/concurrent_flat_set.hpp>
|
||||
#include <boost/unordered/concurrent_node_map.hpp>
|
||||
#include <boost/unordered/concurrent_node_set.hpp>
|
||||
#include <boost/core/lightweight_test.hpp>
|
||||
|
||||
using test::default_generator;
|
||||
|
||||
using map_type = boost::unordered::concurrent_flat_map<raii, raii>;
|
||||
using node_map_type = boost::unordered::concurrent_node_map<raii, raii>;
|
||||
using set_type = boost::unordered::concurrent_flat_set<raii>;
|
||||
using node_set_type = boost::unordered::concurrent_node_set<raii>;
|
||||
|
||||
map_type* test_map;
|
||||
map_type* test_node_map;
|
||||
set_type* test_set;
|
||||
node_set_type* test_node_set;
|
||||
|
||||
template<typename F>
|
||||
void detect_reentrancy(F f)
|
||||
@@ -105,7 +111,7 @@ namespace {
|
||||
// clang-format off
|
||||
UNORDERED_TEST(
|
||||
reentrancy_tests,
|
||||
((test_map)(test_set))
|
||||
((test_map)(test_node_map)(test_set)(test_node_set))
|
||||
((value_type_generator_factory))
|
||||
((default_generator)))
|
||||
// clang-format on
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// Copyright (C) 2023 Christian Mazakas
|
||||
// Copyright (C) 2023 Joaquin M Lopez Munoz
|
||||
// Copyright (C) 2023-2024 Joaquin M Lopez Munoz
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
@@ -7,6 +7,8 @@
|
||||
|
||||
#include <boost/unordered/concurrent_flat_map.hpp>
|
||||
#include <boost/unordered/concurrent_flat_set.hpp>
|
||||
#include <boost/unordered/concurrent_node_map.hpp>
|
||||
#include <boost/unordered/concurrent_node_set.hpp>
|
||||
|
||||
using test::default_generator;
|
||||
using test::limited_range;
|
||||
@@ -18,11 +20,19 @@ using key_equal = stateful_key_equal;
|
||||
using map_type = boost::unordered::concurrent_flat_map<raii, raii, hasher,
|
||||
key_equal, stateful_allocator<std::pair<raii const, raii> > >;
|
||||
|
||||
using node_map_type = boost::unordered::concurrent_node_map<raii, raii, hasher,
|
||||
key_equal, stateful_allocator<std::pair<raii const, raii> > >;
|
||||
|
||||
using set_type = boost::unordered::concurrent_flat_set<raii, hasher,
|
||||
key_equal, stateful_allocator<raii> >;
|
||||
|
||||
using node_set_type = boost::unordered::concurrent_node_set<raii, hasher,
|
||||
key_equal, stateful_allocator<raii> >;
|
||||
|
||||
map_type* test_map;
|
||||
node_map_type* test_node_map;
|
||||
set_type* test_set;
|
||||
node_set_type* test_node_set;
|
||||
|
||||
namespace {
|
||||
test::seed_t initialize_seed{748775921};
|
||||
@@ -187,15 +197,15 @@ namespace {
|
||||
// clang-format off
|
||||
UNORDERED_TEST(
|
||||
rehash_no_insert,
|
||||
((test_map)(test_set)))
|
||||
((test_map)(test_node_map)(test_set)(test_node_set)))
|
||||
|
||||
UNORDERED_TEST(
|
||||
reserve_no_insert,
|
||||
((test_map)(test_set)))
|
||||
((test_map)(test_node_map)(test_set)(test_node_set)))
|
||||
|
||||
UNORDERED_TEST(
|
||||
insert_and_erase_with_rehash,
|
||||
((test_map)(test_set))
|
||||
((test_map)(test_node_map)(test_set)(test_node_set))
|
||||
((value_type_generator_factory))
|
||||
((default_generator)(sequential)(limited_range)))
|
||||
// clang-format on
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright (C) 2023 Joaquin M Lopez Munoz
|
||||
// Copyright (C) 2023-2024 Joaquin M Lopez Munoz
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
@@ -12,6 +12,8 @@
|
||||
#include <boost/serialization/nvp.hpp>
|
||||
#include <boost/unordered/concurrent_flat_map.hpp>
|
||||
#include <boost/unordered/concurrent_flat_set.hpp>
|
||||
#include <boost/unordered/concurrent_node_map.hpp>
|
||||
#include <boost/unordered/concurrent_node_set.hpp>
|
||||
|
||||
namespace {
|
||||
|
||||
@@ -70,11 +72,15 @@ namespace {
|
||||
|
||||
boost::concurrent_flat_map<
|
||||
test::object, test::object, test::hash, test::equal_to>* test_flat_map;
|
||||
boost::concurrent_node_map<
|
||||
test::object, test::object, test::hash, test::equal_to>* test_node_map;
|
||||
boost::concurrent_flat_set<
|
||||
test::object, test::hash, test::equal_to>* test_flat_set;
|
||||
boost::concurrent_node_set<
|
||||
test::object, test::hash, test::equal_to>* test_node_set;
|
||||
|
||||
UNORDERED_TEST(serialization_tests,
|
||||
((test_flat_map)(test_flat_set))
|
||||
((test_flat_map)(test_node_map)(test_flat_set)(test_node_set))
|
||||
((text_archive)(xml_archive))
|
||||
((default_generator)))
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2024 Joaquin M Lopez Muoz.
|
||||
// Copyright 2024 Joaquin M Lopez Munoz.
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// Copyright (C) 2023 Christian Mazakas
|
||||
// Copyright (C) 2023 Joaquin M Lopez Munoz
|
||||
// Copyright (C) 2023-2024 Joaquin M Lopez Munoz
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
@@ -7,6 +7,8 @@
|
||||
|
||||
#include <boost/unordered/concurrent_flat_map.hpp>
|
||||
#include <boost/unordered/concurrent_flat_set.hpp>
|
||||
#include <boost/unordered/concurrent_node_map.hpp>
|
||||
#include <boost/unordered/concurrent_node_set.hpp>
|
||||
|
||||
test::seed_t initialize_seed{996130204};
|
||||
|
||||
@@ -62,9 +64,15 @@ using key_equal = stateful_key_equal;
|
||||
using map_type = boost::unordered::concurrent_flat_map<raii, raii, hasher,
|
||||
key_equal, stateful_allocator<std::pair<raii const, raii> > >;
|
||||
|
||||
using node_map_type = boost::unordered::concurrent_node_map<raii, raii, hasher,
|
||||
key_equal, stateful_allocator<std::pair<raii const, raii> > >;
|
||||
|
||||
using set_type = boost::unordered::concurrent_flat_set<raii, hasher,
|
||||
key_equal, stateful_allocator<raii> >;
|
||||
|
||||
using node_set_type = boost::unordered::concurrent_node_set<raii, hasher,
|
||||
key_equal, stateful_allocator<raii> >;
|
||||
|
||||
template <class T> struct is_nothrow_member_swappable
|
||||
{
|
||||
static bool const value =
|
||||
@@ -293,21 +301,28 @@ namespace {
|
||||
map_type* map;
|
||||
replace_allocator<map_type, pocs_allocator>* pocs_map;
|
||||
|
||||
node_map_type* node_map;
|
||||
replace_allocator<node_map_type, pocs_allocator>* pocs_node_map;
|
||||
|
||||
set_type* set;
|
||||
replace_allocator<set_type, pocs_allocator>* pocs_set;
|
||||
|
||||
node_set_type* node_set;
|
||||
replace_allocator<node_set_type, pocs_allocator>* pocs_node_set;
|
||||
|
||||
} // namespace
|
||||
|
||||
// clang-format off
|
||||
UNORDERED_TEST(
|
||||
swap_tests,
|
||||
((map)(pocs_map)(set)(pocs_set))
|
||||
((map)(pocs_map)(node_map)(pocs_node_map)
|
||||
(set)(pocs_set)(node_set)(pocs_node_set))
|
||||
((member_fn_swap)(free_fn_swap))
|
||||
((value_type_generator_factory))
|
||||
((default_generator)(sequential)(limited_range)))
|
||||
|
||||
UNORDERED_TEST(insert_and_swap,
|
||||
((map)(set))
|
||||
((map)(node_map)(set)(node_set))
|
||||
((member_fn_swap)(free_fn_swap))
|
||||
((value_type_generator_factory))
|
||||
((default_generator)(sequential)(limited_range)))
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
// Copyright (C) 2023 Christian Mazakas
|
||||
// Copyright (C) 2024 Joaquin M Lopez Munoz
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#include "helpers.hpp"
|
||||
|
||||
#include <boost/unordered/concurrent_flat_map.hpp>
|
||||
#include <boost/unordered/concurrent_node_map.hpp>
|
||||
|
||||
#include <boost/core/ignore_unused.hpp>
|
||||
|
||||
@@ -161,8 +163,15 @@ namespace {
|
||||
|
||||
BOOST_TEST_EQ(raii::default_constructor, x.size());
|
||||
BOOST_TEST_EQ(raii::copy_constructor, x.size());
|
||||
// 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))
|
||||
|
||||
+170
-7
@@ -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)
|
||||
|
||||
@@ -17,7 +17,10 @@
|
||||
|
||||
#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>
|
||||
#include <boost/iterator/transform_iterator.hpp>
|
||||
|
||||
@@ -27,7 +30,10 @@
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <chrono>
|
||||
#include <functional>
|
||||
#include <iterator>
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
|
||||
namespace {
|
||||
@@ -967,9 +973,158 @@ 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
|
||||
{
|
||||
mutable_pair(int first_ = 0, int second_ = 0):
|
||||
first{first_}, second{second_} {}
|
||||
|
||||
int first = 0;
|
||||
mutable int second = 0;
|
||||
};
|
||||
|
||||
struct null_mutable_pair
|
||||
{
|
||||
operator mutable_pair() const { return {0,0}; }
|
||||
};
|
||||
|
||||
struct mutable_pair_hash
|
||||
{
|
||||
using is_transparent = void;
|
||||
|
||||
std::size_t operator()(const mutable_pair& x) const
|
||||
{
|
||||
return boost::hash<int>{}(x.first);
|
||||
}
|
||||
|
||||
std::size_t operator()(const null_mutable_pair&) const
|
||||
{
|
||||
return boost::hash<int>{}(0);
|
||||
}
|
||||
};
|
||||
|
||||
struct mutable_pair_equal_to
|
||||
{
|
||||
using is_transparent = void;
|
||||
|
||||
bool operator()(const mutable_pair& x, const mutable_pair& y) const
|
||||
{
|
||||
return x.first == y.first;
|
||||
}
|
||||
|
||||
bool operator()(const null_mutable_pair&, const mutable_pair& y) const
|
||||
{
|
||||
return 0 == y.first;
|
||||
}
|
||||
|
||||
bool operator()(const mutable_pair& x, const null_mutable_pair&) const
|
||||
{
|
||||
return x.first == 0;
|
||||
}
|
||||
};
|
||||
|
||||
template<typename F>
|
||||
void exclusive_access_for(F f)
|
||||
{
|
||||
std::atomic_int num_started{0};
|
||||
std::atomic_int in_visit{0};
|
||||
boost::compat::latch finish{1};
|
||||
|
||||
auto bound_f = [&] {
|
||||
++num_started;
|
||||
f([&] (const mutable_pair& x) {
|
||||
++in_visit;
|
||||
++x.second;
|
||||
finish.wait();
|
||||
--in_visit;
|
||||
return true;
|
||||
});
|
||||
};
|
||||
|
||||
std::thread t1{bound_f}, t2{bound_f};
|
||||
while(num_started != 2) {
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||
}
|
||||
BOOST_TEST(in_visit <= 1);
|
||||
finish.count_down();
|
||||
t1.join();
|
||||
t2.join();
|
||||
}
|
||||
|
||||
template<class X>
|
||||
void exclusive_access_set_visit(X*)
|
||||
{
|
||||
using visit_function = std::function<void (const mutable_pair&)>;
|
||||
using returning_visit_function = std::function<bool (const mutable_pair&)>;
|
||||
X x;
|
||||
x.insert({0, 0});
|
||||
|
||||
exclusive_access_for([&](visit_function f) {
|
||||
x.visit({0, 0}, f);
|
||||
});
|
||||
exclusive_access_for([&](visit_function f) {
|
||||
x.visit(null_mutable_pair{}, f);
|
||||
});
|
||||
|
||||
exclusive_access_for([&](visit_function f) {
|
||||
mutable_pair a[] = {{0, 0}};
|
||||
x.visit(std::begin(a), std::end(a), f);
|
||||
});
|
||||
|
||||
exclusive_access_for([&](visit_function f) {
|
||||
x.visit_all(f);
|
||||
});
|
||||
exclusive_access_for([&](returning_visit_function f) {
|
||||
x.visit_while(f);
|
||||
});
|
||||
|
||||
#if defined(BOOST_UNORDERED_PARALLEL_ALGORITHMS)
|
||||
exclusive_access_for([&](visit_function f) {
|
||||
x.visit_all(std::execution::par, f);
|
||||
});
|
||||
exclusive_access_for([&](returning_visit_function f) {
|
||||
x.visit_while(std::execution::par, f);
|
||||
});
|
||||
#endif
|
||||
|
||||
exclusive_access_for([&](visit_function f) {
|
||||
const mutable_pair p;
|
||||
x.insert_or_visit(p, f);
|
||||
});
|
||||
exclusive_access_for([&](visit_function f) {
|
||||
x.insert_or_visit({0,0}, f);
|
||||
});
|
||||
exclusive_access_for([&](visit_function f) {
|
||||
x.insert_or_visit(null_mutable_pair{}, f);
|
||||
});
|
||||
|
||||
exclusive_access_for([&](visit_function f) {
|
||||
mutable_pair a[] = {{0, 0}};
|
||||
x.insert_or_visit(std::begin(a), std::end(a), f);
|
||||
});
|
||||
exclusive_access_for([&](visit_function f) {
|
||||
std::initializer_list<mutable_pair> il = {{0, 0}};
|
||||
x.insert_or_visit(il, f);
|
||||
});
|
||||
|
||||
exclusive_access_for([&](visit_function f) {
|
||||
x.emplace_or_visit(0, 0, f);
|
||||
});
|
||||
}
|
||||
|
||||
boost::concurrent_flat_set<
|
||||
mutable_pair, mutable_pair_hash, mutable_pair_equal_to>* mutable_set;
|
||||
boost::concurrent_node_set<
|
||||
mutable_pair, mutable_pair_hash, mutable_pair_equal_to>* mutable_node_set;
|
||||
|
||||
} // namespace
|
||||
|
||||
@@ -981,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))
|
||||
@@ -989,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))
|
||||
@@ -1018,12 +1174,19 @@ 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))
|
||||
)
|
||||
|
||||
// https://github.com/boostorg/unordered/issues/260
|
||||
|
||||
UNORDERED_TEST(
|
||||
exclusive_access_set_visit,
|
||||
((mutable_set)(mutable_node_set))
|
||||
)
|
||||
|
||||
// clang-format on
|
||||
|
||||
RUN_TESTS()
|
||||
|
||||
@@ -0,0 +1,199 @@
|
||||
// Copyright 2024 Braden Ganetsky
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// https://www.boost.org/LICENSE_1_0.txt
|
||||
|
||||
// This is a file for testing of visualizations,
|
||||
// such as Visual Studio Natvis or GDB pretty printers.
|
||||
// Run this test and break at the label called `break_here`.
|
||||
// Inspect the variables to test correctness.
|
||||
|
||||
#if 0 // Change to `#if 1` to test turning off SIMD optimizations
|
||||
#define BOOST_UNORDERED_DISABLE_SSE2
|
||||
#define BOOST_UNORDERED_DISABLE_NEON
|
||||
#endif
|
||||
|
||||
#define BOOST_UNORDERED_ENABLE_STATS
|
||||
|
||||
#include <boost/core/lightweight_test.hpp>
|
||||
|
||||
#include <boost/interprocess/allocators/allocator.hpp>
|
||||
#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>
|
||||
#include <boost/unordered/unordered_node_map.hpp>
|
||||
#include <boost/unordered/unordered_node_set.hpp>
|
||||
#include <boost/unordered/unordered_set.hpp>
|
||||
#include <boost/uuid/random_generator.hpp>
|
||||
#include <boost/uuid/uuid_io.hpp>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <typeinfo>
|
||||
|
||||
// Prevent any "unused" errors
|
||||
template <class... Args> void use(Args&&...) {}
|
||||
|
||||
using map_value_type = std::pair<const std::string, int>;
|
||||
using set_value_type = std::string;
|
||||
|
||||
template <class Tester> void visualization_test(Tester& tester)
|
||||
{
|
||||
// clang-format off
|
||||
auto fca_map_ptr = tester.template construct_map<boost::unordered_map>();
|
||||
auto fca_multimap_ptr = tester.template construct_map<boost::unordered_multimap>();
|
||||
auto fca_set_ptr = tester.template construct_set<boost::unordered_set>();
|
||||
auto fca_multiset_ptr = tester.template construct_set<boost::unordered_multiset>();
|
||||
auto& fca_map = *fca_map_ptr;
|
||||
auto& fca_multimap = *fca_multimap_ptr;
|
||||
auto& fca_set = *fca_set_ptr;
|
||||
auto& fca_multiset = *fca_multiset_ptr;
|
||||
|
||||
auto foa_flat_map_ptr = tester.template construct_map<boost::unordered_flat_map>();
|
||||
auto foa_flat_set_ptr = tester.template construct_set<boost::unordered_flat_set>();
|
||||
auto foa_node_map_ptr = tester.template construct_map<boost::unordered_node_map>();
|
||||
auto foa_node_set_ptr = tester.template construct_set<boost::unordered_node_set>();
|
||||
auto& foa_flat_map = *foa_flat_map_ptr;
|
||||
auto& foa_flat_set = *foa_flat_set_ptr;
|
||||
auto& foa_node_map = *foa_node_map_ptr;
|
||||
auto& foa_node_set = *foa_node_set_ptr;
|
||||
|
||||
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) {
|
||||
const auto str = std::to_string(i * 2);
|
||||
const auto num = i * 11;
|
||||
|
||||
fca_map.emplace(str, num);
|
||||
fca_multimap.emplace(str, num);
|
||||
fca_multimap.emplace(str, num + 1);
|
||||
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);
|
||||
fca_multiset.emplace(str);
|
||||
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();
|
||||
auto fca_map_end = fca_map.end();
|
||||
auto fca_multimap_begin = fca_multimap.begin();
|
||||
auto fca_multimap_end = fca_multimap.end();
|
||||
auto fca_set_begin = fca_set.begin();
|
||||
auto fca_set_end = fca_set.end();
|
||||
auto fca_multiset_begin = fca_multiset.begin();
|
||||
auto fca_multiset_end = fca_multiset.end();
|
||||
|
||||
auto foa_flat_map_begin = foa_flat_map.begin();
|
||||
auto foa_flat_map_end = foa_flat_map.end();
|
||||
auto foa_flat_set_begin = foa_flat_set.begin();
|
||||
auto foa_flat_set_end = foa_flat_set.end();
|
||||
auto foa_node_map_begin = foa_node_map.begin();
|
||||
auto foa_node_map_end = foa_node_map.end();
|
||||
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, 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,
|
||||
foa_flat_set_end, foa_node_map_begin, foa_node_map_end, foa_node_set_begin,
|
||||
foa_node_set_end);
|
||||
|
||||
goto break_here;
|
||||
break_here:;
|
||||
}
|
||||
|
||||
class offset_ptr_tester_
|
||||
{
|
||||
static constexpr std::size_t SEGMENT_SIZE = 64 * 1024;
|
||||
std::string segment_name = to_string(boost::uuids::random_generator()());
|
||||
boost::interprocess::managed_shared_memory segment{
|
||||
boost::interprocess::create_only, segment_name.c_str(), SEGMENT_SIZE};
|
||||
|
||||
using map_allocator = boost::interprocess::allocator<map_value_type,
|
||||
boost::interprocess::managed_shared_memory::segment_manager>;
|
||||
using set_allocator = boost::interprocess::allocator<set_value_type,
|
||||
boost::interprocess::managed_shared_memory::segment_manager>;
|
||||
|
||||
template <template <class...> class MapTemplate>
|
||||
using map_type = MapTemplate<std::string, int, boost::hash<std::string>,
|
||||
std::equal_to<std::string>, map_allocator>;
|
||||
template <template <class...> class SetTemplate>
|
||||
using set_type = SetTemplate<std::string, boost::hash<std::string>,
|
||||
std::equal_to<std::string>, set_allocator>;
|
||||
|
||||
public:
|
||||
offset_ptr_tester_()
|
||||
{
|
||||
boost::interprocess::shared_memory_object::remove(segment_name.c_str());
|
||||
}
|
||||
~offset_ptr_tester_()
|
||||
{
|
||||
boost::interprocess::shared_memory_object::remove(segment_name.c_str());
|
||||
}
|
||||
|
||||
template <template <class...> class MapTemplate>
|
||||
map_type<MapTemplate>* construct_map()
|
||||
{
|
||||
return &*segment.construct<map_type<MapTemplate> >(
|
||||
typeid(map_type<MapTemplate>).name())(
|
||||
map_allocator(segment.get_segment_manager()));
|
||||
}
|
||||
template <template <class...> class SetTemplate>
|
||||
set_type<SetTemplate>* construct_set()
|
||||
{
|
||||
return &*segment.construct<set_type<SetTemplate> >(
|
||||
typeid(set_type<SetTemplate>).name())(
|
||||
set_allocator(segment.get_segment_manager()));
|
||||
}
|
||||
} offset_ptr_tester;
|
||||
|
||||
class default_tester_
|
||||
{
|
||||
template <template <class...> class MapTemplate>
|
||||
using map_type = MapTemplate<std::string, int, boost::hash<std::string>,
|
||||
std::equal_to<std::string>, std::allocator<map_value_type> >;
|
||||
template <template <class...> class SetTemplate>
|
||||
using set_type = SetTemplate<std::string, boost::hash<std::string>,
|
||||
std::equal_to<std::string>, std::allocator<set_value_type> >;
|
||||
|
||||
public:
|
||||
template <template <class...> class MapTemplate>
|
||||
std::unique_ptr<map_type<MapTemplate> > construct_map()
|
||||
{
|
||||
using type = map_type<MapTemplate>;
|
||||
return std::unique_ptr<type>(new type());
|
||||
}
|
||||
template <template <class...> class SetTemplate>
|
||||
std::unique_ptr<set_type<SetTemplate> > construct_set()
|
||||
{
|
||||
using type = set_type<SetTemplate>;
|
||||
return std::unique_ptr<type>(new type());
|
||||
}
|
||||
} default_tester;
|
||||
|
||||
int main()
|
||||
{
|
||||
visualization_test(default_tester);
|
||||
visualization_test(offset_ptr_tester);
|
||||
|
||||
return boost::report_errors();
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
|
||||
// Copyright 2024 Joaquin M Lopez Munz.
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or move at http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#ifndef BOOST_UNORDERED_TEST_REPLACE_ALLOCATOR
|
||||
#define BOOST_UNORDERED_TEST_REPLACE_ALLOCATOR
|
||||
|
||||
#include <boost/core/allocator_access.hpp>
|
||||
#include <utility>
|
||||
|
||||
namespace test {
|
||||
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> > >;
|
||||
};
|
||||
} // namespace test
|
||||
|
||||
#endif // !defined(BOOST_UNORDERED_TEST_REPLACE_ALLOCATOR)
|
||||
@@ -0,0 +1,46 @@
|
||||
|
||||
// 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)
|
||||
|
||||
#if !defined(BOOST_UNORDERED_TEST_NON_DEFAULT_CTBLE_ALLOCATOR_HEADER)
|
||||
#define BOOST_UNORDERED_TEST_NON_DEFAULT_CTBLE_ALLOCATOR_HEADER
|
||||
|
||||
#include <memory>
|
||||
|
||||
namespace test
|
||||
{
|
||||
template <class T> struct non_default_ctble_allocator
|
||||
{
|
||||
typedef T value_type;
|
||||
|
||||
non_default_ctble_allocator(int) {}
|
||||
|
||||
template <class U>
|
||||
non_default_ctble_allocator(const non_default_ctble_allocator<U>&) {}
|
||||
|
||||
template<class U>
|
||||
bool operator==(non_default_ctble_allocator<U> const &) const noexcept
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
template<class U>
|
||||
bool operator!=(non_default_ctble_allocator<U> const &) const noexcept
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
T* allocate(std::size_t n)
|
||||
{
|
||||
return std::allocator<T>().allocate(n);
|
||||
}
|
||||
|
||||
void deallocate(T* p, std::size_t n)
|
||||
{
|
||||
std::allocator<T>().deallocate(p, n);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -7,12 +7,16 @@
|
||||
#include "../helpers/unordered.hpp"
|
||||
|
||||
#include "../helpers/test.hpp"
|
||||
#include "../helpers/replace_allocator.hpp"
|
||||
#include "../objects/test.hpp"
|
||||
#include "../objects/cxx11_allocator.hpp"
|
||||
#include "../objects/non_default_ctble_allocator.hpp"
|
||||
#include "../helpers/random_values.hpp"
|
||||
#include "../helpers/tracker.hpp"
|
||||
#include "../helpers/equivalent.hpp"
|
||||
|
||||
#include <vector>
|
||||
|
||||
#if defined(BOOST_MSVC)
|
||||
#pragma warning(disable : 4127) // conditional expression is constant
|
||||
#endif
|
||||
@@ -288,6 +292,27 @@ namespace assign_tests {
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
BOOST_LIGHTWEIGHT_TEST_OSTREAM << "gh276\n";
|
||||
{
|
||||
// https://github.com/boostorg/unordered/issues/276
|
||||
|
||||
using value_type = typename T::value_type;
|
||||
using replaced_allocator_container = test::replace_allocator<
|
||||
T, test::non_default_ctble_allocator<int> >;
|
||||
using replaced_allocator_type =
|
||||
typename replaced_allocator_container::allocator_type;
|
||||
|
||||
test::random_values<T> v(4, generator);
|
||||
std::vector<value_type> vv(v.begin(), v.end());
|
||||
|
||||
replaced_allocator_container
|
||||
x(replaced_allocator_type(0)),
|
||||
y(vv.begin(), vv.begin() + 4, replaced_allocator_type(0));
|
||||
|
||||
x = {vv[0], vv[1], vv[2], vv[3]};
|
||||
BOOST_TEST(x == y);
|
||||
}
|
||||
}
|
||||
|
||||
using test::default_generator;
|
||||
|
||||
@@ -52,23 +52,39 @@ template class instantiate_node_map<test::minimal::assignable const,
|
||||
test::minimal::allocator<int> >;
|
||||
|
||||
#else
|
||||
#define INSTANTIATE(type) \
|
||||
template class boost::unordered::detail::instantiate_##type
|
||||
|
||||
INSTANTIATE(map)<int, int, boost::hash<int>, std::equal_to<int>,
|
||||
template <typename K, typename M, typename H, typename P, typename A>
|
||||
class instantiate_map
|
||||
{
|
||||
typedef boost::unordered_map<K, M, H, P, A> container;
|
||||
container x;
|
||||
typename container::node_type node_type;
|
||||
typename container::insert_return_type insert_return_type;
|
||||
};
|
||||
|
||||
template <typename K, typename M, typename H, typename P, typename A>
|
||||
class instantiate_multimap
|
||||
{
|
||||
typedef boost::unordered_multimap<K, M, H, P, A> container;
|
||||
container x;
|
||||
typename container::node_type node_type;
|
||||
};
|
||||
|
||||
template class instantiate_map<int, int, boost::hash<int>, std::equal_to<int>,
|
||||
test::minimal::allocator<int> >;
|
||||
INSTANTIATE(multimap)<int const, int const, boost::hash<int>,
|
||||
template class instantiate_multimap<int const, int const, boost::hash<int>,
|
||||
std::equal_to<int>, test::minimal::allocator<int> >;
|
||||
|
||||
INSTANTIATE(
|
||||
map)<test::minimal::assignable const, test::minimal::default_assignable const,
|
||||
template class instantiate_map<test::minimal::assignable const,
|
||||
test::minimal::default_assignable const,
|
||||
test::minimal::hash<test::minimal::assignable>,
|
||||
test::minimal::equal_to<test::minimal::assignable>,
|
||||
test::minimal::allocator<int> >;
|
||||
INSTANTIATE(multimap)<test::minimal::assignable, test::minimal::assignable,
|
||||
test::minimal::hash<test::minimal::assignable>,
|
||||
template class instantiate_multimap<test::minimal::assignable,
|
||||
test::minimal::assignable, test::minimal::hash<test::minimal::assignable>,
|
||||
test::minimal::equal_to<test::minimal::assignable>,
|
||||
test::minimal::allocator<int> >;
|
||||
|
||||
#endif
|
||||
|
||||
template <template <class Key, class T, class H = boost::hash<Key>,
|
||||
|
||||
@@ -52,19 +52,32 @@ template class instantiate_node_set<test::minimal::assignable const,
|
||||
|
||||
#else
|
||||
|
||||
#define INSTANTIATE(type) \
|
||||
template class boost::unordered::detail::instantiate_##type
|
||||
template <typename T, typename H, typename P, typename A> class instantiate_set
|
||||
{
|
||||
typedef boost::unordered_set<T, H, P, A> container;
|
||||
container x;
|
||||
typename container::node_type node_type;
|
||||
typename container::insert_return_type insert_return_type;
|
||||
};
|
||||
|
||||
INSTANTIATE(set)<int, boost::hash<int>, std::equal_to<int>,
|
||||
test::minimal::allocator<int> >;
|
||||
INSTANTIATE(multiset)<int const, boost::hash<int>, std::equal_to<int>,
|
||||
test::minimal::allocator<int> >;
|
||||
template <typename T, typename H, typename P, typename A>
|
||||
class instantiate_multiset
|
||||
{
|
||||
typedef boost::unordered_multiset<T, H, P, A> container;
|
||||
container x;
|
||||
typename container::node_type node_type;
|
||||
};
|
||||
|
||||
INSTANTIATE(set)<test::minimal::assignable const,
|
||||
template class instantiate_set<int, boost::hash<int>, std::equal_to<int>,
|
||||
test::minimal::allocator<int> >;
|
||||
template class instantiate_multiset<int const, boost::hash<int>,
|
||||
std::equal_to<int>, test::minimal::allocator<int> >;
|
||||
|
||||
template class instantiate_set<test::minimal::assignable const,
|
||||
test::minimal::hash<test::minimal::assignable>,
|
||||
test::minimal::equal_to<test::minimal::assignable>,
|
||||
test::minimal::allocator<int> >;
|
||||
INSTANTIATE(multiset)<test::minimal::assignable,
|
||||
template class instantiate_multiset<test::minimal::assignable,
|
||||
test::minimal::hash<test::minimal::assignable>,
|
||||
test::minimal::equal_to<test::minimal::assignable>,
|
||||
test::minimal::allocator<int> >;
|
||||
|
||||
@@ -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>,
|
||||
|
||||
@@ -0,0 +1,184 @@
|
||||
// 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 "../helpers/replace_allocator.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 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 = test::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 = test::replace_allocator<X, Allocator>;
|
||||
using node_type = typename replaced_allocator_container::node_type;
|
||||
|
||||
replaced_allocator_container x1(allocators.first), x2(allocators.second);
|
||||
x1.emplace(value_type());
|
||||
x2.emplace(value_type());
|
||||
|
||||
node_type nh1, nh2;
|
||||
|
||||
nh1 = x1.extract(0);
|
||||
swap(nh1, nh2);
|
||||
|
||||
BOOST_TEST(nh1.empty());
|
||||
BOOST_TEST(!nh2.empty());
|
||||
BOOST_TEST(nh2.get_allocator() == x1.get_allocator());
|
||||
|
||||
nh1 = x2.extract(0);
|
||||
swap(nh1, nh2);
|
||||
|
||||
BOOST_TEST(!nh1.empty());
|
||||
BOOST_TEST(nh1.get_allocator() == x1.get_allocator());
|
||||
BOOST_TEST(!nh2.empty());
|
||||
BOOST_TEST(nh2.get_allocator() == x2.get_allocator());
|
||||
}
|
||||
|
||||
#if BOOST_WORKAROUND(BOOST_MSVC, <= 1900)
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable : 4592) // symbol will be dynamically initialized
|
||||
#endif
|
||||
|
||||
std::pair<
|
||||
std::allocator<int>, std::allocator<int> > test_std_allocators({},{});
|
||||
std::pair<
|
||||
nonassignable_allocator<int>,
|
||||
nonassignable_allocator<int> > test_nonassignable_allocators({},{});
|
||||
std::pair<
|
||||
pocx_allocator<int>, pocx_allocator<int> > test_pocx_allocators(5,6);
|
||||
|
||||
#if BOOST_WORKAROUND(BOOST_MSVC, <= 1900)
|
||||
#pragma warning(pop) // C4592
|
||||
#endif
|
||||
|
||||
#if defined(BOOST_UNORDERED_FOA_TESTS)
|
||||
boost::unordered_node_map<int, int>* test_map;
|
||||
boost::unordered_node_set<int>* test_set;
|
||||
#elif defined(BOOST_UNORDERED_CFOA_TESTS)
|
||||
boost::concurrent_node_map<int, int>* test_map;
|
||||
boost::concurrent_node_set<int>* test_set;
|
||||
#else
|
||||
boost::unordered_map<int, int>* test_map;
|
||||
boost::unordered_set<int>* test_set;
|
||||
#endif
|
||||
} // namespace
|
||||
|
||||
// clang-format off
|
||||
UNORDERED_TEST(
|
||||
node_handle_allocator_tests,
|
||||
((test_map)(test_set))
|
||||
((test_std_allocators)(test_nonassignable_allocators)
|
||||
(test_pocx_allocators)))
|
||||
|
||||
UNORDERED_TEST(
|
||||
node_handle_allocator_swap_tests,
|
||||
((test_map)(test_set))
|
||||
((test_std_allocators)(test_pocx_allocators)))
|
||||
// clang-format on
|
||||
|
||||
RUN_TESTS()
|
||||
@@ -33,12 +33,23 @@ namespace pmr_allocator_tests {
|
||||
test_string_flat_map;
|
||||
static boost::unordered::pmr::concurrent_flat_map<pmr_string, pmr_string>*
|
||||
test_pmr_string_flat_map;
|
||||
static boost::unordered::pmr::concurrent_node_map<std::string, std::string>*
|
||||
test_string_node_map;
|
||||
static boost::unordered::pmr::concurrent_node_map<pmr_string, pmr_string>*
|
||||
test_pmr_string_node_map;
|
||||
static boost::unordered::pmr::concurrent_flat_set<std::string>*
|
||||
test_string_flat_set;
|
||||
static boost::unordered::pmr::concurrent_flat_set<pmr_string>*
|
||||
test_pmr_string_flat_set;
|
||||
static boost::unordered::pmr::concurrent_node_set<std::string>*
|
||||
test_string_node_set;
|
||||
static boost::unordered::pmr::concurrent_node_set<pmr_string>*
|
||||
test_pmr_string_node_set;
|
||||
#define PMR_ALLOCATOR_TESTS_ARGS \
|
||||
((test_string_flat_map)(test_pmr_string_flat_map)(test_string_flat_set)(test_pmr_string_flat_set))
|
||||
((test_string_flat_map)(test_pmr_string_flat_map) \
|
||||
(test_string_node_map)(test_pmr_string_node_map) \
|
||||
(test_string_flat_set)(test_pmr_string_flat_set) \
|
||||
(test_string_node_set)(test_pmr_string_node_set))
|
||||
#elif defined(BOOST_UNORDERED_FOA_TESTS)
|
||||
static boost::unordered::pmr::unordered_flat_map<std::string, std::string>*
|
||||
test_string_flat_map;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2024 Joaquin M Lopez Muoz.
|
||||
// Copyright 2024 Joaquin M Lopez Munoz.
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
@@ -7,8 +7,12 @@
|
||||
#ifdef BOOST_UNORDERED_CFOA_TESTS
|
||||
#include <boost/unordered/concurrent_flat_map.hpp>
|
||||
#include <boost/unordered/concurrent_flat_set.hpp>
|
||||
#include <boost/unordered/concurrent_node_map.hpp>
|
||||
#include <boost/unordered/concurrent_node_set.hpp>
|
||||
#include <boost/unordered/unordered_flat_map.hpp>
|
||||
#include <boost/unordered/unordered_flat_set.hpp>
|
||||
#include <boost/unordered/unordered_node_map.hpp>
|
||||
#include <boost/unordered/unordered_node_set.hpp>
|
||||
#include "../cfoa/helpers.hpp"
|
||||
#else
|
||||
#include "../helpers/unordered.hpp"
|
||||
@@ -19,6 +23,7 @@
|
||||
#include "../helpers/test.hpp"
|
||||
#include <boost/assert.hpp>
|
||||
#include <boost/core/make_span.hpp>
|
||||
#include <cmath>
|
||||
#include <cstring>
|
||||
|
||||
template <class T> struct unequal_allocator
|
||||
@@ -45,16 +50,20 @@ template <class T> struct unequal_allocator
|
||||
int n_;
|
||||
};
|
||||
|
||||
bool exact_same(double x, double y)
|
||||
bool esentially_same(double x, double y)
|
||||
{
|
||||
return std::memcmp(
|
||||
reinterpret_cast<void*>(&x), reinterpret_cast<void*>(&y),
|
||||
sizeof(double))==0;
|
||||
// Some optimizer-related issues in GCC X86 result in last-bit differences
|
||||
// on doubles that should otherwise be identical.
|
||||
|
||||
// https://stackoverflow.com/a/253874/213114
|
||||
|
||||
static constexpr double epsilon = 1.0E-6;
|
||||
return fabs(x - y) <= ( (fabs(x) > fabs(y) ? fabs(x) : fabs(y)) * epsilon);
|
||||
}
|
||||
|
||||
bool not_exact_same(double x, double y)
|
||||
bool not_esentially_same(double x, double y)
|
||||
{
|
||||
return !exact_same(x, y);
|
||||
return !esentially_same(x, y);
|
||||
}
|
||||
|
||||
enum check_stats_contition
|
||||
@@ -69,19 +78,19 @@ void check_stat(const Stats& s, check_stats_contition cond)
|
||||
{
|
||||
switch (cond) {
|
||||
case stats_empty:
|
||||
BOOST_TEST(exact_same(s.average, 0.0));
|
||||
BOOST_TEST(exact_same(s.variance, 0.0));
|
||||
BOOST_TEST(exact_same(s.deviation, 0.0));
|
||||
BOOST_TEST(esentially_same(s.average, 0.0));
|
||||
BOOST_TEST(esentially_same(s.variance, 0.0));
|
||||
BOOST_TEST(esentially_same(s.deviation, 0.0));
|
||||
break;
|
||||
case stats_full:
|
||||
BOOST_TEST_GT(s.average, 0.0);
|
||||
if(not_exact_same(s.variance, 0.0)) {
|
||||
if(not_esentially_same(s.variance, 0.0)) {
|
||||
BOOST_TEST_GT(s.variance, 0.0);
|
||||
BOOST_TEST_GT(s.deviation, 0.0);
|
||||
}
|
||||
break;
|
||||
case stats_mostly_full:
|
||||
if(not_exact_same(s.variance, 0.0)) {
|
||||
if(not_esentially_same(s.variance, 0.0)) {
|
||||
BOOST_TEST_GT(s.average, 0.0);
|
||||
BOOST_TEST_GT(s.variance, 0.0);
|
||||
BOOST_TEST_GT(s.deviation, 0.0);
|
||||
@@ -94,9 +103,9 @@ void check_stat(const Stats& s, check_stats_contition cond)
|
||||
|
||||
template <class Stats> void check_stat(const Stats& s1, const Stats& s2)
|
||||
{
|
||||
BOOST_TEST(exact_same(s1.average, s2.average));
|
||||
BOOST_TEST(exact_same(s1.variance, s2.variance));
|
||||
BOOST_TEST(exact_same(s1.deviation, s2.deviation));
|
||||
BOOST_TEST(esentially_same(s1.average, s2.average));
|
||||
BOOST_TEST(esentially_same(s1.variance, s2.variance));
|
||||
BOOST_TEST(esentially_same(s1.deviation, s2.deviation));
|
||||
}
|
||||
|
||||
template <class Stats>
|
||||
@@ -345,15 +354,28 @@ UNORDERED_AUTO_TEST (stats_) {
|
||||
boost::concurrent_flat_map<
|
||||
int, int, boost::hash<int>, std::equal_to<int>,
|
||||
unequal_allocator< std::pair< const int, int> >>>();
|
||||
test_stats<
|
||||
boost::concurrent_node_map<
|
||||
int, int, boost::hash<int>, std::equal_to<int>,
|
||||
unequal_allocator< std::pair< const int, int> >>>();
|
||||
test_stats<
|
||||
boost::concurrent_flat_set<
|
||||
int, boost::hash<int>, std::equal_to<int>, unequal_allocator<int>>>();
|
||||
test_stats<
|
||||
boost::concurrent_node_set<
|
||||
int, boost::hash<int>, std::equal_to<int>, unequal_allocator<int>>>();
|
||||
test_stats_concurrent_unordered_interop<
|
||||
boost::unordered_flat_map<int, int>,
|
||||
boost::concurrent_flat_map<int, int>>();
|
||||
test_stats_concurrent_unordered_interop<
|
||||
boost::unordered_node_map<int, int>,
|
||||
boost::concurrent_node_map<int, int>>();
|
||||
test_stats_concurrent_unordered_interop<
|
||||
boost::unordered_flat_set<int>,
|
||||
boost::concurrent_flat_set<int>>();
|
||||
test_stats_concurrent_unordered_interop<
|
||||
boost::unordered_node_set<int>,
|
||||
boost::concurrent_node_set<int>>();
|
||||
#elif defined(BOOST_UNORDERED_FOA_TESTS)
|
||||
test_stats<
|
||||
boost::unordered_flat_map<
|
||||
|
||||
Reference in New Issue
Block a user