Compare commits

..

15 Commits

Author SHA1 Message Date
joaquintides e8cd7da67a updated concurrent map benchmark plots 2023-07-18 18:16:48 +02:00
joaquintides d2797abcb7 dropped unnecessary type trimming 2023-07-13 12:26:50 +02:00
joaquintides ffb34fdcde rewritten to appease VS2017 and older 2023-07-13 09:52:59 +02:00
joaquintides 38eca0efc0 refactored takes_arg_as_const_reference.hpp again, fixed tests 2023-07-12 18:35:35 +02:00
joaquintides 4f8a037957 worked around noexcept-type warning 2023-07-11 21:04:02 +02:00
joaquintides 61d92b5847 refactored takes_arg_as_const_reference.hpp 2023-07-11 20:08:03 +02:00
joaquintides 33f4b04f69 worked around VS2015 issue 2023-07-10 19:54:45 +02:00
joaquintides 8b024986da restricted force_evaluation workaround to VS 2023-07-10 18:17:40 +02:00
joaquintides 4948a3a12e simplified force_evaluation 2023-07-10 17:47:42 +02:00
joaquintides 35c265f313 worked around C3517 error in VS2017 and older 2023-07-10 12:07:42 +02:00
joaquintides 15b63cc169 made some lambdas generic (copypaste error) 2023-07-10 10:14:46 +02:00
joaquintides 3de21a474c used shared group access for visitation functions taking its arg as a cref 2023-07-09 20:01:24 +02:00
joaquintides 6a6a3084da worked around noexcept forbidding in function typedefs 2023-07-09 19:32:09 +02:00
joaquintides abd69e859e fixed error with non-local capturing lambdas 2023-07-09 19:10:59 +02:00
joaquintides ff20d71676 added takes_arg_as_const_reference.hpp and tests 2023-07-09 18:42:14 +02:00
133 changed files with 1978 additions and 15741 deletions
+1 -1
View File
@@ -20,4 +20,4 @@ b2 -d0 headers
if not "%CXXSTD%" == "" set CXXSTD=cxxstd=%CXXSTD%
if not "%ADDRMD%" == "" set ADDRMD=address-model=%ADDRMD%
b2 --abbreviate-paths -j3 libs/%LIBRARY%/test toolset=%TOOLSET% %CXXSTD% %ADDRMD% variant=debug,release embed-manifest-via=linker
b2 -j3 libs/%LIBRARY%/test toolset=%TOOLSET% %CXXSTD% %ADDRMD% variant=debug,release embed-manifest-via=linker
+38 -54
View File
@@ -32,16 +32,6 @@ env:
CODECOV_NAME: Github Actions
jobs:
runner-selection:
# runs-on: ubuntu-latest
runs-on: ${{ github.repository_owner == 'boostorg' && fromJSON('[ "self-hosted", "linux", "x64", "ubuntu-latest-aws" ]') || 'ubuntu-latest' }}
outputs:
labelmatrix: ${{ steps.aws_hosted_runners.outputs.labelmatrix }}
steps:
- name: AWS Hosted Runners
id: aws_hosted_runners
uses: cppalliance/aws-hosted-runners@v1.0.0
posix:
defaults:
run:
@@ -52,55 +42,52 @@ jobs:
matrix:
include:
# Linux, gcc
- { compiler: gcc-7, cxxstd: '03,11,14,17', os: 'ubuntu-20.04', install: 'g++-7' }
- { compiler: gcc-8, cxxstd: '03,11,14,17', os: 'ubuntu-20.04', install: 'g++-8' }
- { compiler: gcc-9, cxxstd: '03,11,14,17', os: 'ubuntu-22.04', install: 'g++-9' }
- { compiler: gcc-10, cxxstd: '03,11,14,17,20', os: 'ubuntu-22.04', install: 'g++-10' }
- { compiler: gcc-11, cxxstd: '03,11,14,17,20', os: 'ubuntu-22.04', install: 'g++-11' }
- { compiler: gcc-7, cxxstd: '03,11,14,17', os: ubuntu-20.04, install: 'g++-7' }
- { compiler: gcc-8, cxxstd: '03,11,14,17', os: ubuntu-20.04, install: 'g++-8' }
- { compiler: gcc-9, cxxstd: '03,11,14,17', os: ubuntu-22.04, install: 'g++-9' }
- { compiler: gcc-10, cxxstd: '03,11,14,17,20', os: ubuntu-22.04, install: 'g++-10' }
- { compiler: gcc-11, cxxstd: '03,11,14,17,20', os: ubuntu-22.04, install: 'g++-11' }
- { name: "gcc-12 w/ sanitizers (03,11,14)", sanitize: yes,
compiler: gcc-12, cxxstd: '03,11,14', os: 'ubuntu-22.04', ccache_key: "san1" }
compiler: gcc-12, cxxstd: '03,11,14', os: ubuntu-22.04, ccache_key: "san1" }
- { name: "gcc-12 w/ sanitizers (17,20,2b)", sanitize: yes,
compiler: gcc-12, cxxstd: '17,20,2b', os: 'ubuntu-22.04', ccache_key: "san2" }
compiler: gcc-12, cxxstd: '17,20,2b', os: ubuntu-22.04, ccache_key: "san2" }
- { name: Collect coverage, coverage: yes,
compiler: gcc-12, cxxstd: '03,20', os: 'ubuntu-22.04', install: 'g++-12-multilib', address-model: '32,64', ccache_key: "cov" }
compiler: gcc-12, cxxstd: '03,20', os: ubuntu-22.04, install: 'g++-12-multilib', address-model: '32,64', ccache_key: "cov" }
- { name: "cfoa tsan (gcc-12)", cxxstd: '11,14,17,20,2b', os: 'ubuntu-22.04', compiler: gcc-12,
- { name: "cfoa tsan (gcc-12)", cxxstd: '11,14,17,20,2b', os: ubuntu-22.04, compiler: gcc-12,
targets: 'libs/unordered/test//cfoa_tests', thread-sanitize: yes, ccache_key: "tsan" }
# Linux, clang, libc++
- { compiler: clang-7, cxxstd: '03,11,14,17', os: 'ubuntu-20.04', stdlib: libc++, install: 'clang-7 libc++-7-dev libc++abi-7-dev' }
- { compiler: clang-10, cxxstd: '03,11,14,17,20', os: 'ubuntu-20.04', stdlib: libc++, install: 'clang-10 libc++-10-dev libc++abi-10-dev' }
- { compiler: clang-11, cxxstd: '03,11,14,17,20', os: 'ubuntu-22.04', stdlib: libc++, install: 'clang-11 libc++-11-dev libc++abi-11-dev' }
# clang-12 doesn't work on 'ubuntu-22.04', the linker can't find -lunwind for some reason
- { compiler: clang-7, cxxstd: '03,11,14,17', os: ubuntu-20.04, stdlib: libc++, install: 'clang-7 libc++-7-dev libc++abi-7-dev' }
- { compiler: clang-10, cxxstd: '03,11,14,17,20', os: ubuntu-20.04, stdlib: libc++, install: 'clang-10 libc++-10-dev libc++abi-10-dev' }
- { compiler: clang-11, cxxstd: '03,11,14,17,20', os: ubuntu-22.04, stdlib: libc++, install: 'clang-11 libc++-11-dev libc++abi-11-dev' }
# clang-12 doesn't work on ubuntu-22.04, the linker can't find -lunwind for some reason
- { name: "clang-12 w/ sanitizers (03,11,14)", sanitize: yes,
compiler: clang-12, cxxstd: '03,11,14', os: 'ubuntu-20.04', stdlib: libc++, install: 'clang-12 libc++-12-dev libc++abi-12-dev', ccache_key: "san1" }
compiler: clang-12, cxxstd: '03,11,14', os: ubuntu-20.04, stdlib: libc++, install: 'clang-12 libc++-12-dev libc++abi-12-dev', ccache_key: "san1" }
- { name: "clang-12 w/ sanitizers (17,20,2b)", sanitize: yes,
compiler: clang-12, cxxstd: '17,20,2b', os: 'ubuntu-20.04', stdlib: libc++, install: 'clang-12 libc++-12-dev libc++abi-12-dev', ccache_key: "san2" }
- { compiler: clang-13, cxxstd: '03,11,14,17,20,2b', os: 'ubuntu-22.04', stdlib: libc++, install: 'clang-13 libc++-13-dev libc++abi-13-dev' }
compiler: clang-12, cxxstd: '17,20,2b', os: ubuntu-20.04, stdlib: libc++, install: 'clang-12 libc++-12-dev libc++abi-12-dev', ccache_key: "san2" }
- { compiler: clang-13, cxxstd: '03,11,14,17,20,2b', os: ubuntu-22.04, stdlib: libc++, install: 'clang-13 libc++-13-dev libc++abi-13-dev' }
# not using libc++ because of https://github.com/llvm/llvm-project/issues/52771
- { name: "clang-14 w/ sanitizers (03,11,14)", sanitize: yes,
compiler: clang-14, cxxstd: '03,11,14', os: 'ubuntu-22.04', ccache_key: "san1" }
compiler: clang-14, cxxstd: '03,11,14', os: ubuntu-22.04, ccache_key: "san1" }
- { name: "clang-14 w/ sanitizers (17,20,2b)", sanitize: yes,
compiler: clang-14, cxxstd: '17,20,2b', os: 'ubuntu-22.04', ccache_key: "san2" }
compiler: clang-14, cxxstd: '17,20,2b', os: ubuntu-22.04, ccache_key: "san2" }
- { name: "cfoa tsan (clang-14)", cxxstd: '11,14,17,20,2b', os: 'ubuntu-22.04', compiler: clang-14,
- { name: "cfoa tsan (clang-14)", cxxstd: '11,14,17,20,2b', os: ubuntu-22.04, compiler: clang-14,
targets: 'libs/unordered/test//cfoa_tests', thread-sanitize: yes,
stdlib: libc++, install: 'clang-14 libc++-14-dev libc++abi-14-dev', ccache_key: "tsan" }
- { compiler: clang-15, cxxstd: '03,11,14,17,20,2b', os: 'ubuntu-22.04', stdlib: libc++, install: 'clang-15 libc++-15-dev libc++abi-15-dev' }
- { compiler: clang-15, cxxstd: '03,11,14,17,20,2b', os: ubuntu-22.04, stdlib: libc++, install: 'clang-15 libc++-15-dev libc++abi-15-dev' }
# OSX, clang
- { compiler: clang, cxxstd: '03,11,14,17,2a', os: 'macos-11' }
- { compiler: clang, cxxstd: '03,11,14,17,2a', os: 'macos-12', sanitize: yes, ccache_key: "san1" }
- { compiler: clang, cxxstd: '11,14,17,2a', os: 'macos-12', thread-sanitize: yes, targets: 'libs/unordered/test//cfoa_tests', ccache_key: "tsan" }
- { compiler: clang, cxxstd: '03,11,14,17,20,2b', os: 'macos-13' }
- { compiler: clang, cxxstd: '03,11,14,17,2a', os: macos-11 }
- { compiler: clang, cxxstd: '03,11,14,17,2a', os: macos-12, sanitize: yes, ccache_key: "san1" }
- { compiler: clang, cxxstd: '11,14,17,2a', os: macos-12, thread-sanitize: yes, targets: 'libs/unordered/test//cfoa_tests', ccache_key: "tsan" }
- { compiler: clang, cxxstd: '03,11,14,17,20,2b', os: macos-13 }
timeout-minutes: 360
# posix (gcc-12 w/ sanitizers is taking longer than 210 minutes
# timeout-minutes: 210
needs: [runner-selection]
runs-on: ${{ fromJSON(needs.runner-selection.outputs.labelmatrix)[matrix.os] }}
timeout-minutes: 210
runs-on: ${{matrix.os}}
container: ${{matrix.container}}
env: {B2_USE_CCACHE: 1}
@@ -244,16 +231,15 @@ jobs:
fail-fast: false
matrix:
include:
- { toolset: msvc-14.0, cxxstd: '14,latest', addrmd: '32,64', os: 'windows-2019', variant: 'debug,release' }
- { toolset: msvc-14.2, cxxstd: '14,17,20,latest', addrmd: '32,64', os: 'windows-2019', variant: 'debug,release' }
- { toolset: msvc-14.3, cxxstd: '14,17,20,latest', addrmd: '32,64', os: 'windows-2022', variant: 'debug,release' }
- { toolset: msvc-14.3, cxxstd: '14', addrmd: '64', os: 'windows-2022', variant: 'debug', defines: '_ALLOW_RTCc_IN_STL', cxxflags: '/RTCc' }
- { toolset: msvc-14.3, cxxstd: '14', addrmd: '32', os: 'windows-2022', variant: 'debug', defines: '_ALLOW_RTCc_IN_STL', cxxflags: '"/RTCc /arch:IA32"' }
- { toolset: clang-win, cxxstd: '14,17,latest', addrmd: '32,64', os: 'windows-2022', variant: 'debug,release' }
- { toolset: gcc, cxxstd: '03,11,14,17,2a', addrmd: '64', os: 'windows-2019', variant: 'debug,release' }
- { toolset: msvc-14.0, cxxstd: '14,latest', addrmd: '32,64', os: windows-2019, variant: 'debug,release' }
- { toolset: msvc-14.2, cxxstd: '14,17,20,latest', addrmd: '32,64', os: windows-2019, variant: 'debug,release' }
- { toolset: msvc-14.3, cxxstd: '14,17,20,latest', addrmd: '32,64', os: windows-2022, variant: 'debug,release' }
- { toolset: msvc-14.3, cxxstd: '14', addrmd: '64', os: windows-2022, variant: 'debug', defines: '_ALLOW_RTCc_IN_STL', cxxflags: '/RTCc' }
- { toolset: msvc-14.3, cxxstd: '14', addrmd: '32', os: windows-2022, variant: 'debug', defines: '_ALLOW_RTCc_IN_STL', cxxflags: '"/RTCc /arch:IA32"' }
- { toolset: clang-win, cxxstd: '14,17,latest', addrmd: '32,64', os: windows-2022, variant: 'debug,release' }
- { toolset: gcc, cxxstd: '03,11,14,17,2a', addrmd: '64', os: windows-2019, variant: 'debug,release' }
needs: [runner-selection]
runs-on: ${{ fromJSON(needs.runner-selection.outputs.labelmatrix)[matrix.os] }}
runs-on: ${{matrix.os}}
steps:
- uses: actions/checkout@v3
@@ -310,8 +296,7 @@ jobs:
- { sys: MINGW32, compiler: gcc, cxxstd: '03,11,17,20' }
- { sys: MINGW64, compiler: gcc, cxxstd: '03,11,17,20' }
needs: [runner-selection]
runs-on: ${{ fromJSON(needs.runner-selection.outputs.labelmatrix)['windows-latest'] }}
runs-on: windows-latest
steps:
- uses: actions/checkout@v3
@@ -365,12 +350,11 @@ jobs:
fail-fast: false
matrix:
include:
- { os: 'ubuntu-20.04', build_shared: ON, build_type: Debug, generator: 'Unix Makefiles' }
- { os: 'windows-2019', build_shared: ON, build_type: Debug, generator: 'Visual Studio 16 2019' }
- { os: ubuntu-20.04, build_shared: ON, build_type: Debug, generator: 'Unix Makefiles' }
- { os: windows-2019, build_shared: ON, build_type: Debug, generator: 'Visual Studio 16 2019' }
timeout-minutes: 120
needs: [runner-selection]
runs-on: ${{ fromJSON(needs.runner-selection.outputs.labelmatrix)[matrix.os] }}
runs-on: ${{matrix.os}}
steps:
- uses: actions/checkout@v3
-13
View File
@@ -6,19 +6,6 @@
:github-pr-url: https://github.com/boostorg/unordered/pull
:cpp: C++
== Release 1.84.0 - Major update
* Added `boost::concurrent_flat_set`.
* Added `[c]visit_while` operations to concurrent containers,
with serial and parallel variants.
* Added efficient move construction of `boost::unordered_flat_(map|set)` from
`boost::concurrent_flat_(map|set)` and vice versa.
* Added debug-mode mechanisms for detecting illegal reentrancies into
a concurrent container from user code.
* Added Boost.Serialization support to all containers and their (non-local) iterator types.
* Added support for fancy pointers to open-addressing and concurrent containers.
This enables scenarios like the use of Boost.Interprocess allocators to construct containers in shared memory.
== Release 1.83.0 - Major update
* Added `boost::concurrent_flat_map`, a fast, thread-safe hashmap based on open addressing.
+13 -13
View File
@@ -148,14 +148,14 @@ 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`
are modelled after `std::unordered_flat_set` and `std::unordered_flat_map`, respectively,
with the crucial difference that iterators are not provided
There is currently no specification in the C++ standard for this or any other concurrent
data structure. `boost::concurrent_flat_map` takes the same template parameters as `std::unordered_map`
and all the maps provided by Boost.Unordered, and its API is modelled after that of
`boost::unordered_flat_map` with the crucial difference that iterators are not provided
due to their inherent problems in concurrent scenarios (high contention, prone to deadlocking):
so, Boost.Unordered concurrent containers are technically not models of
so, `boost::concurrent_flat_map` is technically not a
https://en.cppreference.com/w/cpp/named_req/Container[Container^], although
they meet all the requirements of https://en.cppreference.com/w/cpp/named_req/AllocatorAwareContainer[AllocatorAware^]
it meets all the requirements of https://en.cppreference.com/w/cpp/named_req/AllocatorAwareContainer[AllocatorAware^]
containers except those implying iterators.
In a non-concurrent unordered container, iterators serve two main purposes:
@@ -163,7 +163,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::concurrent_flat_map` uses _internal visitation_
facilities as a thread-safe substitute. Classical operations returning an iterator to an
element already existing in the container, like for instance:
@@ -191,15 +191,15 @@ template<class F> size_t visit_all(F f);
----
of which there are parallelized versions in C++17 compilers with parallel
algorithm support. In general, the interface of concurrent containers
is derived from that of their non-concurrent counterparts by a fairly straightforward
process of replacing iterators with visitation where applicable. If for
regular maps `iterator` and `const_iterator` provide mutable and const access to elements,
algorithm support. In general, the interface of `boost::concurrent_flat_map`
is derived from that of `boost::unordered_flat_map` by a fairly straightforward
process of replacing iterators with visitation where applicable. If
`iterator` and `const_iterator` provide mutable and const access to elements,
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.
explicit const visitation).
One notable operation not provided by `boost::concurrent_flat_map` is `operator[]`/`at`, which can be
The one notable operation not provided 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`].
+18 -71
View File
@@ -3,8 +3,8 @@
:idprefix: concurrent_
Boost.Unordered provides `boost::concurrent_flat_set` and `boost::concurrent_flat_map`,
hash tables that allow concurrent write/read access from
Boost.Unordered currently provides just one concurrent container named `boost::concurrent_flat_map`.
`boost::concurrent_flat_map` is a hash table that allows concurrent write/read access from
different threads without having to implement any synchronzation mechanism on the user's side.
[source,c++]
@@ -36,16 +36,16 @@ In the example above, threads access `m` without synchronization, just as we'd d
single-threaded scenario. In an ideal setting, if a given workload is distributed among
_N_ threads, execution is _N_ times faster than with one thread —this limit is
never attained in practice due to synchronization overheads and _contention_ (one thread
waiting for another to leave a locked portion of the map), but Boost.Unordered concurrent containers
are designed to perform with very little overhead and typically achieve _linear scaling_
waiting for another to leave a locked portion of the map), but `boost::concurrent_flat_map`
is designed to perform with very little overhead and typically achieves _linear scaling_
(that is, performance is proportional to the number of threads up to the number of
logical cores in the CPU).
== Visitation-based API
The first thing a new user of `boost::concurrent_flat_set` or `boost::concurrent_flat_map`
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^]
The first thing a new user of `boost::concurrent_flat_map` will notice is that this
class _does not provide iterators_ (which makes it technically
not a https://en.cppreference.com/w/cpp/named_req/Container[Container^]
in the C++ standard sense). The reason for this is that iterators are inherently
thread-unsafe. Consider this hypothetical code:
@@ -73,7 +73,7 @@ m.visit(k, [](const auto& x) { // x is the element with key k (if it exists)
----
The visitation function passed by the user (in this case, a lambda function)
is executed internally by Boost.Unordered in
is executed internally by `boost::concurrent_flat_map` in
a thread-safe manner, so it can access the element without worrying about other
threads interfering in the process.
@@ -112,7 +112,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 `boost::concurrent_flat_map`, and
many classical operations have visitation-enabled variations:
[source,c++]
@@ -129,17 +129,13 @@ the element: as a general rule, operations on a `boost::concurrent_flat_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,
visitation is always const access.
Consult the references of
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.
in higher parallelization. Consult the xref:#concurrent_flat_map[reference]
for a complete list of available operations.
== Whole-Table Visitation
In the absence of iterators, `visit_all` is provided
as an alternative way to process all the elements in the container:
In the absence of iterators, `boost::concurrent_flat_map` provides `visit_all`
as an alternative way to process all the elements in the map:
[source,c++]
----
@@ -158,28 +154,7 @@ m.visit_all(std::execution::par, [](auto& x) { // run in parallel
});
----
Traversal can be interrupted midway:
[source,c++]
----
// finds the key to a given (unique) value
int key = 0;
int value = ...;
bool found = !m.visit_while([&](const auto& x) {
if(x.second == value) {
key = x.first;
return false; // finish
}
else {
return true; // keep on visiting
}
});
if(found) { ... }
----
There is one last whole-table visitation operation, `erase_if`:
There is another whole-table visitation operation, `erase_if`:
[source,c++]
----
@@ -188,15 +163,15 @@ m.erase_if([](auto& x) {
});
----
`visit_while` and `erase_if` can also be parallelized. Note that, in order to increase efficiency,
whole-table visitation operations do not block the table during execution: this implies that elements
`erase_if` can also be parallelized. Note that, in order to increase efficiency,
these operations do not block the table during execution: this implies that elements
may be inserted, modified or erased by other threads during visitation. It is
advisable not to assume too much about the exact global state of a concurrent container
advisable not to assume too much about the exact global state of a `boost::concurrent_flat_map`
at any point in your program.
== Blocking Operations
``boost::concurrent_flat_set``s and ``boost::concurrent_flat_map``s can be copied, assigned, cleared and merged just like any
``boost::concurrent_flat_map``s can be copied, assigned, cleared and merged just like any
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
@@ -205,31 +180,3 @@ and the user need not take any special precaution, but overall performance may b
Another blocking operation is _rehashing_, which happens explicitly via `rehash`/`reserve`
or during insertion when the table's load hits `max_load()`. As with non-concurrent containers,
reserving space in advance of bulk insertions will generally speed up the process.
== 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.
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:
the population phase can be done in parallel with `boost::concurrent_flat_map` and results
then transferred to the final container.
[source,c++]
----
std::vector<std::string> words = ...;
// Insert words in parallel
boost::concurrent_flat_map<std::string_view, std::size_t> m0;
std::for_each(
std::execution::par, words.begin(), words.end(),
[&](const auto& word) {
m0.try_emplace_or_visit(word, 1, [](auto& x) { ++x.second; });
});
// Transfer to a regular unordered_flat_map
boost::unordered_flat_map m=std::move(m0);
----
+7 -120
View File
@@ -69,7 +69,6 @@ namespace boost {
explicit xref:#concurrent_flat_map_allocator_constructor[concurrent_flat_map](const Allocator& a);
xref:#concurrent_flat_map_copy_constructor_with_allocator[concurrent_flat_map](const concurrent_flat_map& other, const Allocator& a);
xref:#concurrent_flat_map_move_constructor_with_allocator[concurrent_flat_map](concurrent_flat_map&& other, const Allocator& a);
xref:#concurrent_flat_map_move_constructor_from_unordered_flat_map[concurrent_flat_map](unordered_flat_map<Key, T, Hash, Pred, Allocator>&& other);
xref:#concurrent_flat_map_initializer_list_constructor[concurrent_flat_map](std::initializer_list<value_type> il,
size_type n = _implementation-defined_
const hasher& hf = hasher(),
@@ -90,10 +89,9 @@ namespace boost {
const allocator_type& a);
xref:#concurrent_flat_map_destructor[~concurrent_flat_map]();
concurrent_flat_map& xref:#concurrent_flat_map_copy_assignment[operator++=++](const concurrent_flat_map& other);
concurrent_flat_map& xref:#concurrent_flat_map_move_assignment[operator++=++](concurrent_flat_map&& other) ++noexcept(
(boost::allocator_traits<Allocator>::is_always_equal::value ||
boost::allocator_traits<Allocator>::propagate_on_container_move_assignment::value) &&
std::is_same<pointer, value_type*>::value);++
concurrent_flat_map& xref:#concurrent_flat_map_move_assignment[operator++=++](concurrent_flat_map&& other)
noexcept(boost::allocator_traits<Allocator>::is_always_equal::value ||
boost::allocator_traits<Allocator>::propagate_on_container_move_assignment::value);
concurrent_flat_map& xref:#concurrent_flat_map_initializer_list_assignment[operator++=++](std::initializer_list<value_type>);
allocator_type xref:#concurrent_flat_map_get_allocator[get_allocator]() const noexcept;
@@ -116,16 +114,6 @@ namespace boost {
template<class ExecutionPolicy, class F>
void xref:#concurrent_flat_map_parallel_cvisit_all[cvisit_all](ExecutionPolicy&& policy, F f) const;
template<class F> bool xref:#concurrent_flat_map_cvisit_while[visit_while](F f);
template<class F> bool xref:#concurrent_flat_map_cvisit_while[visit_while](F f) const;
template<class F> bool xref:#concurrent_flat_map_cvisit_while[cvisit_while](F f) const;
template<class ExecutionPolicy, class F>
bool xref:#concurrent_flat_map_parallel_cvisit_while[visit_while](ExecutionPolicy&& policy, F f);
template<class ExecutionPolicy, class F>
bool xref:#concurrent_flat_map_parallel_cvisit_while[visit_while](ExecutionPolicy&& policy, F f) const;
template<class ExecutionPolicy, class F>
bool xref:#concurrent_flat_map_parallel_cvisit_while[cvisit_while](ExecutionPolicy&& policy, F f) const;
// capacity
++[[nodiscard]]++ bool xref:#concurrent_flat_map_empty[empty]() const noexcept;
size_type xref:#concurrent_flat_map_size[size]() const noexcept;
@@ -317,7 +305,8 @@ https://en.cppreference.com/w/cpp/named_req/Erasable[Erasable^] from the table.
|_Allocator_
|An allocator whose value type is the same as the table's value type.
Allocators using https://en.cppreference.com/w/cpp/named_req/Allocator#Fancy_pointers[fancy pointers] are supported.
`std::allocator_traits<Allocator>::pointer` and `std::allocator_traits<Allocator>::const_pointer`
must be convertible to/from `value_type*` and `const value_type*`, respectively.
|===
@@ -375,18 +364,6 @@ if concurrent outstanding operations on `y` do not access `x` directly or indire
---
=== Configuration Macros
==== `BOOST_UNORDERED_DISABLE_REENTRANCY_CHECK`
In debug builds (more precisely, when
link:../../../assert/doc/html/assert.html#boost_assert_is_void[`BOOST_ASSERT_IS_VOID`^]
is not defined), __container reentrancies__ (illegaly invoking an operation on `m` from within
a function visiting elements of `m`) are detected and signalled through `BOOST_ASSERT_MSG`.
When run-time speed is a concern, the feature can be disabled by globally defining
this macro.
=== Constructors
==== Default Constructor
@@ -514,21 +491,6 @@ Concurrency:;; Blocking on `other`.
---
==== Move Constructor from unordered_flat_map
```c++
concurrent_flat_map(unordered_flat_map<Key, T, Hash, Pred, Allocator>&& other);
```
Move construction from a xref:#unordered_flat_map[`unordered_flat_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`.
[horizontal]
Complexity:;; O(`bucket_count()`)
---
==== Initializer List Constructor
[source,c++,subs="+quotes"]
----
@@ -673,9 +635,8 @@ Concurrency:;; Blocking on `*this` and `other`.
==== Move Assignment
```c++
concurrent_flat_map& operator=(concurrent_flat_map&& other)
noexcept((boost::allocator_traits<Allocator>::is_always_equal::value ||
boost::allocator_traits<Allocator>::propagate_on_container_move_assignment::value) &&
std::is_same<pointer, value_type*>::value);
noexcept(boost::allocator_traits<Allocator>::is_always_equal::value ||
boost::allocator_traits<Allocator>::propagate_on_container_move_assignment::value);
```
The move assignment operator. Destroys previously existing elements, swaps the hash function and predicate from `other`,
and move-assigns the allocator from `other` if `Alloc::propagate_on_container_move_assignment` exists and `Alloc::propagate_on_container_move_assignment::value` is `true`.
@@ -759,50 +720,6 @@ 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;
```
Successively invokes `f` with references to each of the elements in the table until `f` returns `false`
or all the elements are visited.
Such references to the elements are const iff `*this` is const.
[horizontal]
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;
```
Invokes `f` with references to each of the elements in the table until `f` returns `false`
or all the elements are visited.
Such references to the elements are const iff `*this` is const.
Execution is parallelized according to the semantics of the execution policy specified.
[horizontal]
Returns:;; `false` iff `f` ever returns `false`.
Throws:;; Depending on the exception handling mechanism of the execution policy used, may call `std::terminate` if an exception is thrown within `f`.
Notes:;; Only available in compilers supporting C++17 parallel algorithms. +
+
These overloads only participate in overload resolution if `std::is_execution_policy_v<std::remove_cvref_t<ExecutionPolicy>>` is `true`. +
+
Unsequenced execution policies are not allowed. +
+
Parallelization implies that execution does not necessary finish as soon as `f` returns `false`, and as a result
`f` may be invoked with further elements for which the return value is also `false`.
---
=== Size and Capacity
==== empty
@@ -1519,33 +1436,3 @@ Equivalent to
-----
c.xref:#concurrent_flat_map_erase_if[erase_if](pred);
-----
=== Serialization
``concurrent_flat_map``s can be archived/retrieved by means of
link:../../../serialization/index.html[Boost.Serialization^] using the API provided
by this library. Both regular and XML archives are supported.
==== Saving an concurrent_flat_map to an archive
Saves all the elements of a `concurrent_flat_map` `x` to an archive (XML archive) `ar`.
[horizontal]
Requires:;; `std::remove_const<key_type>::type` and `std::remove_const<mapped_type>::type`
are serializable (XML serializable), and they do support Boost.Serialization
`save_construct_data`/`load_construct_data` protocol (automatically suported by
https://en.cppreference.com/w/cpp/named_req/DefaultConstructible[DefaultConstructible^]
types).
Concurrency:;; Blocking on `x`.
---
==== Loading an concurrent_flat_map from an archive
Deletes all preexisting elements of a `concurrent_flat_map` `x` and inserts
from an archive (XML archive) `ar` restored copies of the elements of the
original `concurrent_flat_map` `other` saved to the storage read by `ar`.
[horizontal]
Requires:;; `x.key_equal()` is functionally equivalent to `other.key_equal()`.
Concurrency:;; Blocking on `x`.
File diff suppressed because it is too large Load Diff
+4 -5
View File
@@ -44,8 +44,7 @@ boost::unordered_flat_map
^.^h|*Concurrent*
^|
^| `boost::concurrent_flat_set` +
`boost::concurrent_flat_map`
^| `boost::concurrent_flat_map`
|===
@@ -57,8 +56,9 @@ in the market within the technical constraints imposed by the required standard
interface to accommodate the implementation.
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.
* Finally, `boost::concurrent_flat_map` (the only **concurrent container** provided
at present) is a hashmap designed and implemented to be used in high-performance
multithreaded scenarios. Its interface is radically different from that of regular C++ containers.
All sets and maps in Boost.Unordered are instantiatied similarly as
`std::unordered_set` and `std::unordered_map`, respectively:
@@ -73,7 +73,6 @@ namespace boost {
class Alloc = std::allocator<Key> >
class unordered_set;
// same for unordered_multiset, unordered_flat_set, unordered_node_set
// and concurrent_flat_set
template <
class Key, class Mapped,
+5 -6
View File
@@ -121,21 +121,20 @@ 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_map`:
* Open-addressing is faster than closed-addressing alternatives, both in non-concurrent and
concurrent scenarios.
* Open-addressing layouts are eminently suitable for concurrent access and modification
with minimal locking. In particular, the metadata array can be used for implementations of
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`,
and vice versa.
* Layout compatibility with Boost.Unordered flat containers allows for fast transfer
of all elements between `boost::concurrent_flat_map` and `boost::unordered_flat_map`.
(This feature has not been implemented yet.)
=== Hash Function and Platform Interoperability
Concurrent containers make the same decisions and provide the same guarantees
`boost::concurrent_flat_map` makes the same decisions and provides the same guarantees
as Boost.Unordered open-addressing containers with regards to
xref:#rationale_hash_function[hash function defaults] and
xref:#rationale_platform_interoperability[platform interoperability].
-1
View File
@@ -11,4 +11,3 @@ include::unordered_flat_set.adoc[]
include::unordered_node_map.adoc[]
include::unordered_node_set.adoc[]
include::concurrent_flat_map.adoc[]
include::concurrent_flat_set.adoc[]
+4 -4
View File
@@ -67,8 +67,8 @@ xref:#rationale_closed_addressing_containers[corresponding section].
== Open-addressing Containers
The diagram shows the basic internal layout of `boost::unordered_flat_set`/`unordered_node_set` and
`boost:unordered_flat_map`/`unordered_node_map`.
The diagram shows the basic internal layout of `boost::unordered_flat_map`/`unordered_node_map` and
`boost:unordered_flat_set`/`unordered_node_set`.
[#img-foa-layout]
@@ -76,7 +76,7 @@ The diagram shows the basic internal layout of `boost::unordered_flat_set`/`unor
image::foa.png[align=center]
As with all open-addressing containers, elements (or pointers to the element nodes in the case of
`boost::unordered_node_set` and `boost::unordered_node_map`) are stored directly in the bucket array.
`boost::unordered_node_map` and `boost::unordered_node_set`) are stored directly in the bucket array.
This array is logically divided into 2^_n_^ _groups_ of 15 elements each.
In addition to the bucket array, there is an associated _metadata array_ with 2^_n_^
16-byte words.
@@ -129,7 +129,7 @@ xref:#rationale_open_addresing_containers[corresponding section].
== Concurrent Containers
`boost::concurrent_flat_set` and `boost::concurrent_flat_map` use the basic
`boost::concurrent_flat_map` uses the basic
xref:#structures_open_addressing_containers[open-addressing layout] described above
augmented with synchronization mechanisms.
+8 -76
View File
@@ -77,7 +77,6 @@ namespace boost {
explicit xref:#unordered_flat_map_allocator_constructor[unordered_flat_map](const Allocator& a);
xref:#unordered_flat_map_copy_constructor_with_allocator[unordered_flat_map](const unordered_flat_map& other, const Allocator& a);
xref:#unordered_flat_map_move_constructor_with_allocator[unordered_flat_map](unordered_flat_map&& other, const Allocator& a);
xref:#unordered_flat_map_move_constructor_from_concurrent_flat_map[unordered_flat_map](concurrent_flat_map<Key, T, Hash, Pred, Allocator>&& other);
xref:#unordered_flat_map_initializer_list_constructor[unordered_flat_map](std::initializer_list<value_type> il,
size_type n = _implementation-defined_
const hasher& hf = hasher(),
@@ -97,10 +96,9 @@ namespace boost {
const allocator_type& a);
xref:#unordered_flat_map_destructor[~unordered_flat_map]();
unordered_flat_map& xref:#unordered_flat_map_copy_assignment[operator++=++](const unordered_flat_map& other);
unordered_flat_map& xref:#unordered_flat_map_move_assignment[operator++=++](unordered_flat_map&& other) ++noexcept(
(boost::allocator_traits<Allocator>::is_always_equal::value ||
boost::allocator_traits<Allocator>::propagate_on_container_move_assignment::value) &&
std::is_same<pointer, value_type*>::value);++
unordered_flat_map& xref:#unordered_flat_map_move_assignment[operator++=++](unordered_flat_map&& other)
noexcept(boost::allocator_traits<Allocator>::is_always_equal::value ||
boost::allocator_traits<Allocator>::propagate_on_container_move_assignment::value);
unordered_flat_map& xref:#unordered_flat_map_initializer_list_assignment[operator++=++](std::initializer_list<value_type>);
allocator_type xref:#unordered_flat_map_get_allocator[get_allocator]() const noexcept;
@@ -313,7 +311,8 @@ https://en.cppreference.com/w/cpp/named_req/Erasable[Erasable^] from the contain
|_Allocator_
|An allocator whose value type is the same as the container's value type.
Allocators using https://en.cppreference.com/w/cpp/named_req/Allocator#Fancy_pointers[fancy pointers] are supported.
`std::allocator_traits<Allocator>::pointer` and `std::allocator_traits<Allocator>::const_pointer`
must be convertible to/from `value_type*` and `const value_type*`, respectively.
|===
@@ -473,22 +472,6 @@ from `other`, and the allocator is copy-constructed from `a`.
---
==== Move Constructor from concurrent_flat_map
```c++
unordered_flat_map(concurrent_flat_map<Key, T, Hash, Pred, Allocator>&& other);
```
Move construction from a xref:#concurrent_flat_map[`concurrent_flat_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`.
[horizontal]
Complexity:;; Constant time.
Concurrency:;; Blocking on `other`.
---
==== Initializer List Constructor
[source,c++,subs="+quotes"]
----
@@ -632,9 +615,8 @@ Requires:;; `value_type` is https://en.cppreference.com/w/cpp/named_req/CopyInse
==== Move Assignment
```c++
unordered_flat_map& operator=(unordered_flat_map&& other)
noexcept((boost::allocator_traits<Allocator>::is_always_equal::value ||
boost::allocator_traits<Allocator>::propagate_on_container_move_assignment::value) &&
std::is_same<pointer, value_type*>::value);
noexcept(boost::allocator_traits<Allocator>::is_always_equal::value ||
boost::allocator_traits<Allocator>::propagate_on_container_move_assignment::value);
```
The move assignment operator. Destroys previously existing elements, swaps the hash function and predicate from `other`,
and move-assigns the allocator from `other` if `Alloc::propagate_on_container_move_assignment` exists and `Alloc::propagate_on_container_move_assignment::value` is `true`.
@@ -1326,7 +1308,7 @@ void rehash(size_type n);
Changes if necessary the size of the bucket array so that there are at least `n` buckets, and so that the load factor is less than or equal to the maximum load factor. When applicable, this will either grow or shrink the `bucket_count()` associated with the container.
When `size() == 0`, `rehash(0)` will deallocate the underlying buckets array. If the provided Allocator uses fancy pointers, a default allocation is subsequently performed.
When `size() == 0`, `rehash(0)` will deallocate the underlying buckets array.
Invalidates iterators, pointers and references, and changes the order of elements.
@@ -1465,54 +1447,4 @@ for (auto i = c.begin(), last = c.end(); i != last; ) {
return original_size - c.size();
```
=== Serialization
``unordered_flat_map``s can be archived/retrieved by means of
link:../../../serialization/index.html[Boost.Serialization^] using the API provided
by this library. Both regular and XML archives are supported.
==== Saving an unordered_flat_map to an archive
Saves all the elements of an `unordered_flat_map` `x` to an archive (XML archive) `ar`.
[horizontal]
Requires:;; `std::remove_const<key_type>::type` and `std::remove_const<mapped_type>::type`
are serializable (XML serializable), and they do support Boost.Serialization
`save_construct_data`/`load_construct_data` protocol (automatically suported by
https://en.cppreference.com/w/cpp/named_req/DefaultConstructible[DefaultConstructible^]
types).
---
==== Loading an unordered_flat_map from an archive
Deletes all preexisting elements of an `unordered_flat_map` `x` and inserts
from an archive (XML archive) `ar` restored copies of the elements of the
original `unordered_flat_map` `other` saved to the storage read by `ar`.
[horizontal]
Requires:;; `x.key_equal()` is functionally equivalent to `other.key_equal()`.
---
==== Saving an iterator/const_iterator to an archive
Saves the positional information of an `iterator` (`const_iterator`) `it`
to an archive (XML archive) `ar`. `it` can be and `end()` iterator.
[horizontal]
Requires:;; The `unordered_flat_map` `x` pointed to by `it` has been previously saved to `ar`,
and no modifying operations have been issued on `x` between saving of `x` and
saving of `it`.
---
==== Loading an iterator/const_iterator from an archive
Makes an `iterator` (`const_iterator`) `it` point to the restored position of
the original `iterator` (`const_iterator`) saved to the storage read by
an archive (XML archive) `ar`.
[horizontal]
Requires:;; If `x` is the `unordered_flat_map` `it` points to, no modifying operations
have been issued on `x` between loading of `x` and loading of `it`.
+9 -76
View File
@@ -71,7 +71,7 @@ namespace boost {
xref:#unordered_flat_set_iterator_range_constructor_with_allocator[unordered_flat_set](InputIterator f, InputIterator l, const allocator_type& a);
explicit xref:#unordered_flat_set_allocator_constructor[unordered_flat_set](const Allocator& a);
xref:#unordered_flat_set_copy_constructor_with_allocator[unordered_flat_set](const unordered_flat_set& other, const Allocator& a);
xref:#unordered_flat_set_move_constructor_from_concurrent_flat_set[unordered_flat_set](concurrent_flat_set<Key, Hash, Pred, Allocator>&& other);
xref:#unordered_flat_set_move_constructor_with_allocator[unordered_flat_set](unordered_flat_set&& other, const Allocator& a);
xref:#unordered_flat_set_initializer_list_constructor[unordered_flat_set](std::initializer_list<value_type> il,
size_type n = _implementation-defined_
const hasher& hf = hasher(),
@@ -91,10 +91,9 @@ namespace boost {
const allocator_type& a);
xref:#unordered_flat_set_destructor[~unordered_flat_set]();
unordered_flat_set& xref:#unordered_flat_set_copy_assignment[operator++=++](const unordered_flat_set& other);
unordered_flat_set& xref:#unordered_flat_set_move_assignment[operator++=++](unordered_flat_set&& other) ++noexcept(
(boost::allocator_traits<Allocator>::is_always_equal::value ||
boost::allocator_traits<Allocator>::propagate_on_container_move_assignment::value) &&
std::is_same<pointer, value_type*>::value);++
unordered_flat_set& xref:#unordered_flat_set_move_assignment[operator++=++](unordered_flat_set&& other)
noexcept(boost::allocator_traits<Allocator>::is_always_equal::value ||
boost::allocator_traits<Allocator>::propagate_on_container_move_assignment::value);
unordered_flat_set& xref:#unordered_flat_set_initializer_list_assignment[operator++=++](std::initializer_list<value_type>);
allocator_type xref:#unordered_flat_set_get_allocator[get_allocator]() const noexcept;
@@ -262,7 +261,8 @@ and https://en.cppreference.com/w/cpp/named_req/Erasable[Erasable^] from the con
|_Allocator_
|An allocator whose value type is the same as the container's value type.
Allocators using https://en.cppreference.com/w/cpp/named_req/Allocator#Fancy_pointers[fancy pointers] are supported.
`std::allocator_traits<Allocator>::pointer` and `std::allocator_traits<Allocator>::const_pointer`
must be convertible to/from `value_type*` and `const value_type*`, respectively.
|===
@@ -422,22 +422,6 @@ from `other`, and the allocator is copy-constructed from `a`.
---
==== Move Constructor from concurrent_flat_set
```c++
unordered_flat_set(concurrent_flat_set<Key, Hash, Pred, Allocator>&& other);
```
Move construction from a xref:#concurrent_flat_set[`concurrent_flat_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`.
[horizontal]
Complexity:;; Constant time.
Concurrency:;; Blocking on `other`.
---
==== Initializer List Constructor
[source,c++,subs="+quotes"]
----
@@ -581,9 +565,8 @@ Requires:;; `value_type` is https://en.cppreference.com/w/cpp/named_req/CopyInse
==== Move Assignment
```c++
unordered_flat_set& operator=(unordered_flat_set&& other)
noexcept((boost::allocator_traits<Allocator>::is_always_equal::value ||
boost::allocator_traits<Allocator>::propagate_on_container_move_assignment::value) &&
std::is_same<pointer, value_type*>::value);
noexcept(boost::allocator_traits<Allocator>::is_always_equal::value ||
boost::allocator_traits<Allocator>::propagate_on_container_move_assignment::value);
```
The move assignment operator. Destroys previously existing elements, swaps the hash function and predicate from `other`,
and move-assigns the allocator from `other` if `Alloc::propagate_on_container_move_assignment` exists and `Alloc::propagate_on_container_move_assignment::value` is `true`.
@@ -1104,7 +1087,7 @@ void rehash(size_type n);
Changes if necessary the size of the bucket array so that there are at least `n` buckets, and so that the load factor is less than or equal to the maximum load factor. When applicable, this will either grow or shrink the `bucket_count()` associated with the container.
When `size() == 0`, `rehash(0)` will deallocate the underlying buckets array. If the provided Allocator uses fancy pointers, a default allocation is subsequently performed.
When `size() == 0`, `rehash(0)` will deallocate the underlying buckets array.
Invalidates iterators, pointers and references, and changes the order of elements.
@@ -1218,54 +1201,4 @@ for (auto i = c.begin(), last = c.end(); i != last; ) {
return original_size - c.size();
```
=== Serialization
``unordered_flat_set``s can be archived/retrieved by means of
link:../../../serialization/index.html[Boost.Serialization^] using the API provided
by this library. Both regular and XML archives are supported.
==== Saving an unordered_flat_set to an archive
Saves all the elements of an `unordered_flat_set` `x` to an archive (XML archive) `ar`.
[horizontal]
Requires:;; `value_type`
is serializable (XML serializable), and it supports Boost.Serialization
`save_construct_data`/`load_construct_data` protocol (automatically suported by
https://en.cppreference.com/w/cpp/named_req/DefaultConstructible[DefaultConstructible^]
types).
---
==== Loading an unordered_flat_set from an archive
Deletes all preexisting elements of an `unordered_flat_set` `x` and inserts
from an archive (XML archive) `ar` restored copies of the elements of the
original `unordered_flat_set` `other` saved to the storage read by `ar`.
[horizontal]
Requires:;; `x.key_equal()` is functionally equivalent to `other.key_equal()`.
---
==== Saving an iterator/const_iterator to an archive
Saves the positional information of an `iterator` (`const_iterator`) `it`
to an archive (XML archive) `ar`. `it` can be and `end()` iterator.
[horizontal]
Requires:;; The `unordered_flat_set` `x` pointed to by `it` has been previously saved to `ar`,
and no modifying operations have been issued on `x` between saving of `x` and
saving of `it`.
---
==== Loading an iterator/const_iterator from an archive
Makes an `iterator` (`const_iterator`) `it` point to the restored position of
the original `iterator` (`const_iterator`) saved to the storage read by
an archive (XML archive) `ar`.
[horizontal]
Requires:;; If `x` is the `unordered_flat_set` `it` points to, no modifying operations
have been issued on `x` between loading of `x` and loading of `it`.
+1 -61
View File
@@ -323,12 +323,7 @@ The elements are organized into buckets. Keys with the same hash code are stored
The number of buckets can be automatically increased by a call to insert, or as the result of calling rehash.
=== Configuration macros
==== `BOOST_UNORDERED_ENABLE_SERIALIZATION_COMPATIBILITY_V0`
Globally define this macro to support loading of ``unordered_map``s saved to
a Boost.Serialization archive with a version of Boost prior to Boost 1.84.
---
=== Typedefs
@@ -1830,59 +1825,4 @@ for (auto i = c.begin(), last = c.end(); i != last; ) {
return original_size - c.size();
```
=== Serialization
``unordered_map``s can be archived/retrieved by means of
link:../../../serialization/index.html[Boost.Serialization^] using the API provided
by this library. Both regular and XML archives are supported.
==== Saving an unordered_map to an archive
Saves all the elements of an `unordered_map` `x` to an archive (XML archive) `ar`.
[horizontal]
Requires:;; `std::remove_const<key_type>::type` and `std::remove_const<mapped_type>::type`
are serializable (XML serializable), and they do support Boost.Serialization
`save_construct_data`/`load_construct_data` protocol (automatically suported by
https://en.cppreference.com/w/cpp/named_req/DefaultConstructible[DefaultConstructible^]
types).
---
==== Loading an unordered_map from an archive
Deletes all preexisting elements of an `unordered_map` `x` and inserts
from an archive (XML archive) `ar` restored copies of the elements of the
original `unordered_map` `other` saved to the storage read by `ar`.
[horizontal]
Requires:;; `value_type` is https://en.cppreference.com/w/cpp/named_req/EmplaceConstructible[EmplaceConstructible^]
from `(std::remove_const<key_type>::type&&, std::remove_const<mapped_type>::type&&)`.
`x.key_equal()` is functionally equivalent to `other.key_equal()`.
Note:;; If the archive was saved using a release of Boost prior to Boost 1.84,
the configuration macro `BOOST_UNORDERED_ENABLE_SERIALIZATION_COMPATIBILITY_V0`
has to be globally defined for this operation to succeed; otherwise, an exception is thrown.
---
==== Saving an iterator/const_iterator to an archive
Saves the positional information of an `iterator` (`const_iterator`) `it`
to an archive (XML archive) `ar`. `it` can be and `end()` iterator.
[horizontal]
Requires:;; The `unordered_map` `x` pointed to by `it` has been previously saved to `ar`,
and no modifying operations have been issued on `x` between saving of `x` and
saving of `it`.
---
==== Loading an iterator/const_iterator from an archive
Makes an `iterator` (`const_iterator`) `it` point to the restored position of
the original `iterator` (`const_iterator`) saved to the storage read by
an archive (XML archive) `ar`.
[horizontal]
Requires:;; If `x` is the `unordered_map` `it` points to, no modifying operations
have been issued on `x` between loading of `x` and loading of `it`.
-62
View File
@@ -290,13 +290,6 @@ The elements are organized into buckets. Keys with the same hash code are stored
The number of buckets can be automatically increased by a call to insert, or as the result of calling rehash.
=== Configuration macros
==== `BOOST_UNORDERED_ENABLE_SERIALIZATION_COMPATIBILITY_V0`
Globally define this macro to support loading of ``unordered_multimap``s saved to
a Boost.Serialization archive with a version of Boost prior to Boost 1.84.
=== Typedefs
[source,c++,subs=+quotes]
@@ -1559,59 +1552,4 @@ for (auto i = c.begin(), last = c.end(); i != last; ) {
return original_size - c.size();
```
=== Serialization
``unordered_multimap``s can be archived/retrieved by means of
link:../../../serialization/index.html[Boost.Serialization^] using the API provided
by this library. Both regular and XML archives are supported.
==== Saving an unordered_multimap to an archive
Saves all the elements of an `unordered_multimap` `x` to an archive (XML archive) `ar`.
[horizontal]
Requires:;; `std::remove_const<key_type>::type` and `std::remove_const<mapped_type>::type`
are serializable (XML serializable), and they do support Boost.Serialization
`save_construct_data`/`load_construct_data` protocol (automatically suported by
https://en.cppreference.com/w/cpp/named_req/DefaultConstructible[DefaultConstructible^]
types).
---
==== Loading an unordered_multimap from an archive
Deletes all preexisting elements of an `unordered_multimap` `x` and inserts
from an archive (XML archive) `ar` restored copies of the elements of the
original `unordered_multimap` `other` saved to the storage read by `ar`.
[horizontal]
Requires:;; `value_type` is https://en.cppreference.com/w/cpp/named_req/EmplaceConstructible[EmplaceConstructible^]
from `(std::remove_const<key_type>::type&&, std::remove_const<mapped_type>::type&&)`.
`x.key_equal()` is functionally equivalent to `other.key_equal()`.
Note:;; If the archive was saved using a release of Boost prior to Boost 1.84,
the configuration macro `BOOST_UNORDERED_ENABLE_SERIALIZATION_COMPATIBILITY_V0`
has to be globally defined for this operation to succeed; otherwise, an exception is thrown.
---
==== Saving an iterator/const_iterator to an archive
Saves the positional information of an `iterator` (`const_iterator`) `it`
to an archive (XML archive) `ar`. `it` can be and `end()` iterator.
[horizontal]
Requires:;; The `unordered_multimap` `x` pointed to by `it` has been previously saved to `ar`,
and no modifying operations have been issued on `x` between saving of `x` and
saving of `it`.
---
==== Loading an iterator/const_iterator from an archive
Makes an `iterator` (`const_iterator`) `it` point to the restored position of
the original `iterator` (`const_iterator`) saved to the storage read by
an archive (XML archive) `ar`.
[horizontal]
Requires:;; If `x` is the `unordered_multimap` `it` points to, no modifying operations
have been issued on `x` between loading of `x` and loading of `it`.
+1 -60
View File
@@ -278,12 +278,7 @@ The elements are organized into buckets. Keys with the same hash code are stored
The number of buckets can be automatically increased by a call to insert, or as the result of calling rehash.
=== Configuration macros
==== `BOOST_UNORDERED_ENABLE_SERIALIZATION_COMPATIBILITY_V0`
Globally define this macro to support loading of ``unordered_multiset``s saved to
a Boost.Serialization archive with a version of Boost prior to Boost 1.84.
---
=== Typedefs
@@ -1490,58 +1485,4 @@ for (auto i = c.begin(), last = c.end(); i != last; ) {
return original_size - c.size();
```
=== Serialization
``unordered_multiset``s can be archived/retrieved by means of
link:../../../serialization/index.html[Boost.Serialization^] using the API provided
by this library. Both regular and XML archives are supported.
==== Saving an unordered_multiset to an archive
Saves all the elements of an `unordered_multiset` `x` to an archive (XML archive) `ar`.
[horizontal]
Requires:;; `value_type`
is serializable (XML serializable), and it supports Boost.Serialization
`save_construct_data`/`load_construct_data` protocol (automatically suported by
https://en.cppreference.com/w/cpp/named_req/DefaultConstructible[DefaultConstructible^]
types).
---
==== Loading an unordered_multiset from an archive
Deletes all preexisting elements of an `unordered_multiset` `x` and inserts
from an archive (XML archive) `ar` restored copies of the elements of the
original `unordered_multiset` `other` saved to the storage read by `ar`.
[horizontal]
Requires:;; `value_type` is https://en.cppreference.com/w/cpp/named_req/MoveInsertable[MoveInsertable^].
`x.key_equal()` is functionally equivalent to `other.key_equal()`.
Note:;; If the archive was saved using a release of Boost prior to Boost 1.84,
the configuration macro `BOOST_UNORDERED_ENABLE_SERIALIZATION_COMPATIBILITY_V0`
has to be globally defined for this operation to succeed; otherwise, an exception is thrown.
---
==== Saving an iterator/const_iterator to an archive
Saves the positional information of an `iterator` (`const_iterator`) `it`
to an archive (XML archive) `ar`. `it` can be and `end()` iterator.
[horizontal]
Requires:;; The `unordered_multiset` `x` pointed to by `it` has been previously saved to `ar`,
and no modifying operations have been issued on `x` between saving of `x` and
saving of `it`.
---
==== Loading an iterator/const_iterator from an archive
Makes an `iterator` (`const_iterator`) `it` point to the restored position of
the original `iterator` (`const_iterator`) saved to the storage read by
an archive (XML archive) `ar`.
[horizontal]
Requires:;; If `x` is the `unordered_multiset` `it` points to, no modifying operations
have been issued on `x` between loading of `x` and loading of `it`.
+8 -62
View File
@@ -95,10 +95,9 @@ namespace boost {
const allocator_type& a);
xref:#unordered_node_map_destructor[~unordered_node_map]();
unordered_node_map& xref:#unordered_node_map_copy_assignment[operator++=++](const unordered_node_map& other);
unordered_node_map& xref:#unordered_node_map_move_assignment[operator++=++](unordered_node_map&& other) ++noexcept(
(boost::allocator_traits<Allocator>::is_always_equal::value ||
boost::allocator_traits<Allocator>::propagate_on_container_move_assignment::value) &&
std::is_same<pointer, value_type*>::value);++
unordered_node_map& xref:#unordered_node_map_move_assignment[operator++=++](unordered_node_map&& other)
noexcept(boost::allocator_traits<Allocator>::is_always_equal::value ||
boost::allocator_traits<Allocator>::propagate_on_container_move_assignment::value);
unordered_node_map& xref:#unordered_node_map_initializer_list_assignment[operator++=++](std::initializer_list<value_type>);
allocator_type xref:#unordered_node_map_get_allocator[get_allocator]() const noexcept;
@@ -315,7 +314,8 @@ https://en.cppreference.com/w/cpp/named_req/Erasable[Erasable^] from the contain
|_Allocator_
|An allocator whose value type is the same as the container's value type.
Allocators using https://en.cppreference.com/w/cpp/named_req/Allocator#Fancy_pointers[fancy pointers] are supported.
`std::allocator_traits<Allocator>::pointer` and `std::allocator_traits<Allocator>::const_pointer`
must be convertible to/from `value_type*` and `const value_type*`, respectively.
|===
@@ -649,9 +649,8 @@ Requires:;; `value_type` is https://en.cppreference.com/w/cpp/named_req/CopyInse
==== Move Assignment
```c++
unordered_node_map& operator=(unordered_node_map&& other)
noexcept((boost::allocator_traits<Allocator>::is_always_equal::value ||
boost::allocator_traits<Allocator>::propagate_on_container_move_assignment::value) &&
std::is_same<pointer, value_type*>::value);
noexcept(boost::allocator_traits<Allocator>::is_always_equal::value ||
boost::allocator_traits<Allocator>::propagate_on_container_move_assignment::value);
```
The move assignment operator. Destroys previously existing elements, swaps the hash function and predicate from `other`,
and move-assigns the allocator from `other` if `Alloc::propagate_on_container_move_assignment` exists and `Alloc::propagate_on_container_move_assignment::value` is `true`.
@@ -1407,7 +1406,7 @@ void rehash(size_type n);
Changes if necessary the size of the bucket array so that there are at least `n` buckets, and so that the load factor is less than or equal to the maximum load factor. When applicable, this will either grow or shrink the `bucket_count()` associated with the container.
When `size() == 0`, `rehash(0)` will deallocate the underlying buckets array. If the provided Allocator uses fancy pointers, a default allocation is subsequently performed.
When `size() == 0`, `rehash(0)` will deallocate the underlying buckets array.
Invalidates iterators and changes the order of elements.
@@ -1546,57 +1545,4 @@ for (auto i = c.begin(), last = c.end(); i != last; ) {
return original_size - c.size();
```
=== Serialization
``unordered_node_map``s can be archived/retrieved by means of
link:../../../serialization/index.html[Boost.Serialization^] using the API provided
by this library. Both regular and XML archives are supported.
==== Saving an unordered_node_map to an archive
Saves all the elements of an `unordered_node_map` `x` to an archive (XML archive) `ar`.
[horizontal]
Requires:;; `std::remove_const<key_type>::type` and `std::remove_const<mapped_type>::type`
are serializable (XML serializable), and they do support Boost.Serialization
`save_construct_data`/`load_construct_data` protocol (automatically suported by
https://en.cppreference.com/w/cpp/named_req/DefaultConstructible[DefaultConstructible^]
types).
---
==== Loading an unordered_node_map from an archive
Deletes all preexisting elements of an `unordered_node_map` `x` and inserts
from an archive (XML archive) `ar` restored copies of the elements of the
original `unordered_node_map` `other` saved to the storage read by `ar`.
[horizontal]
Requires:;; `key_type` and `mapped_type` are constructible from
`std::remove_const<key_type>::type&&` and `std::remove_const<mapped_type>::type&&`,
respectively.
`x.key_equal()` is functionally equivalent to `other.key_equal()`.
---
==== Saving an iterator/const_iterator to an archive
Saves the positional information of an `iterator` (`const_iterator`) `it`
to an archive (XML archive) `ar`. `it` can be and `end()` iterator.
[horizontal]
Requires:;; The `unordered_node_map` `x` pointed to by `it` has been previously saved to `ar`,
and no modifying operations have been issued on `x` between saving of `x` and
saving of `it`.
---
==== Loading an iterator/const_iterator from an archive
Makes an `iterator` (`const_iterator`) `it` point to the restored position of
the original `iterator` (`const_iterator`) saved to the storage read by
an archive (XML archive) `ar`.
[horizontal]
Requires:;; If `x` is the `unordered_node_map` `it` points to, no modifying operations
have been issued on `x` between loading of `x` and loading of `it`.
+8 -60
View File
@@ -90,10 +90,9 @@ namespace boost {
const allocator_type& a);
xref:#unordered_node_set_destructor[~unordered_node_set]();
unordered_node_set& xref:#unordered_node_set_copy_assignment[operator++=++](const unordered_node_set& other);
unordered_node_set& xref:#unordered_node_set_move_assignment[operator++=++](unordered_node_set&& other) ++noexcept(
(boost::allocator_traits<Allocator>::is_always_equal::value ||
boost::allocator_traits<Allocator>::propagate_on_container_move_assignment::value) &&
std::is_same<pointer, value_type*>::value);++
unordered_node_set& xref:#unordered_node_set_move_assignment[operator++=++](unordered_node_set&& other)
noexcept(boost::allocator_traits<Allocator>::is_always_equal::value ||
boost::allocator_traits<Allocator>::propagate_on_container_move_assignment::value);
unordered_node_set& xref:#unordered_node_set_initializer_list_assignment[operator++=++](std::initializer_list<value_type>);
allocator_type xref:#unordered_node_set_get_allocator[get_allocator]() const noexcept;
@@ -265,7 +264,8 @@ namespace boost {
|_Allocator_
|An allocator whose value type is the same as the container's value type.
Allocators using https://en.cppreference.com/w/cpp/named_req/Allocator#Fancy_pointers[fancy pointers] are supported.
`std::allocator_traits<Allocator>::pointer` and `std::allocator_traits<Allocator>::const_pointer`
must be convertible to/from `value_type*` and `const value_type*`, respectively.
|===
@@ -602,9 +602,8 @@ Requires:;; `value_type` is https://en.cppreference.com/w/cpp/named_req/CopyInse
==== Move Assignment
```c++
unordered_node_set& operator=(unordered_node_set&& other)
noexcept((boost::allocator_traits<Allocator>::is_always_equal::value ||
boost::allocator_traits<Allocator>::propagate_on_container_move_assignment::value) &&
std::is_same<pointer, value_type*>::value);
noexcept(boost::allocator_traits<Allocator>::is_always_equal::value ||
boost::allocator_traits<Allocator>::propagate_on_container_move_assignment::value);
```
The move assignment operator. Destroys previously existing elements, swaps the hash function and predicate from `other`,
and move-assigns the allocator from `other` if `Alloc::propagate_on_container_move_assignment` exists and `Alloc::propagate_on_container_move_assignment::value` is `true`.
@@ -1189,7 +1188,7 @@ void rehash(size_type n);
Changes if necessary the size of the bucket array so that there are at least `n` buckets, and so that the load factor is less than or equal to the maximum load factor. When applicable, this will either grow or shrink the `bucket_count()` associated with the container.
When `size() == 0`, `rehash(0)` will deallocate the underlying buckets array. If the provided Allocator uses fancy pointers, a default allocation is subsequently performed.
When `size() == 0`, `rehash(0)` will deallocate the underlying buckets array.
Invalidates iterators and changes the order of elements.
@@ -1303,55 +1302,4 @@ for (auto i = c.begin(), last = c.end(); i != last; ) {
return original_size - c.size();
```
=== Serialization
``unordered_node_set``s can be archived/retrieved by means of
link:../../../serialization/index.html[Boost.Serialization^] using the API provided
by this library. Both regular and XML archives are supported.
==== Saving an unordered_node_set to an archive
Saves all the elements of an `unordered_node_set` `x` to an archive (XML archive) `ar`.
[horizontal]
Requires:;; `value_type`
is serializable (XML serializable), and it supports Boost.Serialization
`save_construct_data`/`load_construct_data` protocol (automatically suported by
https://en.cppreference.com/w/cpp/named_req/DefaultConstructible[DefaultConstructible^]
types).
---
==== Loading an unordered_node_set from an archive
Deletes all preexisting elements of an `unordered_node_set` `x` and inserts
from an archive (XML archive) `ar` restored copies of the elements of the
original `unordered_node_set` `other` saved to the storage read by `ar`.
[horizontal]
Requires:;; `value_type` is https://en.cppreference.com/w/cpp/named_req/MoveInsertable[MoveInsertable^].
`x.key_equal()` is functionally equivalent to `other.key_equal()`.
---
==== Saving an iterator/const_iterator to an archive
Saves the positional information of an `iterator` (`const_iterator`) `it`
to an archive (XML archive) `ar`. `it` can be and `end()` iterator.
[horizontal]
Requires:;; The `unordered_node_set` `x` pointed to by `it` has been previously saved to `ar`,
and no modifying operations have been issued on `x` between saving of `x` and
saving of `it`.
---
==== Loading an iterator/const_iterator from an archive
Makes an `iterator` (`const_iterator`) `it` point to the restored position of
the original `iterator` (`const_iterator`) saved to the storage read by
an archive (XML archive) `ar`.
[horizontal]
Requires:;; If `x` is the `unordered_node_set` `it` points to, no modifying operations
have been issued on `x` between loading of `x` and loading of `it`.
+1 -60
View File
@@ -279,12 +279,7 @@ The elements are organized into buckets. Keys with the same hash code are stored
The number of buckets can be automatically increased by a call to insert, or as the result of calling rehash.
=== Configuration macros
==== `BOOST_UNORDERED_ENABLE_SERIALIZATION_COMPATIBILITY_V0`
Globally define this macro to support loading of ``unordered_set``s saved to
a Boost.Serialization archive with a version of Boost prior to Boost 1.84.
---
=== Typedefs
@@ -1555,58 +1550,4 @@ for (auto i = c.begin(), last = c.end(); i != last; ) {
return original_size - c.size();
```
=== Serialization
``unordered_set``s can be archived/retrieved by means of
link:../../../serialization/index.html[Boost.Serialization^] using the API provided
by this library. Both regular and XML archives are supported.
==== Saving an unordered_set to an archive
Saves all the elements of an `unordered_set` `x` to an archive (XML archive) `ar`.
[horizontal]
Requires:;; `value_type`
is serializable (XML serializable), and it supports Boost.Serialization
`save_construct_data`/`load_construct_data` protocol (automatically suported by
https://en.cppreference.com/w/cpp/named_req/DefaultConstructible[DefaultConstructible^]
types).
---
==== Loading an unordered_set from an archive
Deletes all preexisting elements of an `unordered_set` `x` and inserts
from an archive (XML archive) `ar` restored copies of the elements of the
original `unordered_set` `other` saved to the storage read by `ar`.
[horizontal]
Requires:;; `value_type` is https://en.cppreference.com/w/cpp/named_req/MoveInsertable[MoveInsertable^].
`x.key_equal()` is functionally equivalent to `other.key_equal()`.
Note:;; If the archive was saved using a release of Boost prior to Boost 1.84,
the configuration macro `BOOST_UNORDERED_ENABLE_SERIALIZATION_COMPATIBILITY_V0`
has to be globally defined for this operation to succeed; otherwise, an exception is thrown.
---
==== Saving an iterator/const_iterator to an archive
Saves the positional information of an `iterator` (`const_iterator`) `it`
to an archive (XML archive) `ar`. `it` can be and `end()` iterator.
[horizontal]
Requires:;; The `unordered_set` `x` pointed to by `it` has been previously saved to `ar`,
and no modifying operations have been issued on `x` between saving of `x` and
saving of `it`.
---
==== Loading an iterator/const_iterator from an archive
Makes an `iterator` (`const_iterator`) `it` point to the restored position of
the original `iterator` (`const_iterator`) saved to the storage read by
an archive (XML archive) `ar`.
[horizontal]
Requires:;; If `x` is the `unordered_set` `it` points to, no modifying operations
have been issued on `x` between loading of `x` and loading of `it`.
+68 -92
View File
@@ -1,4 +1,4 @@
/* Fast open-addressing concurrent hashmap.
/* Fast open-addressing concurrent hash table.
*
* Copyright 2023 Christian Mazakas.
* Distributed under the Boost Software License, Version 1.0.
@@ -12,21 +12,71 @@
#define BOOST_UNORDERED_CONCURRENT_FLAT_MAP_HPP
#include <boost/unordered/concurrent_flat_map_fwd.hpp>
#include <boost/unordered/detail/concurrent_static_asserts.hpp>
#include <boost/unordered/detail/foa/concurrent_table.hpp>
#include <boost/unordered/detail/foa/flat_map_types.hpp>
#include <boost/unordered/detail/type_traits.hpp>
#include <boost/unordered/unordered_flat_map_fwd.hpp>
#include <boost/container_hash/hash.hpp>
#include <boost/core/allocator_access.hpp>
#include <boost/core/serialization.hpp>
#include <boost/mp11/algorithm.hpp>
#include <boost/mp11/list.hpp>
#include <boost/type_traits/type_identity.hpp>
#include <functional>
#include <type_traits>
#include <utility>
#define BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(F) \
static_assert(boost::unordered::detail::is_invocable<F, value_type&>::value, \
"The provided Callable must be invocable with value_type&");
#define BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F) \
static_assert( \
boost::unordered::detail::is_invocable<F, value_type const&>::value, \
"The provided Callable must be invocable with value_type const&");
#if BOOST_CXX_VERSION >= 202002L
#define BOOST_UNORDERED_STATIC_ASSERT_EXEC_POLICY(P) \
static_assert(!std::is_base_of<std::execution::parallel_unsequenced_policy, \
ExecPolicy>::value, \
"ExecPolicy must be sequenced."); \
static_assert( \
!std::is_base_of<std::execution::unsequenced_policy, ExecPolicy>::value, \
"ExecPolicy must be sequenced.");
#else
#define BOOST_UNORDERED_STATIC_ASSERT_EXEC_POLICY(P) \
static_assert(!std::is_base_of<std::execution::parallel_unsequenced_policy, \
ExecPolicy>::value, \
"ExecPolicy must be sequenced.");
#endif
#define BOOST_UNORDERED_COMMA ,
#define BOOST_UNORDERED_LAST_ARG(Arg, Args) \
mp11::mp_back<mp11::mp_list<Arg BOOST_UNORDERED_COMMA Args> >
#define BOOST_UNORDERED_STATIC_ASSERT_LAST_ARG_INVOCABLE(Arg, Args) \
BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(BOOST_UNORDERED_LAST_ARG(Arg, Args))
#define BOOST_UNORDERED_STATIC_ASSERT_LAST_ARG_CONST_INVOCABLE(Arg, Args) \
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE( \
BOOST_UNORDERED_LAST_ARG(Arg, Args))
namespace boost {
namespace unordered {
namespace detail {
template <class F, class... Args>
struct is_invocable
: std::is_constructible<std::function<void(Args...)>,
std::reference_wrapper<typename std::remove_reference<F>::type> >
{
};
} // namespace detail
template <class Key, class T, class Hash, class Pred, class Allocator>
class concurrent_flat_map
{
@@ -34,16 +84,10 @@ namespace boost {
template <class Key2, class T2, class Hash2, class Pred2,
class Allocator2>
friend class concurrent_flat_map;
template <class Key2, class T2, class Hash2, class Pred2,
class Allocator2>
friend class unordered_flat_map;
using type_policy = detail::foa::flat_map_types<Key, T>;
using table_type =
detail::foa::concurrent_table<type_policy, Hash, Pred, Allocator>;
table_type table_;
detail::foa::concurrent_table<type_policy, Hash, Pred, Allocator> table_;
template <class K, class V, class H, class KE, class A>
bool friend operator==(concurrent_flat_map<K, V, H, KE, A> const& lhs,
@@ -53,11 +97,6 @@ namespace boost {
friend typename concurrent_flat_map<K, V, H, KE, A>::size_type erase_if(
concurrent_flat_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_flat_map<K, V, H, KE, A>& c,
unsigned int version);
public:
using key_type = Key;
using mapped_type = T;
@@ -184,13 +223,6 @@ namespace boost {
{
}
concurrent_flat_map(
unordered_flat_map<Key, T, Hash, Pred, Allocator>&& other)
: table_(std::move(other.table_))
{
}
~concurrent_flat_map() = default;
concurrent_flat_map& operator=(concurrent_flat_map const& rhs)
@@ -199,8 +231,10 @@ namespace boost {
return *this;
}
concurrent_flat_map& operator=(concurrent_flat_map&& rhs) noexcept(
noexcept(std::declval<table_type&>() = std::declval<table_type&&>()))
concurrent_flat_map& operator=(concurrent_flat_map&& rhs)
noexcept(boost::allocator_is_always_equal<Allocator>::type::value ||
boost::allocator_propagate_on_container_move_assignment<
Allocator>::type::value)
{
table_ = std::move(rhs.table_);
return *this;
@@ -244,14 +278,6 @@ namespace boost {
return table_.visit(k, f);
}
template<typename FwdIterator,typename F>
BOOST_FORCEINLINE
std::size_t visit(FwdIterator first,FwdIterator last,F f)const
{
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
return table_.visit(first, last, f);
}
template <class K, class F>
BOOST_FORCEINLINE typename std::enable_if<
detail::are_transparent<K, hasher, key_equal>::value, size_type>::type
@@ -329,56 +355,6 @@ namespace boost {
}
#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
///
@@ -435,7 +411,6 @@ namespace boost {
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);
}
@@ -490,7 +465,7 @@ namespace boost {
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);
this->insert_or_visit(ilist.begin(), ilist.end(), f);
}
template <class... Args> BOOST_FORCEINLINE bool emplace(Args&&... args)
@@ -736,13 +711,6 @@ namespace boost {
return c.table_.erase_if(pred);
}
template<class Archive, class K, class V, class H, class KE, class A>
void serialize(
Archive& ar, concurrent_flat_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,
@@ -839,4 +807,12 @@ namespace boost {
using unordered::concurrent_flat_map;
} // namespace boost
#undef BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE
#undef BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE
#undef BOOST_UNORDERED_STATIC_ASSERT_EXEC_POLICY
#undef BOOST_UNORDERED_COMMA
#undef BOOST_UNORDERED_LAST_ARG
#undef BOOST_UNORDERED_STATIC_ASSERT_LAST_ARG_INVOCABLE
#undef BOOST_UNORDERED_STATIC_ASSERT_LAST_ARG_CONST_INVOCABLE
#endif // BOOST_UNORDERED_CONCURRENT_FLAT_MAP_HPP
@@ -1,4 +1,4 @@
/* Fast open-addressing concurrent hashmap.
/* Fast open-addressing concurrent hash table.
*
* Copyright 2023 Christian Mazakas.
* Distributed under the Boost Software License, Version 1.0.
@@ -1,697 +0,0 @@
/* Fast open-addressing concurrent hashset.
*
* Copyright 2023 Christian Mazakas.
* Copyright 2023 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_FLAT_SET_HPP
#define BOOST_UNORDERED_CONCURRENT_FLAT_SET_HPP
#include <boost/unordered/concurrent_flat_set_fwd.hpp>
#include <boost/unordered/detail/concurrent_static_asserts.hpp>
#include <boost/unordered/detail/foa/concurrent_table.hpp>
#include <boost/unordered/detail/foa/flat_set_types.hpp>
#include <boost/unordered/detail/type_traits.hpp>
#include <boost/unordered/unordered_flat_set_fwd.hpp>
#include <boost/container_hash/hash.hpp>
#include <boost/core/allocator_access.hpp>
#include <boost/core/serialization.hpp>
#include <boost/type_traits/type_identity.hpp>
#include <utility>
namespace boost {
namespace unordered {
template <class Key, class Hash, class Pred, class Allocator>
class concurrent_flat_set
{
private:
template <class Key2, class Hash2, class Pred2, class Allocator2>
friend class concurrent_flat_set;
template <class Key2, class Hash2, class Pred2, class Allocator2>
friend class unordered_flat_set;
using type_policy = detail::foa::flat_set_types<Key>;
detail::foa::concurrent_table<type_policy, Hash, Pred, Allocator> table_;
template <class K, class H, class KE, class A>
bool friend operator==(concurrent_flat_set<K, H, KE, A> const& lhs,
concurrent_flat_set<K, H, KE, A> const& rhs);
template <class K, class H, class KE, class A, class Predicate>
friend typename concurrent_flat_set<K, H, KE, A>::size_type erase_if(
concurrent_flat_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_flat_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::type_identity<Hash>::type;
using key_equal = typename boost::type_identity<Pred>::type;
using allocator_type = typename boost::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;
concurrent_flat_set()
: concurrent_flat_set(detail::foa::default_bucket_count)
{
}
explicit concurrent_flat_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_flat_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_flat_set(concurrent_flat_set const& rhs)
: table_(rhs.table_,
boost::allocator_select_on_container_copy_construction(
rhs.get_allocator()))
{
}
concurrent_flat_set(concurrent_flat_set&& rhs)
: table_(std::move(rhs.table_))
{
}
template <class InputIterator>
concurrent_flat_set(
InputIterator f, InputIterator l, allocator_type const& a)
: concurrent_flat_set(f, l, 0, hasher(), key_equal(), a)
{
}
explicit concurrent_flat_set(allocator_type const& a)
: table_(detail::foa::default_bucket_count, hasher(), key_equal(), a)
{
}
concurrent_flat_set(
concurrent_flat_set const& rhs, allocator_type const& a)
: table_(rhs.table_, a)
{
}
concurrent_flat_set(concurrent_flat_set&& rhs, allocator_type const& a)
: table_(std::move(rhs.table_), a)
{
}
concurrent_flat_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_flat_set(n, hf, eql, a)
{
this->insert(il.begin(), il.end());
}
concurrent_flat_set(size_type n, const allocator_type& a)
: concurrent_flat_set(n, hasher(), key_equal(), a)
{
}
concurrent_flat_set(
size_type n, const hasher& hf, const allocator_type& a)
: concurrent_flat_set(n, hf, key_equal(), a)
{
}
template <typename InputIterator>
concurrent_flat_set(
InputIterator f, InputIterator l, size_type n, const allocator_type& a)
: concurrent_flat_set(f, l, n, hasher(), key_equal(), a)
{
}
template <typename InputIterator>
concurrent_flat_set(InputIterator f, InputIterator l, size_type n,
const hasher& hf, const allocator_type& a)
: concurrent_flat_set(f, l, n, hf, key_equal(), a)
{
}
concurrent_flat_set(
std::initializer_list<value_type> il, const allocator_type& a)
: concurrent_flat_set(
il, detail::foa::default_bucket_count, hasher(), key_equal(), a)
{
}
concurrent_flat_set(std::initializer_list<value_type> il, size_type n,
const allocator_type& a)
: concurrent_flat_set(il, n, hasher(), key_equal(), a)
{
}
concurrent_flat_set(std::initializer_list<value_type> il, size_type n,
const hasher& hf, const allocator_type& a)
: concurrent_flat_set(il, n, hf, key_equal(), a)
{
}
concurrent_flat_set(
unordered_flat_set<Key, Hash, Pred, Allocator>&& other)
: table_(std::move(other.table_))
{
}
~concurrent_flat_set() = default;
concurrent_flat_set& operator=(concurrent_flat_set const& rhs)
{
table_ = rhs.table_;
return *this;
}
concurrent_flat_set& operator=(concurrent_flat_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_flat_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) 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) 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 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) 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) 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) 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());
}
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_cvisit(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);
}
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_cvisit(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_cvisit(*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_cvisit(ilist.begin(), ilist.end(), f);
}
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... 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_cvisit(
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_flat_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_);
}
void clear() noexcept { table_.clear(); }
template <typename H2, typename P2>
size_type merge(concurrent_flat_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_flat_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); }
/// 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_flat_set<Key, Hash, KeyEqual, Allocator> const& lhs,
concurrent_flat_set<Key, Hash, KeyEqual, Allocator> const& rhs)
{
return lhs.table_ == rhs.table_;
}
template <class Key, class Hash, class KeyEqual, class Allocator>
bool operator!=(
concurrent_flat_set<Key, Hash, KeyEqual, Allocator> const& lhs,
concurrent_flat_set<Key, Hash, KeyEqual, Allocator> const& rhs)
{
return !(lhs == rhs);
}
template <class Key, class Hash, class Pred, class Alloc>
void swap(concurrent_flat_set<Key, Hash, Pred, Alloc>& x,
concurrent_flat_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_flat_set<K, H, P, A>::size_type erase_if(
concurrent_flat_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_flat_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 = boost::enable_if_t<detail::is_input_iterator_v<InputIterator> >,
class = boost::enable_if_t<detail::is_hash_v<Hash> >,
class = boost::enable_if_t<detail::is_pred_v<Pred> >,
class = boost::enable_if_t<detail::is_allocator_v<Allocator> > >
concurrent_flat_set(InputIterator, InputIterator,
std::size_t = boost::unordered::detail::foa::default_bucket_count,
Hash = Hash(), Pred = Pred(), Allocator = Allocator())
-> concurrent_flat_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 = boost::enable_if_t<detail::is_hash_v<Hash> >,
class = boost::enable_if_t<detail::is_pred_v<Pred> >,
class = boost::enable_if_t<detail::is_allocator_v<Allocator> > >
concurrent_flat_set(std::initializer_list<T>,
std::size_t = boost::unordered::detail::foa::default_bucket_count,
Hash = Hash(), Pred = Pred(), Allocator = Allocator())
-> concurrent_flat_set< T, Hash, Pred, Allocator>;
template <class InputIterator, class Allocator,
class = boost::enable_if_t<detail::is_input_iterator_v<InputIterator> >,
class = boost::enable_if_t<detail::is_allocator_v<Allocator> > >
concurrent_flat_set(InputIterator, InputIterator, std::size_t, Allocator)
-> concurrent_flat_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 = boost::enable_if_t<detail::is_input_iterator_v<InputIterator> >,
class = boost::enable_if_t<detail::is_allocator_v<Allocator> > >
concurrent_flat_set(InputIterator, InputIterator, Allocator)
-> concurrent_flat_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 = boost::enable_if_t<detail::is_hash_v<Hash> >,
class = boost::enable_if_t<detail::is_input_iterator_v<InputIterator> >,
class = boost::enable_if_t<detail::is_allocator_v<Allocator> > >
concurrent_flat_set(
InputIterator, InputIterator, std::size_t, Hash, Allocator)
-> concurrent_flat_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 = boost::enable_if_t<detail::is_allocator_v<Allocator> > >
concurrent_flat_set(std::initializer_list<T>, std::size_t, Allocator)
-> concurrent_flat_set<T, boost::hash<T>,std::equal_to<T>, Allocator>;
template <class T, class Allocator,
class = boost::enable_if_t<detail::is_allocator_v<Allocator> > >
concurrent_flat_set(std::initializer_list<T >, Allocator)
-> concurrent_flat_set<T, boost::hash<T>, std::equal_to<T>, Allocator>;
template <class T, class Hash, class Allocator,
class = boost::enable_if_t<detail::is_hash_v<Hash> >,
class = boost::enable_if_t<detail::is_allocator_v<Allocator> > >
concurrent_flat_set(std::initializer_list<T >, std::size_t,Hash, Allocator)
-> concurrent_flat_set<T, Hash, std::equal_to<T>, Allocator>;
#endif
} // namespace unordered
using unordered::concurrent_flat_set;
} // namespace boost
#endif // BOOST_UNORDERED_CONCURRENT_FLAT_SET_HPP
@@ -1,55 +0,0 @@
/* Fast open-addressing concurrent hashset.
*
* Copyright 2023 Christian Mazakas.
* Copyright 2023 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_FLAT_SET_FWD_HPP
#define BOOST_UNORDERED_CONCURRENT_FLAT_SET_FWD_HPP
#include <boost/container_hash/hash_fwd.hpp>
#include <functional>
#include <memory>
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_flat_set;
template <class Key, class Hash, class KeyEqual, class Allocator>
bool operator==(
concurrent_flat_set<Key, Hash, KeyEqual, Allocator> const& lhs,
concurrent_flat_set<Key, Hash, KeyEqual, Allocator> const& rhs);
template <class Key, class Hash, class KeyEqual, class Allocator>
bool operator!=(
concurrent_flat_set<Key, Hash, KeyEqual, Allocator> const& lhs,
concurrent_flat_set<Key, Hash, KeyEqual, Allocator> const& rhs);
template <class Key, class Hash, class Pred, class Alloc>
void swap(concurrent_flat_set<Key, Hash, Pred, Alloc>& x,
concurrent_flat_set<Key, Hash, Pred, Alloc>& y)
noexcept(noexcept(x.swap(y)));
template <class K, class H, class P, class A, class Predicate>
typename concurrent_flat_set<K, H, P, A>::size_type erase_if(
concurrent_flat_set<K, H, P, A>& c, Predicate pred);
} // namespace unordered
using boost::unordered::concurrent_flat_set;
using boost::unordered::swap;
using boost::unordered::operator==;
using boost::unordered::operator!=;
} // namespace boost
#endif // BOOST_UNORDERED_CONCURRENT_FLAT_SET_FWD_HPP
@@ -1,72 +0,0 @@
/* Copyright 2023 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_ARCHIVE_CONSTRUCTED_HPP
#define BOOST_UNORDERED_DETAIL_ARCHIVE_CONSTRUCTED_HPP
#include <boost/config.hpp>
#include <boost/core/addressof.hpp>
#include <boost/core/no_exceptions_support.hpp>
#include <boost/core/noncopyable.hpp>
#include <boost/core/serialization.hpp>
#include <boost/type_traits/aligned_storage.hpp>
#include <boost/type_traits/alignment_of.hpp>
namespace boost{
namespace unordered{
namespace detail{
/* constructs a stack-based object from a serialization archive */
template<typename T>
struct archive_constructed:private noncopyable
{
template<class Archive>
archive_constructed(const char* name,Archive& ar,unsigned int version)
{
core::load_construct_data_adl(ar,boost::addressof(get()),version);
BOOST_TRY{
ar>>core::make_nvp(name,get());
}
BOOST_CATCH(...){
get().~T();
BOOST_RETHROW;
}
BOOST_CATCH_END
}
~archive_constructed()
{
get().~T();
}
#if defined(BOOST_GCC)&&(BOOST_GCC>=4*10000+6*100)
#define BOOST_UNORDERED_IGNORE_WSTRICT_ALIASING
#endif
#if defined(BOOST_UNORDERED_IGNORE_WSTRICT_ALIASING)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wstrict-aliasing"
#endif
T& get(){return *reinterpret_cast<T*>(&space);}
#if defined(BOOST_UNORDERED_IGNORE_WSTRICT_ALIASING)
#pragma GCC diagnostic pop
#undef BOOST_UNORDERED_IGNORE_WSTRICT_ALIASING
#endif
private:
typename aligned_storage<sizeof(T),alignment_of<T>::value>::type space;
};
} /* namespace detail */
} /* namespace unordered */
} /* namespace boost */
#endif
@@ -1,27 +0,0 @@
/* Copyright 2023 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_BAD_ARCHIVE_EXCEPTION_HPP
#define BOOST_UNORDERED_DETAIL_BAD_ARCHIVE_EXCEPTION_HPP
#include <stdexcept>
namespace boost{
namespace unordered{
namespace detail{
struct bad_archive_exception:std::runtime_error
{
bad_archive_exception():std::runtime_error("Invalid or corrupted archive"){}
};
} /* namespace detail */
} /* namespace unordered */
} /* namespace boost */
#endif
@@ -1,75 +0,0 @@
/* Copyright 2023 Christian Mazakas.
* Copyright 2023 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_CONCURRENT_STATIC_ASSERTS_HPP
#define BOOST_UNORDERED_DETAIL_CONCURRENT_STATIC_ASSERTS_HPP
#include <boost/mp11/algorithm.hpp>
#include <boost/mp11/list.hpp>
#include <functional>
#include <type_traits>
#define BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(F) \
static_assert(boost::unordered::detail::is_invocable<F, value_type&>::value, \
"The provided Callable must be invocable with value_type&");
#define BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F) \
static_assert( \
boost::unordered::detail::is_invocable<F, value_type const&>::value, \
"The provided Callable must be invocable with value_type const&");
#if BOOST_CXX_VERSION >= 202002L
#define BOOST_UNORDERED_STATIC_ASSERT_EXEC_POLICY(P) \
static_assert(!std::is_base_of<std::execution::parallel_unsequenced_policy, \
ExecPolicy>::value, \
"ExecPolicy must be sequenced."); \
static_assert( \
!std::is_base_of<std::execution::unsequenced_policy, ExecPolicy>::value, \
"ExecPolicy must be sequenced.");
#else
#define BOOST_UNORDERED_STATIC_ASSERT_EXEC_POLICY(P) \
static_assert(!std::is_base_of<std::execution::parallel_unsequenced_policy, \
ExecPolicy>::value, \
"ExecPolicy must be sequenced.");
#endif
#define BOOST_UNORDERED_DETAIL_COMMA ,
#define BOOST_UNORDERED_DETAIL_LAST_ARG(Arg, Args) \
mp11::mp_back<mp11::mp_list<Arg BOOST_UNORDERED_DETAIL_COMMA Args> >
#define BOOST_UNORDERED_STATIC_ASSERT_LAST_ARG_INVOCABLE(Arg, Args) \
BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE( \
BOOST_UNORDERED_DETAIL_LAST_ARG(Arg, Args))
#define BOOST_UNORDERED_STATIC_ASSERT_LAST_ARG_CONST_INVOCABLE(Arg, Args) \
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE( \
BOOST_UNORDERED_DETAIL_LAST_ARG(Arg, Args))
namespace boost {
namespace unordered {
namespace detail {
template <class F, class... Args>
struct is_invocable
: std::is_constructible<std::function<void(Args...)>,
std::reference_wrapper<typename std::remove_reference<F>::type> >
{
};
} // namespace detail
} // namespace unordered
} // namespace boost
#endif // BOOST_UNORDERED_DETAIL_CONCURRENT_STATIC_ASSERTS_HPP
+3 -24
View File
@@ -1,4 +1,4 @@
// Copyright (C) 2022-2023 Joaquin M Lopez Munoz.
// Copyright (C) 2022 Joaquin M Lopez Munoz.
// Copyright (C) 2022 Christian Mazakas
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
@@ -114,18 +114,16 @@ to normal separate chaining implementations.
*/
#include <boost/unordered/detail/prime_fmod.hpp>
#include <boost/unordered/detail/serialize_tracked_address.hpp>
#include <boost/core/addressof.hpp>
#include <boost/core/allocator_access.hpp>
#include <boost/core/bit.hpp>
#include <boost/core/empty_value.hpp>
#include <boost/core/invoke_swap.hpp>
#include <boost/core/no_exceptions_support.hpp>
#include <boost/core/serialization.hpp>
#include <boost/cstdint.hpp>
#include <boost/move/core.hpp>
#include <boost/move/utility_core.hpp>
#include <boost/swap.hpp>
#include <boost/type_traits/aligned_storage.hpp>
#include <boost/type_traits/alignment_of.hpp>
@@ -289,25 +287,6 @@ namespace boost {
p = pbg->buckets + x;
}
}
template<typename Archive>
friend void serialization_track(
Archive& ar, grouped_bucket_iterator const& x)
{
// requires: not at end() position
track_address(ar, x.p);
track_address(ar, x.pbg);
}
friend class boost::serialization::access;
template<typename Archive>
void serialize(Archive& ar,unsigned int)
{
// requires: not at end() position
serialize_tracked_address(ar, p);
serialize_tracked_address(ar, pbg);
}
};
template <class Node> struct const_grouped_local_bucket_iterator;
@@ -651,7 +630,7 @@ namespace boost {
bool b = boost::allocator_propagate_on_container_swap<
allocator_type>::type::value;
if (b) {
boost::core::invoke_swap(get_node_allocator(), other.get_node_allocator());
boost::swap(get_node_allocator(), other.get_node_allocator());
}
}
File diff suppressed because it is too large Load Diff
+158 -290
View File
@@ -133,10 +133,10 @@
#define BOOST_UNORDERED_THREAD_SANITIZER
#endif
#define BOOST_UNORDERED_STATIC_ASSERT_HASH_PRED(Hash, Pred) \
static_assert(boost::is_nothrow_swappable<Hash>::value, \
"Template parameter Hash is required to be nothrow Swappable."); \
static_assert(boost::is_nothrow_swappable<Pred>::value, \
#define BOOST_UNORDERED_STATIC_ASSERT_HASH_PRED(Hash, Pred) \
static_assert(boost::is_nothrow_swappable<Hash>::value, \
"Template parameter Hash is required to be nothrow Swappable."); \
static_assert(boost::is_nothrow_swappable<Pred>::value, \
"Template parameter Pred is required to be nothrow Swappable");
namespace boost{
@@ -148,7 +148,7 @@ static constexpr std::size_t default_bucket_count=0;
/* foa::table_core is the common base of foa::table and foa::concurrent_table,
* which in their turn serve as the foundational core of
* boost::unordered_(flat|node)_(map|set) and boost::concurrent_flat_(map|set),
* boost::unordered_(flat|node)_(map|set) and boost::concurrent_flat_map,
* respectively. Its main internal design aspects are:
*
* - Element slots are logically split into groups of size N=15. The number
@@ -337,49 +337,38 @@ private:
{
static constexpr boost::uint32_t word[]=
{
0x08080808u,0x09090909u,0x02020202u,0x03030303u,0x04040404u,0x05050505u,
0x06060606u,0x07070707u,0x08080808u,0x09090909u,0x0A0A0A0Au,0x0B0B0B0Bu,
0x0C0C0C0Cu,0x0D0D0D0Du,0x0E0E0E0Eu,0x0F0F0F0Fu,0x10101010u,0x11111111u,
0x12121212u,0x13131313u,0x14141414u,0x15151515u,0x16161616u,0x17171717u,
0x18181818u,0x19191919u,0x1A1A1A1Au,0x1B1B1B1Bu,0x1C1C1C1Cu,0x1D1D1D1Du,
0x1E1E1E1Eu,0x1F1F1F1Fu,0x20202020u,0x21212121u,0x22222222u,0x23232323u,
0x24242424u,0x25252525u,0x26262626u,0x27272727u,0x28282828u,0x29292929u,
0x2A2A2A2Au,0x2B2B2B2Bu,0x2C2C2C2Cu,0x2D2D2D2Du,0x2E2E2E2Eu,0x2F2F2F2Fu,
0x30303030u,0x31313131u,0x32323232u,0x33333333u,0x34343434u,0x35353535u,
0x36363636u,0x37373737u,0x38383838u,0x39393939u,0x3A3A3A3Au,0x3B3B3B3Bu,
0x3C3C3C3Cu,0x3D3D3D3Du,0x3E3E3E3Eu,0x3F3F3F3Fu,0x40404040u,0x41414141u,
0x42424242u,0x43434343u,0x44444444u,0x45454545u,0x46464646u,0x47474747u,
0x48484848u,0x49494949u,0x4A4A4A4Au,0x4B4B4B4Bu,0x4C4C4C4Cu,0x4D4D4D4Du,
0x4E4E4E4Eu,0x4F4F4F4Fu,0x50505050u,0x51515151u,0x52525252u,0x53535353u,
0x54545454u,0x55555555u,0x56565656u,0x57575757u,0x58585858u,0x59595959u,
0x5A5A5A5Au,0x5B5B5B5Bu,0x5C5C5C5Cu,0x5D5D5D5Du,0x5E5E5E5Eu,0x5F5F5F5Fu,
0x60606060u,0x61616161u,0x62626262u,0x63636363u,0x64646464u,0x65656565u,
0x66666666u,0x67676767u,0x68686868u,0x69696969u,0x6A6A6A6Au,0x6B6B6B6Bu,
0x6C6C6C6Cu,0x6D6D6D6Du,0x6E6E6E6Eu,0x6F6F6F6Fu,0x70707070u,0x71717171u,
0x72727272u,0x73737373u,0x74747474u,0x75757575u,0x76767676u,0x77777777u,
0x78787878u,0x79797979u,0x7A7A7A7Au,0x7B7B7B7Bu,0x7C7C7C7Cu,0x7D7D7D7Du,
0x7E7E7E7Eu,0x7F7F7F7Fu,0x80808080u,0x81818181u,0x82828282u,0x83838383u,
0x84848484u,0x85858585u,0x86868686u,0x87878787u,0x88888888u,0x89898989u,
0x8A8A8A8Au,0x8B8B8B8Bu,0x8C8C8C8Cu,0x8D8D8D8Du,0x8E8E8E8Eu,0x8F8F8F8Fu,
0x90909090u,0x91919191u,0x92929292u,0x93939393u,0x94949494u,0x95959595u,
0x96969696u,0x97979797u,0x98989898u,0x99999999u,0x9A9A9A9Au,0x9B9B9B9Bu,
0x9C9C9C9Cu,0x9D9D9D9Du,0x9E9E9E9Eu,0x9F9F9F9Fu,0xA0A0A0A0u,0xA1A1A1A1u,
0xA2A2A2A2u,0xA3A3A3A3u,0xA4A4A4A4u,0xA5A5A5A5u,0xA6A6A6A6u,0xA7A7A7A7u,
0xA8A8A8A8u,0xA9A9A9A9u,0xAAAAAAAAu,0xABABABABu,0xACACACACu,0xADADADADu,
0xAEAEAEAEu,0xAFAFAFAFu,0xB0B0B0B0u,0xB1B1B1B1u,0xB2B2B2B2u,0xB3B3B3B3u,
0xB4B4B4B4u,0xB5B5B5B5u,0xB6B6B6B6u,0xB7B7B7B7u,0xB8B8B8B8u,0xB9B9B9B9u,
0xBABABABAu,0xBBBBBBBBu,0xBCBCBCBCu,0xBDBDBDBDu,0xBEBEBEBEu,0xBFBFBFBFu,
0xC0C0C0C0u,0xC1C1C1C1u,0xC2C2C2C2u,0xC3C3C3C3u,0xC4C4C4C4u,0xC5C5C5C5u,
0xC6C6C6C6u,0xC7C7C7C7u,0xC8C8C8C8u,0xC9C9C9C9u,0xCACACACAu,0xCBCBCBCBu,
0xCCCCCCCCu,0xCDCDCDCDu,0xCECECECEu,0xCFCFCFCFu,0xD0D0D0D0u,0xD1D1D1D1u,
0xD2D2D2D2u,0xD3D3D3D3u,0xD4D4D4D4u,0xD5D5D5D5u,0xD6D6D6D6u,0xD7D7D7D7u,
0xD8D8D8D8u,0xD9D9D9D9u,0xDADADADAu,0xDBDBDBDBu,0xDCDCDCDCu,0xDDDDDDDDu,
0xDEDEDEDEu,0xDFDFDFDFu,0xE0E0E0E0u,0xE1E1E1E1u,0xE2E2E2E2u,0xE3E3E3E3u,
0xE4E4E4E4u,0xE5E5E5E5u,0xE6E6E6E6u,0xE7E7E7E7u,0xE8E8E8E8u,0xE9E9E9E9u,
0xEAEAEAEAu,0xEBEBEBEBu,0xECECECECu,0xEDEDEDEDu,0xEEEEEEEEu,0xEFEFEFEFu,
0xF0F0F0F0u,0xF1F1F1F1u,0xF2F2F2F2u,0xF3F3F3F3u,0xF4F4F4F4u,0xF5F5F5F5u,
0xF6F6F6F6u,0xF7F7F7F7u,0xF8F8F8F8u,0xF9F9F9F9u,0xFAFAFAFAu,0xFBFBFBFBu,
0xFCFCFCFCu,0xFDFDFDFDu,0xFEFEFEFEu,0xFFFFFFFFu,
0x08080808u,0x09090909u,0x02020202u,0x03030303u,0x04040404u,0x05050505u,0x06060606u,0x07070707u,
0x08080808u,0x09090909u,0x0A0A0A0Au,0x0B0B0B0Bu,0x0C0C0C0Cu,0x0D0D0D0Du,0x0E0E0E0Eu,0x0F0F0F0Fu,
0x10101010u,0x11111111u,0x12121212u,0x13131313u,0x14141414u,0x15151515u,0x16161616u,0x17171717u,
0x18181818u,0x19191919u,0x1A1A1A1Au,0x1B1B1B1Bu,0x1C1C1C1Cu,0x1D1D1D1Du,0x1E1E1E1Eu,0x1F1F1F1Fu,
0x20202020u,0x21212121u,0x22222222u,0x23232323u,0x24242424u,0x25252525u,0x26262626u,0x27272727u,
0x28282828u,0x29292929u,0x2A2A2A2Au,0x2B2B2B2Bu,0x2C2C2C2Cu,0x2D2D2D2Du,0x2E2E2E2Eu,0x2F2F2F2Fu,
0x30303030u,0x31313131u,0x32323232u,0x33333333u,0x34343434u,0x35353535u,0x36363636u,0x37373737u,
0x38383838u,0x39393939u,0x3A3A3A3Au,0x3B3B3B3Bu,0x3C3C3C3Cu,0x3D3D3D3Du,0x3E3E3E3Eu,0x3F3F3F3Fu,
0x40404040u,0x41414141u,0x42424242u,0x43434343u,0x44444444u,0x45454545u,0x46464646u,0x47474747u,
0x48484848u,0x49494949u,0x4A4A4A4Au,0x4B4B4B4Bu,0x4C4C4C4Cu,0x4D4D4D4Du,0x4E4E4E4Eu,0x4F4F4F4Fu,
0x50505050u,0x51515151u,0x52525252u,0x53535353u,0x54545454u,0x55555555u,0x56565656u,0x57575757u,
0x58585858u,0x59595959u,0x5A5A5A5Au,0x5B5B5B5Bu,0x5C5C5C5Cu,0x5D5D5D5Du,0x5E5E5E5Eu,0x5F5F5F5Fu,
0x60606060u,0x61616161u,0x62626262u,0x63636363u,0x64646464u,0x65656565u,0x66666666u,0x67676767u,
0x68686868u,0x69696969u,0x6A6A6A6Au,0x6B6B6B6Bu,0x6C6C6C6Cu,0x6D6D6D6Du,0x6E6E6E6Eu,0x6F6F6F6Fu,
0x70707070u,0x71717171u,0x72727272u,0x73737373u,0x74747474u,0x75757575u,0x76767676u,0x77777777u,
0x78787878u,0x79797979u,0x7A7A7A7Au,0x7B7B7B7Bu,0x7C7C7C7Cu,0x7D7D7D7Du,0x7E7E7E7Eu,0x7F7F7F7Fu,
0x80808080u,0x81818181u,0x82828282u,0x83838383u,0x84848484u,0x85858585u,0x86868686u,0x87878787u,
0x88888888u,0x89898989u,0x8A8A8A8Au,0x8B8B8B8Bu,0x8C8C8C8Cu,0x8D8D8D8Du,0x8E8E8E8Eu,0x8F8F8F8Fu,
0x90909090u,0x91919191u,0x92929292u,0x93939393u,0x94949494u,0x95959595u,0x96969696u,0x97979797u,
0x98989898u,0x99999999u,0x9A9A9A9Au,0x9B9B9B9Bu,0x9C9C9C9Cu,0x9D9D9D9Du,0x9E9E9E9Eu,0x9F9F9F9Fu,
0xA0A0A0A0u,0xA1A1A1A1u,0xA2A2A2A2u,0xA3A3A3A3u,0xA4A4A4A4u,0xA5A5A5A5u,0xA6A6A6A6u,0xA7A7A7A7u,
0xA8A8A8A8u,0xA9A9A9A9u,0xAAAAAAAAu,0xABABABABu,0xACACACACu,0xADADADADu,0xAEAEAEAEu,0xAFAFAFAFu,
0xB0B0B0B0u,0xB1B1B1B1u,0xB2B2B2B2u,0xB3B3B3B3u,0xB4B4B4B4u,0xB5B5B5B5u,0xB6B6B6B6u,0xB7B7B7B7u,
0xB8B8B8B8u,0xB9B9B9B9u,0xBABABABAu,0xBBBBBBBBu,0xBCBCBCBCu,0xBDBDBDBDu,0xBEBEBEBEu,0xBFBFBFBFu,
0xC0C0C0C0u,0xC1C1C1C1u,0xC2C2C2C2u,0xC3C3C3C3u,0xC4C4C4C4u,0xC5C5C5C5u,0xC6C6C6C6u,0xC7C7C7C7u,
0xC8C8C8C8u,0xC9C9C9C9u,0xCACACACAu,0xCBCBCBCBu,0xCCCCCCCCu,0xCDCDCDCDu,0xCECECECEu,0xCFCFCFCFu,
0xD0D0D0D0u,0xD1D1D1D1u,0xD2D2D2D2u,0xD3D3D3D3u,0xD4D4D4D4u,0xD5D5D5D5u,0xD6D6D6D6u,0xD7D7D7D7u,
0xD8D8D8D8u,0xD9D9D9D9u,0xDADADADAu,0xDBDBDBDBu,0xDCDCDCDCu,0xDDDDDDDDu,0xDEDEDEDEu,0xDFDFDFDFu,
0xE0E0E0E0u,0xE1E1E1E1u,0xE2E2E2E2u,0xE3E3E3E3u,0xE4E4E4E4u,0xE5E5E5E5u,0xE6E6E6E6u,0xE7E7E7E7u,
0xE8E8E8E8u,0xE9E9E9E9u,0xEAEAEAEAu,0xEBEBEBEBu,0xECECECECu,0xEDEDEDEDu,0xEEEEEEEEu,0xEFEFEFEFu,
0xF0F0F0F0u,0xF1F1F1F1u,0xF2F2F2F2u,0xF3F3F3F3u,0xF4F4F4F4u,0xF5F5F5F5u,0xF6F6F6F6u,0xF7F7F7F7u,
0xF8F8F8F8u,0xF9F9F9F9u,0xFAFAFAFAu,0xFBFBFBFBu,0xFCFCFCFCu,0xFDFDFDFDu,0xFEFEFEFEu,0xFFFFFFFFu,
};
return (int)word[narrow_cast<unsigned char>(hash)];
@@ -560,8 +549,7 @@ private:
}
/* Copied from
* https://github.com/simd-everywhere/simde/blob/master/simde/x86/
* sse2.h#L3763
* https://github.com/simd-everywhere/simde/blob/master/simde/x86/sse2.h#L3763
*/
static inline int simde_mm_movemask_epi8(uint8x16_t a)
@@ -640,8 +628,7 @@ struct group15
BOOST_ASSERT(pos<N);
return
pos==N-1&&
(m[0] & boost::uint64_t(0x4000400040004000ull))==
boost::uint64_t(0x4000ull)&&
(m[0] & boost::uint64_t(0x4000400040004000ull))==boost::uint64_t(0x4000ull)&&
(m[1] & boost::uint64_t(0x4000400040004000ull))==0;
}
@@ -800,8 +787,8 @@ private:
*
* - size_index(n) returns an unspecified "index" number used in other policy
* operations.
* - size(size_index_) returns the number of groups for the given index. It
* is guaranteed that size(size_index(n)) >= n.
* - size(size_index_) returns the number of groups for the given index. It is
* guaranteed that size(size_index(n)) >= n.
* - min_size() is the minimum number of groups permissible, i.e.
* size(size_index(0)).
* - position(hash,size_index_) maps hash to a position in the range
@@ -951,142 +938,74 @@ Group* dummy_groups()
const_cast<typename Group::dummy_group_type*>(storage));
}
template<
typename Ptr,typename Ptr2,
typename std::enable_if<!std::is_same<Ptr,Ptr2>::value>::type* = nullptr
>
Ptr to_pointer(Ptr2 p)
{
if(!p){return nullptr;}
return boost::pointer_traits<Ptr>::pointer_to(*p);
}
template<typename Ptr>
Ptr to_pointer(Ptr p)
{
return p;
}
template<typename Arrays,typename Allocator>
struct arrays_holder
{
arrays_holder(const Arrays& arrays,const Allocator& al):
arrays_{arrays},al_{al}
{}
/* not defined but VS in pre-C++17 mode needs to see it for RVO */
arrays_holder(arrays_holder const&);
arrays_holder& operator=(arrays_holder const&)=delete;
~arrays_holder(){if(!released_)arrays_.delete_(al_,arrays_);}
const Arrays& release()
{
released_=true;
return arrays_;
}
private:
Arrays arrays_;
Allocator al_;
bool released_=false;
};
template<typename Value,typename Group,typename SizePolicy,typename Allocator>
template<typename Value,typename Group,typename SizePolicy>
struct table_arrays
{
using allocator_type=typename boost::allocator_rebind<Allocator,Value>::type;
using value_type=Value;
using group_type=Group;
static constexpr auto N=group_type::N;
using size_policy=SizePolicy;
using value_type_pointer=
typename boost::allocator_pointer<allocator_type>::type;
using group_type_pointer=
typename boost::pointer_traits<value_type_pointer>::template
rebind<group_type>;
using group_type_pointer_traits=boost::pointer_traits<group_type_pointer>;
table_arrays(
std::size_t gsi,std::size_t gsm,
group_type_pointer pg,value_type_pointer pe):
groups_size_index{gsi},groups_size_mask{gsm},groups_{pg},elements_{pe}{}
table_arrays(std::size_t gsi,std::size_t gsm,group_type *pg,value_type *pe):
groups_size_index{gsi},groups_size_mask{gsm},groups{pg},elements{pe}{}
value_type* elements()const noexcept{return boost::to_address(elements_);}
group_type* groups()const noexcept{return boost::to_address(groups_);}
static void set_arrays(table_arrays& arrays,allocator_type al,std::size_t n)
template<typename Allocator>
static table_arrays new_(Allocator& al,std::size_t n)
{
return set_arrays(
arrays,al,n,std::is_same<group_type*,group_type_pointer>{});
}
using storage_allocator=
typename boost::allocator_rebind<Allocator, Value>::type;
using storage_traits=boost::allocator_traits<storage_allocator>;
static void set_arrays(
table_arrays& arrays,allocator_type al,std::size_t,
std::false_type /* always allocate */)
{
using storage_traits=boost::allocator_traits<allocator_type>;
auto groups_size_index=arrays.groups_size_index;
auto groups_size=size_policy::size(groups_size_index);
auto sal=allocator_type(al);
arrays.elements_=storage_traits::allocate(sal,buffer_size(groups_size));
/* Align arrays.groups to sizeof(group_type). table_iterator critically
* depends on such alignment for its increment operation.
*/
auto p=reinterpret_cast<unsigned char*>(arrays.elements()+groups_size*N-1);
p+=(uintptr_t(sizeof(group_type))-
reinterpret_cast<uintptr_t>(p))%sizeof(group_type);
arrays.groups_=
group_type_pointer_traits::pointer_to(*reinterpret_cast<group_type*>(p));
initialize_groups(
arrays.groups(),groups_size,
std::integral_constant<
bool,
#if BOOST_WORKAROUND(BOOST_LIBSTDCXX_VERSION,<50000)
/* std::is_trivially_constructible not provided */
boost::has_trivial_constructor<group_type>::value
#else
std::is_trivially_constructible<group_type>::value
#endif
>{});
arrays.groups()[groups_size-1].set_sentinel();
}
static void set_arrays(
table_arrays& arrays,allocator_type al,std::size_t n,
std::true_type /* optimize for n==0*/)
{
if(!n){
arrays.groups_=dummy_groups<group_type,size_policy::min_size()>();
}
else{
set_arrays(arrays,al,n,std::false_type{});
}
}
static table_arrays new_(allocator_type al,std::size_t n)
{
auto groups_size_index=size_index_for<group_type,size_policy>(n);
auto groups_size=size_policy::size(groups_size_index);
table_arrays arrays{groups_size_index,groups_size-1,nullptr,nullptr};
set_arrays(arrays,al,n);
if(!n){
arrays.groups=dummy_groups<group_type,size_policy::min_size()>();
}
else{
auto sal=storage_allocator(al);
arrays.elements=boost::to_address(
storage_traits::allocate(sal,buffer_size(groups_size)));
/* Align arrays.groups to sizeof(group_type). table_iterator critically
* depends on such alignment for its increment operation.
*/
auto p=reinterpret_cast<unsigned char*>(arrays.elements+groups_size*N-1);
p+=(uintptr_t(sizeof(group_type))-
reinterpret_cast<uintptr_t>(p))%sizeof(group_type);
arrays.groups=reinterpret_cast<group_type*>(p);
initialize_groups(
arrays.groups,groups_size,
std::integral_constant<
bool,
#if BOOST_WORKAROUND(BOOST_LIBSTDCXX_VERSION,<50000)
/* std::is_trivially_constructible not provided */
boost::has_trivial_constructor<group_type>::value
#else
std::is_trivially_constructible<group_type>::value
#endif
>{});
arrays.groups[groups_size-1].set_sentinel();
}
return arrays;
}
static void delete_(allocator_type al,table_arrays& arrays)noexcept
template<typename Allocator>
static void delete_(Allocator& al,table_arrays& arrays)noexcept
{
using storage_traits=boost::allocator_traits<allocator_type>;
using storage_alloc=typename boost::allocator_rebind<Allocator,Value>::type;
using storage_traits=boost::allocator_traits<storage_alloc>;
using pointer=typename storage_traits::pointer;
using pointer_traits=boost::pointer_traits<pointer>;
auto sal=allocator_type(al);
if(arrays.elements()){
auto sal=storage_alloc(al);
if(arrays.elements){
storage_traits::deallocate(
sal,arrays.elements_,buffer_size(arrays.groups_size_mask+1));
sal,pointer_traits::pointer_to(*arrays.elements),
buffer_size(arrays.groups_size_mask+1));
}
}
@@ -1105,7 +1024,7 @@ struct table_arrays
}
static void initialize_groups(
group_type* pg,std::size_t size,std::true_type /* memset */)
group_type* groups_,std::size_t size,std::true_type /* memset */)
{
/* memset faster/not slower than manual, assumes all zeros is group_type's
* default layout.
@@ -1114,19 +1033,19 @@ struct table_arrays
*/
std::memset(
reinterpret_cast<unsigned char*>(pg),0,sizeof(group_type)*size);
reinterpret_cast<unsigned char*>(groups_),0,sizeof(group_type)*size);
}
static void initialize_groups(
group_type* pg,std::size_t size,std::false_type /* manual */)
group_type* groups_,std::size_t size,std::false_type /* manual */)
{
while(size--!=0)::new (pg++) group_type();
while(size--!=0)::new (groups_++) group_type();
}
std::size_t groups_size_index;
std::size_t groups_size_mask;
group_type_pointer groups_;
value_type_pointer elements_;
std::size_t groups_size_index;
std::size_t groups_size_mask;
group_type *groups;
value_type *elements;
};
struct if_constexpr_void_else{void operator()()const{}};
@@ -1261,8 +1180,7 @@ alloc_make_insert_type(const Allocator& al,Args&&... args)
* init_type and element_type:
*
* - TypePolicy::key_type and TypePolicy::value_type have the obvious
* meaning. TypePolicy::mapped_type is expected to be provided as well
* when key_type and value_type are not the same.
* meaning.
*
* - TypePolicy::init_type is the type implicitly converted to when
* writing x.insert({...}). For maps, this is std::pair<Key,T> rather
@@ -1272,8 +1190,8 @@ alloc_make_insert_type(const Allocator& al,Args&&... args)
* both init_type and value_type references.
*
* - TypePolicy::construct and TypePolicy::destroy are used for the
* construction and destruction of the internal types: value_type,
* init_type and element_type.
* construction and destruction of the internal types: value_type, init_type
* and element_type.
*
* - TypePolicy::move is used to provide move semantics for the internal
* types used by the container during rehashing and emplace. These types
@@ -1339,12 +1257,8 @@ public:
>::type;
using alloc_traits=boost::allocator_traits<Allocator>;
using element_type=typename type_policy::element_type;
using arrays_type=Arrays<element_type,group_type,size_policy,Allocator>;
using arrays_type=Arrays<element_type,group_type,size_policy>;
using size_ctrl_type=SizeControl;
static constexpr auto uses_fancy_pointers=!std::is_same<
typename alloc_traits::pointer,
typename alloc_traits::value_type*
>::value;
using key_type=typename type_policy::key_type;
using init_type=typename type_policy::init_type;
@@ -1359,7 +1273,6 @@ public:
using size_type=std::size_t;
using difference_type=std::ptrdiff_t;
using locator=table_locator<group_type,element_type>;
using arrays_holder_type=arrays_holder<arrays_type,Allocator>;
table_core(
std::size_t n=default_bucket_count,const Hash& h_=Hash(),
@@ -1369,43 +1282,23 @@ public:
size_ctrl{initial_max_load(),0}
{}
/* genericize on an ArraysFn so that we can do things like delay an
* allocation for the group_access data required by cfoa after the move
* constructors of Hash, Pred have been invoked
*/
template<typename ArraysFn>
table_core(
Hash&& h_,Pred&& pred_,Allocator&& al_,
ArraysFn arrays_fn,const size_ctrl_type& size_ctrl_):
hash_base{empty_init,std::move(h_)},
pred_base{empty_init,std::move(pred_)},
allocator_base{empty_init,std::move(al_)},
arrays(arrays_fn()),size_ctrl(size_ctrl_)
{}
table_core(const table_core& x):
table_core{x,alloc_traits::select_on_container_copy_construction(x.al())}{}
template<typename ArraysFn>
table_core(table_core&& x,arrays_holder_type&& ah,ArraysFn arrays_fn):
table_core(
std::move(x.h()),std::move(x.pred()),std::move(x.al()),
arrays_fn,x.size_ctrl)
{
x.arrays=ah.release();
x.size_ctrl.ml=x.initial_max_load();
x.size_ctrl.size=0;
}
table_core(table_core&& x)
noexcept(
std::is_nothrow_move_constructible<Hash>::value&&
std::is_nothrow_move_constructible<Pred>::value&&
std::is_nothrow_move_constructible<Allocator>::value&&
!uses_fancy_pointers):
table_core{
std::move(x),x.make_empty_arrays(),[&x]{return x.arrays;}}
{}
std::is_nothrow_move_constructible<Allocator>::value):
hash_base{empty_init,std::move(x.h())},
pred_base{empty_init,std::move(x.pred())},
allocator_base{empty_init,std::move(x.al())},
arrays(x.arrays),size_ctrl(x.size_ctrl)
{
x.arrays=x.new_arrays(0);
x.size_ctrl.ml=x.initial_max_load();
x.size_ctrl.size=0;
}
table_core(const table_core& x,const Allocator& al_):
table_core{std::size_t(std::ceil(float(x.size())/mlf)),x.h(),x.pred(),al_}
@@ -1443,24 +1336,6 @@ public:
delete_arrays(arrays);
}
std::size_t initial_max_load()const
{
static constexpr std::size_t small_capacity=2*N-1;
auto capacity_=capacity();
if(capacity_<=small_capacity){
return capacity_; /* we allow 100% usage */
}
else{
return (std::size_t)(mlf*(float)(capacity_));
}
}
arrays_holder_type make_empty_arrays()const
{
return make_arrays(0);
}
table_core& operator=(const table_core& x)
{
BOOST_UNORDERED_STATIC_ASSERT_HASH_PRED(Hash, Pred)
@@ -1475,6 +1350,9 @@ public:
hasher tmp_h=x.h();
key_equal tmp_p=x.pred();
/* already noexcept, clear() before we swap the Hash, Pred just in case
* the clear() impl relies on them at some point in the future.
*/
clear();
/* Because we've asserted at compile-time that Hash and Pred are nothrow
@@ -1486,12 +1364,7 @@ public:
swap(pred(),tmp_p);
if_constexpr<pocca>([&,this]{
if(al()!=x.al()){
auto ah=x.make_arrays(std::size_t(std::ceil(float(x.size())/mlf)));
delete_arrays(arrays);
arrays=ah.release();
size_ctrl.ml=initial_max_load();
}
if(al()!=x.al())reserve(0);
copy_assign_if<pocca>(al(),x.al());
});
/* noshrink: favor memory reuse over tightness */
@@ -1508,8 +1381,8 @@ public:
table_core& operator=(table_core&& x)
noexcept(
(alloc_traits::propagate_on_container_move_assignment::value||
alloc_traits::is_always_equal::value)&&!uses_fancy_pointers)
alloc_traits::propagate_on_container_move_assignment::value||
alloc_traits::is_always_equal::value)
{
BOOST_UNORDERED_STATIC_ASSERT_HASH_PRED(Hash, Pred)
@@ -1530,24 +1403,16 @@ public:
using std::swap;
clear();
swap(h(),x.h());
swap(pred(),x.pred());
if(pocma||al()==x.al()){
auto ah=x.make_empty_arrays();
swap(h(),x.h());
swap(pred(),x.pred());
delete_arrays(arrays);
reserve(0);
move_assign_if<pocma>(al(),x.al());
arrays=x.arrays;
size_ctrl.ml=std::size_t(x.size_ctrl.ml);
size_ctrl.size=std::size_t(x.size_ctrl.size);
x.arrays=ah.release();
x.size_ctrl.ml=x.initial_max_load();
x.size_ctrl.size=0;
swap(arrays,x.arrays);
swap(size_ctrl,x.size_ctrl);
}
else{
swap(h(),x.h());
swap(pred(),x.pred());
/* noshrink: favor memory reuse over tightness */
noshrink_reserve(x.size());
clear_on_exit c{x};
@@ -1608,12 +1473,11 @@ public:
prober pb(pos0);
do{
auto pos=pb.get();
auto pg=arrays.groups()+pos;
auto pg=arrays.groups+pos;
auto mask=pg->match(hash);
if(mask){
auto elements=arrays.elements();
BOOST_UNORDERED_ASSUME(elements!=nullptr);
auto p=elements+pos*N;
BOOST_UNORDERED_ASSUME(arrays.elements!=nullptr);
auto p=arrays.elements+pos*N;
BOOST_UNORDERED_PREFETCH_ELEMENTS(p,N);
do{
auto n=unchecked_countr_zero(mask);
@@ -1662,9 +1526,9 @@ public:
void clear()noexcept
{
auto p=arrays.elements();
auto p=arrays.elements;
if(p){
for(auto pg=arrays.groups(),last=pg+arrays.groups_size_mask+1;
for(auto pg=arrays.groups,last=pg+arrays.groups_size_mask+1;
pg!=last;++pg,p+=N){
auto mask=match_really_occupied(pg,last);
while(mask){
@@ -1674,7 +1538,7 @@ public:
/* we wipe the entire metadata to reset the overflow byte as well */
pg->initialize();
}
arrays.groups()[arrays.groups_size_mask].set_sentinel();
arrays.groups[arrays.groups_size_mask].set_sentinel();
size_ctrl.ml=initial_max_load();
size_ctrl.size=0;
}
@@ -1685,7 +1549,7 @@ public:
std::size_t capacity()const noexcept
{
return arrays.elements()?(arrays.groups_size_mask+1)*N-1:0;
return arrays.elements?(arrays.groups_size_mask+1)*N-1:0;
}
float load_factor()const noexcept
@@ -1904,9 +1768,9 @@ public:
static auto for_all_elements_while(const arrays_type& arrays_,F f)
->decltype(f(nullptr,0,nullptr),bool())
{
auto p=arrays_.elements();
auto p=arrays_.elements;
if(p){
for(auto pg=arrays_.groups(),last=pg+arrays_.groups_size_mask+1;
for(auto pg=arrays_.groups,last=pg+arrays_.groups_size_mask+1;
pg!=last;++pg,p+=N){
auto mask=match_really_occupied(pg,last);
while(mask){
@@ -1940,15 +1804,14 @@ private:
pred_base{empty_init,std::move(pred_)},
allocator_base{empty_init,al_},arrays(new_arrays(0)),
size_ctrl{initial_max_load(),0}
{
}
{}
arrays_type new_arrays(std::size_t n)const
arrays_type new_arrays(std::size_t n)
{
return arrays_type::new_(al(),n);
}
arrays_type new_arrays_for_growth()const
arrays_type new_arrays_for_growth()
{
/* Due to the anti-drift mechanism (see recover_slot), the new arrays may
* be of the same size as the old arrays; in the limit, erasing one
@@ -1969,11 +1832,6 @@ private:
arrays_type::delete_(al(),arrays_);
}
arrays_holder_type make_arrays(std::size_t n)const
{
return {new_arrays(n),al()};
}
template<typename Key,typename... Args>
void construct_element_from_try_emplace_args(
element_type* p,std::false_type,Key&& x,Args&&... args)
@@ -2012,10 +1870,9 @@ private:
void fast_copy_elements_from(const table_core& x)
{
if(arrays.elements()&&x.arrays.elements()){
if(arrays.elements){
copy_elements_array_from(x);
copy_groups_array_from(x);
size_ctrl.ml=std::size_t(x.size_ctrl.ml);
size_ctrl.size=std::size_t(x.size_ctrl.size);
}
}
@@ -2046,8 +1903,8 @@ private:
* copy-assignable when we're relying on trivial copy constructibility.
*/
std::memcpy(
reinterpret_cast<unsigned char*>(arrays.elements()),
reinterpret_cast<unsigned char*>(x.arrays.elements()),
reinterpret_cast<unsigned char*>(arrays.elements),
reinterpret_cast<unsigned char*>(x.arrays.elements),
x.capacity()*sizeof(value_type));
}
@@ -2057,14 +1914,14 @@ private:
std::size_t num_constructed=0;
BOOST_TRY{
x.for_all_elements([&,this](const element_type* p){
construct_element(arrays.elements()+(p-x.arrays.elements()),*p);
construct_element(arrays.elements+(p-x.arrays.elements),*p);
++num_constructed;
});
}
BOOST_CATCH(...){
if(num_constructed){
x.for_all_elements_while([&,this](const element_type* p){
destroy_element(arrays.elements()+(p-x.arrays.elements()));
destroy_element(arrays.elements+(p-x.arrays.elements));
return --num_constructed!=0;
});
}
@@ -2089,24 +1946,22 @@ private:
const table_core& x, std::true_type /* -> memcpy */)
{
std::memcpy(
arrays.groups(),x.arrays.groups(),
arrays.groups,x.arrays.groups,
(arrays.groups_size_mask+1)*sizeof(group_type));
}
void copy_groups_array_from(
const table_core& x, std::false_type /* -> manual */)
{
auto pg=arrays.groups();
auto xpg=x.arrays.groups();
for(std::size_t i=0;i<arrays.groups_size_mask+1;++i){
pg[i]=xpg[i];
arrays.groups[i]=x.arrays.groups[i];
}
}
void recover_slot(unsigned char* pc)
{
/* If this slot potentially caused overflow, we decrease the maximum load
* so that average probe length won't increase unboundedly in repeated
/* If this slot potentially caused overflow, we decrease the maximum load so
* that average probe length won't increase unboundedly in repeated
* insert/erase cycles (drift).
*/
size_ctrl.ml-=group_type::maybe_caused_overflow(pc);
@@ -2119,6 +1974,19 @@ private:
recover_slot(reinterpret_cast<unsigned char*>(pg)+pos);
}
std::size_t initial_max_load()const
{
static constexpr std::size_t small_capacity=2*N-1;
auto capacity_=capacity();
if(capacity_<=small_capacity){
return capacity_; /* we allow 100% usage */
}
else{
return (std::size_t)(mlf*(float)(capacity_));
}
}
static std::size_t capacity_for(std::size_t n)
{
return size_policy::size(size_index_for<group_type,size_policy>(n))*N-1;
@@ -2216,11 +2084,11 @@ private:
{
for(prober pb(pos0);;pb.next(arrays_.groups_size_mask)){
auto pos=pb.get();
auto pg=arrays_.groups()+pos;
auto pg=arrays_.groups+pos;
auto mask=pg->match_available();
if(BOOST_LIKELY(mask!=0)){
auto n=unchecked_countr_zero(mask);
auto p=arrays_.elements()+pos*N+n;
auto p=arrays_.elements+pos*N+n;
construct_element(p,std::forward<Args>(args)...);
pg->set(n,hash);
return {pg,n,p};
@@ -9,30 +9,26 @@
#ifndef BOOST_UNORDERED_DETAIL_FOA_ELEMENT_TYPE_HPP
#define BOOST_UNORDERED_DETAIL_FOA_ELEMENT_TYPE_HPP
#include <boost/core/pointer_traits.hpp>
namespace boost{
namespace unordered{
namespace detail{
namespace foa{
template<class T,class VoidPtr>
template<class T>
struct element_type
{
using value_type=T;
using pointer=typename boost::pointer_traits<VoidPtr>::template rebind<T>;
pointer p;
value_type* p;
/*
* we use a deleted copy constructor here so the type is no longer
* trivially copy-constructible which inhibits our memcpy
* optimizations when copying the tables
*/
element_type()=default;
element_type(pointer p_):p(p_){}
element_type(element_type const&)=delete;
element_type(element_type&& rhs)noexcept
element_type() = default;
element_type(value_type* p_):p(p_){}
element_type(element_type const&) = delete;
element_type(element_type&& rhs) noexcept
{
p = rhs.p;
rhs.p = nullptr;
@@ -14,7 +14,6 @@ namespace boost {
template <class Key, class T> struct flat_map_types
{
using key_type = Key;
using mapped_type = T;
using raw_key_type = typename std::remove_const<Key>::type;
using raw_mapped_type = typename std::remove_const<T>::type;
@@ -5,17 +5,14 @@
#ifndef BOOST_UNORDERED_DETAIL_FOA_NODE_MAP_TYPES_HPP
#define BOOST_UNORDERED_DETAIL_FOA_NODE_MAP_TYPES_HPP
#include <boost/unordered/detail/foa/element_type.hpp>
#include <boost/core/allocator_access.hpp>
#include <boost/core/no_exceptions_support.hpp>
#include <boost/core/pointer_traits.hpp>
namespace boost {
namespace unordered {
namespace detail {
namespace foa {
template <class Key, class T, class VoidPtr> struct node_map_types
template <class Key, class T> struct node_map_types
{
using key_type = Key;
using mapped_type = T;
@@ -26,7 +23,7 @@ namespace boost {
using value_type = std::pair<Key const, T>;
using moved_type = std::pair<raw_key_type&&, raw_mapped_type&&>;
using element_type = foa::element_type<value_type, VoidPtr>;
using element_type = foa::element_type<value_type>;
static value_type& value_from(element_type const& x)
{
@@ -85,15 +82,18 @@ namespace boost {
template <class A, class... Args>
static void construct(A& al, element_type* p, Args&&... args)
{
p->p = boost::allocator_allocate(al, 1);
p->p = boost::to_address(boost::allocator_allocate(al, 1));
BOOST_TRY
{
boost::allocator_construct(
al, boost::to_address(p->p), std::forward<Args>(args)...);
boost::allocator_construct(al, p->p, std::forward<Args>(args)...);
}
BOOST_CATCH(...)
{
boost::allocator_deallocate(al, p->p, 1);
using pointer_type = typename boost::allocator_pointer<A>::type;
using pointer_traits = boost::pointer_traits<pointer_type>;
boost::allocator_deallocate(
al, pointer_traits::pointer_to(*(p->p)), 1);
BOOST_RETHROW
}
BOOST_CATCH_END
@@ -113,8 +113,12 @@ namespace boost {
static void destroy(A& al, element_type* p) noexcept
{
if (p->p) {
destroy(al, boost::to_address(p->p));
boost::allocator_deallocate(al, p->p, 1);
using pointer_type = typename boost::allocator_pointer<A>::type;
using pointer_traits = boost::pointer_traits<pointer_type>;
destroy(al, p->p);
boost::allocator_deallocate(
al, pointer_traits::pointer_to(*(p->p)), 1);
}
}
};
@@ -5,10 +5,7 @@
#ifndef BOOST_UNORDERED_DETAIL_FOA_NODE_SET_TYPES_HPP
#define BOOST_UNORDERED_DETAIL_FOA_NODE_SET_TYPES_HPP
#include <boost/unordered/detail/foa/element_type.hpp>
#include <boost/core/allocator_access.hpp>
#include <boost/core/no_exceptions_support.hpp>
#include <boost/core/pointer_traits.hpp>
namespace boost {
@@ -16,7 +13,7 @@ namespace boost {
namespace detail {
namespace foa {
template <class Key, class VoidPtr> struct node_set_types
template <class Key> struct node_set_types
{
using key_type = Key;
using init_type = Key;
@@ -24,7 +21,7 @@ namespace boost {
static Key const& extract(value_type const& key) { return key; }
using element_type = foa::element_type<value_type, VoidPtr>;
using element_type = foa::element_type<value_type>;
static value_type& value_from(element_type const& x) { return *x.p; }
static Key const& extract(element_type const& k) { return *k.p; }
@@ -55,15 +52,17 @@ namespace boost {
template <class A, class... Args>
static void construct(A& al, element_type* p, Args&&... args)
{
p->p = boost::allocator_allocate(al, 1);
p->p = boost::to_address(boost::allocator_allocate(al, 1));
BOOST_TRY
{
boost::allocator_construct(
al, boost::to_address(p->p), std::forward<Args>(args)...);
boost::allocator_construct(al, p->p, std::forward<Args>(args)...);
}
BOOST_CATCH(...)
{
boost::allocator_deallocate(al, p->p, 1);
boost::allocator_deallocate(al,
boost::pointer_traits<typename boost::allocator_pointer<
A>::type>::pointer_to(*p->p),
1);
BOOST_RETHROW
}
BOOST_CATCH_END
@@ -78,8 +77,11 @@ namespace boost {
static void destroy(A& al, element_type* p) noexcept
{
if (p->p) {
destroy(al, boost::to_address(p->p));
boost::allocator_deallocate(al, p->p, 1);
destroy(al, p->p);
boost::allocator_deallocate(al,
boost::pointer_traits<typename boost::allocator_pointer<
A>::type>::pointer_to(*(p->p)),
1);
}
}
};
@@ -1,138 +0,0 @@
/* Copyright 2023 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_REENTRANCY_CHECK_HPP
#define BOOST_UNORDERED_DETAIL_FOA_REENTRANCY_CHECK_HPP
#include <boost/assert.hpp>
#include <utility>
#if !defined(BOOST_UNORDERED_DISABLE_REENTRANCY_CHECK)&& \
!defined(BOOST_ASSERT_IS_VOID)
#define BOOST_UNORDERED_REENTRANCY_CHECK
#endif
namespace boost{
namespace unordered{
namespace detail{
namespace foa{
#if defined(BOOST_UNORDERED_REENTRANCY_CHECK)
class entry_trace
{
public:
entry_trace(const void* px_):px{px_}
{
if(px){
BOOST_ASSERT_MSG(!find(px),"reentrancy not allowed");
header()=this;
}
}
/* not used but VS in pre-C++17 mode needs to see it for RVO */
entry_trace(const entry_trace&);
~entry_trace(){clear();}
void clear()
{
if(px){
header()=next;
px=nullptr;
}
}
private:
static entry_trace*& header()
{
thread_local entry_trace *pe=nullptr;
return pe;
}
static bool find(const void* px)
{
for(auto pe=header();pe;pe=pe->next){
if(pe->px==px)return true;
}
return false;
}
const void *px;
entry_trace *next=header();
};
template<typename LockGuard>
struct reentrancy_checked
{
template<typename... Args>
reentrancy_checked(const void* px,Args&&... args):
tr{px},lck{std::forward<Args>(args)...}{}
void unlock()
{
lck.unlock();
tr.clear();
}
entry_trace tr;
LockGuard lck;
};
template<typename LockGuard>
struct reentrancy_bichecked
{
template<typename... Args>
reentrancy_bichecked(const void* px,const void* py,Args&&... args):
tr1{px},tr2{py!=px?py:nullptr},lck{std::forward<Args>(args)...}{}
void unlock()
{
lck.unlock();
tr2.clear();
tr1.clear();
}
entry_trace tr1,tr2;
LockGuard lck;
};
#else
template<typename LockGuard>
struct reentrancy_checked
{
template<typename... Args>
reentrancy_checked(const void*,Args&&... args):
lck{std::forward<Args>(args)...}{}
void unlock(){lck.unlock();}
LockGuard lck;
};
template<typename LockGuard>
struct reentrancy_bichecked
{
template<typename... Args>
reentrancy_bichecked(const void*,const void*,Args&&... args):
lck{std::forward<Args>(args)...}{}
void unlock(){lck.unlock();}
LockGuard lck;
};
#endif
} /* namespace foa */
} /* namespace detail */
} /* namespace unordered */
} /* namespace boost */
#endif
+52 -122
View File
@@ -15,9 +15,7 @@
#include <boost/assert.hpp>
#include <boost/config.hpp>
#include <boost/config/workaround.hpp>
#include <boost/core/serialization.hpp>
#include <boost/unordered/detail/foa/core.hpp>
#include <boost/unordered/detail/serialize_tracked_address.hpp>
#include <cstddef>
#include <iterator>
#include <memory>
@@ -81,17 +79,12 @@ class table;
/* internal conversion from const_iterator to iterator */
struct const_iterator_cast_tag{};
template<typename TypePolicy,typename GroupPtr,bool Const>
template<typename TypePolicy,typename Group,bool Const>
class table_iterator
{
using group_pointer_traits=boost::pointer_traits<GroupPtr>;
using type_policy=TypePolicy;
using table_element_type=typename type_policy::element_type;
using group_type=typename group_pointer_traits::element_type;
using table_element_pointer=
typename group_pointer_traits::template rebind<table_element_type>;
using char_pointer=
typename group_pointer_traits::template rebind<unsigned char>;
using group_type=Group;
static constexpr auto N=group_type::N;
static constexpr auto regular_layout=group_type::regular_layout;
@@ -108,22 +101,22 @@ public:
table_iterator()=default;
template<bool Const2,typename std::enable_if<!Const2>::type* =nullptr>
table_iterator(const table_iterator<TypePolicy,GroupPtr,Const2>& x):
pc_{x.pc_},p_{x.p_}{}
table_iterator(const table_iterator<TypePolicy,Group,Const2>& x):
pc{x.pc},p{x.p}{}
table_iterator(
const_iterator_cast_tag, const table_iterator<TypePolicy,GroupPtr,true>& x):
pc_{x.pc_},p_{x.p_}{}
const_iterator_cast_tag, const table_iterator<TypePolicy,Group,true>& x):
pc{x.pc},p{x.p}{}
inline reference operator*()const noexcept
{return type_policy::value_from(*p());}
{return type_policy::value_from(*p);}
inline pointer operator->()const noexcept
{return std::addressof(type_policy::value_from(*p()));}
{return std::addressof(type_policy::value_from(*p));}
inline table_iterator& operator++()noexcept{increment();return *this;}
inline table_iterator operator++(int)noexcept
{auto x=*this;increment();return x;}
friend inline bool operator==(
const table_iterator& x,const table_iterator& y)
{return x.p()==y.p();}
{return x.p==y.p;}
friend inline bool operator!=(
const table_iterator& x,const table_iterator& y)
{return !(x==y);}
@@ -133,106 +126,77 @@ private:
template<typename> friend class table_erase_return_type;
template<typename,typename,typename,typename> friend class table;
table_iterator(group_type* pg,std::size_t n,const table_element_type* p):
pc_{to_pointer<char_pointer>(
reinterpret_cast<unsigned char*>(const_cast<group_type*>(pg))+n)},
p_{to_pointer<table_element_pointer>(const_cast<table_element_type*>(p))}
{}
unsigned char* pc()const noexcept{return boost::to_address(pc_);}
table_element_type* p()const noexcept{return boost::to_address(p_);}
table_iterator(Group* pg,std::size_t n,const table_element_type* p_):
pc{reinterpret_cast<unsigned char*>(const_cast<group_type*>(pg))+n},
p{const_cast<table_element_type*>(p_)}
{}
inline void increment()noexcept
{
BOOST_ASSERT(p()!=nullptr);
BOOST_ASSERT(p!=nullptr);
increment(std::integral_constant<bool,regular_layout>{});
}
inline void increment(std::true_type /* regular layout */)noexcept
{
using diff_type=
typename boost::pointer_traits<char_pointer>::difference_type;
for(;;){
++p_;
if(reinterpret_cast<uintptr_t>(pc())%sizeof(group_type)==N-1){
pc_+=static_cast<diff_type>(sizeof(group_type)-(N-1));
++p;
if(reinterpret_cast<uintptr_t>(pc)%sizeof(group_type)==N-1){
pc+=sizeof(group_type)-(N-1);
break;
}
++pc_;
if(!group_type::is_occupied(pc()))continue;
if(BOOST_UNLIKELY(group_type::is_sentinel(pc())))p_=nullptr;
++pc;
if(!group_type::is_occupied(pc))continue;
if(BOOST_UNLIKELY(group_type::is_sentinel(pc)))p=nullptr;
return;
}
for(;;){
int mask=reinterpret_cast<group_type*>(pc())->match_occupied();
int mask=reinterpret_cast<group_type*>(pc)->match_occupied();
if(mask!=0){
auto n=unchecked_countr_zero(mask);
if(BOOST_UNLIKELY(reinterpret_cast<group_type*>(pc())->is_sentinel(n))){
p_=nullptr;
if(BOOST_UNLIKELY(reinterpret_cast<group_type*>(pc)->is_sentinel(n))){
p=nullptr;
}
else{
pc_+=static_cast<diff_type>(n);
p_+=static_cast<diff_type>(n);
pc+=n;
p+=n;
}
return;
}
pc_+=static_cast<diff_type>(sizeof(group_type));
p_+=static_cast<diff_type>(N);
pc+=sizeof(group_type);
p+=N;
}
}
inline void increment(std::false_type /* interleaved */)noexcept
{
using diff_type=
typename boost::pointer_traits<char_pointer>::difference_type;
std::size_t n0=reinterpret_cast<uintptr_t>(pc())%sizeof(group_type);
pc_-=static_cast<diff_type>(n0);
std::size_t n0=reinterpret_cast<uintptr_t>(pc)%sizeof(group_type);
pc-=n0;
int mask=(
reinterpret_cast<group_type*>(pc())->match_occupied()>>(n0+1))<<(n0+1);
reinterpret_cast<group_type*>(pc)->match_occupied()>>(n0+1))<<(n0+1);
if(!mask){
do{
pc_+=sizeof(group_type);
p_+=N;
pc+=sizeof(group_type);
p+=N;
}
while((mask=reinterpret_cast<group_type*>(pc())->match_occupied())==0);
while((mask=reinterpret_cast<group_type*>(pc)->match_occupied())==0);
}
auto n=unchecked_countr_zero(mask);
if(BOOST_UNLIKELY(reinterpret_cast<group_type*>(pc())->is_sentinel(n))){
p_=nullptr;
if(BOOST_UNLIKELY(reinterpret_cast<group_type*>(pc)->is_sentinel(n))){
p=nullptr;
}
else{
pc_+=static_cast<diff_type>(n);
p_-=static_cast<diff_type>(n0);
p_+=static_cast<diff_type>(n);
pc+=n;
p-=n0;
p+=n;
}
}
template<typename Archive>
friend void serialization_track(Archive& ar,const table_iterator& x)
{
if(x.p()){
track_address(ar,x.pc_);
track_address(ar,x.p_);
}
}
friend class boost::serialization::access;
template<typename Archive>
void serialize(Archive& ar,unsigned int)
{
if(!p())pc_=nullptr;
serialize_tracked_address(ar,pc_);
serialize_tracked_address(ar,p_);
}
char_pointer pc_=nullptr;
table_element_pointer p_=nullptr;
unsigned char *pc=nullptr;
table_element_type *p=nullptr;
};
/* Returned by table::erase([const_]iterator) to avoid iterator increment
@@ -242,11 +206,11 @@ private:
template<typename Iterator>
class table_erase_return_type;
template<typename TypePolicy,typename GroupPtr,bool Const>
class table_erase_return_type<table_iterator<TypePolicy,GroupPtr,Const>>
template<typename TypePolicy,typename Group,bool Const>
class table_erase_return_type<table_iterator<TypePolicy,Group,Const>>
{
using iterator=table_iterator<TypePolicy,GroupPtr,Const>;
using const_iterator=table_iterator<TypePolicy,GroupPtr,true>;
using iterator=table_iterator<TypePolicy,Group,Const>;
using const_iterator=table_iterator<TypePolicy,Group,true>;
public:
/* can't delete it because VS in pre-C++17 mode needs to see it for RVO */
@@ -300,9 +264,6 @@ private:
* checking is done by boost::unordered_(flat|node)_(map|set).
*/
template<typename,typename,typename,typename>
class concurrent_table; /* concurrent/non-concurrent interop */
template <typename TypePolicy,typename Hash,typename Pred,typename Allocator>
using table_core_impl=
table_core<TypePolicy,group15<plain_integral>,table_arrays,
@@ -323,15 +284,7 @@ class table:table_core_impl<TypePolicy,Hash,Pred,Allocator>
using group_type=typename super::group_type;
using super::N;
using prober=typename super::prober;
using arrays_type=typename super::arrays_type;
using size_ctrl_type=typename super::size_ctrl_type;
using locator=typename super::locator;
using compatible_concurrent_table=
concurrent_table<TypePolicy,Hash,Pred,Allocator>;
using group_type_pointer=typename boost::pointer_traits<
typename boost::allocator_pointer<Allocator>::type
>::template rebind<group_type>;
friend compatible_concurrent_table;
public:
using key_type=typename super::key_type;
@@ -342,6 +295,7 @@ public:
private:
static constexpr bool has_mutable_iterator=
!std::is_same<key_type,value_type>::value;
public:
using hasher=typename super::hasher;
using key_equal=typename super::key_equal;
@@ -352,10 +306,10 @@ public:
using const_reference=typename super::const_reference;
using size_type=typename super::size_type;
using difference_type=typename super::difference_type;
using const_iterator=table_iterator<type_policy,group_type_pointer,true>;
using const_iterator=table_iterator<type_policy,group_type,true>;
using iterator=typename std::conditional<
has_mutable_iterator,
table_iterator<type_policy,group_type_pointer,false>,
table_iterator<type_policy,group_type,false>,
const_iterator>::type;
using erase_return_type=table_erase_return_type<iterator>;
@@ -369,8 +323,6 @@ public:
table(table&& x)=default;
table(const table& x,const Allocator& al_):super{x,al_}{}
table(table&& x,const Allocator& al_):super{std::move(x),al_}{}
table(compatible_concurrent_table&& x):
table(std::move(x),x.exclusive_access()){}
~table()=default;
table& operator=(const table& x)=default;
@@ -380,9 +332,9 @@ public:
iterator begin()noexcept
{
iterator it{this->arrays.groups(),0,this->arrays.elements()};
if(this->arrays.elements()&&
!(this->arrays.groups()[0].match_occupied()&0x1))++it;
iterator it{this->arrays.groups,0,this->arrays.elements};
if(this->arrays.elements&&
!(this->arrays.groups[0].match_occupied()&0x1))++it;
return it;
}
@@ -448,7 +400,7 @@ public:
BOOST_FORCEINLINE
erase_return_type erase(const_iterator pos)noexcept
{
super::erase(pos.pc(),pos.p());
super::erase(pos.pc,pos.p);
return {pos};
}
@@ -479,7 +431,7 @@ public:
BOOST_ASSERT(pos!=end());
erase_on_exit e{*this,pos};
(void)e;
return std::move(*pos.p());
return std::move(*pos.p);
}
// TODO: should we accept different allocator too?
@@ -544,28 +496,6 @@ public:
friend bool operator!=(const table& x,const table& y){return !(x==y);}
private:
template<typename ArraysType>
table(compatible_concurrent_table&& x,arrays_holder<ArraysType,Allocator>&& ah):
super{
std::move(x.h()),std::move(x.pred()),std::move(x.al()),
[&x]{return arrays_type{
x.arrays.groups_size_index,x.arrays.groups_size_mask,
to_pointer<group_type_pointer>(
reinterpret_cast<group_type*>(x.arrays.groups())),
x.arrays.elements_};},
size_ctrl_type{x.size_ctrl.ml,x.size_ctrl.size}}
{
compatible_concurrent_table::arrays_type::delete_group_access(x.al(),x.arrays);
x.arrays=ah.release();
x.size_ctrl.ml=x.initial_max_load();
x.size_ctrl.size=0;
}
template<typename ExclusiveLockGuard>
table(compatible_concurrent_table&& x,ExclusiveLockGuard):
table(std::move(x),x.make_empty_arrays())
{}
struct erase_on_exit
{
erase_on_exit(table& x_,const_iterator it_):x{x_},it{it_}{}
@@ -0,0 +1,98 @@
/* Copyright 2023 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_TAKES_ARG_AS_CONST_REFERENCE_HPP
#define BOOST_UNORDERED_DETAIL_FOA_TAKES_ARG_AS_CONST_REFERENCE_HPP
#include <boost/type_traits/make_void.hpp>
#include <type_traits>
#include <utility>
namespace boost{
namespace unordered{
namespace detail{
namespace foa{
template<typename Arg,typename R,typename... Args>
void check_function_1st_arg(R(*)(Arg,Args...));
template<typename Arg,typename R,typename... Args>
void check_function_1st_arg(R(*)(Arg,Args...,...));
#define BOOST_UNORDERED_CHECK_MEMFUN_1ST_ARG(qualifier) \
template<typename Arg,typename R,typename C,typename... Args> \
void check_memfun_1st_arg(R(C::*)(Arg,Args...)qualifier); \
\
template<typename Arg,typename R,typename C,typename... Args> \
void check_memfun_1st_arg(R(C::*)(Arg,Args...,...)qualifier);
/* VS warns when a pp function is directly called with an empty arg */
#define BOOST_UNORDERED_EMPTY_PP_ARG()
BOOST_UNORDERED_CHECK_MEMFUN_1ST_ARG(BOOST_UNORDERED_EMPTY_PP_ARG())
BOOST_UNORDERED_CHECK_MEMFUN_1ST_ARG(const)
BOOST_UNORDERED_CHECK_MEMFUN_1ST_ARG(volatile)
BOOST_UNORDERED_CHECK_MEMFUN_1ST_ARG(const volatile)
BOOST_UNORDERED_CHECK_MEMFUN_1ST_ARG(&)
BOOST_UNORDERED_CHECK_MEMFUN_1ST_ARG(const&)
BOOST_UNORDERED_CHECK_MEMFUN_1ST_ARG(volatile&)
BOOST_UNORDERED_CHECK_MEMFUN_1ST_ARG(const volatile&)
BOOST_UNORDERED_CHECK_MEMFUN_1ST_ARG(&&)
BOOST_UNORDERED_CHECK_MEMFUN_1ST_ARG(const&&)
BOOST_UNORDERED_CHECK_MEMFUN_1ST_ARG(volatile&&)
BOOST_UNORDERED_CHECK_MEMFUN_1ST_ARG(const volatile&&)
#undef BOOST_UNORDERED_EMPTY_PP_ARG
#undef BOOST_UNORDERED_CHECK_MEMFUN_1ST_ARG
/* Detects if f(x) takes x as a const reference. From an implementation
* technique by Kenneth Gorking.
* Requires: F is invocable with an Arg&.
*/
template<typename F,typename Arg,typename=void>
struct takes_arg_as_const_reference0:std::false_type{};
template<typename F,typename Arg>
struct takes_arg_as_const_reference0<
F,Arg,
decltype(check_function_1st_arg<const Arg&>(std::declval<F>()))
>:std::true_type{};
template<typename F,typename Arg>
struct takes_arg_as_const_reference0<
F,Arg,
boost::void_t<
decltype(std::declval<F>().operator()(std::declval<Arg&>()))
>
>:std::true_type{};
template<typename F,typename Arg,typename=void>
struct takes_arg_as_const_reference:takes_arg_as_const_reference0<F,Arg>{};
template<
typename F,typename Arg,
typename R=decltype(std::declval<F>()(std::declval<Arg&>())),
typename RawF=
typename std::remove_cv<typename std::remove_reference<F>::type>::type
>
decltype(check_memfun_1st_arg<Arg&,R,RawF>(&RawF::operator()))
check_operator_call_takes_arg_as_reference();
template<typename F,typename Arg>
struct takes_arg_as_const_reference<
F,Arg,
decltype(check_operator_call_takes_arg_as_reference<F,Arg>())
>:std::false_type{};
} /* namespace foa */
} /* namespace detail */
} /* namespace unordered */
} /* namespace boost */
#endif
@@ -1,6 +1,6 @@
// Copyright (C) 2003-2004 Jeremy B. Maitin-Shepard.
// Copyright (C) 2005-2016 Daniel James
// Copyright (C) 2022-2023 Joaquin M Lopez Munoz.
// Copyright (C) 2022 Joaquin M Lopez Munoz.
// Copyright (C) 2022 Christian Mazakas
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
@@ -17,10 +17,8 @@
#include <boost/assert.hpp>
#include <boost/core/allocator_traits.hpp>
#include <boost/core/bit.hpp>
#include <boost/core/invoke_swap.hpp>
#include <boost/core/no_exceptions_support.hpp>
#include <boost/core/pointer_traits.hpp>
#include <boost/core/serialization.hpp>
#include <boost/limits.hpp>
#include <boost/move/move.hpp>
#include <boost/preprocessor/arithmetic/inc.hpp>
@@ -31,6 +29,7 @@
#include <boost/preprocessor/repetition/repeat_from_to.hpp>
#include <boost/preprocessor/seq/enum.hpp>
#include <boost/preprocessor/seq/size.hpp>
#include <boost/swap.hpp>
#include <boost/throw_exception.hpp>
#include <boost/tuple/tuple.hpp>
#include <boost/type_traits/add_lvalue_reference.hpp>
@@ -48,7 +47,6 @@
#include <boost/type_traits/make_void.hpp>
#include <boost/type_traits/remove_const.hpp>
#include <boost/unordered/detail/fca.hpp>
#include <boost/unordered/detail/serialize_tracked_address.hpp>
#include <boost/unordered/detail/type_traits.hpp>
#include <boost/unordered/detail/fwd.hpp>
#include <boost/utility/addressof.hpp>
@@ -260,8 +258,8 @@ namespace boost {
void swap(compressed& x)
{
boost::core::invoke_swap(first(), x.first());
boost::core::invoke_swap(second(), x.second());
boost::swap(first(), x.first());
boost::swap(second(), x.second());
}
private:
@@ -644,7 +642,7 @@ namespace boost {
move(x);
}
} else if (has_value_) {
boost::core::invoke_swap(value_.value(), x.value_.value());
boost::swap(value_.value(), x.value_.value());
}
}
@@ -1705,25 +1703,6 @@ namespace boost {
p = (++itb)->next;
}
}
template<typename Archive>
friend void serialization_track(Archive& ar, const iterator& x)
{
if(x.p){
track_address(ar, x.p);
serialization_track(ar, x.itb);
}
}
friend class boost::serialization::access;
template<typename Archive>
void serialize(Archive& ar,unsigned int)
{
if(!p) itb = bucket_iterator();
serialize_tracked_address(ar, p);
ar & core::make_nvp("bucket_iterator", itb);
}
};
template <class Node, class Bucket> class c_iterator
@@ -1814,25 +1793,6 @@ namespace boost {
p = (++itb)->next;
}
}
template<typename Archive>
friend void serialization_track(Archive& ar, const c_iterator& x)
{
if(x.p){
track_address(ar, x.p);
serialization_track(ar, x.itb);
}
}
friend class boost::serialization::access;
template<typename Archive>
void serialize(Archive& ar,unsigned int)
{
if(!p) itb = bucket_iterator();
serialize_tracked_address(ar, p);
ar & core::make_nvp("bucket_iterator", itb);
}
};
} // namespace iterator_detail
@@ -2089,7 +2049,7 @@ namespace boost {
x.switch_functions();
buckets_.swap(x.buckets_);
boost::core::invoke_swap(size_, x.size_);
boost::swap(size_, x.size_);
std::swap(mlf_, x.mlf_);
std::swap(max_load_, x.max_load_);
}
@@ -2098,7 +2058,7 @@ namespace boost {
void swap(table& x, true_type)
{
buckets_.swap(x.buckets_);
boost::core::invoke_swap(size_, x.size_);
boost::swap(size_, x.size_);
std::swap(mlf_, x.mlf_);
std::swap(max_load_, x.max_load_);
this->current_functions().swap(x.current_functions());
@@ -1,74 +0,0 @@
/* Copyright 2023 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_SERIALIZATION_VERSION_HPP
#define BOOST_UNORDERED_DETAIL_SERIALIZATION_VERSION_HPP
#include <boost/config.hpp>
#include <boost/core/serialization.hpp>
namespace boost{
namespace unordered{
namespace detail{
/* boost::serialization::load_construct_adl(ar,t,version) requires user code
* to pass the serialization version for t, when this information is really
* stored in the archive. serialization_version<T> circumvents this design
* error by acting as a regular serializable type with the same serialization
* version as T; loading/saving serialization_version<T> does nothing with
* the archive data itself but captures the stored serialization version
* at load() time.
*/
template<typename T>
struct serialization_version
{
serialization_version():
value(boost::serialization::version<serialization_version>::value){}
serialization_version& operator=(unsigned int x){value=x;return *this;};
operator unsigned int()const{return value;}
private:
friend class boost::serialization::access;
template<class Archive>
void serialize(Archive& ar,unsigned int version)
{
core::split_member(ar,*this,version);
}
template<class Archive>
void save(Archive&,unsigned int)const{}
template<class Archive>
void load(Archive&,unsigned int version)
{
this->value=version;
}
unsigned int value;
};
} /* namespace detail */
} /* namespace unordered */
namespace serialization{
template<typename T>
struct version<boost::unordered::detail::serialization_version<T> >
{
BOOST_STATIC_CONSTANT(int,value=version<T>::value);
};
} /* namespace serialization */
} /* namespace boost */
#endif
@@ -1,208 +0,0 @@
/* Copyright 2023 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_SERIALIZE_CONTAINER_HPP
#define BOOST_UNORDERED_DETAIL_SERIALIZE_CONTAINER_HPP
#include <boost/core/addressof.hpp>
#include <boost/core/serialization.hpp>
#include <boost/move/move.hpp>
#include <boost/throw_exception.hpp>
#include <boost/type_traits/is_same.hpp>
#include <boost/type_traits/remove_const.hpp>
#include <boost/unordered/detail/archive_constructed.hpp>
#include <boost/unordered/detail/bad_archive_exception.hpp>
#include <boost/unordered/detail/serialization_version.hpp>
#include <cstddef>
namespace boost{
namespace unordered{
namespace detail{
/* serialize_container(ar,x,v) serializes any of the unordered associative
* containers in Boost.Unordered. Iterator serialization is also supported
* through the following protocol:
* - At saving time, for each iterator it in [x.begin(),x.end()),
* serialization_track(ar,it) is ADL-called to instruct the archive to
* track the positions internally pointed to by the iterator via
* track_address().
* - At loading time, these addresses are mapped to those of the equivalent
* reconstructed positions using again serialization_track(ar,it).
* - Serializing an iterator reduces to serializing pointers to previously
* tracked addresses via serialize_address().
*/
template<typename Iterator>
std::pair<Iterator,bool> adapt_insert_return_type(Iterator it)
{
return std::pair<Iterator,bool>(it,true);
}
template<typename Iterator>
std::pair<Iterator,bool> adapt_insert_return_type(std::pair<Iterator,bool> p)
{
return p;
}
template<typename Set,bool IsSaving> struct load_or_save_unordered_set;
template<typename Set> struct load_or_save_unordered_set<Set,true> /* save */
{
template<typename Archive>
void operator()(Archive& ar,const Set& x,unsigned int)const
{
typedef typename Set::value_type value_type;
typedef typename Set::const_iterator const_iterator;
const std::size_t s=x.size();
const serialization_version<value_type> value_version;
ar<<core::make_nvp("count",s);
ar<<core::make_nvp("value_version",value_version);
for(const_iterator first=x.begin(),last=x.end();first!=last;++first){
core::save_construct_data_adl(ar,boost::addressof(*first),value_version);
ar<<core::make_nvp("item",*first);
serialization_track(ar,first);
}
}
};
template<typename Set> struct load_or_save_unordered_set<Set,false> /* load */
{
template<typename Archive>
void operator()(Archive& ar,Set& x,unsigned int)const
{
typedef typename Set::value_type value_type;
typedef typename Set::iterator iterator;
std::size_t s;
serialization_version<value_type> value_version;
ar>>core::make_nvp("count",s);
ar>>core::make_nvp("value_version",value_version);
x.clear();
x.reserve(s); /* critical so that iterator tracking is stable */
for(std::size_t n=0;n<s;++n){
archive_constructed<value_type> value("item",ar,value_version);
std::pair<iterator,bool> p=adapt_insert_return_type(
x.insert(boost::move(value.get())));
if(!p.second)throw_exception(bad_archive_exception());
ar.reset_object_address(
boost::addressof(*p.first),boost::addressof(value.get()));
serialization_track(ar,p.first);
}
}
};
template<typename Map,bool IsSaving> struct load_or_save_unordered_map;
template<typename Map> struct load_or_save_unordered_map<Map,true> /* save */
{
template<typename Archive>
void operator()(Archive& ar,const Map& x,unsigned int)const
{
typedef typename boost::remove_const<
typename Map::key_type>::type key_type;
typedef typename boost::remove_const<
typename Map::mapped_type>::type mapped_type;
typedef typename Map::const_iterator const_iterator;
const std::size_t s=x.size();
const serialization_version<key_type> key_version;
const serialization_version<mapped_type> mapped_version;
ar<<core::make_nvp("count",s);
ar<<core::make_nvp("key_version",key_version);
ar<<core::make_nvp("mapped_version",mapped_version);
for(const_iterator first=x.begin(),last=x.end();first!=last;++first){
/* To remain lib-independent from Boost.Serialization and not rely on
* the user having included the serialization code for std::pair
* (boost/serialization/utility.hpp), we serialize the key and the
* mapped value separately.
*/
core::save_construct_data_adl(
ar,boost::addressof(first->first),key_version);
ar<<core::make_nvp("key",first->first);
core::save_construct_data_adl(
ar,boost::addressof(first->second),mapped_version);
ar<<core::make_nvp("mapped",first->second);
serialization_track(ar,first);
}
}
};
template<typename Map> struct load_or_save_unordered_map<Map,false> /* load */
{
template<typename Archive>
void operator()(Archive& ar,Map& x,unsigned int)const
{
typedef typename boost::remove_const<
typename Map::key_type>::type key_type;
typedef typename boost::remove_const<
typename Map::mapped_type>::type mapped_type;
typedef typename Map::iterator iterator;
std::size_t s;
serialization_version<key_type> key_version;
serialization_version<mapped_type> mapped_version;
ar>>core::make_nvp("count",s);
ar>>core::make_nvp("key_version",key_version);
ar>>core::make_nvp("mapped_version",mapped_version);
x.clear();
x.reserve(s); /* critical so that iterator tracking is stable */
for(std::size_t n=0;n<s;++n){
archive_constructed<key_type> key("key",ar,key_version);
archive_constructed<mapped_type> mapped("mapped",ar,mapped_version);
std::pair<iterator,bool> p=adapt_insert_return_type(
x.emplace(boost::move(key.get()),boost::move(mapped.get())));
if(!p.second)throw_exception(bad_archive_exception());
ar.reset_object_address(
boost::addressof(p.first->first),boost::addressof(key.get()));
ar.reset_object_address(
boost::addressof(p.first->second),boost::addressof(mapped.get()));
serialization_track(ar,p.first);
}
}
};
template<typename Container,bool IsSet,bool IsSaving>
struct load_or_save_container;
template<typename Set,bool IsSaving>
struct load_or_save_container<Set,true,IsSaving>:
load_or_save_unordered_set<Set,IsSaving>{};
template<typename Map,bool IsSaving>
struct load_or_save_container<Map,false,IsSaving>:
load_or_save_unordered_map<Map,IsSaving>{};
template<typename Archive,typename Container>
void serialize_container(Archive& ar,Container& x,unsigned int version)
{
load_or_save_container<
Container,
boost::is_same<
typename Container::key_type,typename Container::value_type>::value,
Archive::is_saving::value>()(ar,x,version);
}
} /* namespace detail */
} /* namespace unordered */
} /* namespace boost */
#endif
@@ -1,156 +0,0 @@
/* Copyright 2023 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_SERIALIZE_FCA_CONTAINER_HPP
#define BOOST_UNORDERED_DETAIL_SERIALIZE_FCA_CONTAINER_HPP
#include <boost/unordered/detail/serialize_container.hpp>
#if defined(BOOST_UNORDERED_ENABLE_SERIALIZATION_COMPATIBILITY_V0)
#define BOOST_UNORDERED_BLOCK_BOOSTDEP_HEADER \
<boost/serialization/archive_input_unordered_map.hpp>
#include BOOST_UNORDERED_BLOCK_BOOSTDEP_HEADER
#undef BOOST_UNORDERED_BLOCK_BOOSTDEP_HEADER
#define BOOST_UNORDERED_BLOCK_BOOSTDEP_HEADER \
<boost/serialization/archive_input_unordered_set.hpp>
#include BOOST_UNORDERED_BLOCK_BOOSTDEP_HEADER
#undef BOOST_UNORDERED_BLOCK_BOOSTDEP_HEADER
#define BOOST_UNORDERED_BLOCK_BOOSTDEP_HEADER \
<boost/serialization/unordered_collections_load_imp.hpp>
#include BOOST_UNORDERED_BLOCK_BOOSTDEP_HEADER
#undef BOOST_UNORDERED_BLOCK_BOOSTDEP_HEADER
#define BOOST_UNORDERED_BLOCK_BOOSTDEP_HEADER \
<boost/serialization/utility.hpp>
#include BOOST_UNORDERED_BLOCK_BOOSTDEP_HEADER
#undef BOOST_UNORDERED_BLOCK_BOOSTDEP_HEADER
#include <boost/unordered/unordered_map_fwd.hpp>
#include <boost/unordered/unordered_set_fwd.hpp>
#else
#include <boost/throw_exception.hpp>
#include <stdexcept>
#endif
namespace boost{
namespace unordered{
namespace detail{
/* Support for boost::unordered_[multi](map|set) loading from legacy archives.
* Until Boost 1.84, serialization of these containers was provided from
* Boost.Serialization via boost/serialization/boost_unordered_(map|set).hpp,
* from that release on support is native in Boost.Unordered. To enable legacy
* archive loading, BOOST_UNORDERED_ENABLE_SERIALIZATION_COMPATIBILITY_V0
* must be defined (it implies header dependency from Boost.Serialization).
*/
#if defined(BOOST_UNORDERED_ENABLE_SERIALIZATION_COMPATIBILITY_V0)
template<typename Archive,typename Container>
struct archive_input;
template<
typename Archive,typename K,typename T,typename H,typename P,typename A
>
struct archive_input<Archive,boost::unordered_map<K,T,H,P,A> >:
boost::serialization::stl::archive_input_unordered_map<
Archive,
boost::unordered_map<K,T,H,P,A>
>
{};
template<
typename Archive,typename K,typename T,typename H,typename P,typename A
>
struct archive_input<Archive,boost::unordered_multimap<K,T,H,P,A> >:
boost::serialization::stl::archive_input_unordered_multimap<
Archive,
boost::unordered_multimap<K,T,H,P,A>
>
{};
template<
typename Archive,typename K,typename H,typename P,typename A
>
struct archive_input<Archive,boost::unordered_set<K,H,P,A> >:
boost::serialization::stl::archive_input_unordered_set<
Archive,
boost::unordered_set<K,H,P,A>
>
{};
template<
typename Archive,typename K,typename H,typename P,typename A
>
struct archive_input<Archive,boost::unordered_multiset<K,H,P,A> >:
boost::serialization::stl::archive_input_unordered_multiset<
Archive,
boost::unordered_multiset<K,H,P,A>
>
{};
#else
struct legacy_archive_exception:std::runtime_error
{
legacy_archive_exception():std::runtime_error(
"Legacy archive detected, define "
"BOOST_UNORDERED_ENABLE_SERIALIZATION_COMPATIBILITY_V0 to load"){}
};
#endif
template<typename Container,bool IsSaving>
struct load_or_save_fca_container;
template<typename Container>
struct load_or_save_fca_container<Container,true> /* save */
{
template<typename Archive>
void operator()(Archive& ar,Container& x,unsigned int version)const
{
serialize_container(ar,x,version);
}
};
template<typename Container>
struct load_or_save_fca_container<Container,false> /* load */
{
template<typename Archive>
void operator()(Archive& ar,Container& x,unsigned int version)const
{
if(version==0){
#if defined(BOOST_UNORDERED_ENABLE_SERIALIZATION_COMPATIBILITY_V0)
boost::serialization::stl::load_unordered_collection<
Archive,Container,archive_input<Archive,Container>
>(ar,x);
#else
throw_exception(legacy_archive_exception());
#endif
}
else{
serialize_container(ar,x,version);
}
}
};
template<typename Archive,typename Container>
void serialize_fca_container(Archive& ar,Container& x,unsigned int version)
{
load_or_save_fca_container<Container,Archive::is_saving::value>()(
ar,x,version);
}
} /* namespace detail */
} /* namespace unordered */
} /* namespace boost */
#endif
@@ -1,103 +0,0 @@
/* Copyright 2023 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_SERIALIZE_TRACKED_ADDRESS_HPP
#define BOOST_UNORDERED_DETAIL_SERIALIZE_TRACKED_ADDRESS_HPP
#include <boost/core/pointer_traits.hpp>
#include <boost/core/serialization.hpp>
#include <boost/throw_exception.hpp>
#include <boost/type_traits/remove_const.hpp>
#include <boost/type_traits/integral_constant.hpp>
#include <boost/unordered/detail/bad_archive_exception.hpp>
namespace boost{
namespace unordered{
namespace detail{
/* Tracked address serialization to support iterator serialization as described
* in serialize_container.hpp. The underlying technique is to reinterpret_cast
* T pointers to serialization_tracker<T> pointers, which, when dereferenced
* and serialized, do not emit any serialization payload to the
* archive, but activate object tracking on the relevant addresses for later
* use with serialize_tracked_address().
*/
template<typename T>
struct serialization_tracker
{
/* An attempt to construct a serialization_tracker means a stray address
* in the archive, that is, one without a previously tracked address.
*/
serialization_tracker(){throw_exception(bad_archive_exception());}
template<typename Archive>
void serialize(Archive&,unsigned int){} /* no data emitted */
};
template<typename Archive,typename Ptr>
void track_address(Archive& ar,Ptr p)
{
typedef typename boost::pointer_traits<Ptr> ptr_traits;
typedef typename boost::remove_const<
typename ptr_traits::element_type>::type element_type;
if(p){
ar&core::make_nvp(
"address",
*reinterpret_cast<serialization_tracker<element_type>*>(
const_cast<element_type*>(
boost::to_address(p))));
}
}
template<typename Archive,typename Ptr>
void serialize_tracked_address(Archive& ar,Ptr& p,boost::true_type /* save */)
{
typedef typename boost::pointer_traits<Ptr> ptr_traits;
typedef typename boost::remove_const<
typename ptr_traits::element_type>::type element_type;
typedef serialization_tracker<element_type> tracker;
tracker* pt=
const_cast<tracker*>(
reinterpret_cast<const tracker*>(
const_cast<const element_type*>(
boost::to_address(p))));
ar<<core::make_nvp("pointer",pt);
}
template<typename Archive,typename Ptr>
void serialize_tracked_address(Archive& ar,Ptr& p,boost::false_type /* load */)
{
typedef typename boost::pointer_traits<Ptr> ptr_traits;
typedef typename boost::remove_const<
typename ptr_traits::element_type>::type element_type;
typedef serialization_tracker<element_type> tracker;
tracker* pt;
ar>>core::make_nvp("pointer",pt);
element_type* pn=const_cast<element_type*>(
reinterpret_cast<const element_type*>(
const_cast<const tracker*>(pt)));
p=pn?ptr_traits::pointer_to(*pn):0;
}
template<typename Archive,typename Ptr>
void serialize_tracked_address(Archive& ar,Ptr& p)
{
serialize_tracked_address(
ar,p,
boost::integral_constant<bool,Archive::is_saving::value>());
}
} /* namespace detail */
} /* namespace unordered */
} /* namespace boost */
#endif
+4 -23
View File
@@ -1,4 +1,4 @@
// Copyright (C) 2022-2023 Christian Mazakas
// Copyright (C) 2022 Christian Mazakas
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -10,10 +10,8 @@
#pragma once
#endif
#include <boost/unordered/concurrent_flat_map_fwd.hpp>
#include <boost/unordered/detail/foa/flat_map_types.hpp>
#include <boost/unordered/detail/foa/table.hpp>
#include <boost/unordered/detail/serialize_container.hpp>
#include <boost/unordered/detail/type_traits.hpp>
#include <boost/unordered/unordered_flat_map_fwd.hpp>
@@ -38,10 +36,6 @@ namespace boost {
template <class Key, class T, class Hash, class KeyEqual, class Allocator>
class unordered_flat_map
{
template <class Key2, class T2, class Hash2, class Pred2,
class Allocator2>
friend class concurrent_flat_map;
using map_types = detail::foa::flat_map_types<Key, T>;
using table_type = detail::foa::table<map_types, Hash, KeyEqual,
@@ -141,7 +135,9 @@ namespace boost {
}
unordered_flat_map(unordered_flat_map&& other)
noexcept(std::is_nothrow_move_constructible<table_type>::value)
noexcept(std::is_nothrow_move_constructible<hasher>::value&&
std::is_nothrow_move_constructible<key_equal>::value&&
std::is_nothrow_move_constructible<allocator_type>::value)
: table_(std::move(other.table_))
{
}
@@ -177,12 +173,6 @@ namespace boost {
{
}
unordered_flat_map(
concurrent_flat_map<Key, T, Hash, KeyEqual, Allocator>&& other)
: table_(std::move(other.table_))
{
}
~unordered_flat_map() = default;
unordered_flat_map& operator=(unordered_flat_map const& other)
@@ -695,15 +685,6 @@ namespace boost {
return erase_if(map.table_, pred);
}
template <class Archive, class Key, class T, class Hash, class KeyEqual,
class Allocator>
void serialize(Archive& ar,
unordered_flat_map<Key, T, Hash, KeyEqual, Allocator>& map,
unsigned int version)
{
detail::serialize_container(ar, map, version);
}
#if defined(BOOST_MSVC)
#pragma warning(pop) /* C4714 */
#endif
+4 -22
View File
@@ -1,4 +1,4 @@
// Copyright (C) 2022-2023 Christian Mazakas
// Copyright (C) 2022 Christian Mazakas
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -10,10 +10,8 @@
#pragma once
#endif
#include <boost/unordered/concurrent_flat_set_fwd.hpp>
#include <boost/unordered/detail/foa/flat_set_types.hpp>
#include <boost/unordered/detail/foa/table.hpp>
#include <boost/unordered/detail/serialize_container.hpp>
#include <boost/unordered/detail/type_traits.hpp>
#include <boost/unordered/unordered_flat_set_fwd.hpp>
@@ -36,9 +34,6 @@ namespace boost {
template <class Key, class Hash, class KeyEqual, class Allocator>
class unordered_flat_set
{
template <class Key2, class Hash2, class KeyEqual2, class Allocator2>
friend class concurrent_flat_set;
using set_types = detail::foa::flat_set_types<Key>;
using table_type = detail::foa::table<set_types, Hash, KeyEqual,
@@ -137,7 +132,9 @@ namespace boost {
}
unordered_flat_set(unordered_flat_set&& other)
noexcept(std::is_nothrow_move_constructible<table_type>::value)
noexcept(std::is_nothrow_move_constructible<hasher>::value&&
std::is_nothrow_move_constructible<key_equal>::value&&
std::is_nothrow_move_constructible<allocator_type>::value)
: table_(std::move(other.table_))
{
}
@@ -173,12 +170,6 @@ namespace boost {
{
}
unordered_flat_set(
concurrent_flat_set<Key, Hash, KeyEqual, Allocator>&& other)
: table_(std::move(other.table_))
{
}
~unordered_flat_set() = default;
unordered_flat_set& operator=(unordered_flat_set const& other)
@@ -514,15 +505,6 @@ namespace boost {
return erase_if(set.table_, pred);
}
template <class Archive, class Key, class Hash, class KeyEqual,
class Allocator>
void serialize(Archive& ar,
unordered_flat_set<Key, Hash, KeyEqual, Allocator>& set,
unsigned int version)
{
detail::serialize_container(ar, set, version);
}
#if defined(BOOST_MSVC)
#pragma warning(pop) /* C4714 */
#endif
+7 -37
View File
@@ -1,6 +1,7 @@
// Copyright (C) 2003-2004 Jeremy B. Maitin-Shepard.
// Copyright (C) 2005-2011 Daniel James.
// Copyright (C) 2022-2023 Christian Mazakas
// Copyright (C) 2022 Christian Mazakas
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -15,13 +16,11 @@
#endif
#include <boost/unordered/detail/requires_cxx11.hpp>
#include <boost/config.hpp>
#include <boost/core/explicit_operator_bool.hpp>
#include <boost/functional/hash.hpp>
#include <boost/move/move.hpp>
#include <boost/type_traits/is_constructible.hpp>
#include <boost/unordered/detail/map.hpp>
#include <boost/unordered/detail/serialize_fca_container.hpp>
#include <boost/unordered/detail/type_traits.hpp>
#if !defined(BOOST_NO_CXX11_HDR_INITIALIZER_LIST)
@@ -1060,13 +1059,6 @@ namespace boost {
#endif
}; // class template unordered_map
template <class Archive, class K, class T, class H, class P, class A>
void serialize(
Archive & ar,unordered_map<K, T, H, P, A>& m,unsigned int version)
{
detail::serialize_fca_container(ar, m, version);
}
#if BOOST_UNORDERED_TEMPLATE_DEDUCTION_GUIDES
template <class InputIterator,
@@ -1770,13 +1762,6 @@ namespace boost {
#endif
}; // class template unordered_multimap
template <class Archive, class K, class T, class H, class P, class A>
void serialize(
Archive & ar,unordered_multimap<K, T, H, P, A>& m,unsigned int version)
{
detail::serialize_fca_container(ar, m, version);
}
#if BOOST_UNORDERED_TEMPLATE_DEDUCTION_GUIDES
template <class InputIterator,
@@ -2980,9 +2965,9 @@ namespace boost {
if (boost::allocator_propagate_on_container_swap<
value_allocator>::type::value ||
!alloc_.has_value() || !n.alloc_.has_value()) {
boost::core::invoke_swap(alloc_, n.alloc_);
boost::swap(alloc_, n.alloc_);
}
boost::core::invoke_swap(ptr_, n.ptr_);
boost::swap(ptr_, n.ptr_);
}
};
@@ -3029,26 +3014,11 @@ namespace boost {
void swap(insert_return_type_map<Iter, NodeType>& x,
insert_return_type_map<Iter, NodeType>& y)
{
boost::core::invoke_swap(x.node, y.node);
boost::core::invoke_swap(x.inserted, y.inserted);
boost::core::invoke_swap(x.position, y.position);
boost::swap(x.node, y.node);
boost::swap(x.inserted, y.inserted);
boost::swap(x.position, y.position);
}
} // namespace unordered
namespace serialization {
template <class K, class T, class H, class P, class A>
struct version<boost::unordered_map<K, T, H, P, A> >
{
BOOST_STATIC_CONSTANT(int, value = 1);
};
template <class K, class T, class H, class P, class A>
struct version<boost::unordered_multimap<K, T, H, P, A> >
{
BOOST_STATIC_CONSTANT(int, value = 1);
};
} // namespace serialization
} // namespace boost
#if defined(BOOST_MSVC)
+4 -13
View File
@@ -14,7 +14,6 @@
#include <boost/unordered/detail/foa/node_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>
#include <boost/unordered/detail/type_traits.hpp>
#include <boost/unordered/unordered_node_map_fwd.hpp>
@@ -75,8 +74,7 @@ namespace boost {
template <class Key, class T, class Hash, class KeyEqual, class Allocator>
class unordered_node_map
{
using map_types = detail::foa::node_map_types<Key, T,
typename boost::allocator_void_pointer<Allocator>::type>;
using map_types = detail::foa::node_map_types<Key, T>;
using table_type = detail::foa::table<map_types, Hash, KeyEqual,
typename boost::allocator_rebind<Allocator,
@@ -180,7 +178,9 @@ namespace boost {
}
unordered_node_map(unordered_node_map&& other)
noexcept(std::is_nothrow_move_constructible<table_type>::value)
noexcept(std::is_nothrow_move_constructible<hasher>::value&&
std::is_nothrow_move_constructible<key_equal>::value&&
std::is_nothrow_move_constructible<allocator_type>::value)
: table_(std::move(other.table_))
{
}
@@ -788,15 +788,6 @@ namespace boost {
return erase_if(map.table_, pred);
}
template <class Archive, class Key, class T, class Hash, class KeyEqual,
class Allocator>
void serialize(Archive& ar,
unordered_node_map<Key, T, Hash, KeyEqual, Allocator>& map,
unsigned int version)
{
detail::serialize_container(ar, map, version);
}
#if defined(BOOST_MSVC)
#pragma warning(pop) /* C4714 */
#endif
+5 -14
View File
@@ -1,4 +1,4 @@
// Copyright (C) 2022-2023 Christian Mazakas
// Copyright (C) 2022 Christian Mazakas
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -14,7 +14,6 @@
#include <boost/unordered/detail/foa/node_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>
#include <boost/unordered/detail/type_traits.hpp>
#include <boost/unordered/unordered_node_set_fwd.hpp>
@@ -66,8 +65,7 @@ namespace boost {
template <class Key, class Hash, class KeyEqual, class Allocator>
class unordered_node_set
{
using set_types = detail::foa::node_set_types<Key,
typename boost::allocator_void_pointer<Allocator>::type>;
using set_types = detail::foa::node_set_types<Key>;
using table_type = detail::foa::table<set_types, Hash, KeyEqual,
typename boost::allocator_rebind<Allocator,
@@ -170,7 +168,9 @@ namespace boost {
}
unordered_node_set(unordered_node_set&& other)
noexcept(std::is_nothrow_move_constructible<table_type>::value)
noexcept(std::is_nothrow_move_constructible<hasher>::value&&
std::is_nothrow_move_constructible<key_equal>::value&&
std::is_nothrow_move_constructible<allocator_type>::value)
: table_(std::move(other.table_))
{
}
@@ -601,15 +601,6 @@ namespace boost {
return erase_if(set.table_, pred);
}
template <class Archive, class Key, class Hash, class KeyEqual,
class Allocator>
void serialize(Archive& ar,
unordered_node_set<Key, Hash, KeyEqual, Allocator>& set,
unsigned int version)
{
detail::serialize_container(ar, set, version);
}
#if defined(BOOST_MSVC)
#pragma warning(pop) /* C4714 */
#endif
+7 -36
View File
@@ -1,6 +1,7 @@
// Copyright (C) 2003-2004 Jeremy B. Maitin-Shepard.
// Copyright (C) 2005-2011 Daniel James.
// Copyright (C) 2022-2023 Christian Mazakas
// Copyright (C) 2022 Christian Mazakas
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -19,7 +20,6 @@
#include <boost/functional/hash.hpp>
#include <boost/move/move.hpp>
#include <boost/unordered/detail/set.hpp>
#include <boost/unordered/detail/serialize_fca_container.hpp>
#include <boost/unordered/detail/type_traits.hpp>
#if !defined(BOOST_NO_CXX11_HDR_INITIALIZER_LIST)
@@ -641,13 +641,6 @@ namespace boost {
#endif
}; // class template unordered_set
template <class Archive, class K, class H, class P, class A>
void serialize(
Archive & ar,unordered_set<K, H, P, A>& c,unsigned int version)
{
detail::serialize_fca_container(ar, c, version);
}
#if BOOST_UNORDERED_TEMPLATE_DEDUCTION_GUIDES
template <class InputIterator,
@@ -1297,13 +1290,6 @@ namespace boost {
#endif
}; // class template unordered_multiset
template <class Archive, class K, class H, class P, class A>
void serialize(
Archive & ar,unordered_multiset<K, H, P, A>& c,unsigned int version)
{
detail::serialize_fca_container(ar, c, version);
}
#if BOOST_UNORDERED_TEMPLATE_DEDUCTION_GUIDES
template <class InputIterator,
@@ -2327,9 +2313,9 @@ namespace boost {
alloc_ == n.alloc_);
if (value_allocator_traits::propagate_on_container_swap::value ||
!alloc_.has_value() || !n.alloc_.has_value()) {
boost::core::invoke_swap(alloc_, n.alloc_);
boost::swap(alloc_, n.alloc_);
}
boost::core::invoke_swap(ptr_, n.ptr_);
boost::swap(ptr_, n.ptr_);
}
};
@@ -2376,26 +2362,11 @@ namespace boost {
void swap(
insert_return_type_set<Iter, NodeType>& x, insert_return_type_set<Iter, NodeType>& y)
{
boost::core::invoke_swap(x.node, y.node);
boost::core::invoke_swap(x.inserted, y.inserted);
boost::core::invoke_swap(x.position, y.position);
boost::swap(x.node, y.node);
boost::swap(x.inserted, y.inserted);
boost::swap(x.position, y.position);
}
} // namespace unordered
namespace serialization {
template <class K, class H, class P, class A>
struct version<boost::unordered_set<K, H, P, A> >
{
BOOST_STATIC_CONSTANT(int, value = 1);
};
template <class K, class H, class P, class A>
struct version<boost::unordered_multiset<K, H, P, A> >
{
BOOST_STATIC_CONSTANT(int, value = 1);
};
} // namespace serialization
} // namespace boost
#if defined(BOOST_MSVC)
+89 -251
View File
@@ -4,12 +4,7 @@
# 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)
import path ;
import regex ;
import testing ;
import ../../config/checks/config : requires ;
path-constant TOP : . ;
# Adding -Wundef is blocked on (at least)
# https://github.com/boostorg/type_traits/issues/165
@@ -20,20 +15,6 @@ local msvc-flags = /wd4494 ;
project
: requirements
[ requires cxx11_rvalue_references
cxx11_auto_declarations
cxx11_nullptr
cxx11_defaulted_functions
cxx11_final
cxx11_hdr_type_traits
cxx11_hdr_initializer_list
cxx11_static_assert
cxx11_smart_ptr
cxx11_constexpr
cxx11_noexcept
cxx11_decltype
cxx11_alignas
]
<warnings>pedantic
<toolset>intel:<warnings>on
@@ -52,163 +33,76 @@ project
<toolset>msvc:<warnings-as-errors>on
;
path-constant BOOST_UNORDERED_TEST_DIR : . ;
run unordered/prime_fmod_tests.cpp ;
run unordered/fwd_set_test.cpp ;
run unordered/fwd_map_test.cpp ;
run unordered/allocator_traits.cpp ;
run unordered/minimal_allocator.cpp ;
run unordered/compile_set.cpp ;
run unordered/compile_map.cpp ;
run unordered/noexcept_tests.cpp ;
run unordered/link_test_1.cpp unordered/link_test_2.cpp ;
run unordered/incomplete_test.cpp ;
run unordered/simple_tests.cpp ;
run unordered/equivalent_keys_tests.cpp ;
run unordered/constructor_tests.cpp ;
run unordered/copy_tests.cpp ;
run unordered/move_tests.cpp ;
run unordered/post_move_tests.cpp ;
run unordered/assign_tests.cpp ;
run unordered/insert_tests.cpp ;
run unordered/insert_stable_tests.cpp ;
run unordered/insert_hint_tests.cpp ;
run unordered/emplace_tests.cpp ;
run unordered/unnecessary_copy_tests.cpp ;
run unordered/erase_tests.cpp : : : <define>BOOST_UNORDERED_SUPPRESS_DEPRECATED ;
run unordered/erase_equiv_tests.cpp ;
run unordered/extract_tests.cpp ;
run unordered/node_handle_tests.cpp ;
run unordered/merge_tests.cpp ;
compile-fail unordered/insert_node_type_fail.cpp : <define>UNORDERED_TEST_MAP : insert_node_type_fail_map ;
compile-fail unordered/insert_node_type_fail.cpp : <define>UNORDERED_TEST_MULTIMAP : insert_node_type_fail_multimap ;
compile-fail unordered/insert_node_type_fail.cpp : <define>UNORDERED_TEST_SET : insert_node_type_fail_set ;
compile-fail unordered/insert_node_type_fail.cpp : <define>UNORDERED_TEST_MULTISET : insert_node_type_fail_multiset ;
run unordered/find_tests.cpp ;
run unordered/at_tests.cpp ;
run unordered/bucket_tests.cpp ;
run unordered/load_factor_tests.cpp ;
run unordered/rehash_tests.cpp ;
run unordered/equality_tests.cpp ;
run unordered/swap_tests.cpp ;
run unordered/deduction_tests.cpp ;
run unordered/scoped_allocator.cpp : : : <toolset>msvc-14.0:<build>no ;
run unordered/transparent_tests.cpp ;
run unordered/reserve_tests.cpp ;
run unordered/contains_tests.cpp ;
run unordered/erase_if.cpp ;
run unordered/scary_tests.cpp ;
run quick.cpp ;
compile unordered/self_include_tests_obj.cpp
: <define>BOOST_UNORDERED_HEADER="boost/unordered_map.hpp"
: tl_unordered_map_hpp ;
compile unordered/self_include_tests_obj.cpp
: <define>BOOST_UNORDERED_HEADER="boost/unordered_set.hpp"
: tl_unordered_set_hpp ;
local include_root = [ path.make $(TOP)/../include ] ;
local headers = [ path.glob-tree $(include_root)/boost/unordered : *.hpp ] ;
local paths ;
local sanitized_paths ;
for local header in $(headers)
{
local path = [ path.relative-to $(include_root) $(header) ] ;
local sanitized = [ path.relative-to "$(include_root)/boost/unordered" $(header) ] ;
sanitized = [ regex.replace $(sanitized) "[/.\\]" "_" ] ;
paths += $(path) ;
sanitized_paths += $(sanitized) ;
compile unordered/self_include_tests_obj.cpp
: <define>BOOST_UNORDERED_HEADER="$(path)" : $(sanitized) ;
}
alias include_tests
: tl_unordered_map_hpp
tl_unordered_set_hpp
$(sanitized_paths) ;
local FCA_TESTS =
allocator_traits
assign_tests
at_tests
bucket_tests
compile_map
compile_set
constructor_tests
contains_tests
copy_tests
deduction_tests
emplace_tests
equality_tests
equivalent_keys_tests
erase_equiv_tests
erase_if
erase_tests
extract_tests
find_tests
fwd_map_test
fwd_set_test
incomplete_test
insert_hint_tests
insert_stable_tests
insert_tests
load_factor_tests
merge_tests
minimal_allocator
move_tests
narrow_cast_tests
node_handle_tests
noexcept_tests
post_move_tests
prime_fmod_tests
rehash_tests
reserve_tests
scary_tests
scoped_allocator
simple_tests
swap_tests
transparent_tests
unnecessary_copy_tests
;
for local test in $(FCA_TESTS)
{
if $(test) = "erase_tests" {
run unordered/$(test).cpp : : : <define>BOOST_UNORDERED_SUPPRESS_DEPRECATED ;
} else if $(test) = "scoped_allocator" {
run unordered/$(test).cpp : : : <toolset>msvc-14.0:<build>no ;
} else {
run unordered/$(test).cpp ;
}
}
run unordered/link_test_1.cpp unordered/link_test_2.cpp : : : : link_test ;
run unordered/compile_set.cpp : : : <define>BOOST_UNORDERED_USE_MOVE : bmove_compile_set ;
run unordered/compile_map.cpp : : : <define>BOOST_UNORDERED_USE_MOVE : bmove_compile_map ;
run unordered/copy_tests.cpp : : : <define>BOOST_UNORDERED_USE_MOVE : bmove_copy ;
run unordered/move_tests.cpp : : : <define>BOOST_UNORDERED_USE_MOVE : bmove_move ;
run unordered/compile_set.cpp : : : <define>BOOST_UNORDERED_USE_MOVE : bmove_compile_set ;
run unordered/compile_map.cpp : : : <define>BOOST_UNORDERED_USE_MOVE : bmove_compile_map ;
run unordered/copy_tests.cpp : : : <define>BOOST_UNORDERED_USE_MOVE : bmove_copy ;
run unordered/move_tests.cpp : : : <define>BOOST_UNORDERED_USE_MOVE : bmove_move ;
run unordered/assign_tests.cpp : : : <define>BOOST_UNORDERED_USE_MOVE : bmove_assign ;
run unordered/serialization_tests.cpp
: $(BOOST_UNORDERED_TEST_DIR)
:
: <define>BOOST_UNORDERED_ENABLE_SERIALIZATION_COMPATIBILITY_V0
<warnings>off # Boost.Serialization headers are not warning-free
<undefined-sanitizer>norecover:<build>no # boost::archive::xml_oarchive does not pass UBSAN
<toolset>msvc:<define>_CRT_SECURE_NO_WARNINGS
<toolset>msvc:<cxxflags>/bigobj
<toolset>gcc:<inlining>on
<toolset>gcc:<optimization>space
<toolset>clang:<inlining>on
<toolset>clang:<optimization>space
<library>/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 ;
compile-fail unordered/insert_node_type_fail.cpp : <define>UNORDERED_TEST_SET : insert_node_type_fail_set ;
compile-fail unordered/insert_node_type_fail.cpp : <define>UNORDERED_TEST_MULTISET : insert_node_type_fail_multiset ;
run exception/constructor_exception_tests.cpp ;
run exception/copy_exception_tests.cpp ;
run exception/assign_exception_tests.cpp ;
run exception/move_assign_exception_tests.cpp ;
run exception/insert_exception_tests.cpp ;
run exception/erase_exception_tests.cpp ;
run exception/rehash_exception_tests.cpp ;
run exception/swap_exception_tests.cpp : : : <define>BOOST_UNORDERED_SWAP_METHOD=2 ;
run exception/merge_exception_tests.cpp ;
run exception/less_tests.cpp ;
local FCA_EXCEPTION_TESTS =
constructor_exception_tests
copy_exception_tests
assign_exception_tests
move_assign_exception_tests
insert_exception_tests
erase_exception_tests
rehash_exception_tests
merge_exception_tests
less_tests
swap_exception_tests
;
run unordered/narrow_cast_tests.cpp ;
run quick.cpp ;
for local test in $(FCA_EXCEPTION_TESTS)
{
if $(test) = "swap_exception_tests" {
run exception/$(test).cpp : : : <define>BOOST_UNORDERED_SWAP_METHOD=2 ;
} else {
run exception/$(test).cpp ;
}
}
import ../../config/checks/config : requires ;
alias fca_exception_tests : $(FCA_EXCEPTION_TESTS) ;
alias fca_tests :
$(FCA_TESTS)
$(FCA_EXCEPTION_TESTS)
link_test
bmove_compile_set
bmove_compile_map
bmove_copy
bmove_move
bmove_assign
insert_node_type_fail_map
insert_node_type_fail_multimap
insert_node_type_fail_set
insert_node_type_fail_multiset
serialization_tests
;
CPP11 = [ requires cxx11_constexpr cxx11_noexcept cxx11_decltype cxx11_alignas ] ;
local FOA_TESTS =
fwd_set_test
@@ -245,80 +139,40 @@ local FOA_TESTS =
extract_tests
node_handle_tests
uses_allocator
hash_is_avalanching_test
;
for local test in $(FOA_TESTS)
{
run unordered/$(test).cpp : : : <define>BOOST_UNORDERED_FOA_TESTS : foa_$(test) ;
run unordered/$(test).cpp : : : $(CPP11) <define>BOOST_UNORDERED_FOA_TESTS : foa_$(test) ;
}
run unordered/link_test_1.cpp unordered/link_test_2.cpp : : : <define>BOOST_UNORDERED_FOA_TESTS : foa_link_test ;
run unordered/scoped_allocator.cpp : : : <toolset>msvc-14.0:<build>no <define>BOOST_UNORDERED_FOA_TESTS : foa_scoped_allocator ;
run unordered/serialization_tests.cpp
:
:
: <define>BOOST_UNORDERED_FOA_TESTS
<warnings>off # Boost.Serialization headers are not warning-free
<undefined-sanitizer>norecover:<build>no # boost::archive::xml_oarchive does not pass UBSAN
<toolset>msvc:<cxxflags>/bigobj
<toolset>gcc:<inlining>on
<toolset>gcc:<optimization>space
<toolset>clang:<inlining>on
<toolset>clang:<optimization>space
<library>/boost//serialization/<warnings>off
: foa_serialization_tests ;
local FOA_EXCEPTION_TESTS =
constructor_exception_tests
copy_exception_tests
assign_exception_tests
move_assign_exception_tests
insert_exception_tests
erase_exception_tests
rehash_exception_tests
swap_exception_tests
merge_exception_tests
;
for local test in $(FOA_EXCEPTION_TESTS)
{
run exception/$(test).cpp : : : <define>BOOST_UNORDERED_FOA_TESTS : foa_$(test) ;
}
local MMAP_CONTAINERS =
unordered_flat_map
unordered_flat_set
unordered_node_map
unordered_node_set
unordered_map
unordered_set
unordered_multimap
unordered_multiset
concurrent_flat_map
concurrent_flat_set
;
for local container in $(MMAP_CONTAINERS)
{
run unordered/mmap_tests.cpp /boost/filesystem//boost_filesystem : :
: <define>BOOST_UNORDERED_FOA_MMAP_MAP_TYPE="boost::$(container)"
<warnings>off
<link>static
<target-os>cygwin:<build>no
: foa_mmap_$(container)_tests ;
}
alias foa_mmap_tests : foa_mmap_$(MMAP_CONTAINERS)_tests ;
run unordered/link_test_1.cpp unordered/link_test_2.cpp : : : $(CPP11) <define>BOOST_UNORDERED_FOA_TESTS : foa_link_test ;
run unordered/scoped_allocator.cpp : : : $(CPP11) <toolset>msvc-14.0:<build>no <define>BOOST_UNORDERED_FOA_TESTS : foa_scoped_allocator ;
run unordered/hash_is_avalanching_test.cpp ;
run exception/constructor_exception_tests.cpp : : : $(CPP11) <define>BOOST_UNORDERED_FOA_TESTS : foa_constructor_exception_tests ;
run exception/copy_exception_tests.cpp : : : $(CPP11) <define>BOOST_UNORDERED_FOA_TESTS : foa_copy_exception_tests ;
run exception/assign_exception_tests.cpp : : : $(CPP11) <define>BOOST_UNORDERED_FOA_TESTS : foa_assign_exception_tests ;
run exception/move_assign_exception_tests.cpp : : : $(CPP11) <define>BOOST_UNORDERED_FOA_TESTS : foa_move_assign_exception_tests ;
run exception/insert_exception_tests.cpp : : : $(CPP11) <define>BOOST_UNORDERED_FOA_TESTS : foa_insert_exception_tests ;
run exception/erase_exception_tests.cpp : : : $(CPP11) <define>BOOST_UNORDERED_FOA_TESTS : foa_erase_exception_tests ;
run exception/rehash_exception_tests.cpp : : : $(CPP11) <define>BOOST_UNORDERED_FOA_TESTS : foa_rehash_exception_tests ;
run exception/swap_exception_tests.cpp : : : $(CPP11) <define>BOOST_UNORDERED_FOA_TESTS : foa_swap_exception_tests ;
run exception/merge_exception_tests.cpp : : : $(CPP11) <define>BOOST_UNORDERED_FOA_TESTS : foa_merge_exception_tests ;
alias foa_tests :
foa_$(FOA_TESTS)
foa_$(FOA_EXCEPTION_TESTS)
foa_link_test
foa_scoped_allocator
foa_serialization_tests
foa_mmap_tests
hash_is_avalanching_test
foa_constructor_exception_tests
foa_copy_exception_tests
foa_assign_exception_tests
foa_move_assign_exception_tests
foa_insert_exception_tests
foa_erase_exception_tests
foa_rehash_exception_tests
foa_swap_exception_tests
foa_merge_exception_tests
;
local CFOA_TESTS =
@@ -348,31 +202,15 @@ local CFOA_TESTS =
rw_spinlock_test6
rw_spinlock_test7
rw_spinlock_test8
reentrancy_check_test
takes_arg_as_cref_tests
;
for local test in $(CFOA_TESTS)
{
run cfoa/$(test).cpp
: requirements <threading>multi
: requirements $(CPP11) <threading>multi
: target-name cfoa_$(test)
;
}
run cfoa/serialization_tests.cpp
:
:
: $(CPP11) <threading>multi
<warnings>off # Boost.Serialization headers are not warning-free
<undefined-sanitizer>norecover:<build>no # boost::archive::xml_oarchive does not pass UBSAN
<toolset>msvc:<cxxflags>/bigobj
<toolset>gcc:<inlining>on
<toolset>gcc:<optimization>space
<toolset>clang:<inlining>on
<toolset>clang:<optimization>space
<library>/boost//serialization/<warnings>off
: cfoa_serialization_tests ;
alias cfoa_tests :
cfoa_$(CFOA_TESTS)
cfoa_serialization_tests ;
alias cfoa_tests : cfoa_$(CFOA_TESTS) ;
+191 -439
View File
File diff suppressed because it is too large Load Diff
+14 -31
View File
@@ -1,12 +1,10 @@
// Copyright (C) 2023 Christian Mazakas
// Copyright (C) 2023 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_flat_set.hpp>
test::seed_t initialize_seed{674140082};
@@ -16,62 +14,49 @@ using test::sequential;
using hasher = stateful_hash;
using key_equal = stateful_key_equal;
using allocator_type = stateful_allocator<std::pair<raii const, raii> >;
using map_type = boost::unordered::concurrent_flat_map<raii, raii, hasher,
key_equal, stateful_allocator<std::pair<raii const, raii> > >;
key_equal, allocator_type>;
using set_type = boost::unordered::concurrent_flat_set<raii, hasher,
key_equal, stateful_allocator<raii> >;
map_type* test_map;
set_type* test_set;
using map_value_type = typename map_type::value_type;
namespace {
template <class X, class GF>
void clear_tests(X*, GF gen_factory, test::random_generator rg)
template <class G> void clear_tests(G gen, test::random_generator rg)
{
using value_type = typename X::value_type;
static constexpr auto value_type_cardinality =
value_cardinality<value_type>::value;
using allocator_type = typename X::allocator_type;
auto gen = gen_factory.template get<X>();
auto values = make_random_values(1024 * 16, [&] { return gen(rg); });
raii::reset_counts();
X x(values.begin(), values.end(), values.size(), hasher(1),
map_type x(values.begin(), values.end(), values.size(), hasher(1),
key_equal(2), allocator_type(3));
auto const old_size = x.size();
auto const old_d = +raii::destructor;
thread_runner(values, [&x](boost::span<value_type> s) {
thread_runner(values, [&x](boost::span<map_value_type> s) {
(void)s;
x.clear();
});
BOOST_TEST(x.empty());
BOOST_TEST_EQ(raii::destructor, old_d + value_type_cardinality * old_size);
BOOST_TEST_EQ(raii::destructor, old_d + 2 * old_size);
check_raii_counts();
}
template <class X, class GF>
void insert_and_clear(X*, GF gen_factory, test::random_generator rg)
template <class G> void insert_and_clear(G gen, test::random_generator rg)
{
using allocator_type = typename X::allocator_type;
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_map =
boost::unordered_flat_map<raii, raii>(values.begin(), values.end());
raii::reset_counts();
std::thread t1, t2;
{
X x(0, hasher(1), key_equal(2), allocator_type(3));
map_type x(0, hasher(1), key_equal(2), allocator_type(3));
std::mutex m;
std::condition_variable cv;
@@ -118,7 +103,7 @@ namespace {
BOOST_TEST_GE(num_clears, 1u);
if (!x.empty()) {
test_fuzzy_matches_reference(x, reference_cont, rg);
test_fuzzy_matches_reference(x, reference_map, rg);
}
}
@@ -130,13 +115,11 @@ namespace {
// clang-format off
UNORDERED_TEST(
clear_tests,
((test_map)(test_set))
((value_type_generator_factory))
((value_type_generator))
((default_generator)(sequential)(limited_range)))
UNORDERED_TEST(insert_and_clear,
((test_map)(test_set))
((value_type_generator_factory))
((value_type_generator))
((default_generator)(sequential)(limited_range)))
// clang-format on
-137
View File
@@ -1,137 +0,0 @@
// Copyright (C) 2023 Christian Mazakas
// Copyright (C) 2023 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/unordered_flat_map.hpp>
#include <boost/unordered/unordered_flat_set.hpp>
#include <cstddef>
#include <type_traits>
#include <utility>
template <typename K>
struct value_cardinality
{
static constexpr std::size_t value=1;
};
template <typename K, typename V>
struct value_cardinality<std::pair<K, V> >
{
static constexpr std::size_t value=2;
};
template <class Container>
struct reference_container_impl;
template <class Container>
using reference_container = typename reference_container_impl<Container>::type;
template <typename K, typename V, typename H, typename P, typename A>
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 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 <class Container>
using flat_container = typename flat_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> >
{
using type = boost::unordered_flat_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> >
{
using type = boost::unordered_flat_set<K, H, P, A>;
};
template <typename Container, template <typename> class Allocator>
struct replace_allocator_impl;
template <typename Container, template <typename> class Allocator>
using replace_allocator =
typename replace_allocator_impl<Container, Allocator>::type;
template <
typename K, typename V, typename H, typename P, typename A,
template <typename> class Allocator
>
struct replace_allocator_impl<
boost::concurrent_flat_map<K, V, H, P, A>, Allocator>
{
using value_type =
typename boost::concurrent_flat_map<K, V, H, P, A>::value_type;
using type =
boost::concurrent_flat_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_flat_set<K, H, P, A>, Allocator>
{
using value_type =
typename boost::concurrent_flat_set<K, H, P, A>::value_type;
using type =
boost::concurrent_flat_set<K, H, P, Allocator<value_type> >;
};
template <typename K>
K const& get_key(K const& x) { return x; }
template <typename K,typename V>
K const& get_key(const std::pair<K, V>& x) { return x.first; }
template <typename K>
K const& get_value(K const& x) { return x; }
template <typename K,typename V>
V const& get_value(const std::pair<K, V>& x) { return x.second; }
template <typename K,typename V>
V& get_value(std::pair<K, V>& x) { return x.second; }
template <class X, class Y>
void test_matches_reference(X const& x, Y const& reference_cont)
{
using value_type = typename X::value_type;
BOOST_TEST_EQ(x.size(), x.visit_all([&](value_type const& v) {
BOOST_TEST(reference_cont.contains(get_key(v)));
BOOST_TEST_EQ(v, *reference_cont.find(get_key(v)));
}));
}
template <class X, class Y>
void test_fuzzy_matches_reference(
X const& x, Y const& reference_cont, test::random_generator rg)
{
using value_type = typename X::value_type;
BOOST_TEST_EQ(x.size(), x.visit_all([&](value_type const& v) {
BOOST_TEST(reference_cont.contains(get_key(v)));
if (rg == test::sequential) {
BOOST_TEST_EQ(v, *reference_cont.find(get_key(v)));
}
}));
}
#endif // BOOST_UNORDERED_TEST_CFOA_COMMON_HELPERS_HPP
File diff suppressed because it is too large Load Diff
+23 -84
View File
@@ -1,76 +1,34 @@
// Copyright (C) 2023 Christian Mazakas
// Copyright (C) 2023 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_flat_set.hpp>
#include <boost/core/ignore_unused.hpp>
namespace {
test::seed_t initialize_seed(335740237);
template <typename Container, typename Value>
bool member_emplace(Container& x, Value const & v)
{
return x.emplace(v.x_);
}
template <typename Container, typename Key, typename Value>
bool member_emplace(Container& x, std::pair<Key, Value> const & v)
{
return x.emplace(v.first.x_, v.second.x_);
}
template <typename Container, typename Value, typename F>
bool member_emplace_or_visit(Container& x, Value& v, F f)
{
return x.emplace_or_visit(v.x_, f);
}
template <typename Container, typename Key, typename Value, typename F>
bool member_emplace_or_visit(Container& x, std::pair<Key, Value>& v, F f)
{
return x.emplace_or_visit(v.first.x_, v.second.x_, f);
}
template <typename Container, typename Value, typename F>
bool member_emplace_or_cvisit(Container& x, Value& v, F f)
{
return x.emplace_or_cvisit(v.x_, f);
}
template <typename Container, typename Key, typename Value, typename F>
bool member_emplace_or_cvisit(Container& x, std::pair<Key, Value>& v, F f)
{
return x.emplace_or_cvisit(v.first.x_, v.second.x_, f);
}
struct lvalue_emplacer_type
{
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;
std::atomic<std::uint64_t> num_inserts{0};
thread_runner(values, [&x, &num_inserts](boost::span<T> s) {
for (auto const& r : s) {
bool b = member_emplace(x, r);
bool b = x.emplace(r.first.x_, r.second.x_);
if (b) {
++num_inserts;
}
}
});
BOOST_TEST_EQ(num_inserts, x.size());
BOOST_TEST_EQ(
raii::default_constructor, value_type_cardinality * values.size());
BOOST_TEST_EQ(raii::default_constructor, 2 * values.size());
BOOST_TEST_EQ(raii::copy_constructor, 0u);
BOOST_TEST_GE(raii::move_constructor, value_type_cardinality * x.size());
BOOST_TEST_GE(raii::move_constructor, 2 * x.size());
BOOST_TEST_EQ(raii::copy_constructor, 0u);
BOOST_TEST_EQ(raii::copy_assignment, 0u);
@@ -82,12 +40,9 @@ 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;
x.reserve(values.size());
lvalue_emplacer_type::operator()(values, x);
BOOST_TEST_EQ(raii::move_constructor, value_type_cardinality * x.size());
BOOST_TEST_EQ(raii::move_constructor, 2 * x.size());
}
} norehash_lvalue_emplacer;
@@ -95,15 +50,12 @@ 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;
std::atomic<std::uint64_t> num_inserts{0};
std::atomic<std::uint64_t> num_invokes{0};
thread_runner(values, [&x, &num_inserts, &num_invokes](boost::span<T> s) {
for (auto& r : s) {
bool b = member_emplace_or_cvisit(
x, r,
bool b = x.emplace_or_cvisit(
r.first.x_, r.second.x_,
[&num_invokes](typename X::value_type const& v) {
(void)v;
++num_invokes;
@@ -118,10 +70,9 @@ namespace {
BOOST_TEST_EQ(num_inserts, x.size());
BOOST_TEST_EQ(num_invokes, values.size() - x.size());
BOOST_TEST_EQ(
raii::default_constructor, value_type_cardinality * values.size());
BOOST_TEST_EQ(raii::default_constructor, 2 * values.size());
BOOST_TEST_EQ(raii::copy_constructor, 0u);
BOOST_TEST_GE(raii::move_constructor, value_type_cardinality * x.size());
BOOST_TEST_GE(raii::move_constructor, 2 * x.size());
BOOST_TEST_EQ(raii::move_assignment, 0u);
BOOST_TEST_EQ(raii::copy_assignment, 0u);
}
@@ -131,23 +82,13 @@ 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;
// concurrent_flat_set visit is always const access
using arg_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;
std::atomic<std::uint64_t> num_inserts{0};
std::atomic<std::uint64_t> num_invokes{0};
thread_runner(values, [&x, &num_inserts, &num_invokes](boost::span<T> s) {
for (auto& r : s) {
bool b = member_emplace_or_visit(
x, r,
[&num_invokes](arg_type& v) {
bool b = x.emplace_or_visit(
r.first.x_, r.second.x_,
[&num_invokes](typename X::value_type& v) {
(void)v;
++num_invokes;
});
@@ -161,21 +102,20 @@ namespace {
BOOST_TEST_EQ(num_inserts, x.size());
BOOST_TEST_EQ(num_invokes, values.size() - x.size());
BOOST_TEST_EQ(
raii::default_constructor, value_type_cardinality * values.size());
BOOST_TEST_EQ(raii::default_constructor, 2 * values.size());
BOOST_TEST_EQ(raii::copy_constructor, 0u);
BOOST_TEST_GE(raii::move_constructor, value_type_cardinality * x.size());
BOOST_TEST_GE(raii::move_constructor, 2 * x.size());
BOOST_TEST_EQ(raii::move_assignment, 0u);
BOOST_TEST_EQ(raii::copy_assignment, 0u);
}
} lvalue_emplace_or_visit;
template <class X, class GF, class F>
void emplace(X*, GF gen_factory, F emplacer, test::random_generator rg)
template <class X, class G, class F>
void emplace(X*, G gen, F emplacer, test::random_generator rg)
{
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_map =
boost::unordered_flat_map<raii, raii>(values.begin(), values.end());
raii::reset_counts();
{
@@ -183,13 +123,13 @@ namespace {
emplacer(values, x);
BOOST_TEST_EQ(x.size(), reference_cont.size());
BOOST_TEST_EQ(x.size(), reference_map.size());
using value_type = typename X::value_type;
BOOST_TEST_EQ(x.size(), x.visit_all([&](value_type const& v) {
BOOST_TEST(reference_cont.contains(get_key(v)));
BOOST_TEST_EQ(x.size(), x.visit_all([&](value_type const& kv) {
BOOST_TEST(reference_map.contains(kv.first));
if (rg == test::sequential) {
BOOST_TEST_EQ(v, *reference_cont.find(get_key(v)));
BOOST_TEST_EQ(kv.second, reference_map[kv.first]);
}
}));
}
@@ -205,7 +145,6 @@ namespace {
}
boost::unordered::concurrent_flat_map<raii, raii>* map;
boost::unordered::concurrent_flat_set<raii>* set;
} // namespace
@@ -217,8 +156,8 @@ using test::sequential;
UNORDERED_TEST(
emplace,
((map)(set))
((value_type_generator_factory)(init_type_generator_factory))
((map))
((value_type_generator)(init_type_generator))
((lvalue_emplacer)(norehash_lvalue_emplacer)
(lvalue_emplace_or_cvisit)(lvalue_emplace_or_visit))
((default_generator)(sequential)(limited_range)))
+11 -44
View File
@@ -1,12 +1,10 @@
// Copyright (C) 2023 Christian Mazakas
// Copyright (C) 2023 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_flat_set.hpp>
test::seed_t initialize_seed{1634048962};
@@ -16,21 +14,16 @@ using test::sequential;
using hasher = stateful_hash;
using key_equal = stateful_key_equal;
using allocator_type = stateful_allocator<std::pair<raii const, raii> >;
using map_type = boost::unordered::concurrent_flat_map<raii, raii, hasher,
key_equal, stateful_allocator<std::pair<raii const, raii> > >;
key_equal, allocator_type>;
using set_type = boost::unordered::concurrent_flat_set<raii, hasher,
key_equal, stateful_allocator<raii> >;
map_type* test_map;
set_type* test_set;
using map_value_type = typename map_type::value_type;
namespace {
UNORDERED_AUTO_TEST (simple_map_equality) {
using allocator_type = map_type::allocator_type;
UNORDERED_AUTO_TEST (simple_equality) {
{
map_type x1(
{{1, 11}, {2, 22}}, 0, hasher(1), key_equal(2), allocator_type(3));
@@ -57,42 +50,17 @@ namespace {
}
}
UNORDERED_AUTO_TEST (simple_set_equality) {
using allocator_type = set_type::allocator_type;
{
set_type x1(
{1, 2}, 0, hasher(1), key_equal(2), allocator_type(3));
set_type 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));
BOOST_TEST_EQ(x1.size(), x2.size());
BOOST_TEST(x1 == x2);
BOOST_TEST(!(x1 != x2));
BOOST_TEST(x1.size() != x3.size());
BOOST_TEST(!(x1 == x3));
BOOST_TEST(x1 != x3);
}
}
template <class X, class GF>
void insert_and_compare(X*, GF gen_factory, test::random_generator rg)
template <class G> void insert_and_compare(G gen, test::random_generator rg)
{
using allocator_type = typename X::allocator_type;
auto gen = gen_factory.template get<X>();
auto vals1 = make_random_values(1024 * 8, [&] { return gen(rg); });
auto reference_cont = reference_container<X>(vals1.begin(), vals1.end());
boost::unordered_flat_map<raii, raii> reference_map(
vals1.begin(), vals1.end());
{
raii::reset_counts();
X x1(vals1.size(), hasher(1), key_equal(2), allocator_type(3));
X x2(vals1.begin(), vals1.end(), vals1.size(), hasher(2),
map_type x1(vals1.size(), hasher(1), key_equal(2), allocator_type(3));
map_type x2(vals1.begin(), vals1.end(), vals1.size(), hasher(2),
key_equal(2), allocator_type(3));
std::thread t1, t2;
@@ -158,7 +126,7 @@ namespace {
BOOST_TEST(x1 == x2);
BOOST_TEST(!(x1 != x2));
test_matches_reference(x1, reference_cont);
test_matches_reference(x1, reference_map);
}
check_raii_counts();
}
@@ -167,8 +135,7 @@ namespace {
// clang-format off
UNORDERED_TEST(
insert_and_compare,
((test_map)(test_set))
((value_type_generator_factory))
((value_type_generator))
((default_generator)(sequential)(limited_range)))
// clang-format on
+49 -112
View File
@@ -1,12 +1,10 @@
// Copyright (C) 2023 Christian Mazakas
// Copyright (C) 2023 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_flat_set.hpp>
#include <boost/core/ignore_unused.hpp>
@@ -17,9 +15,6 @@ 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;
std::atomic<std::uint64_t> num_erased{0};
auto const old_size = x.size();
@@ -31,11 +26,11 @@ namespace {
BOOST_TEST_EQ(raii::default_constructor + raii::copy_constructor +
raii::move_constructor,
raii::destructor + value_type_cardinality * x.size());
raii::destructor + 2 * x.size());
thread_runner(values, [&values, &num_erased, &x](boost::span<T>) {
for (auto const& v : values) {
auto count = x.erase(get_key(v));
for (auto const& k : values) {
auto count = x.erase(k.first);
num_erased += count;
BOOST_TEST_LE(count, 1u);
BOOST_TEST_GE(count, 0u);
@@ -46,7 +41,7 @@ namespace {
BOOST_TEST_EQ(raii::copy_constructor, old_cc);
BOOST_TEST_EQ(raii::move_constructor, old_mc);
BOOST_TEST_EQ(raii::destructor, old_d + value_type_cardinality * old_size);
BOOST_TEST_EQ(raii::destructor, old_d + 2 * old_size);
BOOST_TEST_EQ(x.size(), 0u);
BOOST_TEST(x.empty());
@@ -58,9 +53,6 @@ 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;
std::atomic<std::uint64_t> num_erased{0};
auto const old_size = x.size();
@@ -72,11 +64,11 @@ namespace {
BOOST_TEST_EQ(raii::default_constructor + raii::copy_constructor +
raii::move_constructor,
raii::destructor + value_type_cardinality * x.size());
raii::destructor + 2 * x.size());
thread_runner(values, [&num_erased, &x](boost::span<T> s) {
for (auto const& v : s) {
auto count = x.erase(get_key(v).x_);
for (auto const& k : s) {
auto count = x.erase(k.first.x_);
num_erased += count;
BOOST_TEST_LE(count, 1u);
BOOST_TEST_GE(count, 0u);
@@ -87,8 +79,7 @@ namespace {
BOOST_TEST_EQ(raii::copy_constructor, old_cc);
BOOST_TEST_EQ(raii::move_constructor, old_mc);
BOOST_TEST_EQ(
raii::destructor, old_d + value_type_cardinality * num_erased);
BOOST_TEST_EQ(raii::destructor, old_d + 2 * num_erased);
BOOST_TEST_EQ(x.size(), 0u);
BOOST_TEST(x.empty());
@@ -101,15 +92,6 @@ namespace {
template <class T, class X> void operator()(std::vector<T>& values, X& x)
{
using value_type = typename X::value_type;
static constexpr auto value_type_cardinality =
value_cardinality<value_type>::value;
// concurrent_flat_set visit is always const access
using arg_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;
std::atomic<std::uint64_t> num_erased{0};
@@ -123,8 +105,8 @@ namespace {
auto max = 0;
x.visit_all([&max](value_type const& v) {
if (get_value(v).x_ > max) {
max = get_value(v).x_;
if (v.second.x_ > max) {
max = v.second.x_;
}
});
@@ -132,15 +114,15 @@ namespace {
auto expected_erasures = 0u;
x.visit_all([&expected_erasures, threshold](value_type const& v) {
if (get_value(v).x_ > threshold) {
if (v.second.x_ > threshold) {
++expected_erasures;
}
});
thread_runner(values, [&num_erased, &x, threshold](boost::span<T> s) {
for (auto const& v : s) {
auto count = x.erase_if(get_key(v),
[threshold](arg_type& w) { return get_value(w).x_ > threshold; });
for (auto const& k : s) {
auto count = x.erase_if(k.first,
[threshold](value_type& v) { return v.second.x_ > threshold; });
num_erased += count;
BOOST_TEST_LE(count, 1u);
BOOST_TEST_GE(count, 0u);
@@ -154,8 +136,7 @@ namespace {
BOOST_TEST_EQ(raii::copy_constructor, old_cc);
BOOST_TEST_EQ(raii::move_constructor, old_mc);
BOOST_TEST_EQ(
raii::destructor, old_d + value_type_cardinality * num_erased);
BOOST_TEST_EQ(raii::destructor, old_d + 2 * num_erased);
}
} lvalue_eraser_if;
@@ -164,15 +145,6 @@ namespace {
template <class T, class X> void operator()(std::vector<T>& values, X& x)
{
using value_type = typename X::value_type;
static constexpr auto value_type_cardinality =
value_cardinality<value_type>::value;
// concurrent_flat_set visit is always const access
using arg_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;
std::atomic<std::uint64_t> num_erased{0};
@@ -186,8 +158,8 @@ namespace {
auto max = 0;
x.visit_all([&max](value_type const& v) {
if (get_value(v).x_ > max) {
max = get_value(v).x_;
if (v.second.x_ > max) {
max = v.second.x_;
}
});
@@ -195,15 +167,15 @@ namespace {
auto expected_erasures = 0u;
x.visit_all([&expected_erasures, threshold](value_type const& v) {
if (get_value(v).x_ > threshold) {
if (v.second.x_ > threshold) {
++expected_erasures;
}
});
thread_runner(values, [&num_erased, &x, threshold](boost::span<T> s) {
for (auto const& v : s) {
auto count = x.erase_if(get_key(v).x_,
[threshold](arg_type& w) { return get_value(w).x_ > threshold; });
for (auto const& k : s) {
auto count = x.erase_if(k.first.x_,
[threshold](value_type& v) { return v.second.x_ > threshold; });
num_erased += count;
BOOST_TEST_LE(count, 1u);
BOOST_TEST_GE(count, 0u);
@@ -217,8 +189,7 @@ namespace {
BOOST_TEST_EQ(raii::copy_constructor, old_cc);
BOOST_TEST_EQ(raii::move_constructor, old_mc);
BOOST_TEST_EQ(
raii::destructor, old_d + value_type_cardinality * num_erased);
BOOST_TEST_EQ(raii::destructor, old_d + 2 * num_erased);
}
} transp_lvalue_eraser_if;
@@ -227,15 +198,6 @@ namespace {
template <class T, class X> void operator()(std::vector<T>& values, X& x)
{
using value_type = typename X::value_type;
static constexpr auto value_type_cardinality =
value_cardinality<value_type>::value;
// concurrent_flat_set visit is always const access
using arg_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;
std::atomic<std::uint64_t> num_erased{0};
@@ -249,8 +211,8 @@ namespace {
auto max = 0;
x.visit_all([&max](value_type const& v) {
if (get_value(v).x_ > max) {
max = get_value(v).x_;
if (v.second.x_ > max) {
max = v.second.x_;
}
});
@@ -258,7 +220,7 @@ namespace {
auto expected_erasures = 0u;
x.visit_all([&expected_erasures, threshold](value_type const& v) {
if (get_value(v).x_ > threshold) {
if (v.second.x_ > threshold) {
++expected_erasures;
}
});
@@ -267,7 +229,7 @@ namespace {
values, [&num_erased, &x, threshold](boost::span<T> /* s */) {
for (std::size_t i = 0; i < 128; ++i) {
auto count = x.erase_if(
[threshold](arg_type& v) { return get_value(v).x_ > threshold; });
[threshold](value_type& v) { return v.second.x_ > threshold; });
num_erased += count;
}
});
@@ -279,8 +241,7 @@ namespace {
BOOST_TEST_EQ(raii::copy_constructor, old_cc);
BOOST_TEST_EQ(raii::move_constructor, old_mc);
BOOST_TEST_EQ(
raii::destructor, old_d + value_type_cardinality * num_erased);
BOOST_TEST_EQ(raii::destructor, old_d + 2 * num_erased);
}
} erase_if;
@@ -289,15 +250,6 @@ namespace {
template <class T, class X> void operator()(std::vector<T>& values, X& x)
{
using value_type = typename X::value_type;
static constexpr auto value_type_cardinality =
value_cardinality<value_type>::value;
// concurrent_flat_set visit is always const access
using arg_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;
std::atomic<std::uint64_t> num_erased{0};
@@ -311,8 +263,8 @@ namespace {
auto max = 0;
x.visit_all([&max](value_type const& v) {
if (get_value(v).x_ > max) {
max = get_value(v).x_;
if (v.second.x_ > max) {
max = v.second.x_;
}
});
@@ -320,7 +272,7 @@ namespace {
auto expected_erasures = 0u;
x.visit_all([&expected_erasures, threshold](value_type const& v) {
if (get_value(v).x_ > threshold) {
if (v.second.x_ > threshold) {
++expected_erasures;
}
});
@@ -329,8 +281,7 @@ namespace {
values, [&num_erased, &x, threshold](boost::span<T> /* s */) {
for (std::size_t i = 0; i < 128; ++i) {
auto count = boost::unordered::erase_if(x,
[threshold](arg_type& v) {
return get_value(v).x_ > threshold; });
[threshold](value_type& v) { return v.second.x_ > threshold; });
num_erased += count;
}
});
@@ -342,8 +293,7 @@ namespace {
BOOST_TEST_EQ(raii::copy_constructor, old_cc);
BOOST_TEST_EQ(raii::move_constructor, old_mc);
BOOST_TEST_EQ(
raii::destructor, old_d + value_type_cardinality * num_erased);
BOOST_TEST_EQ(raii::destructor, old_d + 2 * num_erased);
}
} free_fn_erase_if;
@@ -353,15 +303,6 @@ namespace {
{
#if defined(BOOST_UNORDERED_PARALLEL_ALGORITHMS)
using value_type = typename X::value_type;
static constexpr auto value_type_cardinality =
value_cardinality<value_type>::value;
// concurrent_flat_set visit is always const access
using arg_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;
std::atomic<std::uint64_t> num_invokes{0};
@@ -375,8 +316,8 @@ namespace {
auto max = 0;
x.visit_all([&max](value_type const& v) {
if (get_value(v).x_ > max) {
max = get_value(v).x_;
if (v.second.x_ > max) {
max = v.second.x_;
}
});
@@ -384,7 +325,7 @@ namespace {
auto expected_erasures = 0u;
x.visit_all([&expected_erasures, threshold](value_type const& v) {
if (get_value(v).x_ > threshold) {
if (v.second.x_ > threshold) {
++expected_erasures;
}
});
@@ -392,9 +333,9 @@ namespace {
thread_runner(values, [&num_invokes, &x, threshold](boost::span<T> s) {
(void)s;
x.erase_if(
std::execution::par, [&num_invokes, threshold](arg_type& v) {
std::execution::par, [&num_invokes, threshold](value_type& v) {
++num_invokes;
return get_value(v).x_ > threshold;
return v.second.x_ > threshold;
});
});
@@ -405,8 +346,7 @@ namespace {
BOOST_TEST_EQ(raii::copy_constructor, old_cc);
BOOST_TEST_EQ(raii::move_constructor, old_mc);
BOOST_TEST_EQ(
raii::destructor, old_d + value_type_cardinality * expected_erasures);
BOOST_TEST_EQ(raii::destructor, old_d + 2 * expected_erasures);
#else
(void)values;
(void)x;
@@ -414,12 +354,12 @@ namespace {
}
} erase_if_exec_policy;
template <class X, class GF, class F>
void erase(X*, GF gen_factory, F eraser, test::random_generator rg)
template <class X, class G, class F>
void erase(X*, G gen, F eraser, test::random_generator rg)
{
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_map =
boost::unordered_flat_map<raii, raii>(values.begin(), values.end());
raii::reset_counts();
{
@@ -427,23 +367,20 @@ namespace {
x.insert(values.begin(), values.end());
BOOST_TEST_EQ(x.size(), reference_cont.size());
BOOST_TEST_EQ(x.size(), reference_map.size());
test_fuzzy_matches_reference(x, reference_cont, rg);
test_fuzzy_matches_reference(x, reference_map, rg);
eraser(values, x);
test_fuzzy_matches_reference(x, reference_cont, rg);
test_fuzzy_matches_reference(x, reference_map, rg);
}
check_raii_counts();
}
boost::unordered::concurrent_flat_map<raii, raii>* map;
boost::unordered::concurrent_flat_set<raii>* set;
boost::unordered::concurrent_flat_map<raii, raii, transp_hash,
transp_key_equal>* transparent_map;
boost::unordered::concurrent_flat_set<raii, transp_hash,
transp_key_equal>* transparent_set;
} // namespace
@@ -454,15 +391,15 @@ using test::sequential;
// clang-format off
UNORDERED_TEST(
erase,
((map)(set))
((value_type_generator_factory)(init_type_generator_factory))
((map))
((value_type_generator)(init_type_generator))
((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))
((value_type_generator_factory)(init_type_generator_factory))
((transparent_map))
((value_type_generator)(init_type_generator))
((transp_lvalue_eraser)(transp_lvalue_eraser_if)(erase_if_exec_policy))
((default_generator)(sequential)(limited_range)))
+43 -95
View File
@@ -1,87 +1,24 @@
// Copyright (C) 2023 Christian Mazakas
// Copyright (C) 2023 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 "exception_helpers.hpp"
#include <boost/unordered/concurrent_flat_map.hpp>
#include <boost/unordered/concurrent_flat_set.hpp>
using allocator_type = stateful_allocator<std::pair<raii const, raii> >;
using hasher = stateful_hash;
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 set_type = boost::unordered::concurrent_flat_set<raii, hasher,
key_equal, stateful_allocator<raii> >;
map_type* test_map;
set_type* test_set;
std::initializer_list<map_type::value_type> map_init_list{
{raii{0}, raii{0}},
{raii{1}, raii{1}},
{raii{2}, raii{2}},
{raii{3}, raii{3}},
{raii{4}, raii{4}},
{raii{5}, raii{5}},
{raii{6}, raii{6}},
{raii{6}, raii{6}},
{raii{7}, raii{7}},
{raii{8}, raii{8}},
{raii{9}, raii{9}},
{raii{10}, raii{10}},
{raii{9}, raii{9}},
{raii{8}, raii{8}},
{raii{7}, raii{7}},
{raii{6}, raii{6}},
{raii{5}, raii{5}},
{raii{4}, raii{4}},
{raii{3}, raii{3}},
{raii{2}, raii{2}},
{raii{1}, raii{1}},
{raii{0}, raii{0}},
};
std::initializer_list<set_type::value_type> set_init_list{
raii{0},
raii{1},
raii{2},
raii{3},
raii{4},
raii{5},
raii{6},
raii{6},
raii{7},
raii{8},
raii{9},
raii{10},
raii{9},
raii{8},
raii{7},
raii{6},
raii{5},
raii{4},
raii{3},
raii{2},
raii{1},
raii{0},
};
auto test_map_and_init_list=std::make_pair(test_map,map_init_list);
auto test_set_and_init_list=std::make_pair(test_set,set_init_list);
key_equal, allocator_type>;
namespace {
test::seed_t initialize_seed(1794114520);
template <class X, class GF>
void copy_assign(X*, GF gen_factory, test::random_generator rg)
template <class G> void copy_assign(G gen, test::random_generator rg)
{
using allocator_type = typename X::allocator_type;
auto gen = gen_factory.template get<X>();
auto values = make_random_values(1024 * 16, [&] { return gen(rg); });
{
@@ -94,12 +31,12 @@ namespace {
values.begin() + static_cast<std::ptrdiff_t>(values.size() / 2);
auto end = values.end();
auto reference_cont = reference_container<X>(begin, mid);
auto reference_map = boost::unordered_flat_map<raii, raii>(begin, mid);
X x(
map_type x(
begin, mid, values.size(), hasher(1), key_equal(2), allocator_type(3));
X y(
map_type y(
mid, end, values.size(), hasher(2), key_equal(1), allocator_type(4));
BOOST_TEST(!y.empty());
@@ -116,17 +53,13 @@ namespace {
disable_exceptions();
BOOST_TEST_GT(num_throws, 0u);
test_fuzzy_matches_reference(y, reference_cont, rg);
test_fuzzy_matches_reference(y, reference_map, rg);
}
check_raii_counts();
}
template <class X, class GF>
void move_assign(X*, GF gen_factory, test::random_generator rg)
template <class G> void move_assign(G gen, test::random_generator rg)
{
using allocator_type = typename X::allocator_type;
auto gen = gen_factory.template get<X>();
auto values = make_random_values(1024 * 16, [&] { return gen(rg); });
{
@@ -139,7 +72,7 @@ namespace {
values.begin() + static_cast<std::ptrdiff_t>(values.size() / 2);
auto end = values.end();
auto reference_cont = reference_container<X>(begin, mid);
auto reference_map = boost::unordered_flat_map<raii, raii>(begin, mid);
BOOST_TEST(
!boost::allocator_is_always_equal<allocator_type>::type::value);
@@ -150,10 +83,10 @@ namespace {
for (std::size_t i = 0; i < 2 * alloc_throw_threshold; ++i) {
disable_exceptions();
X x(begin, mid, values.size(), hasher(1), key_equal(2),
map_type x(begin, mid, values.size(), hasher(1), key_equal(2),
allocator_type(3));
X y(
map_type y(
mid, end, values.size(), hasher(2), key_equal(1), allocator_type(4));
enable_exceptions();
@@ -163,7 +96,7 @@ namespace {
++num_throws;
}
disable_exceptions();
test_fuzzy_matches_reference(y, reference_cont, rg);
test_fuzzy_matches_reference(y, reference_map, rg);
}
BOOST_TEST_GT(num_throws, 0u);
@@ -171,22 +104,43 @@ namespace {
check_raii_counts();
}
template <class X, class IL>
void intializer_list_assign(std::pair<X*, IL> p)
{
using allocator_type = typename X::allocator_type;
UNORDERED_AUTO_TEST (intializer_list_assign) {
using value_type = typename map_type::value_type;
auto init_list = p.second;
std::initializer_list<value_type> values{
value_type{raii{0}, raii{0}},
value_type{raii{1}, raii{1}},
value_type{raii{2}, raii{2}},
value_type{raii{3}, raii{3}},
value_type{raii{4}, raii{4}},
value_type{raii{5}, raii{5}},
value_type{raii{6}, raii{6}},
value_type{raii{6}, raii{6}},
value_type{raii{7}, raii{7}},
value_type{raii{8}, raii{8}},
value_type{raii{9}, raii{9}},
value_type{raii{10}, raii{10}},
value_type{raii{9}, raii{9}},
value_type{raii{8}, raii{8}},
value_type{raii{7}, raii{7}},
value_type{raii{6}, raii{6}},
value_type{raii{5}, raii{5}},
value_type{raii{4}, raii{4}},
value_type{raii{3}, raii{3}},
value_type{raii{2}, raii{2}},
value_type{raii{1}, raii{1}},
value_type{raii{0}, raii{0}},
};
{
raii::reset_counts();
unsigned num_throws = 0;
for (std::size_t i = 0; i < throw_threshold; ++i) {
X x(0, hasher(1), key_equal(2), allocator_type(3));
map_type x(0, hasher(1), key_equal(2), allocator_type(3));
enable_exceptions();
try {
x = init_list;
x = values;
} catch (...) {
++num_throws;
}
@@ -206,19 +160,13 @@ using test::sequential;
// clang-format off
UNORDERED_TEST(
copy_assign,
((test_map)(test_set))
((exception_value_type_generator_factory))
((exception_value_type_generator))
((default_generator)(sequential)(limited_range)))
UNORDERED_TEST(
move_assign,
((test_map)(test_set))
((exception_value_type_generator_factory))
((exception_value_type_generator))
((default_generator)(sequential)))
UNORDERED_TEST(
intializer_list_assign,
((test_map_and_init_list)(test_set_and_init_list)))
// clang-format on
RUN_TESTS()
+52 -115
View File
@@ -1,84 +1,23 @@
// Copyright (C) 2023 Christian Mazakas
// Copyright (C) 2023 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 "exception_helpers.hpp"
#include <boost/unordered/concurrent_flat_map.hpp>
#include <boost/unordered/concurrent_flat_set.hpp>
using allocator_type = stateful_allocator<std::pair<raii const, raii> >;
using hasher = stateful_hash;
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 set_type = boost::unordered::concurrent_flat_set<raii, hasher,
key_equal, stateful_allocator<raii> >;
map_type* test_map;
set_type* test_set;
std::initializer_list<map_type::value_type> map_init_list{
{raii{0}, raii{0}},
{raii{1}, raii{1}},
{raii{2}, raii{2}},
{raii{3}, raii{3}},
{raii{4}, raii{4}},
{raii{5}, raii{5}},
{raii{6}, raii{6}},
{raii{6}, raii{6}},
{raii{7}, raii{7}},
{raii{8}, raii{8}},
{raii{9}, raii{9}},
{raii{10}, raii{10}},
{raii{9}, raii{9}},
{raii{8}, raii{8}},
{raii{7}, raii{7}},
{raii{6}, raii{6}},
{raii{5}, raii{5}},
{raii{4}, raii{4}},
{raii{3}, raii{3}},
{raii{2}, raii{2}},
{raii{1}, raii{1}},
{raii{0}, raii{0}},
};
std::initializer_list<set_type::value_type> set_init_list{
raii{0},
raii{1},
raii{2},
raii{3},
raii{4},
raii{5},
raii{6},
raii{6},
raii{7},
raii{8},
raii{9},
raii{10},
raii{9},
raii{8},
raii{7},
raii{6},
raii{5},
raii{4},
raii{3},
raii{2},
raii{1},
raii{0},
};
auto test_map_and_init_list=std::make_pair(test_map,map_init_list);
auto test_set_and_init_list=std::make_pair(test_set,set_init_list);
key_equal, allocator_type>;
namespace {
test::seed_t initialize_seed(795610904);
template <class X>
void bucket_constructor(X*)
{
UNORDERED_AUTO_TEST (bucket_constructor) {
raii::reset_counts();
bool was_thrown = false;
@@ -86,7 +25,7 @@ namespace {
enable_exceptions();
for (std::size_t i = 0; i < alloc_throw_threshold; ++i) {
try {
X m(128);
map_type m(128);
} catch (...) {
was_thrown = true;
}
@@ -96,12 +35,8 @@ namespace {
BOOST_TEST(was_thrown);
}
template <class X, class GF>
void iterator_range(X*, GF gen_factory, test::random_generator rg)
template <class G> void iterator_range(G gen, test::random_generator rg)
{
using allocator_type = typename X::allocator_type;
auto gen = gen_factory.template get<X>();
auto values = make_random_values(1024 * 16, [&] { return gen(rg); });
{
@@ -111,7 +46,7 @@ namespace {
enable_exceptions();
try {
X x(values.begin(), values.end(), 0, hasher(1), key_equal(2),
map_type x(values.begin(), values.end(), 0, hasher(1), key_equal(2),
allocator_type(3));
} catch (...) {
was_thrown = true;
@@ -129,7 +64,7 @@ namespace {
enable_exceptions();
try {
X x(values.begin(), values.end(), allocator_type(3));
map_type x(values.begin(), values.end(), allocator_type(3));
} catch (...) {
was_thrown = true;
}
@@ -146,7 +81,7 @@ namespace {
enable_exceptions();
try {
X x(
map_type x(
values.begin(), values.end(), values.size(), allocator_type(3));
} catch (...) {
was_thrown = true;
@@ -164,7 +99,7 @@ namespace {
enable_exceptions();
try {
X x(values.begin(), values.end(), values.size(), hasher(1),
map_type x(values.begin(), values.end(), values.size(), hasher(1),
allocator_type(3));
} catch (...) {
was_thrown = true;
@@ -176,12 +111,8 @@ namespace {
}
}
template <class X, class GF>
void copy_constructor(X*, GF gen_factory, test::random_generator rg)
template <class G> void copy_constructor(G gen, test::random_generator rg)
{
using allocator_type = typename X::allocator_type;
auto gen = gen_factory.template get<X>();
auto values = make_random_values(1024 * 16, [&] { return gen(rg); });
{
@@ -190,10 +121,10 @@ namespace {
bool was_thrown = false;
try {
X x(values.begin(), values.end(), 0);
map_type x(values.begin(), values.end(), 0);
enable_exceptions();
X y(x);
map_type y(x);
} catch (...) {
was_thrown = true;
}
@@ -209,10 +140,10 @@ namespace {
bool was_thrown = false;
try {
X x(values.begin(), values.end(), 0);
map_type x(values.begin(), values.end(), 0);
enable_exceptions();
X y(x, allocator_type(4));
map_type y(x, allocator_type(4));
} catch (...) {
was_thrown = true;
}
@@ -223,24 +154,20 @@ namespace {
}
}
template <class X, class GF>
void move_constructor(X*, GF gen_factory, test::random_generator rg)
template <class G> void move_constructor(G gen, test::random_generator rg)
{
using allocator_type = typename X::allocator_type;
auto gen = gen_factory.template get<X>();
auto values = make_random_values(1024 * 16, [&] { return gen(rg); });
{
raii::reset_counts();
bool was_thrown = false;
try {
X x(values.begin(), values.end(), 0);
map_type x(values.begin(), values.end(), 0);
enable_exceptions();
X y(std::move(x), allocator_type(4));
map_type y(std::move(x), allocator_type(4));
} catch (...) {
was_thrown = true;
}
@@ -251,12 +178,33 @@ namespace {
}
}
template <class X, class IL>
void initializer_list_bucket_count(std::pair<X*, IL> p)
{
using allocator_type = typename X::allocator_type;
UNORDERED_AUTO_TEST (initializer_list_bucket_count) {
using value_type = typename map_type::value_type;
auto init_list = p.second;
std::initializer_list<value_type> values{
value_type{raii{0}, raii{0}},
value_type{raii{1}, raii{1}},
value_type{raii{2}, raii{2}},
value_type{raii{3}, raii{3}},
value_type{raii{4}, raii{4}},
value_type{raii{5}, raii{5}},
value_type{raii{6}, raii{6}},
value_type{raii{6}, raii{6}},
value_type{raii{7}, raii{7}},
value_type{raii{8}, raii{8}},
value_type{raii{9}, raii{9}},
value_type{raii{10}, raii{10}},
value_type{raii{9}, raii{9}},
value_type{raii{8}, raii{8}},
value_type{raii{7}, raii{7}},
value_type{raii{6}, raii{6}},
value_type{raii{5}, raii{5}},
value_type{raii{4}, raii{4}},
value_type{raii{3}, raii{3}},
value_type{raii{2}, raii{2}},
value_type{raii{1}, raii{1}},
value_type{raii{0}, raii{0}},
};
{
raii::reset_counts();
@@ -265,7 +213,7 @@ namespace {
enable_exceptions();
for (std::size_t i = 0; i < throw_threshold; ++i) {
try {
X x(init_list, 0, hasher(1), key_equal(2), allocator_type(3));
map_type x(values, 0, hasher(1), key_equal(2), allocator_type(3));
} catch (...) {
++num_throws;
}
@@ -283,7 +231,7 @@ namespace {
enable_exceptions();
for (std::size_t i = 0; i < alloc_throw_threshold * 2; ++i) {
try {
X x(init_list, allocator_type(3));
map_type x(values, allocator_type(3));
} catch (...) {
++num_throws;
}
@@ -301,7 +249,7 @@ namespace {
enable_exceptions();
for (std::size_t i = 0; i < alloc_throw_threshold * 2; ++i) {
try {
X x(init_list, init_list.size() * 2, allocator_type(3));
map_type x(values, values.size() * 2, allocator_type(3));
} catch (...) {
++num_throws;
}
@@ -319,7 +267,7 @@ namespace {
enable_exceptions();
for (std::size_t i = 0; i < throw_threshold; ++i) {
try {
X x(init_list, init_list.size() * 2, hasher(1), allocator_type(3));
map_type x(values, values.size() * 2, hasher(1), allocator_type(3));
} catch (...) {
++num_throws;
}
@@ -337,31 +285,20 @@ using test::limited_range;
using test::sequential;
// clang-format off
UNORDERED_TEST(
bucket_constructor,
((test_map)(test_set)))
UNORDERED_TEST(
iterator_range,
((test_map)(test_set))
((exception_value_type_generator_factory))
((exception_value_type_generator))
((default_generator)(sequential)(limited_range)))
UNORDERED_TEST(
copy_constructor,
((test_map)(test_set))
((exception_value_type_generator_factory))
((exception_value_type_generator))
((default_generator)(sequential)))
UNORDERED_TEST(
move_constructor,
((test_map)(test_set))
((exception_value_type_generator_factory))
((exception_value_type_generator))
((default_generator)(sequential)))
UNORDERED_TEST(
initializer_list_bucket_count,
((test_map_and_init_list)(test_set_and_init_list)))
// clang-format on
RUN_TESTS()
+30 -71
View File
@@ -1,12 +1,10 @@
// Copyright (C) 2023 Christian Mazakas
// Copyright (C) 2023 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 "exception_helpers.hpp"
#include <boost/unordered/concurrent_flat_map.hpp>
#include <boost/unordered/concurrent_flat_set.hpp>
#include <boost/core/ignore_unused.hpp>
@@ -17,9 +15,6 @@ 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;
std::atomic<std::uint64_t> num_erased{0};
auto const old_size = x.size();
@@ -32,9 +27,9 @@ namespace {
enable_exceptions();
thread_runner(values, [&values, &num_erased, &x](boost::span<T>) {
for (auto const& v : values) {
for (auto const& k : values) {
try {
auto count = x.erase(get_key(v));
auto count = x.erase(k.first);
BOOST_TEST_LE(count, 1u);
BOOST_TEST_GE(count, 0u);
@@ -51,8 +46,7 @@ namespace {
BOOST_TEST_EQ(raii::copy_constructor, old_cc);
BOOST_TEST_EQ(raii::move_constructor, old_mc);
BOOST_TEST_EQ(
raii::destructor, old_d + value_type_cardinality * num_erased);
BOOST_TEST_EQ(raii::destructor, old_d + 2 * num_erased);
}
} lvalue_eraser;
@@ -61,15 +55,6 @@ namespace {
template <class T, class X> void operator()(std::vector<T>& values, X& x)
{
using value_type = typename X::value_type;
static constexpr auto value_type_cardinality =
value_cardinality<value_type>::value;
// concurrent_flat_set visit is always const access
using arg_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;
std::atomic<std::uint64_t> num_erased{0};
@@ -83,8 +68,8 @@ namespace {
auto max = 0;
x.visit_all([&max](value_type const& v) {
if (get_value(v).x_ > max) {
max = get_value(v).x_;
if (v.second.x_ > max) {
max = v.second.x_;
}
});
@@ -92,17 +77,17 @@ namespace {
auto expected_erasures = 0u;
x.visit_all([&expected_erasures, threshold](value_type const& v) {
if (get_value(v).x_ > threshold) {
if (v.second.x_ > threshold) {
++expected_erasures;
}
});
enable_exceptions();
thread_runner(values, [&num_erased, &x, threshold](boost::span<T> s) {
for (auto const& v : s) {
for (auto const& k : s) {
try {
auto count = x.erase_if(get_key(v),
[threshold](arg_type& w) { return get_value(w).x_ > threshold; });
auto count = x.erase_if(k.first,
[threshold](value_type& v) { return v.second.x_ > threshold; });
num_erased += count;
BOOST_TEST_LE(count, 1u);
BOOST_TEST_GE(count, 0u);
@@ -119,8 +104,7 @@ namespace {
BOOST_TEST_EQ(raii::copy_constructor, old_cc);
BOOST_TEST_EQ(raii::move_constructor, old_mc);
BOOST_TEST_EQ(
raii::destructor, old_d + value_type_cardinality * num_erased);
BOOST_TEST_EQ(raii::destructor, old_d + 2 * num_erased);
}
} lvalue_eraser_if;
@@ -129,15 +113,6 @@ namespace {
template <class T, class X> void operator()(std::vector<T>& values, X& x)
{
using value_type = typename X::value_type;
static constexpr auto value_type_cardinality =
value_cardinality<value_type>::value;
// concurrent_flat_set visit is always const access
using arg_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;
auto const old_size = x.size();
@@ -149,8 +124,8 @@ namespace {
auto max = 0;
x.visit_all([&max](value_type const& v) {
if (get_value(v).x_ > max) {
max = get_value(v).x_;
if (v.second.x_ > max) {
max = v.second.x_;
}
});
@@ -158,7 +133,7 @@ namespace {
auto expected_erasures = 0u;
x.visit_all([&expected_erasures, threshold](value_type const& v) {
if (get_value(v).x_ > threshold) {
if (v.second.x_ > threshold) {
++expected_erasures;
}
});
@@ -167,14 +142,14 @@ namespace {
thread_runner(values, [&x, threshold](boost::span<T> /* s */) {
for (std::size_t i = 0; i < 256; ++i) {
try {
x.erase_if([threshold](arg_type& v) {
x.erase_if([threshold](value_type& v) {
static std::atomic<std::uint32_t> c{0};
auto t = ++c;
if (should_throw && (t % throw_threshold == 0)) {
throw exception_tag{};
}
return get_value(v).x_ > threshold;
return v.second.x_ > threshold;
});
} catch (...) {
}
@@ -186,9 +161,7 @@ namespace {
BOOST_TEST_EQ(raii::copy_constructor, old_cc);
BOOST_TEST_EQ(raii::move_constructor, old_mc);
BOOST_TEST_EQ(
raii::destructor,
old_d + value_type_cardinality * (old_size - x.size()));
BOOST_TEST_EQ(raii::destructor, old_d + 2 * (old_size - x.size()));
}
} erase_if;
@@ -197,15 +170,6 @@ namespace {
template <class T, class X> void operator()(std::vector<T>& values, X& x)
{
using value_type = typename X::value_type;
static constexpr auto value_type_cardinality =
value_cardinality<value_type>::value;
// concurrent_flat_set visit is always const access
using arg_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;
auto const old_size = x.size();
@@ -217,8 +181,8 @@ namespace {
auto max = 0;
x.visit_all([&max](value_type const& v) {
if (get_value(v).x_ > max) {
max = get_value(v).x_;
if (v.second.x_ > max) {
max = v.second.x_;
}
});
@@ -228,14 +192,14 @@ namespace {
thread_runner(values, [&x, threshold](boost::span<T> /* s */) {
for (std::size_t i = 0; i < 256; ++i) {
try {
boost::unordered::erase_if(x, [threshold](arg_type& v) {
boost::unordered::erase_if(x, [threshold](value_type& v) {
static std::atomic<std::uint32_t> c{0};
auto t = ++c;
if (should_throw && (t % throw_threshold == 0)) {
throw exception_tag{};
}
return get_value(v).x_ > threshold;
return v.second.x_ > threshold;
});
} catch (...) {
@@ -248,18 +212,16 @@ namespace {
BOOST_TEST_EQ(raii::copy_constructor, old_cc);
BOOST_TEST_EQ(raii::move_constructor, old_mc);
BOOST_TEST_EQ(
raii::destructor,
old_d + value_type_cardinality * (old_size - x.size()));
BOOST_TEST_EQ(raii::destructor, old_d + 2 * (old_size - x.size()));
}
} free_fn_erase_if;
template <class X, class GF, class F>
void erase(X*, GF gen_factory, F eraser, test::random_generator rg)
template <class X, class G, class F>
void erase(X*, G gen, F eraser, test::random_generator rg)
{
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_map =
boost::unordered_flat_map<raii, raii>(values.begin(), values.end());
raii::reset_counts();
@@ -269,13 +231,13 @@ namespace {
x.insert(v);
}
BOOST_TEST_EQ(x.size(), reference_cont.size());
BOOST_TEST_EQ(x.size(), reference_map.size());
BOOST_TEST_EQ(raii::destructor, 0u);
test_fuzzy_matches_reference(x, reference_cont, rg);
test_fuzzy_matches_reference(x, reference_map, rg);
eraser(values, x);
test_fuzzy_matches_reference(x, reference_cont, rg);
test_fuzzy_matches_reference(x, reference_map, rg);
}
check_raii_counts();
@@ -283,8 +245,6 @@ namespace {
boost::unordered::concurrent_flat_map<raii, raii, stateful_hash,
stateful_key_equal, stateful_allocator<std::pair<raii const, raii> > >* map;
boost::unordered::concurrent_flat_set<raii, stateful_hash,
stateful_key_equal, stateful_allocator<raii> >* set;
} // namespace
@@ -295,9 +255,8 @@ using test::sequential;
// clang-format off
UNORDERED_TEST(
erase,
((map)(set))
((exception_value_type_generator_factory)
(exception_init_type_generator_factory))
((map))
((exception_value_type_generator)(exception_init_type_generator))
((lvalue_eraser)(lvalue_eraser_if)(erase_if)(free_fn_erase_if))
((default_generator)(sequential)(limited_range)))
+26 -49
View File
@@ -1,20 +1,14 @@
// Copyright (C) 2023 Christian Mazakas
// Copyright (C) 2023 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_EXCEPTION_HELPERS_HPP
#define BOOST_UNORDERED_TEST_CFOA_EXCEPTION_HELPERS_HPP
#include "../helpers/generators.hpp"
#include "../helpers/test.hpp"
#include "common_helpers.hpp"
#include <boost/compat/latch.hpp>
#include <boost/container_hash/hash.hpp>
#include <boost/core/span.hpp>
#include <boost/unordered/unordered_flat_map.hpp>
#include <boost/unordered/unordered_flat_set.hpp>
#include <algorithm>
#include <atomic>
@@ -314,54 +308,16 @@ std::size_t hash_value(raii const& r) noexcept
return hasher(r.x_);
}
template <typename K>
struct exception_value_generator
struct exception_value_type_generator_type
{
using value_type = raii;
value_type operator()(test::random_generator rg)
{
int* p = nullptr;
int a = generate(p, rg);
return value_type(a);
}
};
template <typename K, typename V>
struct exception_value_generator<std::pair<K, V> >
{
static constexpr bool const_key = std::is_const<K>::value;
static constexpr bool const_mapped = std::is_const<V>::value;
using value_type = std::pair<
typename std::conditional<const_key, raii const, raii>::type,
typename std::conditional<const_mapped, raii const, raii>::type>;
value_type operator()(test::random_generator rg)
std::pair<raii const, raii> operator()(test::random_generator rg)
{
int* p = nullptr;
int a = generate(p, rg);
int b = generate(p, rg);
return std::make_pair(raii{a}, raii{b});
}
};
struct exception_value_type_generator_factory_type
{
template <typename Container>
exception_value_generator<typename Container::value_type> get()
{
return {};
}
} exception_value_type_generator_factory;
struct exception_init_type_generator_factory_type
{
template <typename Container>
exception_value_generator<typename Container::init_type> get()
{
return {};
}
} exception_init_type_generator_factory;
} exception_value_type_generator;
struct exception_init_type_generator_type
{
@@ -432,6 +388,29 @@ template <class T, class F> void thread_runner(std::vector<T>& values, F f)
}
}
template <class X, class Y>
void test_matches_reference(X const& x, Y const& reference_map)
{
using value_type = typename X::value_type;
BOOST_TEST_EQ(x.size(), x.visit_all([&](value_type const& kv) {
BOOST_TEST(reference_map.contains(kv.first));
BOOST_TEST_EQ(kv.second, reference_map.find(kv.first)->second);
}));
}
template <class X, class Y>
void test_fuzzy_matches_reference(
X const& x, Y const& reference_map, test::random_generator rg)
{
using value_type = typename X::value_type;
BOOST_TEST_EQ(x.size(), x.visit_all([&](value_type const& kv) {
BOOST_TEST(reference_map.contains(kv.first));
if (rg == test::sequential) {
BOOST_TEST_EQ(kv.second, reference_map.find(kv.first)->second);
}
}));
}
template <class T> using span_value_type = typename T::value_type;
void check_raii_counts()
@@ -463,5 +442,3 @@ auto make_random_values(std::size_t count, F f) -> std::vector<decltype(f())>
}
return v;
}
#endif // BOOST_UNORDERED_TEST_CFOA_EXCEPTION_HELPERS_HPP
+13 -43
View File
@@ -1,12 +1,10 @@
// Copyright (C) 2023 Christian Mazakas
// Copyright (C) 2023 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 "exception_helpers.hpp"
#include <boost/unordered/concurrent_flat_map.hpp>
#include <boost/unordered/concurrent_flat_set.hpp>
#include <boost/core/ignore_unused.hpp>
@@ -86,9 +84,6 @@ 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;
x.reserve(values.size());
BOOST_TEST_EQ(raii::copy_constructor, 0u);
@@ -97,19 +92,11 @@ namespace {
rvalue_inserter_type::operator()(values, x);
if (std::is_same<T, typename X::value_type>::value) {
if (std::is_same<typename X::key_type,
typename X::value_type>::value) {
BOOST_TEST_EQ(raii::copy_constructor, 0u);
BOOST_TEST_EQ(raii::move_constructor, x.size());
}
else {
BOOST_TEST_EQ(raii::copy_constructor, x.size());
BOOST_TEST_EQ(raii::move_constructor, x.size());
}
BOOST_TEST_EQ(raii::copy_constructor, x.size());
BOOST_TEST_EQ(raii::move_constructor, x.size());
} else {
BOOST_TEST_EQ(raii::copy_constructor, 0u);
BOOST_TEST_EQ(
raii::move_constructor, value_type_cardinality * x.size());
BOOST_TEST_EQ(raii::move_constructor, 2 * x.size());
}
}
} norehash_rvalue_inserter;
@@ -259,13 +246,6 @@ namespace {
{
template <class T, class X> void operator()(std::vector<T>& values, X& x)
{
// concurrent_flat_set visit is always const access
using arg_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;
std::atomic<std::uint64_t> num_inserts{0};
enable_exceptions();
@@ -273,7 +253,7 @@ namespace {
for (auto& r : s) {
try {
bool b =
x.insert_or_visit(r, [](arg_type& v) { (void)v; });
x.insert_or_visit(r, [](typename X::value_type& v) { (void)v; });
if (b) {
++num_inserts;
@@ -326,13 +306,6 @@ namespace {
{
template <class T, class X> void operator()(std::vector<T>& values, X& x)
{
// concurrent_flat_set visit is always const access
using arg_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;
std::atomic<std::uint64_t> num_inserts{0};
enable_exceptions();
@@ -340,7 +313,7 @@ namespace {
for (auto& r : s) {
try {
bool b = x.insert_or_visit(
std::move(r), [](arg_type& v) { (void)v; });
std::move(r), [](typename X::value_type& v) { (void)v; });
if (b) {
++num_inserts;
@@ -404,14 +377,14 @@ namespace {
}
} iterator_range_insert_or_visit;
template <class X, class GF, class F>
void insert(X*, GF gen_factory, F inserter, test::random_generator rg)
template <class X, class G, class F>
void insert(X*, G gen, F inserter, test::random_generator rg)
{
disable_exceptions();
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_map =
boost::unordered_flat_map<raii, raii>(values.begin(), values.end());
raii::reset_counts();
{
@@ -419,15 +392,13 @@ namespace {
inserter(values, x);
test_fuzzy_matches_reference(x, reference_cont, rg);
test_fuzzy_matches_reference(x, reference_map, rg);
}
check_raii_counts();
}
boost::unordered::concurrent_flat_map<raii, raii, stateful_hash,
stateful_key_equal, stateful_allocator<std::pair<raii const, raii> > >* map;
boost::unordered::concurrent_flat_set<raii, stateful_hash,
stateful_key_equal, stateful_allocator<raii> >* set;
} // namespace
@@ -438,9 +409,8 @@ using test::sequential;
// clang-format off
UNORDERED_TEST(
insert,
((map)(set))
((exception_value_type_generator_factory)
(exception_init_type_generator_factory))
((map))
((exception_value_type_generator)(exception_init_type_generator))
((lvalue_inserter)(rvalue_inserter)(iterator_range_inserter)
(norehash_lvalue_inserter)(norehash_rvalue_inserter)
(lvalue_insert_or_cvisit)(lvalue_insert_or_visit)
@@ -451,7 +421,7 @@ UNORDERED_TEST(
UNORDERED_TEST(
insert,
((map))
((exception_init_type_generator_factory))
((exception_init_type_generator))
((lvalue_insert_or_assign_copy_assign)(lvalue_insert_or_assign_move_assign)
(rvalue_insert_or_assign_copy_assign)(rvalue_insert_or_assign_move_assign))
((default_generator)(sequential)(limited_range)))
+11 -21
View File
@@ -1,38 +1,29 @@
// Copyright (C) 2023 Christian Mazakas
// Copyright (C) 2023 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 "exception_helpers.hpp"
#include <boost/unordered/concurrent_flat_map.hpp>
#include <boost/unordered/concurrent_flat_set.hpp>
#include <boost/core/ignore_unused.hpp>
using allocator_type = stateful_allocator<std::pair<raii const, raii> >;
using hasher = stateful_hash;
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 set_type = boost::unordered::concurrent_flat_set<raii, hasher,
key_equal, stateful_allocator<raii> >;
map_type* test_map;
set_type* test_set;
key_equal, allocator_type>;
namespace {
test::seed_t initialize_seed(223333016);
template <class X, class GF>
void merge(X*, GF gen_factory, test::random_generator rg)
template <class G> void merge(G gen, test::random_generator rg)
{
using allocator_type = typename X::allocator_type;
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_map =
boost::unordered_flat_map<raii, raii>(values.begin(), values.end());
raii::reset_counts();
@@ -46,10 +37,10 @@ namespace {
for (unsigned i = 0; i < 5 * alloc_throw_threshold; ++i) {
disable_exceptions();
X x1(0, hasher(1), key_equal(2), allocator_type(3));
map_type x1(0, hasher(1), key_equal(2), allocator_type(3));
x1.insert(begin, mid);
X x2(0, hasher(2), key_equal(1), allocator_type(3));
map_type x2(0, hasher(2), key_equal(1), allocator_type(3));
x2.insert(mid, end);
enable_exceptions();
@@ -60,8 +51,8 @@ namespace {
}
disable_exceptions();
test_fuzzy_matches_reference(x1, reference_cont, rg);
test_fuzzy_matches_reference(x2, reference_cont, rg);
test_fuzzy_matches_reference(x1, reference_map, rg);
test_fuzzy_matches_reference(x2, reference_map, rg);
}
BOOST_TEST_GT(num_throws, 0u);
@@ -79,8 +70,7 @@ using test::sequential;
// clang-format off
UNORDERED_TEST(
merge,
((test_map)(test_set))
((exception_value_type_generator_factory))
((exception_value_type_generator))
((default_generator)(sequential)(limited_range)))
// clang-format on
+13 -67
View File
@@ -1,12 +1,10 @@
// Copyright (C) 2023 Christian Mazakas
// Copyright (C) 2023 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/config/workaround.hpp>
#include <boost/unordered/concurrent_flat_map_fwd.hpp>
#include <boost/unordered/concurrent_flat_set_fwd.hpp>
#include <limits>
test::seed_t initialize_seed{32304628};
@@ -36,89 +34,37 @@ bool unequal_call(boost::unordered::concurrent_flat_map<T, T>& x1,
return x1 != x2;
}
template <class T>
void swap_call(boost::unordered::concurrent_flat_set<T>& x1,
boost::unordered::concurrent_flat_set<T>& x2)
{
swap(x1, x2);
}
template <class T>
bool equal_call(boost::unordered::concurrent_flat_set<T>& x1,
boost::unordered::concurrent_flat_set<T>& x2)
{
return x1 == x2;
}
template <class T>
bool unequal_call(boost::unordered::concurrent_flat_set<T>& x1,
boost::unordered::concurrent_flat_set<T>& x2)
{
return x1 != x2;
}
#include <boost/unordered/concurrent_flat_map.hpp>
#include <boost/unordered/concurrent_flat_set.hpp>
using map_type = boost::unordered::concurrent_flat_map<int, int>;
using set_type = boost::unordered::concurrent_flat_map<int, int>;
map_type* test_map;
set_type* test_set;
template <typename X>
void fwd_swap_call(X*)
{
#if !defined(BOOST_CLANG_VERSION) || \
BOOST_WORKAROUND(BOOST_CLANG_VERSION, < 30700) || \
#if !defined(BOOST_CLANG_VERSION) || \
BOOST_WORKAROUND(BOOST_CLANG_VERSION, < 30700) || \
BOOST_WORKAROUND(BOOST_CLANG_VERSION, >= 30800)
// clang-3.7 seems to have a codegen bug here so we workaround it
X x1, x2;
UNORDERED_AUTO_TEST (fwd_swap_call) {
map_type x1, x2;
swap_call(x1, x2);
#endif
}
template <typename X>
void fwd_equal_call(X*)
{
X x1, x2;
#endif
UNORDERED_AUTO_TEST (fwd_equal_call) {
map_type x1, x2;
BOOST_TEST(equal_call(x1, x2));
}
template <typename X>
void fwd_unequal_call(X*)
{
X x1, x2;
UNORDERED_AUTO_TEST (fwd_unequal_call) {
map_type x1, x2;
BOOST_TEST_NOT(unequal_call(x1, x2));
}
// this isn't the best place for this test but it's better than introducing a
// new file
template <typename X>
void max_size(X*)
{
X x1;
UNORDERED_AUTO_TEST (max_size) {
map_type x1;
BOOST_TEST_EQ(
x1.max_size(), std::numeric_limits<typename X::size_type>::max());
x1.max_size(), std::numeric_limits<typename map_type::size_type>::max());
}
// clang-format off
UNORDERED_TEST(
fwd_swap_call,
((test_map)(test_set)))
UNORDERED_TEST(
fwd_equal_call,
((test_map)(test_set)))
UNORDERED_TEST(
fwd_unequal_call,
((test_map)(test_set)))
UNORDERED_TEST(
max_size,
((test_map)(test_set)))
// clang-format on
RUN_TESTS()
+36 -91
View File
@@ -1,5 +1,4 @@
// Copyright (C) 2023 Christian Mazakas
// Copyright (C) 2023 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,15 +7,11 @@
#include "../helpers/generators.hpp"
#include "../helpers/test.hpp"
#include "common_helpers.hpp"
#include <boost/compat/latch.hpp>
#include <boost/container_hash/hash.hpp>
#include <boost/core/span.hpp>
#include <boost/unordered/concurrent_flat_map_fwd.hpp>
#include <boost/unordered/concurrent_flat_set_fwd.hpp>
#include <boost/unordered/unordered_flat_map.hpp>
#include <boost/unordered/unordered_flat_set.hpp>
#include <algorithm>
#include <atomic>
@@ -128,27 +123,6 @@ struct stateful_key_equal
}
};
template <class T> struct cfoa_ptr
{
private:
template <class> friend struct stateful_allocator2;
T* p_ = nullptr;
cfoa_ptr(T* p) : p_(p) {}
public:
using element_type = T;
cfoa_ptr() = default;
cfoa_ptr(std::nullptr_t) : p_(nullptr){};
template <class U> using rebind = cfoa_ptr<U>;
T* operator->() const noexcept { return p_; }
static cfoa_ptr<T> pointer_to(element_type& r) { return {std::addressof(r)}; }
};
template <class T> struct stateful_allocator
{
int x_ = -1;
@@ -177,36 +151,6 @@ template <class T> struct stateful_allocator
bool operator!=(stateful_allocator const& rhs) const { return x_ != rhs.x_; }
};
template <class T> struct stateful_allocator2
{
int x_ = -1;
using value_type = T;
using pointer = cfoa_ptr<T>;
stateful_allocator2() = default;
stateful_allocator2(stateful_allocator2 const&) = default;
stateful_allocator2(stateful_allocator2&&) = default;
stateful_allocator2(int const x) : x_{x} {}
template <class U>
stateful_allocator2(stateful_allocator2<U> const& rhs) : x_{rhs.x_}
{
}
pointer allocate(std::size_t n)
{
return {static_cast<T*>(::operator new(n * sizeof(T)))};
}
void deallocate(pointer p, std::size_t) { ::operator delete(p.p_); }
bool operator==(stateful_allocator2 const& rhs) const { return x_ == rhs.x_; }
bool operator!=(stateful_allocator2 const& rhs) const { return x_ != rhs.x_; }
};
struct raii
{
static std::atomic<std::uint32_t> default_constructor;
@@ -333,48 +277,27 @@ auto make_random_values(std::size_t count, F f) -> std::vector<decltype(f())>
return v;
}
template <typename K>
struct value_generator
struct value_type_generator_type
{
using value_type = raii;
value_type operator()(test::random_generator rg)
{
int* p = nullptr;
int a = generate(p, rg);
return value_type(a);
}
};
template <typename K, typename V>
struct value_generator<std::pair<K, V> >
{
static constexpr bool const_key = std::is_const<K>::value;
static constexpr bool const_mapped = std::is_const<V>::value;
using value_type = std::pair<
typename std::conditional<const_key, raii const, raii>::type,
typename std::conditional<const_mapped, raii const, raii>::type>;
value_type operator()(test::random_generator rg)
std::pair<raii const, raii> operator()(test::random_generator rg)
{
int* p = nullptr;
int a = generate(p, rg);
int b = generate(p, rg);
return std::make_pair(raii{a}, raii{b});
}
};
} value_type_generator;
struct value_type_generator_factory_type
struct init_type_generator_type
{
template <typename Container>
value_generator<typename Container::value_type> get() { return {}; }
} value_type_generator_factory;
struct init_type_generator_factory_type
{
template <typename Container>
value_generator<typename Container::init_type> get() { return {}; }
} init_type_generator_factory;
std::pair<raii, raii> operator()(test::random_generator rg)
{
int* p = nullptr;
int a = generate(p, rg);
int b = generate(p, rg);
return std::make_pair(raii{a}, raii{b});
}
} init_type_generator;
template <class T>
std::vector<boost::span<T> > split(
@@ -434,6 +357,29 @@ template <class T, class F> void thread_runner(std::vector<T>& values, F f)
}
}
template <class X, class Y>
void test_matches_reference(X const& x, Y const& reference_map)
{
using value_type = typename X::value_type;
BOOST_TEST_EQ(x.size(), x.visit_all([&](value_type const& kv) {
BOOST_TEST(reference_map.contains(kv.first));
BOOST_TEST_EQ(kv.second, reference_map.find(kv.first)->second);
}));
}
template <class X, class Y>
void test_fuzzy_matches_reference(
X const& x, Y const& reference_map, test::random_generator rg)
{
using value_type = typename X::value_type;
BOOST_TEST_EQ(x.size(), x.visit_all([&](value_type const& kv) {
BOOST_TEST(reference_map.contains(kv.first));
if (rg == test::sequential) {
BOOST_TEST_EQ(kv.second, reference_map.find(kv.first)->second);
}
}));
}
template <class T> using span_value_type = typename T::value_type;
void check_raii_counts()
@@ -512,7 +458,6 @@ template <class T> class ptr
public:
ptr() : ptr_(0) {}
ptr(std::nullptr_t) : ptr_(nullptr) {}
explicit ptr(void_ptr const& x) : ptr_((T*)x.ptr_) {}
T& operator*() const { return *ptr_; }
@@ -645,4 +590,4 @@ public:
fancy_allocator& operator=(fancy_allocator const&) { return *this; }
};
#endif // BOOST_UNORDERED_TEST_CFOA_HELPERS_HPP
#endif // BOOST_UNORDERED_TEST_CFOA_HELPERS_HPP
+84 -213
View File
@@ -1,32 +1,18 @@
// Copyright (C) 2023 Christian Mazakas
// Copyright (C) 2023 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/config.hpp>
#include <boost/unordered/concurrent_flat_map.hpp>
#include <boost/unordered/concurrent_flat_set.hpp>
#include <boost/core/ignore_unused.hpp>
#if defined(BOOST_MSVC)
#pragma warning(disable : 4127) // conditional expression is constant
#endif
struct raii_convertible
{
int x = 0, y = 0 ;
int x, y;
raii_convertible(int x_, int y_) : x{x_}, y{y_} {}
template <typename T>
raii_convertible(T const & t) : x{t.x_} {}
template <typename T, typename Q>
raii_convertible(std::pair<T, Q> const & p) : x{p.first.x_}, y{p.second.x_}
{}
operator raii() { return {x}; }
operator std::pair<raii const, raii>() { return {x, y}; }
};
@@ -37,9 +23,6 @@ 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;
std::atomic<std::uint64_t> num_inserts{0};
thread_runner(values, [&x, &num_inserts](boost::span<T> s) {
for (auto const& r : s) {
@@ -50,8 +33,7 @@ namespace {
}
});
BOOST_TEST_EQ(num_inserts, x.size());
BOOST_TEST_EQ(
raii::copy_constructor, value_type_cardinality * x.size());
BOOST_TEST_EQ(raii::copy_constructor, 2 * x.size());
BOOST_TEST_EQ(raii::copy_assignment, 0u);
BOOST_TEST_EQ(raii::move_assignment, 0u);
}
@@ -61,13 +43,9 @@ 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;
x.reserve(values.size());
lvalue_inserter_type::operator()(values, x);
BOOST_TEST_EQ(
raii::copy_constructor, value_type_cardinality * x.size());
BOOST_TEST_EQ(raii::copy_constructor, 2 * x.size());
BOOST_TEST_EQ(raii::move_constructor, 0u);
}
} norehash_lvalue_inserter;
@@ -89,8 +67,7 @@ namespace {
});
BOOST_TEST_EQ(num_inserts, x.size());
if (std::is_same<T, typename X::value_type>::value &&
!std::is_same<typename X::key_type, typename X::value_type>::value) {
if (std::is_same<T, typename X::value_type>::value) {
BOOST_TEST_EQ(raii::copy_constructor, x.size());
} else {
BOOST_TEST_EQ(raii::copy_constructor, 0u);
@@ -105,9 +82,6 @@ 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;
x.reserve(values.size());
BOOST_TEST_EQ(raii::copy_constructor, 0u);
@@ -116,19 +90,11 @@ namespace {
rvalue_inserter_type::operator()(values, x);
if (std::is_same<T, typename X::value_type>::value) {
if (std::is_same<typename X::key_type,
typename X::value_type>::value) {
BOOST_TEST_EQ(raii::copy_constructor, 0u);
BOOST_TEST_EQ(raii::move_constructor, x.size());
}
else {
BOOST_TEST_EQ(raii::copy_constructor, x.size());
BOOST_TEST_EQ(raii::move_constructor, x.size());
}
BOOST_TEST_EQ(raii::copy_constructor, x.size());
BOOST_TEST_EQ(raii::move_constructor, x.size());
} else {
BOOST_TEST_EQ(raii::copy_constructor, 0u);
BOOST_TEST_EQ(
raii::move_constructor, value_type_cardinality * x.size());
BOOST_TEST_EQ(raii::move_constructor, 2 * x.size());
}
}
} norehash_rvalue_inserter;
@@ -137,21 +103,17 @@ 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;
std::vector<raii_convertible> values2;
values2.reserve(values.size());
for (auto const& v : values) {
values2.push_back(raii_convertible(v));
for (auto const& p : values) {
values2.push_back(raii_convertible(p.first.x_, p.second.x_));
}
thread_runner(values2, [&x](boost::span<raii_convertible> s) {
x.insert(s.begin(), s.end());
});
BOOST_TEST_EQ(
raii::default_constructor, value_type_cardinality * values2.size());
BOOST_TEST_EQ(raii::default_constructor, 2 * values2.size());
#if BOOST_WORKAROUND(BOOST_GCC_VERSION, >= 50300) && \
BOOST_WORKAROUND(BOOST_GCC_VERSION, < 50500)
// some versions of old gcc have trouble eliding copies here
@@ -291,9 +253,6 @@ 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;
std::atomic<std::uint64_t> num_inserts{0};
std::atomic<std::uint64_t> num_invokes{0};
thread_runner(values, [&x, &num_inserts, &num_invokes](boost::span<T> s) {
@@ -314,8 +273,7 @@ namespace {
BOOST_TEST_EQ(num_invokes, values.size() - x.size());
BOOST_TEST_EQ(raii::default_constructor, 0u);
BOOST_TEST_EQ(
raii::copy_constructor, value_type_cardinality * x.size());
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);
BOOST_TEST_EQ(raii::move_assignment, 0u);
@@ -326,22 +284,12 @@ 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;
// concurrent_flat_set visit is always const access
using arg_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;
std::atomic<std::uint64_t> num_inserts{0};
std::atomic<std::uint64_t> num_invokes{0};
thread_runner(values, [&x, &num_inserts, &num_invokes](boost::span<T> s) {
for (auto& r : s) {
bool b =
x.insert_or_visit(r, [&num_invokes](arg_type& v) {
x.insert_or_visit(r, [&num_invokes](typename X::value_type& v) {
(void)v;
++num_invokes;
});
@@ -356,7 +304,7 @@ namespace {
BOOST_TEST_EQ(num_invokes, values.size() - x.size());
BOOST_TEST_EQ(raii::default_constructor, 0u);
BOOST_TEST_EQ(raii::copy_constructor, value_type_cardinality * x.size());
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);
BOOST_TEST_EQ(raii::move_assignment, 0u);
@@ -367,9 +315,6 @@ 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;
std::atomic<std::uint64_t> num_inserts{0};
std::atomic<std::uint64_t> num_invokes{0};
thread_runner(values, [&x, &num_inserts, &num_invokes](boost::span<T> s) {
@@ -392,19 +337,11 @@ namespace {
BOOST_TEST_EQ(raii::default_constructor, 0u);
if (std::is_same<T, typename X::value_type>::value) {
if (std::is_same<typename X::key_type,
typename X::value_type>::value) {
BOOST_TEST_EQ(raii::copy_constructor, 0u);
BOOST_TEST_GE(raii::move_constructor, x.size());
}
else {
BOOST_TEST_EQ(raii::copy_constructor, x.size());
BOOST_TEST_GE(raii::move_constructor, x.size());
}
BOOST_TEST_EQ(raii::copy_constructor, x.size());
BOOST_TEST_GE(raii::move_constructor, x.size());
} else {
BOOST_TEST_EQ(raii::copy_constructor, 0u);
BOOST_TEST_GE(
raii::move_constructor, value_type_cardinality * x.size());
BOOST_TEST_GE(raii::move_constructor, 2 * x.size());
}
}
} rvalue_insert_or_cvisit;
@@ -413,22 +350,12 @@ 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;
// concurrent_flat_set visit is always const access
using arg_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;
std::atomic<std::uint64_t> num_inserts{0};
std::atomic<std::uint64_t> num_invokes{0};
thread_runner(values, [&x, &num_inserts, &num_invokes](boost::span<T> s) {
for (auto& r : s) {
bool b = x.insert_or_visit(
std::move(r), [&num_invokes](arg_type& v) {
std::move(r), [&num_invokes](typename X::value_type& v) {
(void)v;
++num_invokes;
});
@@ -444,19 +371,11 @@ namespace {
BOOST_TEST_EQ(raii::default_constructor, 0u);
if (std::is_same<T, typename X::value_type>::value) {
if (std::is_same<typename X::key_type,
typename X::value_type>::value) {
BOOST_TEST_EQ(raii::copy_constructor, 0u);
BOOST_TEST_GE(raii::move_constructor, x.size());
}
else {
BOOST_TEST_EQ(raii::copy_constructor, x.size());
BOOST_TEST_GE(raii::move_constructor, x.size());
}
BOOST_TEST_EQ(raii::copy_constructor, x.size());
BOOST_TEST_GE(raii::move_constructor, x.size());
} else {
BOOST_TEST_EQ(raii::copy_constructor, 0u);
BOOST_TEST_GE(
raii::move_constructor, value_type_cardinality * x.size());
BOOST_TEST_GE(raii::move_constructor, 2 * x.size());
}
}
} rvalue_insert_or_visit;
@@ -465,13 +384,10 @@ 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;
std::vector<raii_convertible> values2;
values2.reserve(values.size());
for (auto const& v : values) {
values2.push_back(raii_convertible(v));
for (auto const& p : values) {
values2.push_back(raii_convertible(p.first.x_, p.second.x_));
}
std::atomic<std::uint64_t> num_invokes{0};
@@ -486,8 +402,7 @@ namespace {
BOOST_TEST_EQ(num_invokes, values.size() - x.size());
BOOST_TEST_EQ(
raii::default_constructor, value_type_cardinality * values2.size());
BOOST_TEST_EQ(raii::default_constructor, 2 * values2.size());
#if BOOST_WORKAROUND(BOOST_GCC_VERSION, >= 50300) && \
BOOST_WORKAROUND(BOOST_GCC_VERSION, < 50500)
// skip test
@@ -502,13 +417,10 @@ 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;
std::vector<raii_convertible> values2;
values2.reserve(values.size());
for (auto const& v : values) {
values2.push_back(raii_convertible(v));
for (auto const& p : values) {
values2.push_back(raii_convertible(p.first.x_, p.second.x_));
}
std::atomic<std::uint64_t> num_invokes{0};
@@ -523,8 +435,7 @@ namespace {
BOOST_TEST_EQ(num_invokes, values.size() - x.size());
BOOST_TEST_EQ(
raii::default_constructor, value_type_cardinality * values2.size());
BOOST_TEST_EQ(raii::default_constructor, 2 * values2.size());
#if BOOST_WORKAROUND(BOOST_GCC_VERSION, >= 50300) && \
BOOST_WORKAROUND(BOOST_GCC_VERSION, < 50500)
// skip test
@@ -535,12 +446,12 @@ namespace {
}
} iterator_range_insert_or_visit;
template <class X, class GF, class F>
void insert(X*, GF gen_factory, F inserter, test::random_generator rg)
template <class X, class G, class F>
void insert(X*, G gen, F inserter, test::random_generator rg)
{
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_map =
boost::unordered_flat_map<raii, raii>(values.begin(), values.end());
raii::reset_counts();
{
@@ -548,13 +459,13 @@ namespace {
inserter(values, x);
BOOST_TEST_EQ(x.size(), reference_cont.size());
BOOST_TEST_EQ(x.size(), reference_map.size());
using value_type = typename X::value_type;
BOOST_TEST_EQ(x.size(), x.visit_all([&](value_type const& v) {
BOOST_TEST(reference_cont.contains(get_key(v)));
BOOST_TEST_EQ(x.size(), x.visit_all([&](value_type const& kv) {
BOOST_TEST(reference_map.contains(kv.first));
if (rg == test::sequential) {
BOOST_TEST_EQ(v, *reference_cont.find(get_key(v)));
BOOST_TEST_EQ(kv.second, reference_map[kv.first]);
}
}));
}
@@ -569,21 +480,39 @@ namespace {
raii::destructor);
}
template <class X, class IL>
void insert_initializer_list(std::pair<X*, IL> p)
template <class X> void insert_initializer_list(X*)
{
using value_type = typename X::value_type;
// concurrent_flat_set visit is always const access
using arg_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;
auto init_list = p.second;
std::initializer_list<value_type> values{
value_type{raii{0}, raii{0}},
value_type{raii{1}, raii{1}},
value_type{raii{2}, raii{2}},
value_type{raii{3}, raii{3}},
value_type{raii{4}, raii{4}},
value_type{raii{5}, raii{5}},
value_type{raii{6}, raii{6}},
value_type{raii{6}, raii{6}},
value_type{raii{7}, raii{7}},
value_type{raii{8}, raii{8}},
value_type{raii{9}, raii{9}},
value_type{raii{10}, raii{10}},
value_type{raii{9}, raii{9}},
value_type{raii{8}, raii{8}},
value_type{raii{7}, raii{7}},
value_type{raii{6}, raii{6}},
value_type{raii{5}, raii{5}},
value_type{raii{4}, raii{4}},
value_type{raii{3}, raii{3}},
value_type{raii{2}, raii{2}},
value_type{raii{1}, raii{1}},
value_type{raii{0}, raii{0}},
};
std::vector<raii> dummy;
auto reference_cont = reference_container<X>(
init_list.begin(), init_list.end());
auto reference_map =
boost::unordered_flat_map<raii, raii>(values.begin(), values.end());
raii::reset_counts();
{
@@ -591,13 +520,13 @@ namespace {
X x;
thread_runner(
dummy, [&x, &init_list](boost::span<raii>) { x.insert(init_list); });
dummy, [&x, &values](boost::span<raii>) { x.insert(values); });
BOOST_TEST_EQ(x.size(), reference_cont.size());
BOOST_TEST_EQ(x.size(), reference_map.size());
BOOST_TEST_EQ(x.size(), x.visit_all([&](value_type const& v) {
BOOST_TEST(reference_cont.contains(get_key(v)));
BOOST_TEST_EQ(v, *reference_cont.find(get_key(v)));
BOOST_TEST_EQ(x.size(), x.visit_all([&](value_type const& kv) {
BOOST_TEST(reference_map.contains(kv.first));
BOOST_TEST_EQ(kv.second, reference_map[kv.first]);
}));
}
@@ -620,27 +549,27 @@ namespace {
X x;
thread_runner(dummy, [&x, &init_list, &num_invokes](boost::span<raii>) {
x.insert_or_visit(init_list, [&num_invokes](arg_type& v) {
thread_runner(dummy, [&x, &values, &num_invokes](boost::span<raii>) {
x.insert_or_visit(values, [&num_invokes](typename X::value_type& v) {
(void)v;
++num_invokes;
});
x.insert_or_cvisit(
init_list, [&num_invokes](typename X::value_type const& v) {
values, [&num_invokes](typename X::value_type const& v) {
(void)v;
++num_invokes;
});
});
BOOST_TEST_EQ(num_invokes, (init_list.size() - x.size()) +
(num_threads - 1) * init_list.size() +
num_threads * init_list.size());
BOOST_TEST_EQ(x.size(), reference_cont.size());
BOOST_TEST_EQ(num_invokes, (values.size() - x.size()) +
(num_threads - 1) * values.size() +
num_threads * values.size());
BOOST_TEST_EQ(x.size(), reference_map.size());
BOOST_TEST_EQ(x.size(), x.visit_all([&](value_type const& v) {
BOOST_TEST(reference_cont.contains(get_key(v)));
BOOST_TEST_EQ(v, *reference_cont.find(get_key(v)));
BOOST_TEST_EQ(x.size(), x.visit_all([&](value_type const& kv) {
BOOST_TEST(reference_map.contains(kv.first));
BOOST_TEST_EQ(kv.second, reference_map[kv.first]);
}));
}
@@ -677,64 +606,6 @@ namespace {
std::equal_to<raii>, fancy_allocator<std::pair<raii const, raii> > >*
fancy_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> > >*
fancy_set;
std::initializer_list<std::pair<raii const, raii> > map_init_list{
{raii{0}, raii{0}},
{raii{1}, raii{1}},
{raii{2}, raii{2}},
{raii{3}, raii{3}},
{raii{4}, raii{4}},
{raii{5}, raii{5}},
{raii{6}, raii{6}},
{raii{6}, raii{6}},
{raii{7}, raii{7}},
{raii{8}, raii{8}},
{raii{9}, raii{9}},
{raii{10}, raii{10}},
{raii{9}, raii{9}},
{raii{8}, raii{8}},
{raii{7}, raii{7}},
{raii{6}, raii{6}},
{raii{5}, raii{5}},
{raii{4}, raii{4}},
{raii{3}, raii{3}},
{raii{2}, raii{2}},
{raii{1}, raii{1}},
{raii{0}, raii{0}},
};
std::initializer_list<raii> set_init_list{
raii{0},
raii{1},
raii{2},
raii{3},
raii{4},
raii{5},
raii{6},
raii{6},
raii{7},
raii{8},
raii{9},
raii{10},
raii{9},
raii{8},
raii{7},
raii{6},
raii{5},
raii{4},
raii{3},
raii{2},
raii{1},
raii{0},
};
auto map_and_init_list=std::make_pair(map,map_init_list);
auto set_and_init_list=std::make_pair(set,set_init_list);
} // namespace
using test::default_generator;
@@ -744,12 +615,12 @@ using test::sequential;
// clang-format off
UNORDERED_TEST(
insert_initializer_list,
((map_and_init_list)(set_and_init_list)))
((map)))
UNORDERED_TEST(
insert,
((map)(fancy_map)(set)(fancy_set))
((value_type_generator_factory)(init_type_generator_factory))
((map)(fancy_map))
((value_type_generator)(init_type_generator))
((lvalue_inserter)(rvalue_inserter)(iterator_range_inserter)
(norehash_lvalue_inserter)(norehash_rvalue_inserter)
(lvalue_insert_or_cvisit)(lvalue_insert_or_visit)
@@ -760,7 +631,7 @@ UNORDERED_TEST(
UNORDERED_TEST(
insert,
((map))
((init_type_generator_factory))
((init_type_generator))
((lvalue_insert_or_assign_copy_assign)(lvalue_insert_or_assign_move_assign)
(rvalue_insert_or_assign_copy_assign)(rvalue_insert_or_assign_move_assign))
((default_generator)(sequential)(limited_range)))
@@ -768,7 +639,7 @@ UNORDERED_TEST(
UNORDERED_TEST(
insert,
((trans_map))
((init_type_generator_factory))
((init_type_generator))
((trans_insert_or_assign_copy_assign)(trans_insert_or_assign_move_assign))
((default_generator)(sequential)(limited_range)))
// clang-format on
+32 -57
View File
@@ -1,12 +1,10 @@
// Copyright (C) 2023 Christian Mazakas
// Copyright (C) 2023 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_flat_set.hpp>
test::seed_t initialize_seed{402031699};
@@ -16,25 +14,12 @@ using test::sequential;
using hasher = stateful_hash;
using key_equal = stateful_key_equal;
using allocator_type = stateful_allocator<std::pair<raii const, raii> >;
using map_type = boost::unordered::concurrent_flat_map<raii, raii,
hasher, key_equal, stateful_allocator<std::pair<raii const, raii> > >;
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 map_type = boost::unordered::concurrent_flat_map<raii, raii, hasher,
key_equal, allocator_type>;
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> >;
map_type* test_map;
map2_type* test_map2;
auto test_maps=std::make_pair(test_map,test_map2);
set_type* test_set;
set2_type* test_set2;
auto test_sets=std::make_pair(test_set,test_set2);
using map_value_type = typename map_type::value_type;
struct
{
@@ -55,23 +40,18 @@ struct
} rvalue_merge;
namespace {
template <typename X, typename Y, class F, class GF>
void merge_tests(
std::pair<X*, Y*>, F merger, GF gen_factory, test::random_generator rg)
template <class F, class G>
void merge_tests(F merger, G gen, test::random_generator rg)
{
using value_type = typename X::value_type;
static constexpr auto value_type_cardinality =
value_cardinality<value_type>::value;
using allocator_type = typename X::allocator_type;
auto gen = gen_factory.template get<X>();
auto values = make_random_values(1024 * 8, [&] { return gen(rg); });
auto reference_cont = reference_container<X>(values.begin(), values.end());
auto ref_map =
boost::unordered_flat_map<raii, raii>(values.begin(), values.end());
{
raii::reset_counts();
X x(values.size(), hasher(1), key_equal(2), allocator_type(3));
map_type x(values.size(), hasher(1), key_equal(2), allocator_type(3));
auto const old_cc = +raii::copy_constructor;
@@ -79,50 +59,48 @@ namespace {
std::atomic<unsigned long long> num_merged{0};
thread_runner(values, [&x, &expected_copies, &num_merged, merger](
boost::span<value_type> s) {
Y y(s.size(), allocator_type(3));
boost::span<map_value_type> s) {
using map2_type = boost::unordered::concurrent_flat_map<raii, raii,
std::hash<raii>, std::equal_to<raii>, allocator_type>;
map2_type y(s.size(), allocator_type(3));
for (auto const& v : s) {
y.insert(v);
}
expected_copies += value_type_cardinality * y.size();
expected_copies += 2 * y.size();
BOOST_TEST(x.get_allocator() == y.get_allocator());
num_merged += merger(x, y);
});
BOOST_TEST_EQ(raii::copy_constructor, old_cc + expected_copies);
BOOST_TEST_EQ(
raii::move_constructor,
value_type_cardinality * reference_cont.size());
BOOST_TEST_EQ(+num_merged, reference_cont.size());
BOOST_TEST_EQ(raii::move_constructor, 2 * ref_map.size());
BOOST_TEST_EQ(+num_merged, ref_map.size());
test_fuzzy_matches_reference(x, reference_cont, rg);
test_fuzzy_matches_reference(x, ref_map, rg);
}
check_raii_counts();
}
template <typename X, typename Y, class GF>
void insert_and_merge_tests(
std::pair<X*, Y*>, GF gen_factory, test::random_generator rg)
template <class G>
void insert_and_merge_tests(G gen, test::random_generator rg)
{
static constexpr auto value_type_cardinality =
value_cardinality<typename X::value_type>::value;
using allocator_type = typename X::allocator_type;
using map2_type = boost::unordered::concurrent_flat_map<raii, raii,
std::hash<raii>, std::equal_to<raii>, allocator_type>;
auto gen = gen_factory.template get<X>();
auto vals1 = make_random_values(1024 * 8, [&] { return gen(rg); });
auto vals2 = make_random_values(1024 * 4, [&] { return gen(rg); });
auto reference_cont = reference_container<X>();
reference_cont.insert(vals1.begin(), vals1.end());
reference_cont.insert(vals2.begin(), vals2.end());
auto ref_map = boost::unordered_flat_map<raii, raii>();
ref_map.insert(vals1.begin(), vals1.end());
ref_map.insert(vals2.begin(), vals2.end());
{
raii::reset_counts();
X x1(2 * vals1.size(), hasher(1), key_equal(2), allocator_type(3));
map_type x1(2 * vals1.size(), hasher(1), key_equal(2), allocator_type(3));
Y x2(2 * vals1.size(), allocator_type(3));
map2_type x2(2 * vals1.size(), allocator_type(3));
std::thread t1, t2, t3;
boost::compat::latch l(2);
@@ -212,13 +190,12 @@ namespace {
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);
BOOST_TEST_EQ(+raii::move_constructor, 2 * num_merges);
BOOST_TEST_GE(call_count, 1u);
}
x1.merge(x2);
test_fuzzy_matches_reference(x1, reference_cont, rg);
test_fuzzy_matches_reference(x1, ref_map, rg);
}
check_raii_counts();
@@ -229,15 +206,13 @@ namespace {
// clang-format off
UNORDERED_TEST(
merge_tests,
((test_maps)(test_sets))
((lvalue_merge)(rvalue_merge))
((value_type_generator_factory))
((value_type_generator))
((default_generator)(sequential)(limited_range)))
UNORDERED_TEST(
insert_and_merge_tests,
((test_maps)(test_sets))
((value_type_generator_factory))
((value_type_generator))
((default_generator)(sequential)(limited_range)))
// clang-format on
-113
View File
@@ -1,113 +0,0 @@
// Copyright 2023 Joaquin M Lopez Munoz
// Distributed under the Boost Software License, Version 1.0.
// https://www.boost.org/LICENSE_1_0.txt
#include <cstdlib>
#define BOOST_ENABLE_ASSERT_HANDLER
static bool reentrancy_detected = false;
namespace boost {
// Caveat lector: a proper handler shouldn't throw as it may be executed
// within a noexcept function.
void assertion_failed_msg(
char const*, char const*, char const*, char const*, long)
{
reentrancy_detected = true;
throw 0;
}
// LCOV_EXCL_START
void assertion_failed(char const*, char const*, char const*, long)
{
std::abort();
}
// LCOV_EXCL_STOP
} // namespace boost
#include "helpers.hpp"
#include <boost/unordered/concurrent_flat_map.hpp>
#include <boost/unordered/concurrent_flat_set.hpp>
#include <boost/core/lightweight_test.hpp>
using test::default_generator;
using map_type = boost::unordered::concurrent_flat_map<raii, raii>;
using set_type = boost::unordered::concurrent_flat_set<raii>;
map_type* test_map;
set_type* test_set;
template<typename F>
void detect_reentrancy(F f)
{
reentrancy_detected = false;
try {
f();
}
catch(int) {}
BOOST_TEST(reentrancy_detected);
}
namespace {
template <class X, class GF>
void reentrancy_tests(X*, GF gen_factory, test::random_generator rg)
{
using key_type = typename X::key_type;
// concurrent_flat_set visit is always const access
using arg_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;
auto gen = gen_factory.template get<X>();
auto values = make_random_values(1024 * 16, [&] { return gen(rg); });
X x1, x2;
x1.insert(values.begin(), values.end());
x2.insert(values.begin(), values.end());
detect_reentrancy([&] {
x1.visit_all([&](arg_type&) { (void)x1.contains(key_type()); });
}); // LCOV_EXCL_LINE
detect_reentrancy([&] {
x1.visit_all([&](arg_type&) { x1.rehash(0); });
}); // LCOV_EXCL_LINE
detect_reentrancy([&] {
x1.visit_all([&](arg_type&) {
x2.visit_all([&](arg_type&) {
x1=x2;
}); // LCOV_EXCL_START
});
});
// LCOV_EXCL_STOP
detect_reentrancy([&] {
x1.visit_all([&](arg_type&) {
x2.visit_all([&](arg_type&) {
x2=x1;
}); // LCOV_EXCL_START
});
});
// LCOV_EXCL_STOP
}
} // namespace
// clang-format off
UNORDERED_TEST(
reentrancy_tests,
((test_map)(test_set))
((value_type_generator_factory))
((default_generator)))
// clang-format on
RUN_TESTS()
+16 -41
View File
@@ -1,12 +1,10 @@
// Copyright (C) 2023 Christian Mazakas
// Copyright (C) 2023 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_flat_set.hpp>
using test::default_generator;
using test::limited_range;
@@ -14,25 +12,18 @@ using test::sequential;
using hasher = stateful_hash;
using key_equal = stateful_key_equal;
using allocator_type = stateful_allocator<std::pair<raii const, raii> >;
using map_type = boost::unordered::concurrent_flat_map<raii, raii, hasher,
key_equal, stateful_allocator<std::pair<raii const, raii> > >;
key_equal, allocator_type>;
using set_type = boost::unordered::concurrent_flat_set<raii, hasher,
key_equal, stateful_allocator<raii> >;
map_type* test_map;
set_type* test_set;
using map_value_type = typename map_type::value_type;
namespace {
test::seed_t initialize_seed{748775921};
template <typename X>
void rehash_no_insert(X*)
{
using allocator_type = typename X::allocator_type;
X x(0, hasher(1), key_equal(2), allocator_type(3));
UNORDERED_AUTO_TEST (rehash_no_insert) {
map_type x(0, hasher(1), key_equal(2), allocator_type(3));
BOOST_TEST_EQ(x.bucket_count(), 0u);
x.rehash(1024);
@@ -46,13 +37,10 @@ namespace {
BOOST_TEST_EQ(x.bucket_count(), 0u);
}
template <typename X>
void reserve_no_insert(X*)
{
using allocator_type = typename X::allocator_type;
using size_type = typename X::size_type;
UNORDERED_AUTO_TEST (reserve_no_insert) {
using size_type = map_type::size_type;
X x(0, hasher(1), key_equal(2), allocator_type(3));
map_type x(0, hasher(1), key_equal(2), allocator_type(3));
auto f = [&x](double c) {
return static_cast<size_type>(std::ceil(c / x.max_load_factor()));
@@ -71,13 +59,9 @@ namespace {
BOOST_TEST_EQ(x.bucket_count(), f(0.0));
}
template <class X, class GF>
void insert_and_erase_with_rehash(
X*, GF gen_factory, test::random_generator rg)
template <class G>
void insert_and_erase_with_rehash(G gen, test::random_generator rg)
{
using allocator_type = typename X::allocator_type;
auto gen = gen_factory.template get<X>();
auto vals1 = make_random_values(1024 * 8, [&] { return gen(rg); });
auto erase_indices = std::vector<std::size_t>(vals1.size());
@@ -86,13 +70,13 @@ namespace {
}
shuffle_values(erase_indices);
auto reference_cont = reference_container<X>();
reference_cont.insert(vals1.begin(), vals1.end());
auto ref_map = boost::unordered_flat_map<raii, raii>();
ref_map.insert(vals1.begin(), vals1.end());
{
raii::reset_counts();
X x(0, hasher(1), key_equal(2), allocator_type(3));
map_type x(0, hasher(1), key_equal(2), allocator_type(3));
std::thread t1, t2, t3;
boost::compat::latch l(2);
@@ -137,7 +121,7 @@ namespace {
for (std::size_t idx = 0; idx < erase_indices.size(); ++idx) {
auto const& val = vals1[erase_indices[idx]];
x.erase(get_key(val));
x.erase(val.first);
if (idx % 100 == 0) {
std::this_thread::yield();
}
@@ -177,7 +161,7 @@ namespace {
BOOST_TEST_GE(call_count, 1u);
test_fuzzy_matches_reference(x, reference_cont, rg);
test_fuzzy_matches_reference(x, ref_map, rg);
}
check_raii_counts();
@@ -185,18 +169,9 @@ namespace {
} // namespace
// clang-format off
UNORDERED_TEST(
rehash_no_insert,
((test_map)(test_set)))
UNORDERED_TEST(
reserve_no_insert,
((test_map)(test_set)))
UNORDERED_TEST(
insert_and_erase_with_rehash,
((test_map)(test_set))
((value_type_generator_factory))
((value_type_generator))
((default_generator)(sequential)(limited_range)))
// clang-format on
-82
View File
@@ -1,82 +0,0 @@
// Copyright (C) 2023 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 "../objects/test.hpp"
#include "../helpers/random_values.hpp"
#include <boost/archive/text_oarchive.hpp>
#include <boost/archive/text_iarchive.hpp>
#include <boost/archive/xml_oarchive.hpp>
#include <boost/archive/xml_iarchive.hpp>
#include <boost/serialization/nvp.hpp>
#include <boost/unordered/concurrent_flat_map.hpp>
#include <boost/unordered/concurrent_flat_set.hpp>
namespace {
template <class Container, typename ArchivePair>
void serialization_tests(
Container*, ArchivePair*, test::random_generator generator)
{
using output_archive = typename ArchivePair::first_type ;
using input_archive = typename ArchivePair::second_type;
BOOST_LIGHTWEIGHT_TEST_OSTREAM << "serialization_tests1\n";
{
Container c;
std::ostringstream oss;
{
output_archive oa(oss);
oa << boost::serialization::make_nvp("container", c);
}
test::random_values<Container> values(100, generator);
Container c2(values.begin(), values.end());
std::istringstream iss(oss.str());
input_archive ia(iss);
ia >> boost::serialization::make_nvp("container", c2);
BOOST_TEST(c2.empty());
}
BOOST_LIGHTWEIGHT_TEST_OSTREAM << "serialization_tests2\n";
{
test::random_values<Container> values(100, generator);
Container c(values.begin(), values.end());
std::ostringstream oss;
{
output_archive oa(oss);
oa << boost::serialization::make_nvp("container", c);
}
Container c2;
std::istringstream iss(oss.str());
input_archive ia(iss);
ia >> boost::serialization::make_nvp("container", c2);
BOOST_TEST(c == c2);
}
}
using test::default_generator;
std::pair<
boost::archive::text_oarchive, boost::archive::text_iarchive>*
text_archive;
std::pair<
boost::archive::xml_oarchive, boost::archive::xml_iarchive>*
xml_archive;
boost::concurrent_flat_map<
test::object, test::object, test::hash, test::equal_to>* test_flat_map;
boost::concurrent_flat_set<
test::object, test::hash, test::equal_to>* test_flat_set;
UNORDERED_TEST(serialization_tests,
((test_flat_map)(test_flat_set))
((text_archive)(xml_archive))
((default_generator)))
}
RUN_TESTS()
+41 -54
View File
@@ -1,12 +1,10 @@
// Copyright (C) 2023 Christian Mazakas
// Copyright (C) 2023 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_flat_set.hpp>
test::seed_t initialize_seed{996130204};
@@ -58,12 +56,17 @@ template <class T> struct pocs_allocator
using hasher = stateful_hash;
using key_equal = stateful_key_equal;
using allocator_type = stateful_allocator<std::pair<raii const, raii> >;
using map_type = boost::unordered::concurrent_flat_map<raii, raii, hasher,
key_equal, stateful_allocator<std::pair<raii const, raii> > >;
key_equal, allocator_type>;
using set_type = boost::unordered::concurrent_flat_set<raii, hasher,
key_equal, stateful_allocator<raii> >;
using map_value_type = typename map_type::value_type;
using pocs_allocator_type = pocs_allocator<std::pair<const raii, raii> >;
using pocs_map_type = boost::unordered::concurrent_flat_map<raii, raii, hasher,
key_equal, pocs_allocator_type>;
template <class T> struct is_nothrow_member_swappable
{
@@ -72,21 +75,13 @@ template <class T> struct is_nothrow_member_swappable
};
BOOST_STATIC_ASSERT(is_nothrow_member_swappable<
replace_allocator<map_type, std::allocator> >::value);
boost::unordered::concurrent_flat_map<int, int, std::hash<int>,
std::equal_to<int>, std::allocator<std::pair<int const, int> > > >::value);
BOOST_STATIC_ASSERT(is_nothrow_member_swappable<
replace_allocator<map_type, pocs_allocator> >::value);
BOOST_STATIC_ASSERT(is_nothrow_member_swappable<pocs_map_type>::value);
BOOST_STATIC_ASSERT(!is_nothrow_member_swappable<map_type>::value);
BOOST_STATIC_ASSERT(is_nothrow_member_swappable<
replace_allocator<set_type, std::allocator> >::value);
BOOST_STATIC_ASSERT(is_nothrow_member_swappable<
replace_allocator<set_type, pocs_allocator> >::value);
BOOST_STATIC_ASSERT(!is_nothrow_member_swappable<set_type>::value);
namespace {
struct
{
@@ -102,31 +97,31 @@ namespace {
}
} free_fn_swap;
template <class X, class F, class GF>
void swap_tests(X*, F swapper, GF gen_factory, test::random_generator rg)
template <class X, class F, class G>
void swap_tests(X*, F swapper, G gen, test::random_generator rg)
{
using value_type = typename X::value_type;
using allocator_type = typename X::allocator_type;
using allocator = typename X::allocator_type;
bool const pocs =
boost::allocator_propagate_on_container_swap<
allocator_type>::type::value;
boost::allocator_propagate_on_container_swap<allocator>::type::value;
auto gen = gen_factory.template get<X>();
auto vals1 = make_random_values(1024 * 8, [&] { return gen(rg); });
auto vals2 = make_random_values(1024 * 4, [&] { return gen(rg); });
auto reference_cont1 = reference_container<X>(vals1.begin(), vals1.end());
auto reference_cont2 = reference_container<X>(vals2.begin(), vals2.end());
auto ref_map1 =
boost::unordered_flat_map<raii, raii>(vals1.begin(), vals1.end());
auto ref_map2 =
boost::unordered_flat_map<raii, raii>(vals2.begin(), vals2.end());
{
raii::reset_counts();
X x1(vals1.begin(), vals1.end(), vals1.size(), hasher(1), key_equal(2),
allocator_type(3));
allocator(3));
X x2(vals2.begin(), vals2.end(), vals2.size(), hasher(2), key_equal(1),
pocs ? allocator_type(4) : allocator_type(3));
pocs ? allocator(4) : allocator(3));
if (pocs) {
BOOST_TEST(x1.get_allocator() != x2.get_allocator());
@@ -137,7 +132,7 @@ namespace {
auto const old_cc = +raii::copy_constructor;
auto const old_mc = +raii::move_constructor;
thread_runner(vals1, [&x1, &x2, swapper](boost::span<value_type> s) {
thread_runner(vals1, [&x1, &x2, swapper](boost::span<map_value_type> s) {
(void)s;
swapper(x1, x2);
@@ -148,20 +143,20 @@ namespace {
BOOST_TEST_EQ(raii::move_constructor, old_mc);
if (pocs) {
if (x1.get_allocator() == allocator_type(3)) {
BOOST_TEST(x2.get_allocator() == allocator_type(4));
if (x1.get_allocator() == allocator(3)) {
BOOST_TEST(x2.get_allocator() == allocator(4));
} else {
BOOST_TEST(x1.get_allocator() == allocator_type(4));
BOOST_TEST(x2.get_allocator() == allocator_type(3));
BOOST_TEST(x1.get_allocator() == allocator(4));
BOOST_TEST(x2.get_allocator() == allocator(3));
}
} else {
BOOST_TEST(x1.get_allocator() == allocator_type(3));
BOOST_TEST(x1.get_allocator() == allocator(3));
BOOST_TEST(x1.get_allocator() == x2.get_allocator());
}
if (x1.size() == reference_cont1.size()) {
test_matches_reference(x1, reference_cont1);
test_matches_reference(x2, reference_cont2);
if (x1.size() == ref_map1.size()) {
test_matches_reference(x1, ref_map1);
test_matches_reference(x2, ref_map2);
BOOST_TEST_EQ(x1.hash_function(), hasher(1));
BOOST_TEST_EQ(x1.key_eq(), key_equal(2));
@@ -169,8 +164,8 @@ namespace {
BOOST_TEST_EQ(x2.hash_function(), hasher(2));
BOOST_TEST_EQ(x2.key_eq(), key_equal(1));
} else {
test_matches_reference(x2, reference_cont1);
test_matches_reference(x1, reference_cont2);
test_matches_reference(x2, ref_map1);
test_matches_reference(x1, ref_map2);
BOOST_TEST_EQ(x1.hash_function(), hasher(2));
BOOST_TEST_EQ(x1.key_eq(), key_equal(1));
@@ -182,21 +177,17 @@ namespace {
check_raii_counts();
}
template <class X, class F, class GF>
void insert_and_swap(
X*, F swapper, GF gen_factory, test::random_generator rg)
template <class F, class G>
void insert_and_swap(F swapper, G gen, test::random_generator rg)
{
using allocator_type = typename X::allocator_type;
auto gen = gen_factory.template get<X>();
auto vals1 = make_random_values(1024 * 8, [&] { return gen(rg); });
auto vals2 = make_random_values(1024 * 4, [&] { return gen(rg); });
{
raii::reset_counts();
X x1(vals1.size(), hasher(1), key_equal(2), allocator_type(3));
X x2(vals2.size(), hasher(2), key_equal(1), allocator_type(3));
map_type x1(vals1.size(), hasher(1), key_equal(2), allocator_type(3));
map_type x2(vals2.size(), hasher(2), key_equal(1), allocator_type(3));
std::thread t1, t2, t3;
boost::compat::latch l(2);
@@ -291,25 +282,21 @@ namespace {
}
map_type* map;
replace_allocator<map_type, pocs_allocator>* pocs_map;
set_type* set;
replace_allocator<set_type, pocs_allocator>* pocs_set;
pocs_map_type* pocs_map;
} // namespace
// clang-format off
UNORDERED_TEST(
swap_tests,
((map)(pocs_map)(set)(pocs_set))
((map)(pocs_map))
((member_fn_swap)(free_fn_swap))
((value_type_generator_factory))
((value_type_generator))
((default_generator)(sequential)(limited_range)))
UNORDERED_TEST(insert_and_swap,
((map)(set))
((member_fn_swap)(free_fn_swap))
((value_type_generator_factory))
((value_type_generator))
((default_generator)(sequential)(limited_range)))
// clang-format on
+178
View File
@@ -0,0 +1,178 @@
// Copyright 2023 Joaquin M Lopez Munoz
// Distributed under the Boost Software License, Version 1.0.
// https://www.boost.org/LICENSE_1_0.txt
#include <boost/config.hpp>
#include <boost/unordered/detail/foa/takes_arg_as_const_reference.hpp>
#include <boost/core/lightweight_test.hpp>
using boost::unordered::detail::foa::takes_arg_as_const_reference;
using f1 = void ( const int& );
void function2( const int& ) noexcept;
using f2 = decltype(function2);
using f3 = void ( const int&, char* );
using f4 = void ( const int&, ... );
using f5 = void ( int& );
void function6( int& ) noexcept;
using f6 = decltype(function6);
using f7 = void ( int&, char* );
using f8 = void ( int&, ... );
struct f9 { void operator()( const int& ); };
struct f10 { void operator()( const int& ) const; };
struct f11 { void operator()( const int& ) volatile; };
struct f12 { void operator()( const int& ) const volatile; };
struct f13 { void operator()( const int& )&; };
struct f14 { void operator()( const int& ) const&; };
struct f15 { void operator()( const int& ) volatile&; };
struct f16 { void operator()( const int& ) const volatile&; };
struct f17 { void operator()( const int& )&&; };
struct f18 { void operator()( const int& ) const&&; };
struct f19 { void operator()( const int& ) volatile&&; };
struct f20 { void operator()( const int& ) const volatile&&; };
struct f21 { void operator()( const int& ) noexcept; };
struct f22 { void operator()( const int& ) const noexcept; };
struct f23 { void operator()( const int&, int=0 ); };
struct f24 { void operator()( const int&, ... ) noexcept; };
struct f25 { void operator()( int& ); };
struct f26 { void operator()( int& ) const; };
struct f27 { void operator()( int& ) volatile; };
struct f28 { void operator()( int& ) const volatile; };
struct f29 { void operator()( int& )&; };
struct f30 { void operator()( int& ) const&; };
struct f31 { void operator()( int& ) volatile&; };
struct f32 { void operator()( int& ) const volatile&; };
struct f33 { void operator()( int& )&&; };
struct f34 { void operator()( int& ) const&&; };
struct f35 { void operator()( int& ) volatile&&; };
struct f36 { void operator()( int& ) const volatile&&; };
struct f37 { void operator()( int& ) noexcept; };
struct f38 { void operator()( int& ) const noexcept; };
struct f39 { void operator()( int&, int=0 ); };
struct f40 { void operator()( int&, ... ) noexcept; };
struct f41 { template<typename Arg> void operator()( const Arg& ); };
struct f42 { template<typename Arg> void operator()( const Arg& ) const; };
struct f43 { template<typename Arg> void operator()( const Arg& ) const noexcept; };
struct f44 { template<typename Arg> void operator()( Arg& ); };
struct f45 { template<typename Arg> void operator()( Arg& ) const; };
struct f46 { template<typename Arg> void operator()( Arg& ) const noexcept; };
struct f47 { template<typename Arg> void operator()( Arg ); };
struct f48 { template<typename Arg> void operator()( Arg ) const; };
struct f49 { template<typename Arg> void operator()( Arg ) const noexcept; };
struct f50
{
void operator()( const int& );
void operator()( char* );
};
struct f51 // expected false negative
{
void operator()( const int& );
template<typename Arg> void operator()( Arg& );
};
using f52=int; // detection doesn't crash even if requirements violated
int main()
{
auto lambda53 = []( const int& ){};
using f53=decltype(lambda53);
int retrieved=0;
auto lambda54 = [&]( const int& x ) mutable { retrieved = x; };
using f54=decltype(lambda54);
auto lambda55 = []( int& x ){ x = 0; };
using f55=decltype(lambda55);
auto lambda56 = [&]( int& x ) mutable { retrieved = x; x = 0; };
using f56=decltype(lambda56);
#if !defined(BOOST_NO_CXX14_GENERIC_LAMBDAS)
auto lambda57 = []( const auto& ){};
using f57=decltype(lambda57);
auto lambda58 = [&]( const auto& x ) mutable { retrieved = x; };
using f58=decltype(lambda58);
auto lambda59 = []( auto& x ){ x = 0; };
using f59=decltype(lambda59);
auto lambda60 = [&]( auto& x ) mutable { retrieved = x; x = 0; };
using f60=decltype(lambda60);
#endif
BOOST_TEST(( takes_arg_as_const_reference<f1, int>::value ));
BOOST_TEST(( takes_arg_as_const_reference<f2, int>::value ));
BOOST_TEST(( takes_arg_as_const_reference<f3, int>::value ));
BOOST_TEST(( takes_arg_as_const_reference<f4, int>::value ));
BOOST_TEST(( !takes_arg_as_const_reference<f5, int>::value ));
BOOST_TEST(( !takes_arg_as_const_reference<f6, int>::value ));
BOOST_TEST(( !takes_arg_as_const_reference<f7, int>::value ));
BOOST_TEST(( !takes_arg_as_const_reference<f8, int>::value ));
BOOST_TEST(( takes_arg_as_const_reference<f1*, int>::value ));
BOOST_TEST(( takes_arg_as_const_reference<f2*, int>::value ));
BOOST_TEST(( takes_arg_as_const_reference<f3*, int>::value ));
BOOST_TEST(( takes_arg_as_const_reference<f4*, int>::value ));
BOOST_TEST(( !takes_arg_as_const_reference<f5*, int>::value ));
BOOST_TEST(( !takes_arg_as_const_reference<f6*, int>::value ));
BOOST_TEST(( !takes_arg_as_const_reference<f7*, int>::value ));
BOOST_TEST(( !takes_arg_as_const_reference<f8*, int>::value ));
BOOST_TEST(( takes_arg_as_const_reference<f1&, int>::value ));
BOOST_TEST(( takes_arg_as_const_reference<f2&, int>::value ));
BOOST_TEST(( takes_arg_as_const_reference<f3&, int>::value ));
BOOST_TEST(( takes_arg_as_const_reference<f4&, int>::value ));
BOOST_TEST(( !takes_arg_as_const_reference<f5&, int>::value ));
BOOST_TEST(( !takes_arg_as_const_reference<f6&, int>::value ));
BOOST_TEST(( !takes_arg_as_const_reference<f7&, int>::value ));
BOOST_TEST(( !takes_arg_as_const_reference<f8&, int>::value ));
BOOST_TEST(( takes_arg_as_const_reference<f9, int>::value ));
BOOST_TEST(( takes_arg_as_const_reference<f10, int>::value ));
BOOST_TEST(( takes_arg_as_const_reference<f11, int>::value ));
BOOST_TEST(( takes_arg_as_const_reference<f12, int>::value ));
BOOST_TEST(( takes_arg_as_const_reference<f13&, int>::value ));
BOOST_TEST(( takes_arg_as_const_reference<f14&, int>::value ));
BOOST_TEST(( takes_arg_as_const_reference<f15&, int>::value ));
BOOST_TEST(( takes_arg_as_const_reference<f16&, int>::value ));
BOOST_TEST(( takes_arg_as_const_reference<f17&&, int>::value ));
BOOST_TEST(( takes_arg_as_const_reference<f18&&, int>::value ));
BOOST_TEST(( takes_arg_as_const_reference<f19&&, int>::value ));
BOOST_TEST(( takes_arg_as_const_reference<f20&&, int>::value ));
BOOST_TEST(( takes_arg_as_const_reference<f21, int>::value ));
BOOST_TEST(( takes_arg_as_const_reference<f22, int>::value ));
BOOST_TEST(( takes_arg_as_const_reference<f23, int>::value ));
BOOST_TEST(( takes_arg_as_const_reference<f24, int>::value ));
BOOST_TEST(( !takes_arg_as_const_reference<f25, int>::value ));
BOOST_TEST(( !takes_arg_as_const_reference<f26, int>::value ));
BOOST_TEST(( !takes_arg_as_const_reference<f27, int>::value ));
BOOST_TEST(( !takes_arg_as_const_reference<f28, int>::value ));
BOOST_TEST(( !takes_arg_as_const_reference<f29&, int>::value ));
BOOST_TEST(( !takes_arg_as_const_reference<f30&, int>::value ));
BOOST_TEST(( !takes_arg_as_const_reference<f31&, int>::value ));
BOOST_TEST(( !takes_arg_as_const_reference<f32&, int>::value ));
BOOST_TEST(( !takes_arg_as_const_reference<f33&&, int>::value ));
BOOST_TEST(( !takes_arg_as_const_reference<f34&&, int>::value ));
BOOST_TEST(( !takes_arg_as_const_reference<f35&&, int>::value ));
BOOST_TEST(( !takes_arg_as_const_reference<f36&&, int>::value ));
BOOST_TEST(( !takes_arg_as_const_reference<f37, int>::value ));
BOOST_TEST(( !takes_arg_as_const_reference<f38, int>::value ));
BOOST_TEST(( !takes_arg_as_const_reference<f39, int>::value ));
BOOST_TEST(( !takes_arg_as_const_reference<f40, int>::value ));
BOOST_TEST(( takes_arg_as_const_reference<f41, int>::value ));
BOOST_TEST(( takes_arg_as_const_reference<f42, int>::value ));
BOOST_TEST(( takes_arg_as_const_reference<f43, int>::value ));
BOOST_TEST(( !takes_arg_as_const_reference<f44, int>::value ));
BOOST_TEST(( !takes_arg_as_const_reference<f45, int>::value ));
BOOST_TEST(( !takes_arg_as_const_reference<f46, int>::value ));
BOOST_TEST(( !takes_arg_as_const_reference<f47, int>::value ));
BOOST_TEST(( !takes_arg_as_const_reference<f48, int>::value ));
BOOST_TEST(( !takes_arg_as_const_reference<f49, int>::value ));
BOOST_TEST(( takes_arg_as_const_reference<f50, int>::value ));
BOOST_TEST(( !takes_arg_as_const_reference<f51, int>::value ));
BOOST_TEST(( !takes_arg_as_const_reference<f52, int>::value ));
BOOST_TEST(( takes_arg_as_const_reference<f53, int>::value ));
BOOST_TEST(( takes_arg_as_const_reference<f54, int>::value ));
BOOST_TEST(( !takes_arg_as_const_reference<f55, int>::value ));
BOOST_TEST(( !takes_arg_as_const_reference<f56, int>::value ));
#if !defined(BOOST_NO_CXX14_GENERIC_LAMBDAS)
BOOST_TEST(( takes_arg_as_const_reference<f57, int>::value ));
BOOST_TEST(( takes_arg_as_const_reference<f58, int>::value ));
BOOST_TEST(( !takes_arg_as_const_reference<f59, int>::value ));
BOOST_TEST(( !takes_arg_as_const_reference<f60, int>::value ));
#endif
return boost::report_errors();
}
-3
View File
@@ -373,9 +373,6 @@ using test::default_generator;
using test::limited_range;
using test::sequential;
value_generator<std::pair<raii const, raii> > value_type_generator;
value_generator<std::pair<raii, raii> > init_type_generator;
// clang-format off
UNORDERED_TEST(
try_emplace,
+92 -367
View File
@@ -1,65 +1,38 @@
// Copyright (C) 2023 Christian Mazakas
// Copyright (C) 2023 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_flat_set.hpp>
#include <boost/core/ignore_unused.hpp>
#include <array>
#include <functional>
#include <vector>
namespace {
test::seed_t initialize_seed(335740237);
auto non_present_keys = []
{
std::array<raii,128> a;
for(std::size_t i = 0; i < a.size(); ++i) {
a[i].x_ = -((int)i + 1);
}
return a;
}();
template<typename T>
raii const & get_non_present_key(T const & x)
{
return non_present_keys[
(std::size_t)get_key(x).x_ % non_present_keys.size()];
}
struct lvalue_visitor_type
{
template <class T, class X, class M>
void operator()(std::vector<T>& values, X& x, M const& reference_cont)
void operator()(std::vector<T>& values, X& x, M const& reference_map)
{
using value_type = typename X::value_type;
// concurrent_flat_set visit is always const access
using arg_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;
std::atomic<std::uint64_t> num_visits{0};
std::atomic<std::uint64_t> total_count{0};
auto mut_visitor = [&num_visits, &reference_cont](arg_type& v) {
BOOST_TEST(reference_cont.contains(get_key(v)));
BOOST_TEST_EQ(v, *reference_cont.find(get_key(v)));
auto mut_visitor = [&num_visits, &reference_map](value_type& v) {
BOOST_TEST(reference_map.contains(v.first));
BOOST_TEST_EQ(v.second, reference_map.find(v.first)->second);
++num_visits;
};
auto const_visitor =
[&num_visits, &reference_cont](value_type const& v) {
BOOST_TEST(reference_cont.contains(get_key(v)));
BOOST_TEST_EQ(v, *reference_cont.find(get_key(v)));
auto const_visitor = [&num_visits, &reference_map](value_type const& v) {
BOOST_TEST(reference_map.contains(v.first));
BOOST_TEST_EQ(v.second, reference_map.find(v.first)->second);
++num_visits;
};
@@ -67,14 +40,14 @@ namespace {
thread_runner(
values, [&x, &mut_visitor, &total_count](boost::span<T> s) {
for (auto const& val : s) {
auto r = get_key(val).x_;
auto r = val.first.x_;
BOOST_TEST(r >= 0);
auto count = x.visit(get_key(val), mut_visitor);
auto count = x.visit(val.first, mut_visitor);
BOOST_TEST_EQ(count, 1u);
total_count += count;
count = x.visit(get_non_present_key(val), mut_visitor);
count = x.visit(val.second, mut_visitor);
BOOST_TEST_EQ(count, 0u);
}
});
@@ -90,16 +63,16 @@ namespace {
thread_runner(
values, [&x, &const_visitor, &total_count](boost::span<T> s) {
for (auto const& val : s) {
auto r = get_key(val).x_;
auto r = val.first.x_;
BOOST_TEST(r >= 0);
auto const& y = x;
auto count = y.visit(get_key(val), const_visitor);
auto count = y.visit(val.first, const_visitor);
BOOST_TEST_EQ(count, 1u);
total_count += count;
count = y.visit(get_non_present_key(val), const_visitor);
count = y.visit(val.second, const_visitor);
BOOST_TEST_EQ(count, 0u);
}
});
@@ -115,15 +88,15 @@ namespace {
thread_runner(
values, [&x, &const_visitor, &total_count](boost::span<T> s) {
for (auto const& val : s) {
auto r = get_key(val).x_;
auto r = val.first.x_;
BOOST_TEST(r >= 0);
auto count = x.cvisit(get_key(val), const_visitor);
auto count = x.cvisit(val.first, const_visitor);
BOOST_TEST_EQ(count, 1u);
total_count += count;
count = x.cvisit(get_non_present_key(val), const_visitor);
count = x.cvisit(val.second, const_visitor);
BOOST_TEST_EQ(count, 0u);
}
});
@@ -138,14 +111,14 @@ namespace {
{
thread_runner(values, [&x, &total_count](boost::span<T> s) {
for (auto const& val : s) {
auto r = get_key(val).x_;
auto r = val.first.x_;
BOOST_TEST(r >= 0);
auto count = x.count(get_key(val));
auto count = x.count(val.first);
BOOST_TEST_EQ(count, 1u);
total_count += count;
count = x.count(get_non_present_key(val));
count = x.count(val.second);
BOOST_TEST_EQ(count, 0u);
}
});
@@ -159,13 +132,13 @@ namespace {
{
thread_runner(values, [&x](boost::span<T> s) {
for (auto const& val : s) {
auto r = get_key(val).x_;
auto r = val.first.x_;
BOOST_TEST(r >= 0);
auto contains = x.contains(get_key(val));
auto contains = x.contains(val.first);
BOOST_TEST(contains);
contains = x.contains(get_non_present_key(val));
contains = x.contains(val.second);
BOOST_TEST(!contains);
}
});
@@ -179,29 +152,22 @@ namespace {
struct transp_visitor_type
{
template <class T, class X, class M>
void operator()(std::vector<T>& values, X& x, M const& reference_cont)
void operator()(std::vector<T>& values, X& x, M const& reference_map)
{
using value_type = typename X::value_type;
// concurrent_flat_set visit is always const access
using arg_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;
std::atomic<std::uint64_t> num_visits{0};
std::atomic<std::uint64_t> total_count{0};
auto mut_visitor = [&num_visits, &reference_cont](arg_type& v) {
BOOST_TEST(reference_cont.contains(get_key(v)));
BOOST_TEST_EQ(v, *reference_cont.find(get_key(v)));
auto mut_visitor = [&num_visits, &reference_map](value_type& v) {
BOOST_TEST(reference_map.contains(v.first));
BOOST_TEST_EQ(v.second, reference_map.find(v.first)->second);
++num_visits;
};
auto const_visitor = [&num_visits, &reference_cont](value_type const& v) {
BOOST_TEST(reference_cont.contains(get_key(v)));
BOOST_TEST_EQ(v, *reference_cont.find(get_key(v)));
auto const_visitor = [&num_visits, &reference_map](value_type const& v) {
BOOST_TEST(reference_map.contains(v.first));
BOOST_TEST_EQ(v.second, reference_map.find(v.first)->second);
++num_visits;
};
@@ -209,15 +175,15 @@ namespace {
thread_runner(
values, [&x, &mut_visitor, &total_count](boost::span<T> s) {
for (auto const& val : s) {
auto r = get_key(val).x_;
auto r = val.first.x_;
BOOST_TEST(r >= 0);
auto count = x.visit(get_key(val).x_, mut_visitor);
auto count = x.visit(val.first.x_, mut_visitor);
BOOST_TEST_EQ(count, 1u);
total_count += count;
count = x.visit(get_non_present_key(val).x_, mut_visitor);
count = x.visit(val.second.x_, mut_visitor);
BOOST_TEST_EQ(count, 0u);
}
});
@@ -233,16 +199,16 @@ namespace {
thread_runner(
values, [&x, &const_visitor, &total_count](boost::span<T> s) {
for (auto const& val : s) {
auto r = get_key(val).x_;
auto r = val.first.x_;
BOOST_TEST(r >= 0);
auto const& y = x;
auto count = y.visit(get_key(val).x_, const_visitor);
auto count = y.visit(val.first.x_, const_visitor);
BOOST_TEST_EQ(count, 1u);
total_count += count;
count = y.visit(get_non_present_key(val).x_, const_visitor);
count = y.visit(val.second.x_, const_visitor);
BOOST_TEST_EQ(count, 0u);
}
});
@@ -258,15 +224,15 @@ namespace {
thread_runner(
values, [&x, &const_visitor, &total_count](boost::span<T> s) {
for (auto const& val : s) {
auto r = get_key(val).x_;
auto r = val.first.x_;
BOOST_TEST(r >= 0);
auto count = x.cvisit(get_key(val).x_, const_visitor);
auto count = x.cvisit(val.first.x_, const_visitor);
BOOST_TEST_EQ(count, 1u);
total_count += count;
count = x.cvisit(get_non_present_key(val).x_, const_visitor);
count = x.cvisit(val.second.x_, const_visitor);
BOOST_TEST_EQ(count, 0u);
}
});
@@ -281,14 +247,14 @@ namespace {
{
thread_runner(values, [&x, &total_count](boost::span<T> s) {
for (auto const& val : s) {
auto r = get_key(val).x_;
auto r = val.first.x_;
BOOST_TEST(r >= 0);
auto count = x.count(get_key(val).x_);
auto count = x.count(val.first.x_);
BOOST_TEST_EQ(count, 1u);
total_count += count;
count = x.count(get_non_present_key(val).x_);
count = x.count(val.second.x_);
BOOST_TEST_EQ(count, 0u);
}
});
@@ -302,13 +268,13 @@ namespace {
{
thread_runner(values, [&x](boost::span<T> s) {
for (auto const& val : s) {
auto r = get_key(val).x_;
auto r = val.first.x_;
BOOST_TEST(r >= 0);
auto contains = x.contains(get_key(val).x_);
auto contains = x.contains(val.first.x_);
BOOST_TEST(contains);
contains = x.contains(get_non_present_key(val).x_);
contains = x.contains(val.second.x_);
BOOST_TEST(!contains);
}
});
@@ -322,31 +288,24 @@ namespace {
struct visit_all_type
{
template <class T, class X, class M>
void operator()(std::vector<T>& values, X& x, M const& reference_cont)
void operator()(std::vector<T>& values, X& x, M const& reference_map)
{
using value_type = typename X::value_type;
// concurrent_flat_set visit is always const access
using arg_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;
std::atomic<std::uint64_t> total_count{0};
auto mut_visitor = [&reference_cont](std::atomic<uint64_t>& num_visits) {
return [&reference_cont, &num_visits](arg_type& v) {
BOOST_TEST(reference_cont.contains(get_key(v)));
BOOST_TEST_EQ(v, *reference_cont.find(get_key(v)));
auto mut_visitor = [&reference_map](std::atomic<uint64_t>& num_visits) {
return [&reference_map, &num_visits](value_type& kv) {
BOOST_TEST(reference_map.contains(kv.first));
BOOST_TEST_EQ(kv.second, reference_map.find(kv.first)->second);
++num_visits;
};
};
auto const_visitor = [&reference_cont](std::atomic<uint64_t>& num_visits) {
return [&reference_cont, &num_visits](value_type const& v) {
BOOST_TEST(reference_cont.contains(get_key(v)));
BOOST_TEST_EQ(v, *reference_cont.find(get_key(v)));
auto const_visitor = [&reference_map](std::atomic<uint64_t>& num_visits) {
return [&reference_map, &num_visits](value_type const& kv) {
BOOST_TEST(reference_map.contains(kv.first));
BOOST_TEST_EQ(kv.second, reference_map.find(kv.first)->second);
++num_visits;
};
};
@@ -390,140 +349,26 @@ namespace {
} visit_all;
struct visit_while_type
{
template <class T, class X, class M>
void operator()(std::vector<T>& values, X& x, M const& reference_cont)
{
using value_type = typename X::value_type;
// concurrent_flat_set visit is always const access
using arg_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;
auto mut_truthy_visitor = [&reference_cont](
std::atomic<uint64_t>& num_visits) {
return [&reference_cont, &num_visits](arg_type& v) {
BOOST_TEST(reference_cont.contains(get_key(v)));
BOOST_TEST_EQ(v, *reference_cont.find(get_key(v)));
++num_visits;
return true;
};
};
auto const_truthy_visitor = [&reference_cont](
std::atomic<uint64_t>& num_visits) {
return [&reference_cont, &num_visits](value_type const& v) {
BOOST_TEST(reference_cont.contains(get_key(v)));
BOOST_TEST_EQ(v, *reference_cont.find(get_key(v)));
++num_visits;
return true;
};
};
auto mut_falsey_visitor = [&reference_cont](
std::atomic<uint64_t>& num_visits) {
return [&reference_cont, &num_visits](arg_type& v) {
BOOST_TEST(reference_cont.contains(get_key(v)));
++num_visits;
return (get_value(v).x_ % 100) == 0;
};
};
auto const_falsey_visitor = [&reference_cont](
std::atomic<uint64_t>& num_visits) {
return [&reference_cont, &num_visits](value_type const& v) {
BOOST_TEST(reference_cont.contains(get_key(v)));
++num_visits;
return (get_value(v).x_ % 100) == 0;
};
};
{
thread_runner(values, [&x, &mut_truthy_visitor](boost::span<T>) {
std::atomic<std::uint64_t> num_visits{0};
BOOST_TEST(x.visit_while(mut_truthy_visitor(num_visits)));
BOOST_TEST_EQ(x.size(), num_visits);
});
}
{
thread_runner(values, [&x, &const_truthy_visitor](boost::span<T>) {
std::atomic<std::uint64_t> num_visits{0};
auto const& y = x;
BOOST_TEST(y.visit_while(const_truthy_visitor(num_visits)));
BOOST_TEST_EQ(x.size(), num_visits);
});
}
{
thread_runner(values, [&x, &const_truthy_visitor](boost::span<T>) {
std::atomic<std::uint64_t> num_visits{0};
BOOST_TEST(x.cvisit_while(const_truthy_visitor(num_visits)));
BOOST_TEST_EQ(x.size(), num_visits);
});
}
{
thread_runner(values, [&x, &mut_falsey_visitor](boost::span<T>) {
std::atomic<std::uint64_t> num_visits{0};
BOOST_TEST_NOT(x.visit_while(mut_falsey_visitor(num_visits)));
BOOST_TEST_LT(num_visits, x.size());
BOOST_TEST_GT(num_visits, 0u);
});
}
{
thread_runner(values, [&x, &const_falsey_visitor](boost::span<T>) {
std::atomic<std::uint64_t> num_visits{0};
auto const& y = x;
BOOST_TEST_NOT(y.visit_while(const_falsey_visitor(num_visits)));
BOOST_TEST_LT(num_visits, x.size());
BOOST_TEST_GT(num_visits, 0u);
});
}
{
thread_runner(values, [&x, &const_falsey_visitor](boost::span<T>) {
std::atomic<std::uint64_t> num_visits{0};
BOOST_TEST_NOT(x.cvisit_while(const_falsey_visitor(num_visits)));
BOOST_TEST_LT(num_visits, x.size());
BOOST_TEST_GT(num_visits, 0u);
});
}
}
} visit_while;
struct exec_policy_visit_all_type
{
template <class T, class X, class M>
void operator()(std::vector<T>& values, X& x, M const& reference_cont)
void operator()(std::vector<T>& values, X& x, M const& reference_map)
{
#if defined(BOOST_UNORDERED_PARALLEL_ALGORITHMS)
using value_type = typename X::value_type;
// concurrent_flat_set visit is always const access
using arg_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;
auto mut_visitor = [&reference_cont](std::atomic<uint64_t>& num_visits) {
return [&reference_cont, &num_visits](arg_type& v) {
BOOST_TEST(reference_cont.contains(get_key(v)));
BOOST_TEST_EQ(v, *reference_cont.find(get_key(v)));
auto mut_visitor = [&reference_map](std::atomic<uint64_t>& num_visits) {
return [&reference_map, &num_visits](value_type& kv) {
BOOST_TEST(reference_map.contains(kv.first));
BOOST_TEST_EQ(kv.second, reference_map.find(kv.first)->second);
++num_visits;
};
};
auto const_visitor = [&reference_cont](std::atomic<uint64_t>& num_visits) {
return [&reference_cont, &num_visits](value_type const& v) {
BOOST_TEST(reference_cont.contains(get_key(v)));
BOOST_TEST_EQ(v, *reference_cont.find(get_key(v)));
auto const_visitor = [&reference_map](std::atomic<uint64_t>& num_visits) {
return [&reference_map, &num_visits](value_type const& kv) {
BOOST_TEST(reference_map.contains(kv.first));
BOOST_TEST_EQ(kv.second, reference_map.find(kv.first)->second);
++num_visits;
};
};
@@ -557,138 +402,24 @@ namespace {
#else
(void)values;
(void)x;
(void)reference_cont;
(void)reference_map;
#endif
}
} exec_policy_visit_all;
struct exec_policy_visit_while_type
template <class X, class G, class F>
void visit(X*, G gen, F visitor, test::random_generator rg)
{
template <class T, class X, class M>
void operator()(std::vector<T>& values, X& x, M const& reference_cont)
{
#if defined(BOOST_UNORDERED_PARALLEL_ALGORITHMS)
using value_type = typename X::value_type;
// concurrent_flat_set visit is always const access
using arg_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;
auto mut_truthy_visitor = [&reference_cont](
std::atomic<uint64_t>& num_visits) {
return [&reference_cont, &num_visits](arg_type& v) {
BOOST_TEST(reference_cont.contains(get_key(v)));
BOOST_TEST_EQ(v, *reference_cont.find(get_key(v)));
++num_visits;
return true;
};
};
auto const_truthy_visitor = [&reference_cont](
std::atomic<uint64_t>& num_visits) {
return [&reference_cont, &num_visits](value_type const& v) {
BOOST_TEST(reference_cont.contains(get_key(v)));
BOOST_TEST_EQ(v, *reference_cont.find(get_key(v)));
++num_visits;
return true;
};
};
auto mut_falsey_visitor = [&reference_cont](
std::atomic<uint64_t>& num_visits) {
return [&reference_cont, &num_visits](arg_type& v) {
BOOST_TEST(reference_cont.contains(get_key(v)));
BOOST_TEST_EQ(v, *reference_cont.find(get_key(v)));
++num_visits;
return (get_value(v).x_ % 100) == 0;
};
};
auto const_falsey_visitor = [&reference_cont](
std::atomic<uint64_t>& num_visits) {
return [&reference_cont, &num_visits](value_type const& v) {
BOOST_TEST(reference_cont.contains(get_key(v)));
BOOST_TEST_EQ(v, *reference_cont.find(get_key(v)));
++num_visits;
return (get_value(v).x_ % 100) == 0;
};
};
{
thread_runner(values, [&x, &mut_truthy_visitor](boost::span<T>) {
std::atomic<std::uint64_t> num_visits{0};
BOOST_TEST(
x.visit_while(std::execution::par, mut_truthy_visitor(num_visits)));
BOOST_TEST_EQ(x.size(), num_visits);
});
}
{
thread_runner(values, [&x, &const_truthy_visitor](boost::span<T>) {
std::atomic<std::uint64_t> num_visits{0};
auto const& y = x;
BOOST_TEST(y.visit_while(
std::execution::par, const_truthy_visitor(num_visits)));
BOOST_TEST_EQ(x.size(), num_visits);
});
}
{
thread_runner(values, [&x, &const_truthy_visitor](boost::span<T>) {
std::atomic<std::uint64_t> num_visits{0};
BOOST_TEST(x.cvisit_while(
std::execution::par, const_truthy_visitor(num_visits)));
BOOST_TEST_EQ(x.size(), num_visits);
});
}
{
thread_runner(values, [&x, &mut_falsey_visitor](boost::span<T>) {
std::atomic<std::uint64_t> num_visits{0};
BOOST_TEST_NOT(
x.visit_while(std::execution::par, mut_falsey_visitor(num_visits)));
BOOST_TEST_LT(num_visits, x.size());
BOOST_TEST_GT(num_visits, 0u);
});
}
{
thread_runner(values, [&x, &const_falsey_visitor](boost::span<T>) {
std::atomic<std::uint64_t> num_visits{0};
auto const& y = x;
BOOST_TEST_NOT(y.visit_while(
std::execution::par, const_falsey_visitor(num_visits)));
BOOST_TEST_LT(num_visits, x.size());
BOOST_TEST_GT(num_visits, 0u);
});
}
{
thread_runner(values, [&x, &const_falsey_visitor](boost::span<T>) {
std::atomic<std::uint64_t> num_visits{0};
BOOST_TEST_NOT(x.cvisit_while(
std::execution::par, const_falsey_visitor(num_visits)));
BOOST_TEST_LT(num_visits, x.size());
BOOST_TEST_GT(num_visits, 0u);
});
}
#else
(void)values;
(void)x;
(void)reference_cont;
#endif
}
} exec_policy_visit_while;
template <class X, class GF, class F>
void visit(X*, GF gen_factory, F visitor, test::random_generator rg)
{
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());
for (auto& val : values) {
if (val.second.x_ == 0) {
val.second.x_ = 1;
}
val.second.x_ *= -1;
}
auto reference_map =
boost::unordered_flat_map<raii, raii>(values.begin(), values.end());
raii::reset_counts();
@@ -697,7 +428,7 @@ namespace {
for (auto const& v : values) {
x.insert(v);
}
BOOST_TEST_EQ(x.size(), reference_cont.size());
BOOST_TEST_EQ(x.size(), reference_map.size());
std::uint64_t old_default_constructor = raii::default_constructor;
std::uint64_t old_copy_constructor = raii::copy_constructor;
@@ -705,7 +436,7 @@ namespace {
std::uint64_t old_copy_assignment = raii::copy_assignment;
std::uint64_t old_move_assignment = raii::move_assignment;
visitor(values, x, reference_cont);
visitor(values, x, reference_map);
BOOST_TEST_EQ(old_default_constructor, raii::default_constructor);
BOOST_TEST_EQ(old_copy_constructor, raii::copy_constructor);
@@ -724,10 +455,9 @@ namespace {
raii::destructor);
}
template <class X, class GF>
void empty_visit(X*, GF gen_factory, test::random_generator rg)
template <class X, class G>
void empty_visit(X*, G gen, test::random_generator rg)
{
auto gen = gen_factory.template get<X>();
auto values = make_random_values(1024 * 16, [&] { return gen(rg); });
using values_type = decltype(values);
using span_value_type = typename values_type::value_type;
@@ -752,7 +482,7 @@ namespace {
BOOST_TEST_EQ(num_visits, 0u);
for (auto const& val : s) {
auto count = x.visit(get_key(val),
auto count = x.visit(val.first,
[&num_visits](typename X::value_type const&) { ++num_visits; });
BOOST_TEST_EQ(count, 0u);
}
@@ -772,8 +502,8 @@ namespace {
BOOST_TEST_EQ(raii::destructor, 0u);
}
template <class X, class GF>
void insert_and_visit(X*, GF gen_factory, test::random_generator rg)
template <class X, class G>
void insert_and_visit(X*, G gen, test::random_generator rg)
{
// here we attempt to ensure happens-before and synchronizes-with
// the visitation thread essentially chases the insertion one
@@ -782,7 +512,6 @@ namespace {
BOOST_TEST(rg == test::sequential);
auto gen = gen_factory.template get<X>();
auto const values = make_random_values(1024 * 16, [&] { return gen(rg); });
{
@@ -809,9 +538,9 @@ namespace {
for (std::size_t idx = 0; idx < values.size(); ++idx) {
std::atomic_bool b{false};
while (!b) {
x.cvisit(get_key(values[idx]),
x.cvisit(values[idx].first,
[&b, &strs, idx, &values](typename X::value_type const& v) {
BOOST_TEST_EQ(get_value(v), get_value(values[idx]));
BOOST_TEST_EQ(v.second, values[idx].second);
BOOST_TEST_EQ(strs[idx], "rawr");
b = true;
});
@@ -828,9 +557,6 @@ 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_flat_set<raii>* set;
boost::unordered::concurrent_flat_set<raii, transp_hash,
transp_key_equal>* transp_set;
} // namespace
@@ -842,30 +568,29 @@ using test::sequential;
UNORDERED_TEST(
visit,
((map)(set))
((value_type_generator_factory)(init_type_generator_factory))
((lvalue_visitor)(visit_all)(visit_while)(exec_policy_visit_all)
(exec_policy_visit_while))
((map))
((value_type_generator)(init_type_generator))
((lvalue_visitor)(visit_all)(exec_policy_visit_all))
((default_generator)(sequential)(limited_range)))
UNORDERED_TEST(
visit,
((transp_map)(transp_set))
((value_type_generator_factory)(init_type_generator_factory))
((transp_map))
((value_type_generator)(init_type_generator))
((transp_visitor))
((default_generator)(sequential)(limited_range)))
UNORDERED_TEST(
empty_visit,
((map)(transp_map)(set)(transp_set))
((value_type_generator_factory)(init_type_generator_factory))
((map)(transp_map))
((value_type_generator)(init_type_generator))
((default_generator)(sequential)(limited_range))
)
UNORDERED_TEST(
insert_and_visit,
((map)(set))
((value_type_generator_factory))
((map))
((value_type_generator))
((sequential))
)
@@ -1,270 +0,0 @@
/* Copyright 2023 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.
*/
/* This program has been used to generate archives of Boost.Unordered
* containers with Boost 1.83, when serialization support was provided
* externally to Boost.Unordered in
* boost/serialization/boost_unordered_(map|set).hpp. Beginning with the
* release of native Boost.Unordered serialization capabilities in Boost
* 1.84, these archives are used to test backwards loading compatibility
* as enabled by BOOST_UNORDERED_ENABLE_SERIALIZATION_COMPATIBILITY_V0.
*/
#include <boost/archive/text_oarchive.hpp>
#include <boost/archive/xml_oarchive.hpp>
#include <boost/serialization/boost_unordered_map.hpp>
#include <boost/serialization/boost_unordered_set.hpp>
#include <boost/serialization/string.hpp>
#include <boost/serialization/vector.hpp>
#include <boost/version.hpp>
#include <fstream>
#include <random>
template<typename Value=unsigned int>
struct random_generator
{
Value operator()()
{
return static_cast<Value>(dist(gen));
}
std::uniform_int_distribution<unsigned int> dist;
std::mt19937 gen{231};
};
template<>
struct random_generator<std::string>
{
std::string operator()()
{
return std::to_string(rng());
}
random_generator<> rng;
};
template<>
struct random_generator<const std::string>:random_generator<std::string>{};
template<typename T,typename Q>
struct random_generator<std::pair<T,Q>>
{
std::pair<T,Q> operator()()
{
return {rngt(),rngq()};
}
random_generator<T> rngt;
random_generator<Q> rngq;
};
template<typename T>
struct non_const
{
typedef T type;
};
template<typename T>
struct non_const<const T>
{
using type=typename non_const<T>::type;
};
template<typename T, typename Q>
struct non_const<std::pair<T, Q>>
{
using type=std::pair<
typename non_const<T>::type,
typename non_const<Q>::type>;
};
template<typename Container,typename Archive>
void generate_legacy_archive(const char* filename,std::size_t n)
{
using value_type=typename Container::value_type;
using vector=std::vector<typename non_const<value_type>::type>;
Container c;
vector v;
random_generator<value_type> rng;
std::uniform_int_distribution<> repeat(0,1);
std::mt19937 gen{231};
for(std::size_t i=0;i<n;++i){
value_type x=rng();
c.insert(x);
v.push_back(x);
if(repeat(gen)){
c.insert(x);
v.push_back(x);
}
}
std::ofstream ofs(filename);
Archive oa(ofs);
oa<<boost::serialization::make_nvp("container",c);
oa<<boost::serialization::make_nvp("values",v);
}
int main()
{
static_assert(BOOST_VERSION<=108300,"to be used with Boost <1.84.");
generate_legacy_archive<
boost::unordered_map<int,int>,boost::archive::text_oarchive
>("map_int_0.txt",0);
generate_legacy_archive<
boost::unordered_map<int,int>,boost::archive::text_oarchive
>("map_int_10.txt",10);
generate_legacy_archive<
boost::unordered_map<int,int>,boost::archive::text_oarchive
>("map_int_100.txt",100);
generate_legacy_archive<
boost::unordered_map<std::string,std::string>,boost::archive::text_oarchive
>("map_string_0.txt",0);
generate_legacy_archive<
boost::unordered_map<std::string,std::string>,boost::archive::text_oarchive
>("map_string_10.txt",10);
generate_legacy_archive<boost::unordered_map<
std::string,std::string>,boost::archive::text_oarchive
>("map_string_100.txt",100);
generate_legacy_archive<
boost::unordered_multimap<int,int>,boost::archive::text_oarchive
>("multimap_int_0.txt",0);
generate_legacy_archive<
boost::unordered_multimap<int,int>,boost::archive::text_oarchive
>("multimap_int_10.txt",10);
generate_legacy_archive<
boost::unordered_multimap<int,int>,boost::archive::text_oarchive
>("multimap_int_100.txt",100);
generate_legacy_archive<
boost::unordered_multimap<std::string,std::string>,boost::archive::text_oarchive
>("multimap_string_0.txt",0);
generate_legacy_archive<
boost::unordered_multimap<std::string,std::string>,boost::archive::text_oarchive
>("multimap_string_10.txt",10);
generate_legacy_archive<
boost::unordered_multimap<std::string,std::string>,boost::archive::text_oarchive
>("multimap_string_100.txt",100);
generate_legacy_archive<
boost::unordered_set<int>,boost::archive::text_oarchive
>("set_int_0.txt",0);
generate_legacy_archive<
boost::unordered_set<int>,boost::archive::text_oarchive
>("set_int_10.txt",10);
generate_legacy_archive<
boost::unordered_set<int>,boost::archive::text_oarchive
>("set_int_100.txt",100);
generate_legacy_archive<
boost::unordered_set<std::string>,boost::archive::text_oarchive
>("set_string_0.txt",0);
generate_legacy_archive<
boost::unordered_set<std::string>,boost::archive::text_oarchive
>("set_string_10.txt",10);
generate_legacy_archive<
boost::unordered_set<std::string>,boost::archive::text_oarchive
>("set_string_100.txt",100);
generate_legacy_archive<
boost::unordered_multiset<int>,boost::archive::text_oarchive
>("multiset_int_0.txt",0);
generate_legacy_archive<
boost::unordered_multiset<int>,boost::archive::text_oarchive
>("multiset_int_10.txt",10);
generate_legacy_archive<
boost::unordered_multiset<int>,boost::archive::text_oarchive
>("multiset_int_100.txt",100);
generate_legacy_archive<
boost::unordered_multiset<std::string>,boost::archive::text_oarchive
>("multiset_string_0.txt",0);
generate_legacy_archive<
boost::unordered_multiset<std::string>,boost::archive::text_oarchive
>("multiset_string_10.txt",10);
generate_legacy_archive<
boost::unordered_multiset<std::string>,boost::archive::text_oarchive
>("multiset_string_100.txt",100);
generate_legacy_archive<
boost::unordered_map<int,int>,boost::archive::xml_oarchive
>("map_int_0.xml",0);
generate_legacy_archive<
boost::unordered_map<int,int>,boost::archive::xml_oarchive
>("map_int_10.xml",10);
generate_legacy_archive<
boost::unordered_map<int,int>,boost::archive::xml_oarchive
>("map_int_100.xml",100);
generate_legacy_archive<
boost::unordered_map<std::string,std::string>,boost::archive::xml_oarchive
>("map_string_0.xml",0);
generate_legacy_archive<
boost::unordered_map<std::string,std::string>,boost::archive::xml_oarchive
>("map_string_10.xml",10);
generate_legacy_archive<
boost::unordered_map<std::string,std::string>,boost::archive::xml_oarchive
>("map_string_100.xml",100);
generate_legacy_archive<
boost::unordered_multimap<int,int>,boost::archive::xml_oarchive
>("multimap_int_0.xml",0);
generate_legacy_archive<
boost::unordered_multimap<int,int>,boost::archive::xml_oarchive
>("multimap_int_10.xml",10);
generate_legacy_archive<
boost::unordered_multimap<int,int>,boost::archive::xml_oarchive
>("multimap_int_100.xml",100);
generate_legacy_archive<
boost::unordered_multimap<std::string,std::string>,boost::archive::xml_oarchive
>("multimap_string_0.xml",0);
generate_legacy_archive<
boost::unordered_multimap<std::string,std::string>,boost::archive::xml_oarchive
>("multimap_string_10.xml",10);
generate_legacy_archive<
boost::unordered_multimap<std::string,std::string>,boost::archive::xml_oarchive
>("multimap_string_100.xml",100);
generate_legacy_archive<
boost::unordered_set<int>,boost::archive::xml_oarchive
>("set_int_0.xml",0);
generate_legacy_archive<
boost::unordered_set<int>,boost::archive::xml_oarchive
>("set_int_10.xml",10);
generate_legacy_archive<
boost::unordered_set<int>,boost::archive::xml_oarchive
>("set_int_100.xml",100);
generate_legacy_archive<
boost::unordered_set<std::string>,boost::archive::xml_oarchive
>("set_string_0.xml",0);
generate_legacy_archive<
boost::unordered_set<std::string>,boost::archive::xml_oarchive
>("set_string_10.xml",10);
generate_legacy_archive<
boost::unordered_set<std::string>,boost::archive::xml_oarchive
>("set_string_100.xml",100);
generate_legacy_archive<
boost::unordered_multiset<int>,boost::archive::xml_oarchive
>("multiset_int_0.xml",0);
generate_legacy_archive<
boost::unordered_multiset<int>,boost::archive::xml_oarchive
>("multiset_int_10.xml",10);
generate_legacy_archive<
boost::unordered_multiset<int>,boost::archive::xml_oarchive
>("multiset_int_100.xml",100);
generate_legacy_archive<
boost::unordered_multiset<std::string>,boost::archive::xml_oarchive
>("multiset_string_0.xml",0);
generate_legacy_archive<
boost::unordered_multiset<std::string>,boost::archive::xml_oarchive
>("multiset_string_10.xml",10);
generate_legacy_archive<
boost::unordered_multiset<std::string>,boost::archive::xml_oarchive
>("multiset_string_100.xml",100);
}
-1
View File
@@ -1 +0,0 @@
22 serialization::archive 19 0 0 0 0 0 0 0 0 0
-14
View File
@@ -1,14 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
<!DOCTYPE boost_serialization>
<boost_serialization signature="serialization::archive" version="19">
<container class_id="0" tracking_level="0" version="0">
<count>0</count>
<bucket_count>0</bucket_count>
<item_version>0</item_version>
</container>
<values class_id="1" tracking_level="0" version="0">
<count>0</count>
<item_version>0</item_version>
</values>
</boost_serialization>
-1
View File
@@ -1 +0,0 @@
22 serialization::archive 19 0 0 10 13 0 0 0 2097663454 2097663454 -1785808236 -1785808236 -938475945 -938475945 1354395138 1354395138 581989062 581989062 -1694006096 -1694006096 1832705803 1832705803 -743509129 -743509129 1021729225 1021729225 -1168265900 -1168265900 0 0 14 0 0 0 -938475945 -938475945 -938475945 -938475945 -1694006096 -1694006096 -1785808236 -1785808236 1021729225 1021729225 1021729225 1021729225 1832705803 1832705803 1832705803 1832705803 581989062 581989062 1354395138 1354395138 2097663454 2097663454 -743509129 -743509129 -743509129 -743509129 -1168265900 -1168265900
-110
View File
@@ -1,110 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
<!DOCTYPE boost_serialization>
<boost_serialization signature="serialization::archive" version="19">
<container class_id="0" tracking_level="0" version="0">
<count>10</count>
<bucket_count>13</bucket_count>
<item_version>0</item_version>
<item class_id="1" tracking_level="0" version="0">
<first>2097663454</first>
<second>2097663454</second>
</item>
<item>
<first>-1785808236</first>
<second>-1785808236</second>
</item>
<item>
<first>-938475945</first>
<second>-938475945</second>
</item>
<item>
<first>1354395138</first>
<second>1354395138</second>
</item>
<item>
<first>581989062</first>
<second>581989062</second>
</item>
<item>
<first>-1694006096</first>
<second>-1694006096</second>
</item>
<item>
<first>1832705803</first>
<second>1832705803</second>
</item>
<item>
<first>-743509129</first>
<second>-743509129</second>
</item>
<item>
<first>1021729225</first>
<second>1021729225</second>
</item>
<item>
<first>-1168265900</first>
<second>-1168265900</second>
</item>
</container>
<values class_id="2" tracking_level="0" version="0">
<count>14</count>
<item_version>0</item_version>
<item class_id="3" tracking_level="0" version="0">
<first>-938475945</first>
<second>-938475945</second>
</item>
<item>
<first>-938475945</first>
<second>-938475945</second>
</item>
<item>
<first>-1694006096</first>
<second>-1694006096</second>
</item>
<item>
<first>-1785808236</first>
<second>-1785808236</second>
</item>
<item>
<first>1021729225</first>
<second>1021729225</second>
</item>
<item>
<first>1021729225</first>
<second>1021729225</second>
</item>
<item>
<first>1832705803</first>
<second>1832705803</second>
</item>
<item>
<first>1832705803</first>
<second>1832705803</second>
</item>
<item>
<first>581989062</first>
<second>581989062</second>
</item>
<item>
<first>1354395138</first>
<second>1354395138</second>
</item>
<item>
<first>2097663454</first>
<second>2097663454</second>
</item>
<item>
<first>-743509129</first>
<second>-743509129</second>
</item>
<item>
<first>-743509129</first>
<second>-743509129</second>
</item>
<item>
<first>-1168265900</first>
<second>-1168265900</second>
</item>
</values>
</boost_serialization>
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large Load Diff
-1
View File
@@ -1 +0,0 @@
22 serialization::archive 19 0 0 0 0 0 0 0 0 0
-14
View File
@@ -1,14 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
<!DOCTYPE boost_serialization>
<boost_serialization signature="serialization::archive" version="19">
<container class_id="0" tracking_level="0" version="0">
<count>0</count>
<bucket_count>0</bucket_count>
<item_version>0</item_version>
</container>
<values class_id="1" tracking_level="0" version="0">
<count>0</count>
<item_version>0</item_version>
</values>
</boost_serialization>
-1
View File
@@ -1 +0,0 @@
22 serialization::archive 19 0 0 10 13 0 0 0 10 1832705803 10 1832705803 10 2097663454 10 2097663454 10 3551458167 10 3551458167 10 2509159060 10 2509159060 10 2600961200 10 2600961200 9 581989062 9 581989062 10 1021729225 10 1021729225 10 3126701396 10 3126701396 10 1354395138 10 1354395138 10 3356491351 10 3356491351 0 0 14 0 0 0 10 3356491351 10 3356491351 10 3356491351 10 3356491351 10 2600961200 10 2600961200 10 2509159060 10 2509159060 10 1021729225 10 1021729225 10 1021729225 10 1021729225 10 1832705803 10 1832705803 10 1832705803 10 1832705803 9 581989062 9 581989062 10 1354395138 10 1354395138 10 2097663454 10 2097663454 10 3551458167 10 3551458167 10 3551458167 10 3551458167 10 3126701396 10 3126701396
-110
View File
@@ -1,110 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
<!DOCTYPE boost_serialization>
<boost_serialization signature="serialization::archive" version="19">
<container class_id="0" tracking_level="0" version="0">
<count>10</count>
<bucket_count>13</bucket_count>
<item_version>0</item_version>
<item class_id="1" tracking_level="0" version="0">
<first>1832705803</first>
<second>1832705803</second>
</item>
<item>
<first>2097663454</first>
<second>2097663454</second>
</item>
<item>
<first>3551458167</first>
<second>3551458167</second>
</item>
<item>
<first>2509159060</first>
<second>2509159060</second>
</item>
<item>
<first>2600961200</first>
<second>2600961200</second>
</item>
<item>
<first>581989062</first>
<second>581989062</second>
</item>
<item>
<first>1021729225</first>
<second>1021729225</second>
</item>
<item>
<first>3126701396</first>
<second>3126701396</second>
</item>
<item>
<first>1354395138</first>
<second>1354395138</second>
</item>
<item>
<first>3356491351</first>
<second>3356491351</second>
</item>
</container>
<values class_id="2" tracking_level="0" version="0">
<count>14</count>
<item_version>0</item_version>
<item class_id="3" tracking_level="0" version="0">
<first>3356491351</first>
<second>3356491351</second>
</item>
<item>
<first>3356491351</first>
<second>3356491351</second>
</item>
<item>
<first>2600961200</first>
<second>2600961200</second>
</item>
<item>
<first>2509159060</first>
<second>2509159060</second>
</item>
<item>
<first>1021729225</first>
<second>1021729225</second>
</item>
<item>
<first>1021729225</first>
<second>1021729225</second>
</item>
<item>
<first>1832705803</first>
<second>1832705803</second>
</item>
<item>
<first>1832705803</first>
<second>1832705803</second>
</item>
<item>
<first>581989062</first>
<second>581989062</second>
</item>
<item>
<first>1354395138</first>
<second>1354395138</second>
</item>
<item>
<first>2097663454</first>
<second>2097663454</second>
</item>
<item>
<first>3551458167</first>
<second>3551458167</second>
</item>
<item>
<first>3551458167</first>
<second>3551458167</second>
</item>
<item>
<first>3126701396</first>
<second>3126701396</second>
</item>
</values>
</boost_serialization>
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large Load Diff
-1
View File
@@ -1 +0,0 @@
22 serialization::archive 19 0 0 0 0 0 0 0 0 0
-14
View File
@@ -1,14 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
<!DOCTYPE boost_serialization>
<boost_serialization signature="serialization::archive" version="19">
<container class_id="0" tracking_level="0" version="0">
<count>0</count>
<bucket_count>0</bucket_count>
<item_version>0</item_version>
</container>
<values class_id="1" tracking_level="0" version="0">
<count>0</count>
<item_version>0</item_version>
</values>
</boost_serialization>
-1
View File
@@ -1 +0,0 @@
22 serialization::archive 19 0 0 14 29 0 0 0 -938475945 -938475945 -938475945 -938475945 -1785808236 -1785808236 -1694006096 -1694006096 1021729225 1021729225 1021729225 1021729225 581989062 581989062 -1168265900 -1168265900 2097663454 2097663454 1354395138 1354395138 -743509129 -743509129 -743509129 -743509129 1832705803 1832705803 1832705803 1832705803 0 0 14 0 0 0 -938475945 -938475945 -938475945 -938475945 -1694006096 -1694006096 -1785808236 -1785808236 1021729225 1021729225 1021729225 1021729225 1832705803 1832705803 1832705803 1832705803 581989062 581989062 1354395138 1354395138 2097663454 2097663454 -743509129 -743509129 -743509129 -743509129 -1168265900 -1168265900
-126
View File
@@ -1,126 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
<!DOCTYPE boost_serialization>
<boost_serialization signature="serialization::archive" version="19">
<container class_id="0" tracking_level="0" version="0">
<count>14</count>
<bucket_count>29</bucket_count>
<item_version>0</item_version>
<item class_id="1" tracking_level="0" version="0">
<first>-938475945</first>
<second>-938475945</second>
</item>
<item>
<first>-938475945</first>
<second>-938475945</second>
</item>
<item>
<first>-1785808236</first>
<second>-1785808236</second>
</item>
<item>
<first>-1694006096</first>
<second>-1694006096</second>
</item>
<item>
<first>1021729225</first>
<second>1021729225</second>
</item>
<item>
<first>1021729225</first>
<second>1021729225</second>
</item>
<item>
<first>581989062</first>
<second>581989062</second>
</item>
<item>
<first>-1168265900</first>
<second>-1168265900</second>
</item>
<item>
<first>2097663454</first>
<second>2097663454</second>
</item>
<item>
<first>1354395138</first>
<second>1354395138</second>
</item>
<item>
<first>-743509129</first>
<second>-743509129</second>
</item>
<item>
<first>-743509129</first>
<second>-743509129</second>
</item>
<item>
<first>1832705803</first>
<second>1832705803</second>
</item>
<item>
<first>1832705803</first>
<second>1832705803</second>
</item>
</container>
<values class_id="2" tracking_level="0" version="0">
<count>14</count>
<item_version>0</item_version>
<item class_id="3" tracking_level="0" version="0">
<first>-938475945</first>
<second>-938475945</second>
</item>
<item>
<first>-938475945</first>
<second>-938475945</second>
</item>
<item>
<first>-1694006096</first>
<second>-1694006096</second>
</item>
<item>
<first>-1785808236</first>
<second>-1785808236</second>
</item>
<item>
<first>1021729225</first>
<second>1021729225</second>
</item>
<item>
<first>1021729225</first>
<second>1021729225</second>
</item>
<item>
<first>1832705803</first>
<second>1832705803</second>
</item>
<item>
<first>1832705803</first>
<second>1832705803</second>
</item>
<item>
<first>581989062</first>
<second>581989062</second>
</item>
<item>
<first>1354395138</first>
<second>1354395138</second>
</item>
<item>
<first>2097663454</first>
<second>2097663454</second>
</item>
<item>
<first>-743509129</first>
<second>-743509129</second>
</item>
<item>
<first>-743509129</first>
<second>-743509129</second>
</item>
<item>
<first>-1168265900</first>
<second>-1168265900</second>
</item>
</values>
</boost_serialization>
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large Load Diff
@@ -1 +0,0 @@
22 serialization::archive 19 0 0 0 0 0 0 0 0 0
@@ -1,14 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
<!DOCTYPE boost_serialization>
<boost_serialization signature="serialization::archive" version="19">
<container class_id="0" tracking_level="0" version="0">
<count>0</count>
<bucket_count>0</bucket_count>
<item_version>0</item_version>
</container>
<values class_id="1" tracking_level="0" version="0">
<count>0</count>
<item_version>0</item_version>
</values>
</boost_serialization>
@@ -1 +0,0 @@
22 serialization::archive 19 0 0 14 29 0 0 0 10 3551458167 10 3551458167 10 3551458167 10 3551458167 10 2600961200 10 2600961200 10 3126701396 10 3126701396 9 581989062 9 581989062 10 1832705803 10 1832705803 10 1832705803 10 1832705803 10 3356491351 10 3356491351 10 3356491351 10 3356491351 10 1021729225 10 1021729225 10 1021729225 10 1021729225 10 2097663454 10 2097663454 10 1354395138 10 1354395138 10 2509159060 10 2509159060 0 0 14 0 0 0 10 3356491351 10 3356491351 10 3356491351 10 3356491351 10 2600961200 10 2600961200 10 2509159060 10 2509159060 10 1021729225 10 1021729225 10 1021729225 10 1021729225 10 1832705803 10 1832705803 10 1832705803 10 1832705803 9 581989062 9 581989062 10 1354395138 10 1354395138 10 2097663454 10 2097663454 10 3551458167 10 3551458167 10 3551458167 10 3551458167 10 3126701396 10 3126701396
-126
View File
@@ -1,126 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
<!DOCTYPE boost_serialization>
<boost_serialization signature="serialization::archive" version="19">
<container class_id="0" tracking_level="0" version="0">
<count>14</count>
<bucket_count>29</bucket_count>
<item_version>0</item_version>
<item class_id="1" tracking_level="0" version="0">
<first>3551458167</first>
<second>3551458167</second>
</item>
<item>
<first>3551458167</first>
<second>3551458167</second>
</item>
<item>
<first>2600961200</first>
<second>2600961200</second>
</item>
<item>
<first>3126701396</first>
<second>3126701396</second>
</item>
<item>
<first>581989062</first>
<second>581989062</second>
</item>
<item>
<first>1832705803</first>
<second>1832705803</second>
</item>
<item>
<first>1832705803</first>
<second>1832705803</second>
</item>
<item>
<first>3356491351</first>
<second>3356491351</second>
</item>
<item>
<first>3356491351</first>
<second>3356491351</second>
</item>
<item>
<first>1021729225</first>
<second>1021729225</second>
</item>
<item>
<first>1021729225</first>
<second>1021729225</second>
</item>
<item>
<first>2097663454</first>
<second>2097663454</second>
</item>
<item>
<first>1354395138</first>
<second>1354395138</second>
</item>
<item>
<first>2509159060</first>
<second>2509159060</second>
</item>
</container>
<values class_id="2" tracking_level="0" version="0">
<count>14</count>
<item_version>0</item_version>
<item class_id="3" tracking_level="0" version="0">
<first>3356491351</first>
<second>3356491351</second>
</item>
<item>
<first>3356491351</first>
<second>3356491351</second>
</item>
<item>
<first>2600961200</first>
<second>2600961200</second>
</item>
<item>
<first>2509159060</first>
<second>2509159060</second>
</item>
<item>
<first>1021729225</first>
<second>1021729225</second>
</item>
<item>
<first>1021729225</first>
<second>1021729225</second>
</item>
<item>
<first>1832705803</first>
<second>1832705803</second>
</item>
<item>
<first>1832705803</first>
<second>1832705803</second>
</item>
<item>
<first>581989062</first>
<second>581989062</second>
</item>
<item>
<first>1354395138</first>
<second>1354395138</second>
</item>
<item>
<first>2097663454</first>
<second>2097663454</second>
</item>
<item>
<first>3551458167</first>
<second>3551458167</second>
</item>
<item>
<first>3551458167</first>
<second>3551458167</second>
</item>
<item>
<first>3126701396</first>
<second>3126701396</second>
</item>
</values>
</boost_serialization>
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large Load Diff
-1
View File
@@ -1 +0,0 @@
22 serialization::archive 19 0 0 0 0 0 0 0
-14
View File
@@ -1,14 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
<!DOCTYPE boost_serialization>
<boost_serialization signature="serialization::archive" version="19">
<container class_id="0" tracking_level="0" version="0">
<count>0</count>
<bucket_count>0</bucket_count>
<item_version>0</item_version>
</container>
<values>
<count>0</count>
<item_version>0</item_version>
</values>
</boost_serialization>
-1
View File
@@ -1 +0,0 @@
22 serialization::archive 19 0 0 14 29 0 -938475945 -938475945 -1785808236 -1694006096 1021729225 1021729225 581989062 -1168265900 2097663454 1354395138 -743509129 -743509129 1832705803 1832705803 14 0 -938475945 -938475945 -1694006096 -1785808236 1021729225 1021729225 1832705803 1832705803 581989062 1354395138 2097663454 -743509129 -743509129 -1168265900

Some files were not shown because too many files have changed in this diff Show More