Compare commits

...

119 Commits

Author SHA1 Message Date
joaquintides f1d8634344 streamlined unprotected_bulk_visit 2023-10-01 18:05:24 +02:00
joaquintides 29791bbf10 micro-optimizations 2023-10-01 09:57:36 +02:00
joaquintides 35541248d4 made unprotected_bulk_visit chunk size dynamic 2023-09-30 19:31:01 +02:00
joaquintides 937081d1be iterator-based bulk visit 2023-09-30 12:40:21 +02:00
joaquintides e21e321bcf reverted "added bulk_visit2" 2023-09-30 09:43:19 +02:00
joaquintides 379b3047ee added bulk_visit2 2023-09-29 19:34:04 +02:00
joaquintides 1a2e5c8ffe adapted post-#211 2023-09-29 18:14:44 +02:00
joaquintides ddc77ebdc9 drafted bulk_visit 2023-09-29 18:10:37 +02:00
joaquintides 6b65c8f230 fixed assignment bug with POCXA, fancy, unequal allocators (#214) 2023-09-26 20:08:13 +02:00
Christian Mazakas b445ff639d Merge pull request #213 from cmazakas/self-include-tests
self-include tests
2023-09-22 13:02:45 -07:00
Christian Mazakas 08187c7a1f Swap _include_tests suffix for _hpp 2023-09-19 12:27:19 -07:00
Christian Mazakas 6953014874 Update self-include tests to use a significantly shorter mangling scheme for msvc targets 2023-09-19 11:42:23 -07:00
Christian Mazakas 7b24cf6607 Cleanup self-include tests to compile solely as object files 2023-09-19 08:51:12 -07:00
Christian Mazakas 6f0a715541 Update drone.bat to abbreviate paths
Our generated executable names exceed the MAX_PATH length Win32 imposes
2023-09-18 15:32:48 -07:00
Christian Mazakas 85c7900339 Add missing element_type includes 2023-09-18 13:25:21 -07:00
Christian Mazakas c0cd51bd2a Update test jamfile to glob for project headers and create self-include tests 2023-09-18 13:25:21 -07:00
joaquintides 73891c6ec8 Merge pull request #212 from boostorg/feature/concurrent_flat_set
Feature/concurrent_flat_set
2023-09-16 18:36:36 +02:00
joaquintides 82f4d9899c stylistic/editorial 2023-09-16 12:17:07 +02:00
joaquintides cad3509a17 added boost::concurrent_flat_set to mmap_tests 2023-09-16 11:18:39 +02:00
joaquintides 44582ecbb9 removed unused typedefs 2023-09-16 10:18:36 +02:00
joaquintides 9e38e3c578 typos 2023-09-16 10:15:36 +02:00
joaquintides d5d6a18298 removed unused utilities 2023-09-16 10:15:36 +02:00
joaquintides 88f8228079 tested concurrent_flat_set 2023-09-16 10:15:36 +02:00
joaquintides 31c3ce97de avoided sign-conversion warning 2023-09-16 10:15:36 +02:00
joaquintides 651f209e20 shut down VS warning 2023-09-16 10:15:36 +02:00
joaquintides d9d4ff1676 removed unused typedef 2023-09-16 10:15:36 +02:00
joaquintides 2ce456768d completed concurrent_flat_set testing 2023-09-16 10:15:36 +02:00
joaquintides 65d4a9cafa fixed try_emplace tests after (value|init)_type_generator removal in helpers.hpp 2023-09-16 10:15:36 +02:00
joaquintides 8d2a5c25ea tested boost::concurrent_flat_set 2023-09-16 10:15:05 +02:00
joaquintides 49f0929466 documented boost::concurrent_flat_set 2023-09-16 09:50:22 +02:00
joaquintides 0673c5653c added boost::concurrent_flat_set 2023-09-16 09:50:22 +02:00
Christian Mazakas a3a1ab6ad2 Merge pull request #211 from boostorg/fix/gh-189
fix/gh-189
2023-09-15 14:59:00 -07:00
Christian Mazakas 7d639e2e86 Update docs noexcept specification for move assignment 2023-09-15 09:50:33 -07:00
Christian Mazakas 32b9f06fe5 Update release notes to include fancy pointer support 2023-09-15 09:50:33 -07:00
Christian Mazakas 3d302980d4 Update mmap_tests to use Boost.UUID to mangle executable names
CIs can run multiple instances of the test suite at once resulting in duplicate shm_names which is disastrous for each test
2023-09-15 09:50:33 -07:00
Christian Mazakas 1dc1962b8c Update cfoa's assign tests to cover fancy and plain pointers for flat_map<-> concurrent interop 2023-09-15 09:50:33 -07:00
Christian Mazakas a0c7112652 Update arrays transfer in table_core moves constructors to be exception-safe 2023-09-15 09:50:33 -07:00
Christian Mazakas b031b61b94 Update docs on rehashing behavior in face of fancy pointer changes 2023-09-15 09:50:33 -07:00
Christian Mazakas b9fe4ed789 Clean up how group_accesses are setup for concurrent_table_arrays 2023-09-15 09:50:33 -07:00
Christian Mazakas c707bcb6d8 Refactor table_iterator to avoid extraneous amounts of boost::to_address 2023-09-15 09:50:33 -07:00
Christian Mazakas 771943e291 Add to_pointer helper for converting raw pointers to fancy
This helps us avoid an unconditional nullptr check when the user's pointer type is fancy
2023-09-15 09:50:33 -07:00
Christian Mazakas 675e30bd95 Clean up dummy_groups and groups allocation 2023-09-15 09:50:33 -07:00
Christian Mazakas 2cab340749 Replace direct accesses of fancy pointers with getters 2023-09-15 09:50:33 -07:00
Christian Mazakas c3786357a6 Update noexcept specification to accomodate for throwing arrays::new_ 2023-09-15 09:50:33 -07:00
Christian Mazakas 33ee8ea302 Disable mmap_tests for cygwin targets
Interprocess tries to erroneously call ftruncate without the corresponding header being included.
2023-09-15 08:22:31 -07:00
Christian Mazakas 583225ad9f Update docs about relaxed Allocator requirements 2023-09-15 08:22:31 -07:00
Christian Mazakas c16a3f2a4a Update table_iterators to store fancy pointers 2023-08-30 09:57:22 -07:00
Christian Mazakas 1d93fa12a2 Update mmap_tests to include iterators 2023-08-30 09:57:22 -07:00
Christian Mazakas f4f322b94d Update requirements for TypeIndex 2023-08-30 09:57:22 -07:00
Christian Mazakas 3299b31787 Update tests to accomodate foa containers allocating groups when the pointer type is fancy 2023-08-30 09:57:22 -07:00
Christian Mazakas 95a37b0d3d Update foa-based containers to store only fancy pointers 2023-08-30 09:57:20 -07:00
Christian Mazakas 4d6ebc7eb3 Update test fancy pointers to be Nullable, add rebind alias template 2023-08-30 09:56:28 -07:00
Christian Mazakas a0679e5ffe Add mmap_tests 2023-08-30 09:56:28 -07:00
Christian Mazakas b01f9ee15d Refactor Jamfile, add C++11 as compiler minimum 2023-08-30 09:56:28 -07:00
Christian Mazakas 215d8e82cb Merge pull request #207 from boostorg/feature/serialization_support
Feature/serialization support
2023-08-29 10:50:21 -07:00
Christian Mazakas 067884a4e2 Merge pull request #210 from boostorg/fix/gh-205
Fix for issue #205
2023-08-28 10:38:13 -07:00
joaquintides ca2a46f290 complied with https://github.com/boostorg/core/commit/5f6fe65 2023-08-25 19:45:16 +02:00
joaquintides d007a5a7bd renamed serialize_node_pointer as serialize_tracked_address (editorial) 2023-08-25 11:34:39 +02:00
joaquintides b0195297f0 simplified dtor call, s/&/boost::addressof 2023-08-18 21:07:58 +02:00
joaquintides 52061a7c64 typo 2023-08-18 21:03:12 +02:00
joaquintides 829a4a1620 worked around lack of std::shuffle in VS2010 2023-08-18 10:35:55 +02:00
joaquintides 6dd58f4e9c added missing carriage return 2023-08-17 10:28:45 +02:00
joaquintides a2ad7966ea removed serialization tests from UBSAN runs 2023-08-17 10:21:54 +02:00
joaquintides 9d897faece added cfoa_serialization_tests 2023-08-17 10:21:54 +02:00
joaquintides 389e967484 added embarrassingly absent BOOST_TEST 2023-08-17 10:21:54 +02:00
joaquintides 0365be763a dealt with serialization_tests's big executable size 2023-08-17 10:21:54 +02:00
joaquintides fc3fca4264 reverted prior (didn't work) 2023-08-17 10:21:54 +02:00
joaquintides bf0ae6e63a marked UB-incurring boost::archive::xml_oarchive ctor/dtor as no_sanitize 2023-08-17 10:21:54 +02:00
joaquintides 9057369f93 refactored to work around GCC 4.4 hiccups with std::pair<X*,...> p(0,...) 2023-08-17 10:21:54 +02:00
joaquintides aec9c48cf4 restored passing of working directory 2023-08-17 10:21:54 +02:00
joaquintides 3968ff0567 uploaded generate_legacy_archives.cpp for preservation 2023-08-17 10:21:54 +02:00
joaquintides 8170716156 de-constified values in legacy_serialization_test to appease some defective C++03 compilers 2023-08-17 10:21:54 +02:00
joaquintides 651727508b removed unneeded working directory passing 2023-08-17 10:21:54 +02:00
joaquintides b3b4853dfa added legacy serialization tests 2023-08-17 10:21:54 +02:00
joaquintides 1f3980986d avoided sign-compare warnings 2023-08-17 10:21:54 +02:00
joaquintides 4e458f0054 added foa_exception_tests 2023-08-17 10:21:54 +02:00
joaquintides 8fe3ebc7b3 simplified serialization building 2023-08-17 10:21:54 +02:00
joaquintides cf298cba7f removed spurious macro definition 2023-08-17 10:21:54 +02:00
joaquintides d83efc5ea4 added first tests of serialization support 2023-08-17 10:21:54 +02:00
joaquintides ff9d08a917 adapted to iterators (eventually) holding fancy pointers 2023-08-17 10:21:54 +02:00
joaquintides e1a30831fe editorial 2023-08-17 10:21:54 +02:00
joaquintides 34e5773a4a documented serialization support 2023-08-17 10:21:45 +02:00
joaquintides c26137f2dd unnamed unused arg 2023-08-17 10:20:25 +02:00
joaquintides 1264805a59 avoided type shadowing 2023-08-17 10:20:25 +02:00
joaquintides bfb6520107 unnamed unused args 2023-08-17 10:20:25 +02:00
joaquintides ab867a65f4 unnamed unused args 2023-08-17 10:20:25 +02:00
joaquintides 5239b101e2 added serialization support (pending docs and testing) 2023-08-17 10:20:25 +02:00
Christian Mazakas c5f64ed1d3 Prevent erroneous copying of groups data, update max load to properly propagate 2023-08-16 10:22:08 -07:00
Christian Mazakas a06498f5ce Add tests catching erroneous copying of groups data 2023-08-16 10:21:27 -07:00
joaquintides bcd8969b9a editorial 2023-08-12 12:43:29 +02:00
joaquintides bc7e77f239 Merge pull request #200 from boostorg/feature/detect_reentrancy
Feature/detect reentrancy
2023-08-12 11:58:29 +02:00
joaquintides 66222eee6a Merge branch 'develop' into feature/detect_reentrancy 2023-08-12 09:54:22 +02:00
joaquintides ee1974dc87 Merge pull request #198 from boostorg/feature/concurrent_unordered_interop
Feature/concurrent unordered interop
2023-08-12 09:48:08 +02:00
Christian Mazakas 4918bb6b7e Add tests for interop assignment 2023-08-11 12:35:44 -07:00
Christian Mazakas e563c89b1c Remove extraneous typename 2023-08-11 12:35:44 -07:00
Christian Mazakas ce076782fd Add tests for interop constructors 2023-08-11 12:35:44 -07:00
joaquintides b206513a11 fixed friend declarations 2023-08-11 12:35:44 -07:00
joaquintides bf4a5efd2d documented concurrent/unordered interop 2023-08-11 12:35:40 -07:00
joaquintides 6b6dde4f97 stylistic 2023-08-11 12:35:13 -07:00
joaquintides ac1a236de6 added concurrent_flat_map(unordered_flat_map&&) 2023-08-11 12:35:13 -07:00
joaquintides d4adcd9b71 added missing std::move's 2023-08-11 12:35:13 -07:00
joaquintides c046b916f8 fix atomic_size_control copy ctor 2023-08-11 12:35:13 -07:00
joaquintides 4be37cfdaf avoided premature instantiation of concurrent_table 2023-08-11 12:35:13 -07:00
joaquintides 6994a37b23 used direct arrays construction in place of empty_initialize
(arrays_type not default constructible)
2023-08-11 12:35:13 -07:00
joaquintides 6bf84067b3 added unordered_flat_map(concurrent_flat_map&&) 2023-08-11 12:35:13 -07:00
Christian Mazakas 48ff743d06 Merge pull request #199 from boostorg/feature/visit_until
Feature/visit until
2023-08-11 12:26:13 -07:00
joaquintides 1979ce98a2 documented BOOST_UNORDERED_DISABLE_REENTRANCY_CHECK 2023-07-28 18:22:20 +02:00
Christian Mazakas a22c133c3d Add tests for visit_while 2023-07-26 10:14:11 -07:00
joaquintides f919ce532a repositioned LCOV annotations 2023-07-26 16:50:25 +02:00
joaquintides 30187f7743 excluded code from coverage analysis 2023-07-26 12:47:31 +02:00
joaquintides dd30162c9e simplified reentrancy check config 2023-07-25 09:18:53 +02:00
joaquintides fde765c494 added reentrancy check to release notes 2023-07-24 20:31:32 +02:00
joaquintides 9c476ef72a typo 2023-07-24 20:19:09 +02:00
joaquintides 470abf41d8 dropped [c]visit_until 2023-07-24 20:13:18 +02:00
joaquintides a3626b5095 fixed reentrancy checking for scoped_bilock 2023-07-24 19:44:09 +02:00
joaquintides 948151bd7d added RVO enabler 2023-07-24 18:43:20 +02:00
joaquintides f965298154 added reentrancy checking 2023-07-24 18:29:30 +02:00
joaquintides 5a4d93a72d documented [c]visit_(until|while) 2023-07-21 19:39:43 +02:00
joaquintides bee4ed2e5f implemented [c]visit_(until|while) 2023-07-21 18:16:28 +02:00
130 changed files with 15726 additions and 1690 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 -j3 libs/%LIBRARY%/test toolset=%TOOLSET% %CXXSTD% %ADDRMD% variant=debug,release embed-manifest-via=linker
b2 --abbreviate-paths -j3 libs/%LIBRARY%/test toolset=%TOOLSET% %CXXSTD% %ADDRMD% variant=debug,release embed-manifest-via=linker
+13
View File
@@ -6,6 +6,19 @@
: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 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
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
due to their inherent problems in concurrent scenarios (high contention, prone to deadlocking):
so, `boost::concurrent_flat_map` is technically not a
so, Boost.Unordered concurrent containers are technically not models of
https://en.cppreference.com/w/cpp/named_req/Container[Container^], although
it meets all the requirements of https://en.cppreference.com/w/cpp/named_req/AllocatorAwareContainer[AllocatorAware^]
they meet 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_map` uses _internal visitation_
In place of iterators, `boost::concurrent_flat_set` and `boost::concurrent_flat_map` use _internal visitation_
facilities as a thread-safe substitute. Classical operations returning an iterator to an
element already existing in the container, like for instance:
@@ -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 `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,
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,
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).
explicit const visitation); In the case of `boost::concurrent_flat_set`, visitation is always const.
The one notable operation not provided is `operator[]`/`at`, which can be
One notable operation not provided by `boost::concurrent_flat_map` is `operator[]`/`at`, which can be
replaced, if in a more convoluted manner, by
xref:#concurrent_flat_map_try_emplace_or_cvisit[`try_emplace_or_visit`].
+71 -18
View File
@@ -3,8 +3,8 @@
:idprefix: concurrent_
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
Boost.Unordered provides `boost::concurrent_flat_set` and `boost::concurrent_flat_map`,
hash tables that allow concurrent write/read access from
different threads without having to implement any synchronzation mechanism on the user's side.
[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::concurrent_flat_map`
is designed to perform with very little overhead and typically achieves _linear scaling_
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_
(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_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^]
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^]
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::concurrent_flat_map` in
is executed internally by Boost.Unordered 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_map`, and
Visitation is prominent in the API provided by `boost::concurrent_flat_set` and `boost::concurrent_flat_map`, and
many classical operations have visitation-enabled variations:
[source,c++]
@@ -129,13 +129,17 @@ 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. Consult the xref:#concurrent_flat_map[reference]
for a complete list of available operations.
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.
== Whole-Table Visitation
In the absence of iterators, `boost::concurrent_flat_map` provides `visit_all`
as an alternative way to process all the elements in the map:
In the absence of iterators, `visit_all` is provided
as an alternative way to process all the elements in the container:
[source,c++]
----
@@ -154,7 +158,28 @@ m.visit_all(std::execution::par, [](auto& x) { // run in parallel
});
----
There is another whole-table visitation operation, `erase_if`:
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`:
[source,c++]
----
@@ -163,15 +188,15 @@ m.erase_if([](auto& x) {
});
----
`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
`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
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 `boost::concurrent_flat_map`
advisable not to assume too much about the exact global state of a concurrent container
at any point in your program.
== Blocking Operations
``boost::concurrent_flat_map``s can be copied, assigned, cleared and merged just like any
``boost::concurrent_flat_set``s and ``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
@@ -180,3 +205,31 @@ 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);
----
+120 -7
View File
@@ -69,6 +69,7 @@ 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(),
@@ -89,9 +90,10 @@ 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);
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_initializer_list_assignment[operator++=++](std::initializer_list<value_type>);
allocator_type xref:#concurrent_flat_map_get_allocator[get_allocator]() const noexcept;
@@ -114,6 +116,16 @@ 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;
@@ -305,8 +317,7 @@ 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.
`std::allocator_traits<Allocator>::pointer` and `std::allocator_traits<Allocator>::const_pointer`
must be convertible to/from `value_type*` and `const value_type*`, respectively.
Allocators using https://en.cppreference.com/w/cpp/named_req/Allocator#Fancy_pointers[fancy pointers] are supported.
|===
@@ -364,6 +375,18 @@ 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
@@ -491,6 +514,21 @@ 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"]
----
@@ -635,8 +673,9 @@ 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);
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);
```
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`.
@@ -720,6 +759,50 @@ 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
@@ -1436,3 +1519,33 @@ 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
+5 -4
View File
@@ -44,7 +44,8 @@ boost::unordered_flat_map
^.^h|*Concurrent*
^|
^| `boost::concurrent_flat_map`
^| `boost::concurrent_flat_set` +
`boost::concurrent_flat_map`
|===
@@ -56,9 +57,8 @@ 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, `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.
* 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.
All sets and maps in Boost.Unordered are instantiatied similarly as
`std::unordered_set` and `std::unordered_map`, respectively:
@@ -73,6 +73,7 @@ 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,
+6 -5
View File
@@ -121,20 +121,21 @@ 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_map`:
also as the foundation of `boost::concurrent_flat_set` and `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 fast transfer
of all elements between `boost::concurrent_flat_map` and `boost::unordered_flat_map`.
(This feature has not been implemented yet.)
* 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.
=== Hash Function and Platform Interoperability
`boost::concurrent_flat_map` makes the same decisions and provides the same guarantees
Concurrent containers make the same decisions and provide 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,3 +11,4 @@ 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_map`/`unordered_node_map` and
`boost:unordered_flat_set`/`unordered_node_set`.
The diagram shows the basic internal layout of `boost::unordered_flat_set`/`unordered_node_set` and
`boost:unordered_flat_map`/`unordered_node_map`.
[#img-foa-layout]
@@ -76,7 +76,7 @@ The diagram shows the basic internal layout of `boost::unordered_flat_map`/`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_map` and `boost::unordered_node_set`) are stored directly in the bucket array.
`boost::unordered_node_set` and `boost::unordered_node_map`) 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_map` uses the basic
`boost::concurrent_flat_set` and `boost::concurrent_flat_map` use the basic
xref:#structures_open_addressing_containers[open-addressing layout] described above
augmented with synchronization mechanisms.
+76 -8
View File
@@ -77,6 +77,7 @@ 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(),
@@ -96,9 +97,10 @@ 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);
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_initializer_list_assignment[operator++=++](std::initializer_list<value_type>);
allocator_type xref:#unordered_flat_map_get_allocator[get_allocator]() const noexcept;
@@ -311,8 +313,7 @@ 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.
`std::allocator_traits<Allocator>::pointer` and `std::allocator_traits<Allocator>::const_pointer`
must be convertible to/from `value_type*` and `const value_type*`, respectively.
Allocators using https://en.cppreference.com/w/cpp/named_req/Allocator#Fancy_pointers[fancy pointers] are supported.
|===
@@ -472,6 +473,22 @@ 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"]
----
@@ -615,8 +632,9 @@ 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);
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);
```
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`.
@@ -1308,7 +1326,7 @@ void rehash(size_type n);
Changes if necessary the size of the bucket array so that there are at least `n` buckets, and so that the load factor is less than or equal to the maximum load factor. When applicable, this will either grow or shrink the `bucket_count()` associated with the container.
When `size() == 0`, `rehash(0)` will deallocate the underlying buckets array.
When `size() == 0`, `rehash(0)` will deallocate the underlying buckets array. If the provided Allocator uses fancy pointers, a default allocation is subsequently performed.
Invalidates iterators, pointers and references, and changes the order of elements.
@@ -1447,4 +1465,54 @@ 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`.
+76 -9
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_with_allocator[unordered_flat_set](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_initializer_list_constructor[unordered_flat_set](std::initializer_list<value_type> il,
size_type n = _implementation-defined_
const hasher& hf = hasher(),
@@ -91,9 +91,10 @@ 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);
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_initializer_list_assignment[operator++=++](std::initializer_list<value_type>);
allocator_type xref:#unordered_flat_set_get_allocator[get_allocator]() const noexcept;
@@ -261,8 +262,7 @@ and https://en.cppreference.com/w/cpp/named_req/Erasable[Erasable^] from the con
|_Allocator_
|An allocator whose value type is the same as the container's value type.
`std::allocator_traits<Allocator>::pointer` and `std::allocator_traits<Allocator>::const_pointer`
must be convertible to/from `value_type*` and `const value_type*`, respectively.
Allocators using https://en.cppreference.com/w/cpp/named_req/Allocator#Fancy_pointers[fancy pointers] are supported.
|===
@@ -422,6 +422,22 @@ 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"]
----
@@ -565,8 +581,9 @@ 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);
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);
```
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`.
@@ -1087,7 +1104,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.
When `size() == 0`, `rehash(0)` will deallocate the underlying buckets array. If the provided Allocator uses fancy pointers, a default allocation is subsequently performed.
Invalidates iterators, pointers and references, and changes the order of elements.
@@ -1201,4 +1218,54 @@ 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`.
+61 -1
View File
@@ -323,7 +323,12 @@ 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
@@ -1825,4 +1830,59 @@ 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,6 +290,13 @@ 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]
@@ -1552,4 +1559,59 @@ 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`.
+60 -1
View File
@@ -278,7 +278,12 @@ 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
@@ -1485,4 +1490,58 @@ 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`.
+62 -8
View File
@@ -95,9 +95,10 @@ 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);
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_initializer_list_assignment[operator++=++](std::initializer_list<value_type>);
allocator_type xref:#unordered_node_map_get_allocator[get_allocator]() const noexcept;
@@ -314,8 +315,7 @@ https://en.cppreference.com/w/cpp/named_req/Erasable[Erasable^] from the contain
|_Allocator_
|An allocator whose value type is the same as the container's value type.
`std::allocator_traits<Allocator>::pointer` and `std::allocator_traits<Allocator>::const_pointer`
must be convertible to/from `value_type*` and `const value_type*`, respectively.
Allocators using https://en.cppreference.com/w/cpp/named_req/Allocator#Fancy_pointers[fancy pointers] are supported.
|===
@@ -649,8 +649,9 @@ 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);
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);
```
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`.
@@ -1406,7 +1407,7 @@ void rehash(size_type n);
Changes if necessary the size of the bucket array so that there are at least `n` buckets, and so that the load factor is less than or equal to the maximum load factor. When applicable, this will either grow or shrink the `bucket_count()` associated with the container.
When `size() == 0`, `rehash(0)` will deallocate the underlying buckets array.
When `size() == 0`, `rehash(0)` will deallocate the underlying buckets array. If the provided Allocator uses fancy pointers, a default allocation is subsequently performed.
Invalidates iterators and changes the order of elements.
@@ -1545,4 +1546,57 @@ 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`.
+60 -8
View File
@@ -90,9 +90,10 @@ 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);
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_initializer_list_assignment[operator++=++](std::initializer_list<value_type>);
allocator_type xref:#unordered_node_set_get_allocator[get_allocator]() const noexcept;
@@ -264,8 +265,7 @@ namespace boost {
|_Allocator_
|An allocator whose value type is the same as the container's value type.
`std::allocator_traits<Allocator>::pointer` and `std::allocator_traits<Allocator>::const_pointer`
must be convertible to/from `value_type*` and `const value_type*`, respectively.
Allocators using https://en.cppreference.com/w/cpp/named_req/Allocator#Fancy_pointers[fancy pointers] are supported.
|===
@@ -602,8 +602,9 @@ 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);
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);
```
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`.
@@ -1188,7 +1189,7 @@ void rehash(size_type n);
Changes if necessary the size of the bucket array so that there are at least `n` buckets, and so that the load factor is less than or equal to the maximum load factor. When applicable, this will either grow or shrink the `bucket_count()` associated with the container.
When `size() == 0`, `rehash(0)` will deallocate the underlying buckets array.
When `size() == 0`, `rehash(0)` will deallocate the underlying buckets array. If the provided Allocator uses fancy pointers, a default allocation is subsequently performed.
Invalidates iterators and changes the order of elements.
@@ -1302,4 +1303,55 @@ 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`.
+60 -1
View File
@@ -279,7 +279,12 @@ 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
@@ -1550,4 +1555,58 @@ 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`.
+92 -68
View File
@@ -1,4 +1,4 @@
/* Fast open-addressing concurrent hash table.
/* Fast open-addressing concurrent hashmap.
*
* Copyright 2023 Christian Mazakas.
* Distributed under the Boost Software License, Version 1.0.
@@ -12,71 +12,21 @@
#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/mp11/algorithm.hpp>
#include <boost/mp11/list.hpp>
#include <boost/core/serialization.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
{
@@ -84,10 +34,16 @@ 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>;
detail::foa::concurrent_table<type_policy, Hash, Pred, Allocator> table_;
using table_type =
detail::foa::concurrent_table<type_policy, Hash, Pred, Allocator>;
table_type table_;
template <class K, class V, class H, class KE, class A>
bool friend operator==(concurrent_flat_map<K, V, H, KE, A> const& lhs,
@@ -97,6 +53,11 @@ 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;
@@ -223,6 +184,13 @@ 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)
@@ -231,10 +199,8 @@ namespace boost {
return *this;
}
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)
concurrent_flat_map& operator=(concurrent_flat_map&& rhs) noexcept(
noexcept(std::declval<table_type&>() = std::declval<table_type&&>()))
{
table_ = std::move(rhs.table_);
return *this;
@@ -278,6 +244,14 @@ 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
@@ -355,6 +329,56 @@ 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
///
@@ -411,6 +435,7 @@ 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);
}
@@ -465,7 +490,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_visit(ilist.begin(), ilist.end(), f);
this->insert_or_cvisit(ilist.begin(), ilist.end(), f);
}
template <class... Args> BOOST_FORCEINLINE bool emplace(Args&&... args)
@@ -711,6 +736,13 @@ 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,
@@ -807,12 +839,4 @@ 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 hash table.
/* Fast open-addressing concurrent hashmap.
*
* Copyright 2023 Christian Mazakas.
* Distributed under the Boost Software License, Version 1.0.
@@ -0,0 +1,697 @@
/* 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
@@ -0,0 +1,55 @@
/* 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
@@ -0,0 +1,72 @@
/* 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
@@ -0,0 +1,27 @@
/* 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
@@ -0,0 +1,75 @@
/* 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
+24 -3
View File
@@ -1,4 +1,4 @@
// Copyright (C) 2022 Joaquin M Lopez Munoz.
// Copyright (C) 2022-2023 Joaquin M Lopez Munoz.
// Copyright (C) 2022 Christian Mazakas
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
@@ -114,16 +114,18 @@ 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>
@@ -287,6 +289,25 @@ 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;
@@ -630,7 +651,7 @@ namespace boost {
bool b = boost::allocator_propagate_on_container_swap<
allocator_type>::type::value;
if (b) {
boost::swap(get_node_allocator(), other.get_node_allocator());
boost::core::invoke_swap(get_node_allocator(), other.get_node_allocator());
}
}
@@ -16,15 +16,22 @@
#include <boost/config.hpp>
#include <boost/core/ignore_unused.hpp>
#include <boost/core/no_exceptions_support.hpp>
#include <boost/core/serialization.hpp>
#include <boost/cstdint.hpp>
#include <boost/mp11/tuple.hpp>
#include <boost/static_assert.hpp>
#include <boost/throw_exception.hpp>
#include <boost/unordered/detail/archive_constructed.hpp>
#include <boost/unordered/detail/bad_archive_exception.hpp>
#include <boost/unordered/detail/foa/core.hpp>
#include <boost/unordered/detail/foa/reentrancy_check.hpp>
#include <boost/unordered/detail/foa/rw_spinlock.hpp>
#include <boost/unordered/detail/foa/tuple_rotate_right.hpp>
#include <boost/unordered/detail/serialization_version.hpp>
#include <cstddef>
#include <functional>
#include <initializer_list>
#include <iterator>
#include <memory>
#include <new>
#include <type_traits>
@@ -241,63 +248,95 @@ group_access* dummy_group_accesses()
/* subclasses table_arrays to add an additional group_access array */
template<typename Value,typename Group,typename SizePolicy>
struct concurrent_table_arrays:table_arrays<Value,Group,SizePolicy>
template<typename Value,typename Group,typename SizePolicy,typename Allocator>
struct concurrent_table_arrays:table_arrays<Value,Group,SizePolicy,Allocator>
{
using super=table_arrays<Value,Group,SizePolicy>;
using group_access_allocator_type=
typename boost::allocator_rebind<Allocator,group_access>::type;
using group_access_pointer=
typename boost::allocator_pointer<group_access_allocator_type>::type;
concurrent_table_arrays(const super& arrays,group_access *pga):
super{arrays},group_accesses{pga}{}
using super=table_arrays<Value,Group,SizePolicy,Allocator>;
template<typename Allocator>
static concurrent_table_arrays new_(Allocator& al,std::size_t n)
concurrent_table_arrays(const super& arrays,group_access_pointer pga):
super{arrays},group_accesses_{pga}{}
group_access* group_accesses()const noexcept{
return boost::to_address(group_accesses_);
}
static concurrent_table_arrays new_(
group_access_allocator_type al,std::size_t n)
{
concurrent_table_arrays arrays{super::new_(al,n),nullptr};
if(!arrays.elements){
arrays.group_accesses=dummy_group_accesses<SizePolicy::min_size()>();
super x{super::new_(al,n)};
BOOST_TRY{
return new_group_access(al,x);
}
else{
using access_alloc=
typename boost::allocator_rebind<Allocator,group_access>::type;
using access_traits=boost::allocator_traits<access_alloc>;
BOOST_TRY{
auto aal=access_alloc(al);
arrays.group_accesses=boost::to_address(
access_traits::allocate(aal,arrays.groups_size_mask+1));
for(std::size_t i=0;i<arrays.groups_size_mask+1;++i){
::new (arrays.group_accesses+i) group_access();
}
}
BOOST_CATCH(...){
super::delete_(al,arrays);
BOOST_RETHROW
}
BOOST_CATCH_END
BOOST_CATCH(...){
super::delete_(al,x);
BOOST_RETHROW
}
BOOST_CATCH_END
}
static void set_group_access(
group_access_allocator_type al,concurrent_table_arrays& arrays)
{
set_group_access(
al,arrays,std::is_same<group_access*,group_access_pointer>{});
}
static void set_group_access(
group_access_allocator_type al,
concurrent_table_arrays& arrays,
std::false_type /* fancy pointers */)
{
arrays.group_accesses_=
boost::allocator_allocate(al,arrays.groups_size_mask+1);
for(std::size_t i=0;i<arrays.groups_size_mask+1;++i){
::new (arrays.group_accesses()+i) group_access();
}
}
static void set_group_access(
group_access_allocator_type al,
concurrent_table_arrays& arrays,
std::true_type /* optimize when elements() is null */)
{
if(!arrays.elements()){
arrays.group_accesses_=
dummy_group_accesses<SizePolicy::min_size()>();
} else {
set_group_access(al,arrays,std::false_type{});
}
}
static concurrent_table_arrays new_group_access(
group_access_allocator_type al,const super& x)
{
concurrent_table_arrays arrays{x,nullptr};
set_group_access(al,arrays);
return arrays;
}
template<typename Allocator>
static void delete_(Allocator& al,concurrent_table_arrays& arrays)noexcept
static void delete_(
group_access_allocator_type al,concurrent_table_arrays& arrays)noexcept
{
if(arrays.elements){
using access_alloc=
typename boost::allocator_rebind<Allocator,group_access>::type;
using access_traits=boost::allocator_traits<access_alloc>;
using pointer=typename access_traits::pointer;
using pointer_traits=boost::pointer_traits<pointer>;
auto aal=access_alloc(al);
access_traits::deallocate(
aal,pointer_traits::pointer_to(*arrays.group_accesses),
arrays.groups_size_mask+1);
}
delete_group_access(al,arrays);
super::delete_(al,arrays);
}
group_access *group_accesses;
static void delete_group_access(
group_access_allocator_type al,concurrent_table_arrays& arrays)noexcept
{
if(arrays.elements()){
boost::allocator_deallocate(
al,arrays.group_accesses_,arrays.groups_size_mask+1);
}
}
group_access_pointer group_accesses_;
};
struct atomic_size_control
@@ -307,7 +346,7 @@ struct atomic_size_control
atomic_size_control(std::size_t ml_,std::size_t size_):
pad0_{},ml{ml_},pad1_{},size{size_}{}
atomic_size_control(atomic_size_control& x):
atomic_size_control(const atomic_size_control& x):
pad0_{},ml{x.ml.load()},pad1_{},size{x.size.load()}{}
/* padding to avoid false sharing internally and with sorrounding data */
@@ -335,9 +374,7 @@ inline void swap(atomic_size_control& x,atomic_size_control& y)
}
/* foa::concurrent_table serves as the foundation for end-user concurrent
* hash containers. The TypePolicy parameter can specify flat/node-based
* map-like and set-like containers, though currently we're only providing
* boost::concurrent_flat_map.
* hash containers.
*
* The exposed interface (completed by the wrapping containers) is not that
* of a regular container (in fact, it does not model Container as understood
@@ -359,7 +396,7 @@ inline void swap(atomic_size_control& x,atomic_size_control& y)
* - Parallel versions of [c]visit_all(f) and erase_if(f) are provided based
* on C++17 stdlib parallel algorithms.
*
* Consult boost::unordered_flat_map docs for the full API reference.
* Consult boost::concurrent_flat_(map|set) docs for the full API reference.
* Heterogeneous lookup is suported by default, that is, without checking for
* any ::is_transparent typedefs --this checking is done by the wrapping
* containers.
@@ -387,10 +424,13 @@ inline void swap(atomic_size_control& x,atomic_size_control& y)
* reduced hash value is set) and the insertion counter is atomically
* incremented: if no other thread has incremented the counter during the
* whole operation (which is checked by comparing with c0), then we're
* good to go and complete the insertion, otherwise we roll back and start
* over.
* good to go and complete the insertion, otherwise we roll back and
* start over.
*/
template<typename,typename,typename,typename>
class table; /* concurrent/non-concurrent interop */
template <typename TypePolicy,typename Hash,typename Pred,typename Allocator>
using concurrent_table_core_impl=table_core<
TypePolicy,group15<atomic_integral>,concurrent_table_arrays,
@@ -412,10 +452,10 @@ class concurrent_table:
using group_type=typename super::group_type;
using super::N;
using prober=typename super::prober;
template<
typename TypePolicy2,typename Hash2,typename Pred2,typename Allocator2>
friend class concurrent_table;
using arrays_type=typename super::arrays_type;
using size_ctrl_type=typename super::size_ctrl_type;
using compatible_nonconcurrent_table=table<TypePolicy,Hash,Pred,Allocator>;
friend compatible_nonconcurrent_table;
public:
using key_type=typename super::key_type;
@@ -450,6 +490,30 @@ public:
concurrent_table(x,al_,x.exclusive_access()){}
concurrent_table(concurrent_table&& x,const Allocator& al_):
concurrent_table(std::move(x),al_,x.exclusive_access()){}
template<typename ArraysType>
concurrent_table(
compatible_nonconcurrent_table&& x,
arrays_holder<ArraysType,Allocator>&& ah):
super{
std::move(x.h()),std::move(x.pred()),std::move(x.al()),
[&x]{return arrays_type::new_group_access(
x.al(),typename arrays_type::super{
x.arrays.groups_size_index,x.arrays.groups_size_mask,
to_pointer<typename arrays_type::group_type_pointer>(
reinterpret_cast<group_type*>(x.arrays.groups())),
x.arrays.elements_});},
size_ctrl_type{x.size_ctrl.ml,x.size_ctrl.size}}
{
x.arrays=ah.release();
x.size_ctrl.ml=x.initial_max_load();
x.size_ctrl.size=0;
}
concurrent_table(compatible_nonconcurrent_table&& x):
concurrent_table(std::move(x),x.make_empty_arrays())
{}
~concurrent_table()=default;
concurrent_table& operator=(const concurrent_table& x)
@@ -459,7 +523,8 @@ public:
return *this;
}
concurrent_table& operator=(concurrent_table&& x)
concurrent_table& operator=(concurrent_table&& x)noexcept(
noexcept(std::declval<super&>() = std::declval<super&&>()))
{
auto lck=exclusive_access(*this,x);
super::operator=(std::move(x));
@@ -500,6 +565,97 @@ public:
return visit(x,std::forward<F>(f));
}
static constexpr std::size_t bulk_visit_size=16;
#if 0
#define BOOST_UNORDERED_BULK_VISIT_CASE(r,b) \
case r: \
res+=unprotected_bulk_visit<b>( \
group_shared{},first,std::forward<F>(f)); \
break;
template<typename FwdIterator,typename F>
std::size_t visit(FwdIterator first,FwdIterator last,F&& f)const
{
BOOST_STATIC_ASSERT(bulk_visit_size==16);
std::size_t res=0;
auto n=static_cast<std::size_t>(std::distance(first,last));
auto m=n/bulk_visit_size;
auto lck=shared_access();
if(m){
for(std::size_t i=m-1;i--;){
res+=unprotected_bulk_visit<bulk_visit_size>(
group_shared{},first,std::forward<F>(f));
std::advance(first,bulk_visit_size);
}
switch(n-m*bulk_visit_size){
BOOST_UNORDERED_BULK_VISIT_CASE(0,bulk_visit_size)
BOOST_UNORDERED_BULK_VISIT_CASE(1,bulk_visit_size+1)
BOOST_UNORDERED_BULK_VISIT_CASE(2,bulk_visit_size+2)
BOOST_UNORDERED_BULK_VISIT_CASE(3,bulk_visit_size+3)
BOOST_UNORDERED_BULK_VISIT_CASE(4,bulk_visit_size+4)
BOOST_UNORDERED_BULK_VISIT_CASE(5,bulk_visit_size+5)
BOOST_UNORDERED_BULK_VISIT_CASE(6,bulk_visit_size+6)
BOOST_UNORDERED_BULK_VISIT_CASE(7,bulk_visit_size+7)
BOOST_UNORDERED_BULK_VISIT_CASE(8,bulk_visit_size+8)
BOOST_UNORDERED_BULK_VISIT_CASE(9,bulk_visit_size+9)
BOOST_UNORDERED_BULK_VISIT_CASE(10,bulk_visit_size+10)
BOOST_UNORDERED_BULK_VISIT_CASE(11,bulk_visit_size+11)
BOOST_UNORDERED_BULK_VISIT_CASE(12,bulk_visit_size+12)
BOOST_UNORDERED_BULK_VISIT_CASE(13,bulk_visit_size+13)
BOOST_UNORDERED_BULK_VISIT_CASE(14,bulk_visit_size+14)
BOOST_UNORDERED_BULK_VISIT_CASE(15,bulk_visit_size+15)
default:break;
}
}
else{
switch(n){
case 0:break;
BOOST_UNORDERED_BULK_VISIT_CASE(1,1)
BOOST_UNORDERED_BULK_VISIT_CASE(2,2)
BOOST_UNORDERED_BULK_VISIT_CASE(3,3)
BOOST_UNORDERED_BULK_VISIT_CASE(4,4)
BOOST_UNORDERED_BULK_VISIT_CASE(5,5)
BOOST_UNORDERED_BULK_VISIT_CASE(6,6)
BOOST_UNORDERED_BULK_VISIT_CASE(7,7)
BOOST_UNORDERED_BULK_VISIT_CASE(8,8)
BOOST_UNORDERED_BULK_VISIT_CASE(9,9)
BOOST_UNORDERED_BULK_VISIT_CASE(10,10)
BOOST_UNORDERED_BULK_VISIT_CASE(11,11)
BOOST_UNORDERED_BULK_VISIT_CASE(12,12)
BOOST_UNORDERED_BULK_VISIT_CASE(13,13)
BOOST_UNORDERED_BULK_VISIT_CASE(14,14)
BOOST_UNORDERED_BULK_VISIT_CASE(15,15)
default:break;
}
}
return res;
}
#undef BOOST_UNORDERED_BULK_VISIT_CASE
#else
template<typename FwdIterator,typename F>
BOOST_FORCEINLINE
std::size_t visit(FwdIterator first,FwdIterator last,F&& f)const
{
std::size_t res=0;
auto n=static_cast<std::size_t>(std::distance(first,last));
auto lck=shared_access();
while(n){
auto m=n<2*bulk_visit_size?n:bulk_visit_size;
res+=unprotected_bulk_visit(group_shared{},first,m,std::forward<F>(f));
n-=m;
std::advance(first,m);
}
return res;
}
#endif
template<typename F> std::size_t visit_all(F&& f)
{
return visit_all_impl(group_exclusive{},std::forward<F>(f));
@@ -539,6 +695,46 @@ public:
}
#endif
template<typename F> bool visit_while(F&& f)
{
return visit_while_impl(group_exclusive{},std::forward<F>(f));
}
template<typename F> bool visit_while(F&& f)const
{
return visit_while_impl(group_shared{},std::forward<F>(f));
}
template<typename F> bool cvisit_while(F&& f)const
{
return visit_while(std::forward<F>(f));
}
#if defined(BOOST_UNORDERED_PARALLEL_ALGORITHMS)
template<typename ExecutionPolicy,typename F>
bool visit_while(ExecutionPolicy&& policy,F&& f)
{
return visit_while_impl(
group_exclusive{},
std::forward<ExecutionPolicy>(policy),std::forward<F>(f));
}
template<typename ExecutionPolicy,typename F>
bool visit_while(ExecutionPolicy&& policy,F&& f)const
{
return visit_while_impl(
group_shared{},
std::forward<ExecutionPolicy>(policy),std::forward<F>(f));
}
template<typename ExecutionPolicy,typename F>
bool cvisit_while(ExecutionPolicy&& policy,F&& f)const
{
return visit_while(
std::forward<ExecutionPolicy>(policy),std::forward<F>(f));
}
#endif
bool empty()const noexcept{return size()==0;}
std::size_t size()const noexcept
@@ -835,11 +1031,14 @@ public:
}
private:
template<typename,typename,typename,typename> friend class concurrent_table;
using mutex_type=rw_spinlock;
using multimutex_type=multimutex<mutex_type,128>; // TODO: adapt 128 to the machine
using shared_lock_guard=shared_lock<mutex_type>;
using exclusive_lock_guard=lock_guard<multimutex_type>;
using exclusive_bilock_guard=scoped_bilock<multimutex_type>;
using shared_lock_guard=reentrancy_checked<shared_lock<mutex_type>>;
using exclusive_lock_guard=reentrancy_checked<lock_guard<multimutex_type>>;
using exclusive_bilock_guard=
reentrancy_bichecked<scoped_bilock<multimutex_type>>;
using group_shared_lock_guard=typename group_access::shared_lock_guard;
using group_exclusive_lock_guard=typename group_access::exclusive_lock_guard;
using group_insert_counter_type=typename group_access::insert_counter_type;
@@ -859,18 +1058,18 @@ private:
{
thread_local auto id=(++thread_counter)%mutexes.size();
return shared_lock_guard{mutexes[id]};
return shared_lock_guard{this,mutexes[id]};
}
inline exclusive_lock_guard exclusive_access()const
{
return exclusive_lock_guard{mutexes};
return exclusive_lock_guard{this,mutexes};
}
static inline exclusive_bilock_guard exclusive_access(
const concurrent_table& x,const concurrent_table& y)
{
return {x.mutexes,y.mutexes};
return {&x,&y,x.mutexes,y.mutexes};
}
template<typename Hash2,typename Pred2>
@@ -878,7 +1077,7 @@ private:
const concurrent_table& x,
const concurrent_table<TypePolicy,Hash2,Pred2,Allocator>& y)
{
return {x.mutexes,y.mutexes};
return {&x,&y,x.mutexes,y.mutexes};
}
/* Tag-dispatched shared/exclusive group access */
@@ -888,18 +1087,18 @@ private:
inline group_shared_lock_guard access(group_shared,std::size_t pos)const
{
return this->arrays.group_accesses[pos].shared_access();
return this->arrays.group_accesses()[pos].shared_access();
}
inline group_exclusive_lock_guard access(
group_exclusive,std::size_t pos)const
{
return this->arrays.group_accesses[pos].exclusive_access();
return this->arrays.group_accesses()[pos].exclusive_access();
}
inline group_insert_counter_type& insert_counter(std::size_t pos)const
{
return this->arrays.group_accesses[pos].insert_counter();
return this->arrays.group_accesses()[pos].insert_counter();
}
/* Const casts value_type& according to the level of group access for
@@ -970,6 +1169,29 @@ private:
}
#endif
template<typename GroupAccessMode,typename F>
bool visit_while_impl(GroupAccessMode access_mode,F&& f)const
{
auto lck=shared_access();
return for_all_elements_while(access_mode,[&](element_type* p){
return f(cast_for(access_mode,type_policy::value_from(*p)));
});
}
#if defined(BOOST_UNORDERED_PARALLEL_ALGORITHMS)
template<typename GroupAccessMode,typename ExecutionPolicy,typename F>
bool visit_while_impl(
GroupAccessMode access_mode,ExecutionPolicy&& policy,F&& f)const
{
auto lck=shared_access();
return for_all_elements_while(
access_mode,std::forward<ExecutionPolicy>(policy),
[&](element_type* p){
return f(cast_for(access_mode,type_policy::value_from(*p)));
});
}
#endif
template<typename GroupAccessMode,typename Key,typename F>
BOOST_FORCEINLINE std::size_t unprotected_visit(
GroupAccessMode access_mode,
@@ -995,10 +1217,10 @@ private:
prober pb(pos0);
do{
auto pos=pb.get();
auto pg=this->arrays.groups+pos;
auto pg=this->arrays.groups()+pos;
auto mask=pg->match(hash);
if(mask){
auto p=this->arrays.elements+pos*N;
auto p=this->arrays.elements()+pos*N;
BOOST_UNORDERED_PREFETCH_ELEMENTS(p,N);
auto lck=access(access_mode,pos);
do{
@@ -1019,6 +1241,153 @@ private:
return 0;
}
#if 0
template<
std::size_t M,typename GroupAccessMode,
typename FwdIterator,typename F
>
BOOST_FORCEINLINE
std::size_t unprotected_bulk_visit(
GroupAccessMode access_mode,FwdIterator first,F&& f)const
{
std::size_t res=0,
hashes[M],
positions[M];
int masks[M];
auto it=first;
for(std::size_t i=0;i<M;++i,++it){
hashes[i]=this->hash_for(*it);
auto pos=positions[i]=this->position_for(hashes[i]);
BOOST_UNORDERED_PREFETCH(this->arrays.groups()+pos);
}
for(std::size_t i=0;i<M;++i){
auto hash=hashes[i];
auto pos=positions[i];
masks[i]=(this->arrays.groups()+pos)->match(hash);
if(masks[i]){
BOOST_UNORDERED_PREFETCH(this->arrays.group_accesses()+pos);
BOOST_UNORDERED_PREFETCH_ELEMENTS(this->arrays.elements()+pos*N,N);
}
}
it=first;
for(std::size_t i=0;i<M;++i,++it){
auto pos=positions[i];
prober pb(pos);
auto pg=this->arrays.groups()+pos;
auto mask=masks[i];
element_type *p;
if(mask){
p=this->arrays.elements()+pos*N;
goto post_prefetch;
}
else{
goto post_mask;
}
do{
pos=pb.get();
pg=this->arrays.groups()+pos;
mask=pg->match(hashes[i]);
if(BOOST_UNLIKELY(mask!=0)){ /* unlikely bc we're past the 1st probe */
p=this->arrays.elements()+pos*N;
BOOST_UNORDERED_PREFETCH_ELEMENTS(p,N);
post_prefetch:
auto lck=access(access_mode,pos);
do{
auto n=unchecked_countr_zero(mask);
if(BOOST_LIKELY(
pg->is_occupied(n)&&
bool(this->pred()(*it,this->key_from(p[n]))))){
f(cast_for(access_mode,type_policy::value_from(p[n])));
++res;
goto next_key;
}
mask&=mask-1;
}while(mask);
}
post_mask:
if(BOOST_LIKELY(pg->is_not_overflowed(hashes[i]))){
goto next_key;
}
}
while(BOOST_LIKELY(pb.next(this->arrays.groups_size_mask)));
next_key:;
}
return res;
}
#else
template<typename GroupAccessMode,typename FwdIterator,typename F>
BOOST_FORCEINLINE std::size_t unprotected_bulk_visit(
GroupAccessMode access_mode,FwdIterator first,std::size_t m,F&& f)const
{
BOOST_ASSERT(s<2*bulk_visit_size);
std::size_t res=0,
hashes[2*bulk_visit_size-1],
positions[2*bulk_visit_size-1];
int masks[2*bulk_visit_size-1];
auto it=first;
for(auto i=m;i--;++it){
auto hash=hashes[i]=this->hash_for(*it);
auto pos=positions[i]=this->position_for(hash);
BOOST_UNORDERED_PREFETCH(this->arrays.groups()+pos);
}
for(auto i=m;i--;){
auto hash=hashes[i];
auto pos=positions[i];
auto mask=masks[i]=(this->arrays.groups()+pos)->match(hash);
if(mask){
BOOST_UNORDERED_PREFETCH(this->arrays.group_accesses()+pos);
BOOST_UNORDERED_PREFETCH_ELEMENTS(this->arrays.elements()+pos*N,N);
}
}
it=first;
for(auto i=m;i--;++it){
auto pos=positions[i];
prober pb(pos);
auto pg=this->arrays.groups()+pos;
auto mask=masks[i];
element_type *p;
if(!mask)goto post_mask;
p=this->arrays.elements()+pos*N;
for(;;){
{
auto lck=access(access_mode,pos);
do{
auto n=unchecked_countr_zero(mask);
if(BOOST_LIKELY(
pg->is_occupied(n)&&
bool(this->pred()(*it,this->key_from(p[n]))))){
f(cast_for(access_mode,type_policy::value_from(p[n])));
++res;
goto next_key;
}
mask&=mask-1;
}while(mask);
}
post_mask:
if(BOOST_LIKELY(pg->is_not_overflowed(hashes[i]))||
BOOST_UNLIKELY(!pb.next(this->arrays.groups_size_mask))){
goto next_key;
}
pos=pb.get();
pg=this->arrays.groups()+pos;
mask=pg->match(hashes[i]);
if(BOOST_LIKELY(mask==0))goto next_key;
p=this->arrays.elements()+pos*N;
BOOST_UNORDERED_PREFETCH_ELEMENTS(p,N);
}
next_key:;
}
return res;
}
#endif
#if defined(BOOST_MSVC)
#pragma warning(pop) /* C4800 */
#endif
@@ -1211,7 +1580,7 @@ private:
if(BOOST_LIKELY(rsize.succeeded())){
for(prober pb(pos0);;pb.next(this->arrays.groups_size_mask)){
auto pos=pb.get();
auto pg=this->arrays.groups+pos;
auto pg=this->arrays.groups()+pos;
auto lck=access(group_exclusive{},pos);
auto mask=pg->match_available();
if(BOOST_LIKELY(mask!=0)){
@@ -1221,7 +1590,7 @@ private:
/* other thread inserted from pos0, need to start over */
goto startover;
}
auto p=this->arrays.elements+pos*N+n;
auto p=this->arrays.elements()+pos*N+n;
this->construct_element(p,std::forward<Args>(args)...);
rslot.commit();
rsize.commit();
@@ -1254,18 +1623,37 @@ private:
auto for_all_elements(GroupAccessMode access_mode,F f)const
->decltype(f(nullptr,0,nullptr),void())
{
auto p=this->arrays.elements;
if(!p)return;
for(auto pg=this->arrays.groups,last=pg+this->arrays.groups_size_mask+1;
pg!=last;++pg,p+=N){
auto lck=access(access_mode,(std::size_t)(pg-this->arrays.groups));
auto mask=this->match_really_occupied(pg,last);
while(mask){
auto n=unchecked_countr_zero(mask);
f(pg,n,p+n);
mask&=mask-1;
for_all_elements_while(
access_mode,[&](group_type* pg,unsigned int n,element_type* p)
{f(pg,n,p);return true;});
}
template<typename GroupAccessMode,typename F>
auto for_all_elements_while(GroupAccessMode access_mode,F f)const
->decltype(f(nullptr),bool())
{
return for_all_elements_while(
access_mode,[&](group_type*,unsigned int,element_type* p){return f(p);});
}
template<typename GroupAccessMode,typename F>
auto for_all_elements_while(GroupAccessMode access_mode,F f)const
->decltype(f(nullptr,0,nullptr),bool())
{
auto p=this->arrays.elements();
if(p){
for(auto pg=this->arrays.groups(),last=pg+this->arrays.groups_size_mask+1;
pg!=last;++pg,p+=N){
auto lck=access(access_mode,(std::size_t)(pg-this->arrays.groups()));
auto mask=this->match_really_occupied(pg,last);
while(mask){
auto n=unchecked_countr_zero(mask);
if(!f(pg,n,p+n))return false;
mask&=mask-1;
}
}
}
return true;
}
#if defined(BOOST_UNORDERED_PARALLEL_ALGORITHMS)
@@ -1284,15 +1672,15 @@ private:
GroupAccessMode access_mode,ExecutionPolicy&& policy,F f)const
->decltype(f(nullptr,0,nullptr),void())
{
if(!this->arrays.elements)return;
auto first=this->arrays.groups,
if(!this->arrays.elements())return;
auto first=this->arrays.groups(),
last=first+this->arrays.groups_size_mask+1;
std::for_each(std::forward<ExecutionPolicy>(policy),first,last,
[&,this](group_type& g){
std::size_t pos=static_cast<std::size_t>(&g-first);
auto p=this->arrays.elements+pos*N;
auto lck=access(access_mode,pos);
auto mask=this->match_really_occupied(&g,last);
auto pos=static_cast<std::size_t>(&g-first);
auto p=this->arrays.elements()+pos*N;
auto lck=access(access_mode,pos);
auto mask=this->match_really_occupied(&g,last);
while(mask){
auto n=unchecked_countr_zero(mask);
f(&g,n,p+n);
@@ -1301,8 +1689,164 @@ private:
}
);
}
template<typename GroupAccessMode,typename ExecutionPolicy,typename F>
bool for_all_elements_while(
GroupAccessMode access_mode,ExecutionPolicy&& policy,F f)const
{
if(!this->arrays.elements())return true;
auto first=this->arrays.groups(),
last=first+this->arrays.groups_size_mask+1;
return std::all_of(std::forward<ExecutionPolicy>(policy),first,last,
[&,this](group_type& g){
auto pos=static_cast<std::size_t>(&g-first);
auto p=this->arrays.elements()+pos*N;
auto lck=access(access_mode,pos);
auto mask=this->match_really_occupied(&g,last);
while(mask){
auto n=unchecked_countr_zero(mask);
if(!f(p+n))return false;
mask&=mask-1;
}
return true;
}
);
}
#endif
friend class boost::serialization::access;
template<typename Archive>
void serialize(Archive& ar,unsigned int version)
{
core::split_member(ar,*this,version);
}
template<typename Archive>
void save(Archive& ar,unsigned int version)const
{
save(
ar,version,
std::integral_constant<bool,std::is_same<key_type,value_type>::value>{});
}
template<typename Archive>
void save(Archive& ar,unsigned int,std::true_type /* set */)const
{
auto lck=exclusive_access();
const std::size_t s=super::size();
const serialization_version<value_type> value_version;
ar<<core::make_nvp("count",s);
ar<<core::make_nvp("value_version",value_version);
super::for_all_elements([&,this](element_type* p){
auto& x=type_policy::value_from(*p);
core::save_construct_data_adl(ar,std::addressof(x),value_version);
ar<<serialization::make_nvp("item",x);
});
}
template<typename Archive>
void save(Archive& ar,unsigned int,std::false_type /* map */)const
{
using raw_key_type=typename std::remove_const<key_type>::type;
using raw_mapped_type=typename std::remove_const<
typename TypePolicy::mapped_type>::type;
auto lck=exclusive_access();
const std::size_t s=super::size();
const serialization_version<raw_key_type> key_version;
const serialization_version<raw_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);
super::for_all_elements([&,this](element_type* p){
/* 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.
*/
auto& x=type_policy::value_from(*p);
core::save_construct_data_adl(
ar,std::addressof(x.first),key_version);
ar<<serialization::make_nvp("key",x.first);
core::save_construct_data_adl(
ar,std::addressof(x.second),mapped_version);
ar<<serialization::make_nvp("mapped",x.second);
});
}
template<typename Archive>
void load(Archive& ar,unsigned int version)
{
load(
ar,version,
std::integral_constant<bool,std::is_same<key_type,value_type>::value>{});
}
template<typename Archive>
void load(Archive& ar,unsigned int,std::true_type /* set */)
{
auto lck=exclusive_access();
std::size_t s;
serialization_version<value_type> value_version;
ar>>core::make_nvp("count",s);
ar>>core::make_nvp("value_version",value_version);
super::clear();
super::reserve(s);
for(std::size_t n=0;n<s;++n){
archive_constructed<value_type> value("item",ar,value_version);
auto& x=value.get();
auto hash=this->hash_for(x);
auto pos0=this->position_for(hash);
if(this->find(x,pos0,hash))throw_exception(bad_archive_exception());
auto loc=this->unchecked_emplace_at(pos0,hash,std::move(x));
ar.reset_object_address(std::addressof(*loc.p),std::addressof(x));
}
}
template<typename Archive>
void load(Archive& ar,unsigned int,std::false_type /* map */)
{
using raw_key_type=typename std::remove_const<key_type>::type;
using raw_mapped_type=typename std::remove_const<
typename TypePolicy::mapped_type>::type;
auto lck=exclusive_access();
std::size_t s;
serialization_version<raw_key_type> key_version;
serialization_version<raw_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);
super::clear();
super::reserve(s);
for(std::size_t n=0;n<s;++n){
archive_constructed<raw_key_type> key("key",ar,key_version);
archive_constructed<raw_mapped_type> mapped("mapped",ar,mapped_version);
auto& k=key.get();
auto& m=mapped.get();
auto hash=this->hash_for(k);
auto pos0=this->position_for(hash);
if(this->find(k,pos0,hash))throw_exception(bad_archive_exception());
auto loc=this->unchecked_emplace_at(pos0,hash,std::move(k),std::move(m));
ar.reset_object_address(std::addressof(loc.p->first),std::addressof(k));
ar.reset_object_address(std::addressof(loc.p->second),std::addressof(m));
}
}
static std::atomic<std::size_t> thread_counter;
mutable multimutex_type mutexes;
};
+290 -158
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,
* boost::unordered_(flat|node)_(map|set) and boost::concurrent_flat_(map|set),
* respectively. Its main internal design aspects are:
*
* - Element slots are logically split into groups of size N=15. The number
@@ -337,38 +337,49 @@ 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)];
@@ -549,7 +560,8 @@ 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)
@@ -628,7 +640,8 @@ 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;
}
@@ -787,8 +800,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
@@ -938,74 +951,142 @@ Group* dummy_groups()
const_cast<typename Group::dummy_group_type*>(storage));
}
template<typename Value,typename Group,typename SizePolicy>
template<
typename Ptr,typename Ptr2,
typename std::enable_if<!std::is_same<Ptr,Ptr2>::value>::type* = nullptr
>
Ptr to_pointer(Ptr2 p)
{
if(!p){return nullptr;}
return boost::pointer_traits<Ptr>::pointer_to(*p);
}
template<typename Ptr>
Ptr to_pointer(Ptr p)
{
return p;
}
template<typename Arrays,typename Allocator>
struct arrays_holder
{
arrays_holder(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>
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 *pg,value_type *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_pointer pg,value_type_pointer pe):
groups_size_index{gsi},groups_size_mask{gsm},groups_{pg},elements_{pe}{}
template<typename Allocator>
static table_arrays new_(Allocator& al,std::size_t n)
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)
{
using storage_allocator=
typename boost::allocator_rebind<Allocator, Value>::type;
using storage_traits=boost::allocator_traits<storage_allocator>;
return set_arrays(
arrays,al,n,std::is_same<group_type*,group_type_pointer>{});
}
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};
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();
}
set_arrays(arrays,al,n);
return arrays;
}
template<typename Allocator>
static void delete_(Allocator& al,table_arrays& arrays)noexcept
static void delete_(allocator_type al,table_arrays& arrays)noexcept
{
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>;
using storage_traits=boost::allocator_traits<allocator_type>;
auto sal=storage_alloc(al);
if(arrays.elements){
auto sal=allocator_type(al);
if(arrays.elements()){
storage_traits::deallocate(
sal,pointer_traits::pointer_to(*arrays.elements),
buffer_size(arrays.groups_size_mask+1));
sal,arrays.elements_,buffer_size(arrays.groups_size_mask+1));
}
}
@@ -1024,7 +1105,7 @@ struct table_arrays
}
static void initialize_groups(
group_type* groups_,std::size_t size,std::true_type /* memset */)
group_type* pg,std::size_t size,std::true_type /* memset */)
{
/* memset faster/not slower than manual, assumes all zeros is group_type's
* default layout.
@@ -1033,19 +1114,19 @@ struct table_arrays
*/
std::memset(
reinterpret_cast<unsigned char*>(groups_),0,sizeof(group_type)*size);
reinterpret_cast<unsigned char*>(pg),0,sizeof(group_type)*size);
}
static void initialize_groups(
group_type* groups_,std::size_t size,std::false_type /* manual */)
group_type* pg,std::size_t size,std::false_type /* manual */)
{
while(size--!=0)::new (groups_++) group_type();
while(size--!=0)::new (pg++) group_type();
}
std::size_t groups_size_index;
std::size_t groups_size_mask;
group_type *groups;
value_type *elements;
std::size_t groups_size_index;
std::size_t groups_size_mask;
group_type_pointer groups_;
value_type_pointer elements_;
};
struct if_constexpr_void_else{void operator()()const{}};
@@ -1180,7 +1261,8 @@ 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.
* meaning. TypePolicy::mapped_type is expected to be provided as well
* when key_type and value_type are not the same.
*
* - TypePolicy::init_type is the type implicitly converted to when
* writing x.insert({...}). For maps, this is std::pair<Key,T> rather
@@ -1190,8 +1272,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
@@ -1257,8 +1339,12 @@ 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>;
using arrays_type=Arrays<element_type,group_type,size_policy,Allocator>;
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;
@@ -1273,6 +1359,7 @@ 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(),
@@ -1282,23 +1369,43 @@ 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):
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;
}
std::is_nothrow_move_constructible<Allocator>::value&&
!uses_fancy_pointers):
table_core{
std::move(x),x.make_empty_arrays(),[&x]{return x.arrays;}}
{}
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_}
@@ -1336,6 +1443,24 @@ 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)
@@ -1350,9 +1475,6 @@ 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
@@ -1364,7 +1486,12 @@ public:
swap(pred(),tmp_p);
if_constexpr<pocca>([&,this]{
if(al()!=x.al())reserve(0);
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();
}
copy_assign_if<pocca>(al(),x.al());
});
/* noshrink: favor memory reuse over tightness */
@@ -1381,8 +1508,8 @@ public:
table_core& operator=(table_core&& x)
noexcept(
alloc_traits::propagate_on_container_move_assignment::value||
alloc_traits::is_always_equal::value)
(alloc_traits::propagate_on_container_move_assignment::value||
alloc_traits::is_always_equal::value)&&!uses_fancy_pointers)
{
BOOST_UNORDERED_STATIC_ASSERT_HASH_PRED(Hash, Pred)
@@ -1403,16 +1530,24 @@ public:
using std::swap;
clear();
swap(h(),x.h());
swap(pred(),x.pred());
if(pocma||al()==x.al()){
reserve(0);
auto ah=x.make_empty_arrays();
swap(h(),x.h());
swap(pred(),x.pred());
delete_arrays(arrays);
move_assign_if<pocma>(al(),x.al());
swap(arrays,x.arrays);
swap(size_ctrl,x.size_ctrl);
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;
}
else{
swap(h(),x.h());
swap(pred(),x.pred());
/* noshrink: favor memory reuse over tightness */
noshrink_reserve(x.size());
clear_on_exit c{x};
@@ -1473,11 +1608,12 @@ 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){
BOOST_UNORDERED_ASSUME(arrays.elements!=nullptr);
auto p=arrays.elements+pos*N;
auto elements=arrays.elements();
BOOST_UNORDERED_ASSUME(elements!=nullptr);
auto p=elements+pos*N;
BOOST_UNORDERED_PREFETCH_ELEMENTS(p,N);
do{
auto n=unchecked_countr_zero(mask);
@@ -1526,9 +1662,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){
@@ -1538,7 +1674,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;
}
@@ -1549,7 +1685,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
@@ -1768,9 +1904,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){
@@ -1804,14 +1940,15 @@ 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)
arrays_type new_arrays(std::size_t n)const
{
return arrays_type::new_(al(),n);
}
arrays_type new_arrays_for_growth()
arrays_type new_arrays_for_growth()const
{
/* 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
@@ -1832,6 +1969,11 @@ 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)
@@ -1870,9 +2012,10 @@ private:
void fast_copy_elements_from(const table_core& x)
{
if(arrays.elements){
if(arrays.elements()&&x.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);
}
}
@@ -1903,8 +2046,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));
}
@@ -1914,14 +2057,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;
});
}
@@ -1946,22 +2089,24 @@ 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){
arrays.groups[i]=x.arrays.groups[i];
pg[i]=xpg[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);
@@ -1974,19 +2119,6 @@ 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;
@@ -2084,11 +2216,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,26 +9,30 @@
#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>
template<class T,class VoidPtr>
struct element_type
{
using value_type=T;
value_type* p;
using pointer=typename boost::pointer_traits<VoidPtr>::template rebind<T>;
pointer p;
/*
* we use a deleted copy constructor here so the type is no longer
* trivially copy-constructible which inhibits our memcpy
* optimizations when copying the tables
*/
element_type() = default;
element_type(value_type* p_):p(p_){}
element_type(element_type const&) = delete;
element_type(element_type&& rhs) noexcept
element_type()=default;
element_type(pointer p_):p(p_){}
element_type(element_type const&)=delete;
element_type(element_type&& rhs)noexcept
{
p = rhs.p;
rhs.p = nullptr;
@@ -14,6 +14,7 @@ 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,6 +5,8 @@
#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>
@@ -13,7 +15,7 @@ namespace boost {
namespace unordered {
namespace detail {
namespace foa {
template <class Key, class T> struct node_map_types
template <class Key, class T, class VoidPtr> struct node_map_types
{
using key_type = Key;
using mapped_type = T;
@@ -24,7 +26,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>;
using element_type = foa::element_type<value_type, VoidPtr>;
static value_type& value_from(element_type const& x)
{
@@ -83,18 +85,15 @@ namespace boost {
template <class A, class... Args>
static void construct(A& al, element_type* p, Args&&... args)
{
p->p = boost::to_address(boost::allocator_allocate(al, 1));
p->p = boost::allocator_allocate(al, 1);
BOOST_TRY
{
boost::allocator_construct(al, p->p, std::forward<Args>(args)...);
boost::allocator_construct(
al, boost::to_address(p->p), std::forward<Args>(args)...);
}
BOOST_CATCH(...)
{
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::allocator_deallocate(al, p->p, 1);
BOOST_RETHROW
}
BOOST_CATCH_END
@@ -114,12 +113,8 @@ namespace boost {
static void destroy(A& al, element_type* p) noexcept
{
if (p->p) {
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);
destroy(al, boost::to_address(p->p));
boost::allocator_deallocate(al, p->p, 1);
}
}
};
@@ -5,6 +5,8 @@
#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>
@@ -14,7 +16,7 @@ namespace boost {
namespace detail {
namespace foa {
template <class Key> struct node_set_types
template <class Key, class VoidPtr> struct node_set_types
{
using key_type = Key;
using init_type = Key;
@@ -22,7 +24,7 @@ namespace boost {
static Key const& extract(value_type const& key) { return key; }
using element_type = foa::element_type<value_type>;
using element_type = foa::element_type<value_type, VoidPtr>;
static value_type& value_from(element_type const& x) { return *x.p; }
static Key const& extract(element_type const& k) { return *k.p; }
@@ -53,17 +55,15 @@ namespace boost {
template <class A, class... Args>
static void construct(A& al, element_type* p, Args&&... args)
{
p->p = boost::to_address(boost::allocator_allocate(al, 1));
p->p = boost::allocator_allocate(al, 1);
BOOST_TRY
{
boost::allocator_construct(al, p->p, std::forward<Args>(args)...);
boost::allocator_construct(
al, boost::to_address(p->p), std::forward<Args>(args)...);
}
BOOST_CATCH(...)
{
boost::allocator_deallocate(al,
boost::pointer_traits<typename boost::allocator_pointer<
A>::type>::pointer_to(*p->p),
1);
boost::allocator_deallocate(al, p->p, 1);
BOOST_RETHROW
}
BOOST_CATCH_END
@@ -78,11 +78,8 @@ namespace boost {
static void destroy(A& al, element_type* p) noexcept
{
if (p->p) {
destroy(al, p->p);
boost::allocator_deallocate(al,
boost::pointer_traits<typename boost::allocator_pointer<
A>::type>::pointer_to(*(p->p)),
1);
destroy(al, boost::to_address(p->p));
boost::allocator_deallocate(al, p->p, 1);
}
}
};
@@ -0,0 +1,138 @@
/* 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
+122 -52
View File
@@ -15,7 +15,9 @@
#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>
@@ -79,12 +81,17 @@ class table;
/* internal conversion from const_iterator to iterator */
struct const_iterator_cast_tag{};
template<typename TypePolicy,typename Group,bool Const>
template<typename TypePolicy,typename GroupPtr,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=Group;
using group_type=typename group_pointer_traits::element_type;
using table_element_pointer=
typename group_pointer_traits::template rebind<table_element_type>;
using char_pointer=
typename group_pointer_traits::template rebind<unsigned char>;
static constexpr auto N=group_type::N;
static constexpr auto regular_layout=group_type::regular_layout;
@@ -101,22 +108,22 @@ public:
table_iterator()=default;
template<bool Const2,typename std::enable_if<!Const2>::type* =nullptr>
table_iterator(const table_iterator<TypePolicy,Group,Const2>& x):
pc{x.pc},p{x.p}{}
table_iterator(const table_iterator<TypePolicy,GroupPtr,Const2>& x):
pc_{x.pc_},p_{x.p_}{}
table_iterator(
const_iterator_cast_tag, const table_iterator<TypePolicy,Group,true>& x):
pc{x.pc},p{x.p}{}
const_iterator_cast_tag, const table_iterator<TypePolicy,GroupPtr,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);}
@@ -126,77 +133,106 @@ private:
template<typename> friend class table_erase_return_type;
template<typename,typename,typename,typename> friend class table;
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_)}
{}
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_);}
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+=sizeof(group_type)-(N-1);
++p_;
if(reinterpret_cast<uintptr_t>(pc())%sizeof(group_type)==N-1){
pc_+=static_cast<diff_type>(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+=n;
p+=n;
pc_+=static_cast<diff_type>(n);
p_+=static_cast<diff_type>(n);
}
return;
}
pc+=sizeof(group_type);
p+=N;
pc_+=static_cast<diff_type>(sizeof(group_type));
p_+=static_cast<diff_type>(N);
}
}
inline void increment(std::false_type /* interleaved */)noexcept
{
std::size_t n0=reinterpret_cast<uintptr_t>(pc)%sizeof(group_type);
pc-=n0;
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);
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+=n;
p-=n0;
p+=n;
pc_+=static_cast<diff_type>(n);
p_-=static_cast<diff_type>(n0);
p_+=static_cast<diff_type>(n);
}
}
unsigned char *pc=nullptr;
table_element_type *p=nullptr;
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;
};
/* Returned by table::erase([const_]iterator) to avoid iterator increment
@@ -206,11 +242,11 @@ private:
template<typename Iterator>
class table_erase_return_type;
template<typename TypePolicy,typename Group,bool Const>
class table_erase_return_type<table_iterator<TypePolicy,Group,Const>>
template<typename TypePolicy,typename GroupPtr,bool Const>
class table_erase_return_type<table_iterator<TypePolicy,GroupPtr,Const>>
{
using iterator=table_iterator<TypePolicy,Group,Const>;
using const_iterator=table_iterator<TypePolicy,Group,true>;
using iterator=table_iterator<TypePolicy,GroupPtr,Const>;
using const_iterator=table_iterator<TypePolicy,GroupPtr,true>;
public:
/* can't delete it because VS in pre-C++17 mode needs to see it for RVO */
@@ -264,6 +300,9 @@ 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,
@@ -284,7 +323,15 @@ 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;
@@ -295,7 +342,6 @@ 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;
@@ -306,10 +352,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,true>;
using const_iterator=table_iterator<type_policy,group_type_pointer,true>;
using iterator=typename std::conditional<
has_mutable_iterator,
table_iterator<type_policy,group_type,false>,
table_iterator<type_policy,group_type_pointer,false>,
const_iterator>::type;
using erase_return_type=table_erase_return_type<iterator>;
@@ -323,6 +369,8 @@ 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;
@@ -332,9 +380,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;
}
@@ -400,7 +448,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};
}
@@ -431,7 +479,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?
@@ -496,6 +544,28 @@ 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_}{}
@@ -1,6 +1,6 @@
// Copyright (C) 2003-2004 Jeremy B. Maitin-Shepard.
// Copyright (C) 2005-2016 Daniel James
// Copyright (C) 2022 Joaquin M Lopez Munoz.
// Copyright (C) 2022-2023 Joaquin M Lopez Munoz.
// Copyright (C) 2022 Christian Mazakas
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
@@ -17,8 +17,10 @@
#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>
@@ -29,7 +31,6 @@
#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>
@@ -47,6 +48,7 @@
#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>
@@ -258,8 +260,8 @@ namespace boost {
void swap(compressed& x)
{
boost::swap(first(), x.first());
boost::swap(second(), x.second());
boost::core::invoke_swap(first(), x.first());
boost::core::invoke_swap(second(), x.second());
}
private:
@@ -642,7 +644,7 @@ namespace boost {
move(x);
}
} else if (has_value_) {
boost::swap(value_.value(), x.value_.value());
boost::core::invoke_swap(value_.value(), x.value_.value());
}
}
@@ -1703,6 +1705,25 @@ 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
@@ -1793,6 +1814,25 @@ 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
@@ -2049,7 +2089,7 @@ namespace boost {
x.switch_functions();
buckets_.swap(x.buckets_);
boost::swap(size_, x.size_);
boost::core::invoke_swap(size_, x.size_);
std::swap(mlf_, x.mlf_);
std::swap(max_load_, x.max_load_);
}
@@ -2058,7 +2098,7 @@ namespace boost {
void swap(table& x, true_type)
{
buckets_.swap(x.buckets_);
boost::swap(size_, x.size_);
boost::core::invoke_swap(size_, x.size_);
std::swap(mlf_, x.mlf_);
std::swap(max_load_, x.max_load_);
this->current_functions().swap(x.current_functions());
@@ -0,0 +1,74 @@
/* 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
@@ -0,0 +1,208 @@
/* 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
@@ -0,0 +1,156 @@
/* 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
@@ -0,0 +1,103 @@
/* 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
+23 -4
View File
@@ -1,4 +1,4 @@
// Copyright (C) 2022 Christian Mazakas
// Copyright (C) 2022-2023 Christian Mazakas
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -10,8 +10,10 @@
#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>
@@ -36,6 +38,10 @@ 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,
@@ -135,9 +141,7 @@ namespace boost {
}
unordered_flat_map(unordered_flat_map&& other)
noexcept(std::is_nothrow_move_constructible<hasher>::value&&
std::is_nothrow_move_constructible<key_equal>::value&&
std::is_nothrow_move_constructible<allocator_type>::value)
noexcept(std::is_nothrow_move_constructible<table_type>::value)
: table_(std::move(other.table_))
{
}
@@ -173,6 +177,12 @@ 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)
@@ -685,6 +695,15 @@ 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
+22 -4
View File
@@ -1,4 +1,4 @@
// Copyright (C) 2022 Christian Mazakas
// Copyright (C) 2022-2023 Christian Mazakas
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -10,8 +10,10 @@
#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>
@@ -34,6 +36,9 @@ 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,
@@ -132,9 +137,7 @@ namespace boost {
}
unordered_flat_set(unordered_flat_set&& other)
noexcept(std::is_nothrow_move_constructible<hasher>::value&&
std::is_nothrow_move_constructible<key_equal>::value&&
std::is_nothrow_move_constructible<allocator_type>::value)
noexcept(std::is_nothrow_move_constructible<table_type>::value)
: table_(std::move(other.table_))
{
}
@@ -170,6 +173,12 @@ 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)
@@ -505,6 +514,15 @@ 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
+37 -7
View File
@@ -1,7 +1,6 @@
// Copyright (C) 2003-2004 Jeremy B. Maitin-Shepard.
// Copyright (C) 2005-2011 Daniel James.
// Copyright (C) 2022 Christian Mazakas
// Copyright (C) 2022-2023 Christian Mazakas
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -16,11 +15,13 @@
#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)
@@ -1059,6 +1060,13 @@ 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,
@@ -1762,6 +1770,13 @@ 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,
@@ -2965,9 +2980,9 @@ namespace boost {
if (boost::allocator_propagate_on_container_swap<
value_allocator>::type::value ||
!alloc_.has_value() || !n.alloc_.has_value()) {
boost::swap(alloc_, n.alloc_);
boost::core::invoke_swap(alloc_, n.alloc_);
}
boost::swap(ptr_, n.ptr_);
boost::core::invoke_swap(ptr_, n.ptr_);
}
};
@@ -3014,11 +3029,26 @@ namespace boost {
void swap(insert_return_type_map<Iter, NodeType>& x,
insert_return_type_map<Iter, NodeType>& y)
{
boost::swap(x.node, y.node);
boost::swap(x.inserted, y.inserted);
boost::swap(x.position, y.position);
boost::core::invoke_swap(x.node, y.node);
boost::core::invoke_swap(x.inserted, y.inserted);
boost::core::invoke_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)
+13 -4
View File
@@ -14,6 +14,7 @@
#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>
@@ -74,7 +75,8 @@ 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>;
using map_types = detail::foa::node_map_types<Key, T,
typename boost::allocator_void_pointer<Allocator>::type>;
using table_type = detail::foa::table<map_types, Hash, KeyEqual,
typename boost::allocator_rebind<Allocator,
@@ -178,9 +180,7 @@ namespace boost {
}
unordered_node_map(unordered_node_map&& other)
noexcept(std::is_nothrow_move_constructible<hasher>::value&&
std::is_nothrow_move_constructible<key_equal>::value&&
std::is_nothrow_move_constructible<allocator_type>::value)
noexcept(std::is_nothrow_move_constructible<table_type>::value)
: table_(std::move(other.table_))
{
}
@@ -788,6 +788,15 @@ 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
+14 -5
View File
@@ -1,4 +1,4 @@
// Copyright (C) 2022 Christian Mazakas
// Copyright (C) 2022-2023 Christian Mazakas
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -14,6 +14,7 @@
#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>
@@ -65,7 +66,8 @@ namespace boost {
template <class Key, class Hash, class KeyEqual, class Allocator>
class unordered_node_set
{
using set_types = detail::foa::node_set_types<Key>;
using set_types = detail::foa::node_set_types<Key,
typename boost::allocator_void_pointer<Allocator>::type>;
using table_type = detail::foa::table<set_types, Hash, KeyEqual,
typename boost::allocator_rebind<Allocator,
@@ -168,9 +170,7 @@ namespace boost {
}
unordered_node_set(unordered_node_set&& other)
noexcept(std::is_nothrow_move_constructible<hasher>::value&&
std::is_nothrow_move_constructible<key_equal>::value&&
std::is_nothrow_move_constructible<allocator_type>::value)
noexcept(std::is_nothrow_move_constructible<table_type>::value)
: table_(std::move(other.table_))
{
}
@@ -601,6 +601,15 @@ 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
+36 -7
View File
@@ -1,7 +1,6 @@
// Copyright (C) 2003-2004 Jeremy B. Maitin-Shepard.
// Copyright (C) 2005-2011 Daniel James.
// Copyright (C) 2022 Christian Mazakas
// Copyright (C) 2022-2023 Christian Mazakas
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -20,6 +19,7 @@
#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,6 +641,13 @@ 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,
@@ -1290,6 +1297,13 @@ 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,
@@ -2313,9 +2327,9 @@ namespace boost {
alloc_ == n.alloc_);
if (value_allocator_traits::propagate_on_container_swap::value ||
!alloc_.has_value() || !n.alloc_.has_value()) {
boost::swap(alloc_, n.alloc_);
boost::core::invoke_swap(alloc_, n.alloc_);
}
boost::swap(ptr_, n.ptr_);
boost::core::invoke_swap(ptr_, n.ptr_);
}
};
@@ -2362,11 +2376,26 @@ namespace boost {
void swap(
insert_return_type_set<Iter, NodeType>& x, insert_return_type_set<Iter, NodeType>& y)
{
boost::swap(x.node, y.node);
boost::swap(x.inserted, y.inserted);
boost::swap(x.position, y.position);
boost::core::invoke_swap(x.node, y.node);
boost::core::invoke_swap(x.inserted, y.inserted);
boost::core::invoke_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)
+254 -91
View File
@@ -4,7 +4,12 @@
# 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
@@ -15,6 +20,20 @@ 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
@@ -33,76 +52,163 @@ project
<toolset>msvc:<warnings-as-errors>on
;
run unordered/prime_fmod_tests.cpp ;
run unordered/fwd_set_test.cpp ;
run unordered/fwd_map_test.cpp ;
run unordered/allocator_traits.cpp ;
run unordered/minimal_allocator.cpp ;
run unordered/compile_set.cpp ;
run unordered/compile_map.cpp ;
run unordered/noexcept_tests.cpp ;
run unordered/link_test_1.cpp unordered/link_test_2.cpp ;
run unordered/incomplete_test.cpp ;
run unordered/simple_tests.cpp ;
run unordered/equivalent_keys_tests.cpp ;
run unordered/constructor_tests.cpp ;
run unordered/copy_tests.cpp ;
run unordered/move_tests.cpp ;
run unordered/post_move_tests.cpp ;
run unordered/assign_tests.cpp ;
run unordered/insert_tests.cpp ;
run unordered/insert_stable_tests.cpp ;
run unordered/insert_hint_tests.cpp ;
run unordered/emplace_tests.cpp ;
run unordered/unnecessary_copy_tests.cpp ;
run unordered/erase_tests.cpp : : : <define>BOOST_UNORDERED_SUPPRESS_DEPRECATED ;
run unordered/erase_equiv_tests.cpp ;
run unordered/extract_tests.cpp ;
run unordered/node_handle_tests.cpp ;
run unordered/merge_tests.cpp ;
compile-fail unordered/insert_node_type_fail.cpp : <define>UNORDERED_TEST_MAP : insert_node_type_fail_map ;
compile-fail unordered/insert_node_type_fail.cpp : <define>UNORDERED_TEST_MULTIMAP : insert_node_type_fail_multimap ;
compile-fail unordered/insert_node_type_fail.cpp : <define>UNORDERED_TEST_SET : insert_node_type_fail_set ;
compile-fail unordered/insert_node_type_fail.cpp : <define>UNORDERED_TEST_MULTISET : insert_node_type_fail_multiset ;
run unordered/find_tests.cpp ;
run unordered/at_tests.cpp ;
run unordered/bucket_tests.cpp ;
run unordered/load_factor_tests.cpp ;
run unordered/rehash_tests.cpp ;
run unordered/equality_tests.cpp ;
run unordered/swap_tests.cpp ;
run unordered/deduction_tests.cpp ;
run unordered/scoped_allocator.cpp : : : <toolset>msvc-14.0:<build>no ;
run unordered/transparent_tests.cpp ;
run unordered/reserve_tests.cpp ;
run unordered/contains_tests.cpp ;
run unordered/erase_if.cpp ;
run unordered/scary_tests.cpp ;
path-constant BOOST_UNORDERED_TEST_DIR : . ;
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 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 ;
run unordered/narrow_cast_tests.cpp ;
run quick.cpp ;
import ../../config/checks/config : requires ;
compile unordered/self_include_tests_obj.cpp
: <define>BOOST_UNORDERED_HEADER="boost/unordered_map.hpp"
: tl_unordered_map_hpp ;
CPP11 = [ requires cxx11_constexpr cxx11_noexcept cxx11_decltype cxx11_alignas ] ;
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/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 ;
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
;
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 ;
}
}
alias fca_exception_tests : $(FCA_EXCEPTION_TESTS) ;
alias fca_tests :
$(FCA_TESTS)
$(FCA_EXCEPTION_TESTS)
link_test
bmove_compile_set
bmove_compile_map
bmove_copy
bmove_move
bmove_assign
insert_node_type_fail_map
insert_node_type_fail_multimap
insert_node_type_fail_set
insert_node_type_fail_multiset
serialization_tests
;
local FOA_TESTS =
fwd_set_test
@@ -139,40 +245,80 @@ local FOA_TESTS =
extract_tests
node_handle_tests
uses_allocator
hash_is_avalanching_test
;
for local test in $(FOA_TESTS)
{
run unordered/$(test).cpp : : : $(CPP11) <define>BOOST_UNORDERED_FOA_TESTS : foa_$(test) ;
run unordered/$(test).cpp : : : <define>BOOST_UNORDERED_FOA_TESTS : foa_$(test) ;
}
run unordered/link_test_1.cpp unordered/link_test_2.cpp : : : $(CPP11) <define>BOOST_UNORDERED_FOA_TESTS : foa_link_test ;
run unordered/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 ;
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 ;
alias foa_tests :
foa_$(FOA_TESTS)
foa_$(FOA_EXCEPTION_TESTS)
foa_link_test
foa_scoped_allocator
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
foa_serialization_tests
foa_mmap_tests
;
local CFOA_TESTS =
@@ -202,14 +348,31 @@ local CFOA_TESTS =
rw_spinlock_test6
rw_spinlock_test7
rw_spinlock_test8
reentrancy_check_test
;
for local test in $(CFOA_TESTS)
{
run cfoa/$(test).cpp
: requirements $(CPP11) <threading>multi
: requirements <threading>multi
: target-name cfoa_$(test)
;
}
alias cfoa_tests : cfoa_$(CFOA_TESTS) ;
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 ;
+442 -194
View File
File diff suppressed because it is too large Load Diff
+31 -14
View File
@@ -1,10 +1,12 @@
// 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};
@@ -14,49 +16,62 @@ 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, allocator_type>;
key_equal, stateful_allocator<std::pair<raii const, raii> > >;
using map_value_type = typename map_type::value_type;
using set_type = boost::unordered::concurrent_flat_set<raii, hasher,
key_equal, stateful_allocator<raii> >;
map_type* test_map;
set_type* test_set;
namespace {
template <class G> void clear_tests(G gen, test::random_generator rg)
template <class X, class GF>
void clear_tests(X*, GF gen_factory, 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();
map_type x(values.begin(), values.end(), values.size(), hasher(1),
X 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<map_value_type> s) {
thread_runner(values, [&x](boost::span<value_type> s) {
(void)s;
x.clear();
});
BOOST_TEST(x.empty());
BOOST_TEST_EQ(raii::destructor, old_d + 2 * old_size);
BOOST_TEST_EQ(raii::destructor, old_d + value_type_cardinality * old_size);
check_raii_counts();
}
template <class G> void insert_and_clear(G gen, test::random_generator rg)
template <class X, class GF>
void insert_and_clear(X*, GF gen_factory, 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_map =
boost::unordered_flat_map<raii, raii>(values.begin(), values.end());
auto reference_cont = reference_container<X>(values.begin(), values.end());
raii::reset_counts();
std::thread t1, t2;
{
map_type x(0, hasher(1), key_equal(2), allocator_type(3));
X x(0, hasher(1), key_equal(2), allocator_type(3));
std::mutex m;
std::condition_variable cv;
@@ -103,7 +118,7 @@ namespace {
BOOST_TEST_GE(num_clears, 1u);
if (!x.empty()) {
test_fuzzy_matches_reference(x, reference_map, rg);
test_fuzzy_matches_reference(x, reference_cont, rg);
}
}
@@ -115,11 +130,13 @@ namespace {
// clang-format off
UNORDERED_TEST(
clear_tests,
((value_type_generator))
((test_map)(test_set))
((value_type_generator_factory))
((default_generator)(sequential)(limited_range)))
UNORDERED_TEST(insert_and_clear,
((value_type_generator))
((test_map)(test_set))
((value_type_generator_factory))
((default_generator)(sequential)(limited_range)))
// clang-format on
+137
View File
@@ -0,0 +1,137 @@
// 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
+84 -23
View File
@@ -1,34 +1,76 @@
// 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 = x.emplace(r.first.x_, r.second.x_);
bool b = member_emplace(x, r);
if (b) {
++num_inserts;
}
}
});
BOOST_TEST_EQ(num_inserts, x.size());
BOOST_TEST_EQ(raii::default_constructor, 2 * values.size());
BOOST_TEST_EQ(
raii::default_constructor, value_type_cardinality * values.size());
BOOST_TEST_EQ(raii::copy_constructor, 0u);
BOOST_TEST_GE(raii::move_constructor, 2 * x.size());
BOOST_TEST_GE(raii::move_constructor, value_type_cardinality * x.size());
BOOST_TEST_EQ(raii::copy_constructor, 0u);
BOOST_TEST_EQ(raii::copy_assignment, 0u);
@@ -40,9 +82,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;
x.reserve(values.size());
lvalue_emplacer_type::operator()(values, x);
BOOST_TEST_EQ(raii::move_constructor, 2 * x.size());
BOOST_TEST_EQ(raii::move_constructor, value_type_cardinality * x.size());
}
} norehash_lvalue_emplacer;
@@ -50,12 +95,15 @@ 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 = x.emplace_or_cvisit(
r.first.x_, r.second.x_,
bool b = member_emplace_or_cvisit(
x, r,
[&num_invokes](typename X::value_type const& v) {
(void)v;
++num_invokes;
@@ -70,9 +118,10 @@ namespace {
BOOST_TEST_EQ(num_inserts, x.size());
BOOST_TEST_EQ(num_invokes, values.size() - x.size());
BOOST_TEST_EQ(raii::default_constructor, 2 * values.size());
BOOST_TEST_EQ(
raii::default_constructor, value_type_cardinality * values.size());
BOOST_TEST_EQ(raii::copy_constructor, 0u);
BOOST_TEST_GE(raii::move_constructor, 2 * x.size());
BOOST_TEST_GE(raii::move_constructor, value_type_cardinality * x.size());
BOOST_TEST_EQ(raii::move_assignment, 0u);
BOOST_TEST_EQ(raii::copy_assignment, 0u);
}
@@ -82,13 +131,23 @@ 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.emplace_or_visit(
r.first.x_, r.second.x_,
[&num_invokes](typename X::value_type& v) {
bool b = member_emplace_or_visit(
x, r,
[&num_invokes](arg_type& v) {
(void)v;
++num_invokes;
});
@@ -102,20 +161,21 @@ namespace {
BOOST_TEST_EQ(num_inserts, x.size());
BOOST_TEST_EQ(num_invokes, values.size() - x.size());
BOOST_TEST_EQ(raii::default_constructor, 2 * values.size());
BOOST_TEST_EQ(
raii::default_constructor, value_type_cardinality * values.size());
BOOST_TEST_EQ(raii::copy_constructor, 0u);
BOOST_TEST_GE(raii::move_constructor, 2 * x.size());
BOOST_TEST_GE(raii::move_constructor, value_type_cardinality * x.size());
BOOST_TEST_EQ(raii::move_assignment, 0u);
BOOST_TEST_EQ(raii::copy_assignment, 0u);
}
} lvalue_emplace_or_visit;
template <class X, class G, class F>
void emplace(X*, G gen, F emplacer, test::random_generator rg)
template <class X, class GF, class F>
void emplace(X*, GF gen_factory, 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_map =
boost::unordered_flat_map<raii, raii>(values.begin(), values.end());
auto reference_cont = reference_container<X>(values.begin(), values.end());
raii::reset_counts();
{
@@ -123,13 +183,13 @@ namespace {
emplacer(values, x);
BOOST_TEST_EQ(x.size(), reference_map.size());
BOOST_TEST_EQ(x.size(), reference_cont.size());
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(x.size(), x.visit_all([&](value_type const& v) {
BOOST_TEST(reference_cont.contains(get_key(v)));
if (rg == test::sequential) {
BOOST_TEST_EQ(kv.second, reference_map[kv.first]);
BOOST_TEST_EQ(v, *reference_cont.find(get_key(v)));
}
}));
}
@@ -145,6 +205,7 @@ namespace {
}
boost::unordered::concurrent_flat_map<raii, raii>* map;
boost::unordered::concurrent_flat_set<raii>* set;
} // namespace
@@ -156,8 +217,8 @@ using test::sequential;
UNORDERED_TEST(
emplace,
((map))
((value_type_generator)(init_type_generator))
((map)(set))
((value_type_generator_factory)(init_type_generator_factory))
((lvalue_emplacer)(norehash_lvalue_emplacer)
(lvalue_emplace_or_cvisit)(lvalue_emplace_or_visit))
((default_generator)(sequential)(limited_range)))
+44 -11
View File
@@ -1,10 +1,12 @@
// 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};
@@ -14,16 +16,21 @@ 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, allocator_type>;
key_equal, stateful_allocator<std::pair<raii const, raii> > >;
using map_value_type = typename map_type::value_type;
using set_type = boost::unordered::concurrent_flat_set<raii, hasher,
key_equal, stateful_allocator<raii> >;
map_type* test_map;
set_type* test_set;
namespace {
UNORDERED_AUTO_TEST (simple_equality) {
UNORDERED_AUTO_TEST (simple_map_equality) {
using allocator_type = map_type::allocator_type;
{
map_type x1(
{{1, 11}, {2, 22}}, 0, hasher(1), key_equal(2), allocator_type(3));
@@ -50,17 +57,42 @@ namespace {
}
}
template <class G> void insert_and_compare(G gen, test::random_generator rg)
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)
{
using allocator_type = typename X::allocator_type;
auto gen = gen_factory.template get<X>();
auto vals1 = make_random_values(1024 * 8, [&] { return gen(rg); });
boost::unordered_flat_map<raii, raii> reference_map(
vals1.begin(), vals1.end());
auto reference_cont = reference_container<X>(vals1.begin(), vals1.end());
{
raii::reset_counts();
map_type x1(vals1.size(), hasher(1), key_equal(2), allocator_type(3));
map_type x2(vals1.begin(), vals1.end(), vals1.size(), hasher(2),
X x1(vals1.size(), hasher(1), key_equal(2), allocator_type(3));
X x2(vals1.begin(), vals1.end(), vals1.size(), hasher(2),
key_equal(2), allocator_type(3));
std::thread t1, t2;
@@ -126,7 +158,7 @@ namespace {
BOOST_TEST(x1 == x2);
BOOST_TEST(!(x1 != x2));
test_matches_reference(x1, reference_map);
test_matches_reference(x1, reference_cont);
}
check_raii_counts();
}
@@ -135,7 +167,8 @@ namespace {
// clang-format off
UNORDERED_TEST(
insert_and_compare,
((value_type_generator))
((test_map)(test_set))
((value_type_generator_factory))
((default_generator)(sequential)(limited_range)))
// clang-format on
+112 -49
View File
@@ -1,10 +1,12 @@
// 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>
@@ -15,6 +17,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;
std::atomic<std::uint64_t> num_erased{0};
auto const old_size = x.size();
@@ -26,11 +31,11 @@ namespace {
BOOST_TEST_EQ(raii::default_constructor + raii::copy_constructor +
raii::move_constructor,
raii::destructor + 2 * x.size());
raii::destructor + value_type_cardinality * x.size());
thread_runner(values, [&values, &num_erased, &x](boost::span<T>) {
for (auto const& k : values) {
auto count = x.erase(k.first);
for (auto const& v : values) {
auto count = x.erase(get_key(v));
num_erased += count;
BOOST_TEST_LE(count, 1u);
BOOST_TEST_GE(count, 0u);
@@ -41,7 +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 + 2 * old_size);
BOOST_TEST_EQ(raii::destructor, old_d + value_type_cardinality * old_size);
BOOST_TEST_EQ(x.size(), 0u);
BOOST_TEST(x.empty());
@@ -53,6 +58,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;
std::atomic<std::uint64_t> num_erased{0};
auto const old_size = x.size();
@@ -64,11 +72,11 @@ namespace {
BOOST_TEST_EQ(raii::default_constructor + raii::copy_constructor +
raii::move_constructor,
raii::destructor + 2 * x.size());
raii::destructor + value_type_cardinality * x.size());
thread_runner(values, [&num_erased, &x](boost::span<T> s) {
for (auto const& k : s) {
auto count = x.erase(k.first.x_);
for (auto const& v : s) {
auto count = x.erase(get_key(v).x_);
num_erased += count;
BOOST_TEST_LE(count, 1u);
BOOST_TEST_GE(count, 0u);
@@ -79,7 +87,8 @@ namespace {
BOOST_TEST_EQ(raii::copy_constructor, old_cc);
BOOST_TEST_EQ(raii::move_constructor, old_mc);
BOOST_TEST_EQ(raii::destructor, old_d + 2 * num_erased);
BOOST_TEST_EQ(
raii::destructor, old_d + value_type_cardinality * num_erased);
BOOST_TEST_EQ(x.size(), 0u);
BOOST_TEST(x.empty());
@@ -92,6 +101,15 @@ 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};
@@ -105,8 +123,8 @@ namespace {
auto max = 0;
x.visit_all([&max](value_type const& v) {
if (v.second.x_ > max) {
max = v.second.x_;
if (get_value(v).x_ > max) {
max = get_value(v).x_;
}
});
@@ -114,15 +132,15 @@ namespace {
auto expected_erasures = 0u;
x.visit_all([&expected_erasures, threshold](value_type const& v) {
if (v.second.x_ > threshold) {
if (get_value(v).x_ > threshold) {
++expected_erasures;
}
});
thread_runner(values, [&num_erased, &x, threshold](boost::span<T> s) {
for (auto const& k : s) {
auto count = x.erase_if(k.first,
[threshold](value_type& v) { return v.second.x_ > threshold; });
for (auto const& v : s) {
auto count = x.erase_if(get_key(v),
[threshold](arg_type& w) { return get_value(w).x_ > threshold; });
num_erased += count;
BOOST_TEST_LE(count, 1u);
BOOST_TEST_GE(count, 0u);
@@ -136,7 +154,8 @@ namespace {
BOOST_TEST_EQ(raii::copy_constructor, old_cc);
BOOST_TEST_EQ(raii::move_constructor, old_mc);
BOOST_TEST_EQ(raii::destructor, old_d + 2 * num_erased);
BOOST_TEST_EQ(
raii::destructor, old_d + value_type_cardinality * num_erased);
}
} lvalue_eraser_if;
@@ -145,6 +164,15 @@ 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};
@@ -158,8 +186,8 @@ namespace {
auto max = 0;
x.visit_all([&max](value_type const& v) {
if (v.second.x_ > max) {
max = v.second.x_;
if (get_value(v).x_ > max) {
max = get_value(v).x_;
}
});
@@ -167,15 +195,15 @@ namespace {
auto expected_erasures = 0u;
x.visit_all([&expected_erasures, threshold](value_type const& v) {
if (v.second.x_ > threshold) {
if (get_value(v).x_ > threshold) {
++expected_erasures;
}
});
thread_runner(values, [&num_erased, &x, threshold](boost::span<T> s) {
for (auto const& k : s) {
auto count = x.erase_if(k.first.x_,
[threshold](value_type& v) { return v.second.x_ > threshold; });
for (auto const& v : s) {
auto count = x.erase_if(get_key(v).x_,
[threshold](arg_type& w) { return get_value(w).x_ > threshold; });
num_erased += count;
BOOST_TEST_LE(count, 1u);
BOOST_TEST_GE(count, 0u);
@@ -189,7 +217,8 @@ namespace {
BOOST_TEST_EQ(raii::copy_constructor, old_cc);
BOOST_TEST_EQ(raii::move_constructor, old_mc);
BOOST_TEST_EQ(raii::destructor, old_d + 2 * num_erased);
BOOST_TEST_EQ(
raii::destructor, old_d + value_type_cardinality * num_erased);
}
} transp_lvalue_eraser_if;
@@ -198,6 +227,15 @@ 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};
@@ -211,8 +249,8 @@ namespace {
auto max = 0;
x.visit_all([&max](value_type const& v) {
if (v.second.x_ > max) {
max = v.second.x_;
if (get_value(v).x_ > max) {
max = get_value(v).x_;
}
});
@@ -220,7 +258,7 @@ namespace {
auto expected_erasures = 0u;
x.visit_all([&expected_erasures, threshold](value_type const& v) {
if (v.second.x_ > threshold) {
if (get_value(v).x_ > threshold) {
++expected_erasures;
}
});
@@ -229,7 +267,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](value_type& v) { return v.second.x_ > threshold; });
[threshold](arg_type& v) { return get_value(v).x_ > threshold; });
num_erased += count;
}
});
@@ -241,7 +279,8 @@ namespace {
BOOST_TEST_EQ(raii::copy_constructor, old_cc);
BOOST_TEST_EQ(raii::move_constructor, old_mc);
BOOST_TEST_EQ(raii::destructor, old_d + 2 * num_erased);
BOOST_TEST_EQ(
raii::destructor, old_d + value_type_cardinality * num_erased);
}
} erase_if;
@@ -250,6 +289,15 @@ 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};
@@ -263,8 +311,8 @@ namespace {
auto max = 0;
x.visit_all([&max](value_type const& v) {
if (v.second.x_ > max) {
max = v.second.x_;
if (get_value(v).x_ > max) {
max = get_value(v).x_;
}
});
@@ -272,7 +320,7 @@ namespace {
auto expected_erasures = 0u;
x.visit_all([&expected_erasures, threshold](value_type const& v) {
if (v.second.x_ > threshold) {
if (get_value(v).x_ > threshold) {
++expected_erasures;
}
});
@@ -281,7 +329,8 @@ 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](value_type& v) { return v.second.x_ > threshold; });
[threshold](arg_type& v) {
return get_value(v).x_ > threshold; });
num_erased += count;
}
});
@@ -293,7 +342,8 @@ namespace {
BOOST_TEST_EQ(raii::copy_constructor, old_cc);
BOOST_TEST_EQ(raii::move_constructor, old_mc);
BOOST_TEST_EQ(raii::destructor, old_d + 2 * num_erased);
BOOST_TEST_EQ(
raii::destructor, old_d + value_type_cardinality * num_erased);
}
} free_fn_erase_if;
@@ -303,6 +353,15 @@ 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};
@@ -316,8 +375,8 @@ namespace {
auto max = 0;
x.visit_all([&max](value_type const& v) {
if (v.second.x_ > max) {
max = v.second.x_;
if (get_value(v).x_ > max) {
max = get_value(v).x_;
}
});
@@ -325,7 +384,7 @@ namespace {
auto expected_erasures = 0u;
x.visit_all([&expected_erasures, threshold](value_type const& v) {
if (v.second.x_ > threshold) {
if (get_value(v).x_ > threshold) {
++expected_erasures;
}
});
@@ -333,9 +392,9 @@ namespace {
thread_runner(values, [&num_invokes, &x, threshold](boost::span<T> s) {
(void)s;
x.erase_if(
std::execution::par, [&num_invokes, threshold](value_type& v) {
std::execution::par, [&num_invokes, threshold](arg_type& v) {
++num_invokes;
return v.second.x_ > threshold;
return get_value(v).x_ > threshold;
});
});
@@ -346,7 +405,8 @@ namespace {
BOOST_TEST_EQ(raii::copy_constructor, old_cc);
BOOST_TEST_EQ(raii::move_constructor, old_mc);
BOOST_TEST_EQ(raii::destructor, old_d + 2 * expected_erasures);
BOOST_TEST_EQ(
raii::destructor, old_d + value_type_cardinality * expected_erasures);
#else
(void)values;
(void)x;
@@ -354,12 +414,12 @@ namespace {
}
} erase_if_exec_policy;
template <class X, class G, class F>
void erase(X*, G gen, F eraser, test::random_generator rg)
template <class X, class GF, class F>
void erase(X*, GF gen_factory, 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_map =
boost::unordered_flat_map<raii, raii>(values.begin(), values.end());
auto reference_cont = reference_container<X>(values.begin(), values.end());
raii::reset_counts();
{
@@ -367,20 +427,23 @@ namespace {
x.insert(values.begin(), values.end());
BOOST_TEST_EQ(x.size(), reference_map.size());
BOOST_TEST_EQ(x.size(), reference_cont.size());
test_fuzzy_matches_reference(x, reference_map, rg);
test_fuzzy_matches_reference(x, reference_cont, rg);
eraser(values, x);
test_fuzzy_matches_reference(x, reference_map, rg);
test_fuzzy_matches_reference(x, reference_cont, 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
@@ -391,15 +454,15 @@ using test::sequential;
// clang-format off
UNORDERED_TEST(
erase,
((map))
((value_type_generator)(init_type_generator))
((map)(set))
((value_type_generator_factory)(init_type_generator_factory))
((lvalue_eraser)(lvalue_eraser_if)(erase_if)(free_fn_erase_if)(erase_if_exec_policy))
((default_generator)(sequential)(limited_range)))
UNORDERED_TEST(
erase,
((transparent_map))
((value_type_generator)(init_type_generator))
((transparent_map)(transparent_set))
((value_type_generator_factory)(init_type_generator_factory))
((transp_lvalue_eraser)(transp_lvalue_eraser_if)(erase_if_exec_policy))
((default_generator)(sequential)(limited_range)))
+95 -43
View File
@@ -1,24 +1,87 @@
// 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>
using allocator_type = stateful_allocator<std::pair<raii const, raii> >;
#include <boost/unordered/concurrent_flat_set.hpp>
using hasher = stateful_hash;
using key_equal = stateful_key_equal;
using map_type = boost::unordered::concurrent_flat_map<raii, raii, hasher,
key_equal, allocator_type>;
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);
namespace {
test::seed_t initialize_seed(1794114520);
template <class G> void copy_assign(G gen, test::random_generator rg)
template <class X, class GF>
void copy_assign(X*, GF gen_factory, 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); });
{
@@ -31,12 +94,12 @@ namespace {
values.begin() + static_cast<std::ptrdiff_t>(values.size() / 2);
auto end = values.end();
auto reference_map = boost::unordered_flat_map<raii, raii>(begin, mid);
auto reference_cont = reference_container<X>(begin, mid);
map_type x(
X x(
begin, mid, values.size(), hasher(1), key_equal(2), allocator_type(3));
map_type y(
X y(
mid, end, values.size(), hasher(2), key_equal(1), allocator_type(4));
BOOST_TEST(!y.empty());
@@ -53,13 +116,17 @@ namespace {
disable_exceptions();
BOOST_TEST_GT(num_throws, 0u);
test_fuzzy_matches_reference(y, reference_map, rg);
test_fuzzy_matches_reference(y, reference_cont, rg);
}
check_raii_counts();
}
template <class G> void move_assign(G gen, test::random_generator rg)
template <class X, class GF>
void move_assign(X*, GF gen_factory, 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); });
{
@@ -72,7 +139,7 @@ namespace {
values.begin() + static_cast<std::ptrdiff_t>(values.size() / 2);
auto end = values.end();
auto reference_map = boost::unordered_flat_map<raii, raii>(begin, mid);
auto reference_cont = reference_container<X>(begin, mid);
BOOST_TEST(
!boost::allocator_is_always_equal<allocator_type>::type::value);
@@ -83,10 +150,10 @@ namespace {
for (std::size_t i = 0; i < 2 * alloc_throw_threshold; ++i) {
disable_exceptions();
map_type x(begin, mid, values.size(), hasher(1), key_equal(2),
X x(begin, mid, values.size(), hasher(1), key_equal(2),
allocator_type(3));
map_type y(
X y(
mid, end, values.size(), hasher(2), key_equal(1), allocator_type(4));
enable_exceptions();
@@ -96,7 +163,7 @@ namespace {
++num_throws;
}
disable_exceptions();
test_fuzzy_matches_reference(y, reference_map, rg);
test_fuzzy_matches_reference(y, reference_cont, rg);
}
BOOST_TEST_GT(num_throws, 0u);
@@ -104,43 +171,22 @@ namespace {
check_raii_counts();
}
UNORDERED_AUTO_TEST (intializer_list_assign) {
using value_type = typename map_type::value_type;
template <class X, class IL>
void intializer_list_assign(std::pair<X*, IL> p)
{
using allocator_type = typename X::allocator_type;
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}},
};
auto init_list = p.second;
{
raii::reset_counts();
unsigned num_throws = 0;
for (std::size_t i = 0; i < throw_threshold; ++i) {
map_type x(0, hasher(1), key_equal(2), allocator_type(3));
X x(0, hasher(1), key_equal(2), allocator_type(3));
enable_exceptions();
try {
x = values;
x = init_list;
} catch (...) {
++num_throws;
}
@@ -160,13 +206,19 @@ using test::sequential;
// clang-format off
UNORDERED_TEST(
copy_assign,
((exception_value_type_generator))
((test_map)(test_set))
((exception_value_type_generator_factory))
((default_generator)(sequential)(limited_range)))
UNORDERED_TEST(
move_assign,
((exception_value_type_generator))
((test_map)(test_set))
((exception_value_type_generator_factory))
((default_generator)(sequential)))
UNORDERED_TEST(
intializer_list_assign,
((test_map_and_init_list)(test_set_and_init_list)))
// clang-format on
RUN_TESTS()
+115 -52
View File
@@ -1,23 +1,84 @@
// 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>
using allocator_type = stateful_allocator<std::pair<raii const, raii> >;
#include <boost/unordered/concurrent_flat_set.hpp>
using hasher = stateful_hash;
using key_equal = stateful_key_equal;
using map_type = boost::unordered::concurrent_flat_map<raii, raii, hasher,
key_equal, allocator_type>;
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);
namespace {
test::seed_t initialize_seed(795610904);
UNORDERED_AUTO_TEST (bucket_constructor) {
template <class X>
void bucket_constructor(X*)
{
raii::reset_counts();
bool was_thrown = false;
@@ -25,7 +86,7 @@ namespace {
enable_exceptions();
for (std::size_t i = 0; i < alloc_throw_threshold; ++i) {
try {
map_type m(128);
X m(128);
} catch (...) {
was_thrown = true;
}
@@ -35,8 +96,12 @@ namespace {
BOOST_TEST(was_thrown);
}
template <class G> void iterator_range(G gen, test::random_generator rg)
template <class X, class GF>
void iterator_range(X*, GF gen_factory, 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); });
{
@@ -46,7 +111,7 @@ namespace {
enable_exceptions();
try {
map_type x(values.begin(), values.end(), 0, hasher(1), key_equal(2),
X x(values.begin(), values.end(), 0, hasher(1), key_equal(2),
allocator_type(3));
} catch (...) {
was_thrown = true;
@@ -64,7 +129,7 @@ namespace {
enable_exceptions();
try {
map_type x(values.begin(), values.end(), allocator_type(3));
X x(values.begin(), values.end(), allocator_type(3));
} catch (...) {
was_thrown = true;
}
@@ -81,7 +146,7 @@ namespace {
enable_exceptions();
try {
map_type x(
X x(
values.begin(), values.end(), values.size(), allocator_type(3));
} catch (...) {
was_thrown = true;
@@ -99,7 +164,7 @@ namespace {
enable_exceptions();
try {
map_type x(values.begin(), values.end(), values.size(), hasher(1),
X x(values.begin(), values.end(), values.size(), hasher(1),
allocator_type(3));
} catch (...) {
was_thrown = true;
@@ -111,8 +176,12 @@ namespace {
}
}
template <class G> void copy_constructor(G gen, test::random_generator rg)
template <class X, class GF>
void copy_constructor(X*, GF gen_factory, 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); });
{
@@ -121,10 +190,10 @@ namespace {
bool was_thrown = false;
try {
map_type x(values.begin(), values.end(), 0);
X x(values.begin(), values.end(), 0);
enable_exceptions();
map_type y(x);
X y(x);
} catch (...) {
was_thrown = true;
}
@@ -140,10 +209,10 @@ namespace {
bool was_thrown = false;
try {
map_type x(values.begin(), values.end(), 0);
X x(values.begin(), values.end(), 0);
enable_exceptions();
map_type y(x, allocator_type(4));
X y(x, allocator_type(4));
} catch (...) {
was_thrown = true;
}
@@ -154,20 +223,24 @@ namespace {
}
}
template <class G> void move_constructor(G gen, test::random_generator rg)
template <class X, class GF>
void move_constructor(X*, GF gen_factory, test::random_generator rg)
{
auto values = make_random_values(1024 * 16, [&] { return gen(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 {
map_type x(values.begin(), values.end(), 0);
X x(values.begin(), values.end(), 0);
enable_exceptions();
map_type y(std::move(x), allocator_type(4));
X y(std::move(x), allocator_type(4));
} catch (...) {
was_thrown = true;
}
@@ -178,33 +251,12 @@ namespace {
}
}
UNORDERED_AUTO_TEST (initializer_list_bucket_count) {
using value_type = typename map_type::value_type;
template <class X, class IL>
void initializer_list_bucket_count(std::pair<X*, IL> p)
{
using allocator_type = typename X::allocator_type;
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}},
};
auto init_list = p.second;
{
raii::reset_counts();
@@ -213,7 +265,7 @@ namespace {
enable_exceptions();
for (std::size_t i = 0; i < throw_threshold; ++i) {
try {
map_type x(values, 0, hasher(1), key_equal(2), allocator_type(3));
X x(init_list, 0, hasher(1), key_equal(2), allocator_type(3));
} catch (...) {
++num_throws;
}
@@ -231,7 +283,7 @@ namespace {
enable_exceptions();
for (std::size_t i = 0; i < alloc_throw_threshold * 2; ++i) {
try {
map_type x(values, allocator_type(3));
X x(init_list, allocator_type(3));
} catch (...) {
++num_throws;
}
@@ -249,7 +301,7 @@ namespace {
enable_exceptions();
for (std::size_t i = 0; i < alloc_throw_threshold * 2; ++i) {
try {
map_type x(values, values.size() * 2, allocator_type(3));
X x(init_list, init_list.size() * 2, allocator_type(3));
} catch (...) {
++num_throws;
}
@@ -267,7 +319,7 @@ namespace {
enable_exceptions();
for (std::size_t i = 0; i < throw_threshold; ++i) {
try {
map_type x(values, values.size() * 2, hasher(1), allocator_type(3));
X x(init_list, init_list.size() * 2, hasher(1), allocator_type(3));
} catch (...) {
++num_throws;
}
@@ -285,20 +337,31 @@ using test::limited_range;
using test::sequential;
// clang-format off
UNORDERED_TEST(
bucket_constructor,
((test_map)(test_set)))
UNORDERED_TEST(
iterator_range,
((exception_value_type_generator))
((test_map)(test_set))
((exception_value_type_generator_factory))
((default_generator)(sequential)(limited_range)))
UNORDERED_TEST(
copy_constructor,
((exception_value_type_generator))
((test_map)(test_set))
((exception_value_type_generator_factory))
((default_generator)(sequential)))
UNORDERED_TEST(
move_constructor,
((exception_value_type_generator))
((test_map)(test_set))
((exception_value_type_generator_factory))
((default_generator)(sequential)))
UNORDERED_TEST(
initializer_list_bucket_count,
((test_map_and_init_list)(test_set_and_init_list)))
// clang-format on
RUN_TESTS()
+71 -30
View File
@@ -1,10 +1,12 @@
// 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>
@@ -15,6 +17,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;
std::atomic<std::uint64_t> num_erased{0};
auto const old_size = x.size();
@@ -27,9 +32,9 @@ namespace {
enable_exceptions();
thread_runner(values, [&values, &num_erased, &x](boost::span<T>) {
for (auto const& k : values) {
for (auto const& v : values) {
try {
auto count = x.erase(k.first);
auto count = x.erase(get_key(v));
BOOST_TEST_LE(count, 1u);
BOOST_TEST_GE(count, 0u);
@@ -46,7 +51,8 @@ namespace {
BOOST_TEST_EQ(raii::copy_constructor, old_cc);
BOOST_TEST_EQ(raii::move_constructor, old_mc);
BOOST_TEST_EQ(raii::destructor, old_d + 2 * num_erased);
BOOST_TEST_EQ(
raii::destructor, old_d + value_type_cardinality * num_erased);
}
} lvalue_eraser;
@@ -55,6 +61,15 @@ 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};
@@ -68,8 +83,8 @@ namespace {
auto max = 0;
x.visit_all([&max](value_type const& v) {
if (v.second.x_ > max) {
max = v.second.x_;
if (get_value(v).x_ > max) {
max = get_value(v).x_;
}
});
@@ -77,17 +92,17 @@ namespace {
auto expected_erasures = 0u;
x.visit_all([&expected_erasures, threshold](value_type const& v) {
if (v.second.x_ > threshold) {
if (get_value(v).x_ > threshold) {
++expected_erasures;
}
});
enable_exceptions();
thread_runner(values, [&num_erased, &x, threshold](boost::span<T> s) {
for (auto const& k : s) {
for (auto const& v : s) {
try {
auto count = x.erase_if(k.first,
[threshold](value_type& v) { return v.second.x_ > threshold; });
auto count = x.erase_if(get_key(v),
[threshold](arg_type& w) { return get_value(w).x_ > threshold; });
num_erased += count;
BOOST_TEST_LE(count, 1u);
BOOST_TEST_GE(count, 0u);
@@ -104,7 +119,8 @@ namespace {
BOOST_TEST_EQ(raii::copy_constructor, old_cc);
BOOST_TEST_EQ(raii::move_constructor, old_mc);
BOOST_TEST_EQ(raii::destructor, old_d + 2 * num_erased);
BOOST_TEST_EQ(
raii::destructor, old_d + value_type_cardinality * num_erased);
}
} lvalue_eraser_if;
@@ -113,6 +129,15 @@ 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();
@@ -124,8 +149,8 @@ namespace {
auto max = 0;
x.visit_all([&max](value_type const& v) {
if (v.second.x_ > max) {
max = v.second.x_;
if (get_value(v).x_ > max) {
max = get_value(v).x_;
}
});
@@ -133,7 +158,7 @@ namespace {
auto expected_erasures = 0u;
x.visit_all([&expected_erasures, threshold](value_type const& v) {
if (v.second.x_ > threshold) {
if (get_value(v).x_ > threshold) {
++expected_erasures;
}
});
@@ -142,14 +167,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](value_type& v) {
x.erase_if([threshold](arg_type& v) {
static std::atomic<std::uint32_t> c{0};
auto t = ++c;
if (should_throw && (t % throw_threshold == 0)) {
throw exception_tag{};
}
return v.second.x_ > threshold;
return get_value(v).x_ > threshold;
});
} catch (...) {
}
@@ -161,7 +186,9 @@ namespace {
BOOST_TEST_EQ(raii::copy_constructor, old_cc);
BOOST_TEST_EQ(raii::move_constructor, old_mc);
BOOST_TEST_EQ(raii::destructor, old_d + 2 * (old_size - x.size()));
BOOST_TEST_EQ(
raii::destructor,
old_d + value_type_cardinality * (old_size - x.size()));
}
} erase_if;
@@ -170,6 +197,15 @@ 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();
@@ -181,8 +217,8 @@ namespace {
auto max = 0;
x.visit_all([&max](value_type const& v) {
if (v.second.x_ > max) {
max = v.second.x_;
if (get_value(v).x_ > max) {
max = get_value(v).x_;
}
});
@@ -192,14 +228,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](value_type& v) {
boost::unordered::erase_if(x, [threshold](arg_type& v) {
static std::atomic<std::uint32_t> c{0};
auto t = ++c;
if (should_throw && (t % throw_threshold == 0)) {
throw exception_tag{};
}
return v.second.x_ > threshold;
return get_value(v).x_ > threshold;
});
} catch (...) {
@@ -212,16 +248,18 @@ namespace {
BOOST_TEST_EQ(raii::copy_constructor, old_cc);
BOOST_TEST_EQ(raii::move_constructor, old_mc);
BOOST_TEST_EQ(raii::destructor, old_d + 2 * (old_size - x.size()));
BOOST_TEST_EQ(
raii::destructor,
old_d + value_type_cardinality * (old_size - x.size()));
}
} free_fn_erase_if;
template <class X, class G, class F>
void erase(X*, G gen, F eraser, test::random_generator rg)
template <class X, class GF, class F>
void erase(X*, GF gen_factory, 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_map =
boost::unordered_flat_map<raii, raii>(values.begin(), values.end());
auto reference_cont = reference_container<X>(values.begin(), values.end());
raii::reset_counts();
@@ -231,13 +269,13 @@ namespace {
x.insert(v);
}
BOOST_TEST_EQ(x.size(), reference_map.size());
BOOST_TEST_EQ(x.size(), reference_cont.size());
BOOST_TEST_EQ(raii::destructor, 0u);
test_fuzzy_matches_reference(x, reference_map, rg);
test_fuzzy_matches_reference(x, reference_cont, rg);
eraser(values, x);
test_fuzzy_matches_reference(x, reference_map, rg);
test_fuzzy_matches_reference(x, reference_cont, rg);
}
check_raii_counts();
@@ -245,6 +283,8 @@ 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
@@ -255,8 +295,9 @@ using test::sequential;
// clang-format off
UNORDERED_TEST(
erase,
((map))
((exception_value_type_generator)(exception_init_type_generator))
((map)(set))
((exception_value_type_generator_factory)
(exception_init_type_generator_factory))
((lvalue_eraser)(lvalue_eraser_if)(erase_if)(free_fn_erase_if))
((default_generator)(sequential)(limited_range)))
+49 -26
View File
@@ -1,14 +1,20 @@
// 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>
@@ -308,16 +314,54 @@ std::size_t hash_value(raii const& r) noexcept
return hasher(r.x_);
}
struct exception_value_type_generator_type
template <typename K>
struct exception_value_generator
{
std::pair<raii const, raii> operator()(test::random_generator rg)
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)
{
int* p = nullptr;
int a = generate(p, rg);
int b = generate(p, rg);
return std::make_pair(raii{a}, raii{b});
}
} exception_value_type_generator;
};
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;
struct exception_init_type_generator_type
{
@@ -388,29 +432,6 @@ 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()
@@ -442,3 +463,5 @@ auto make_random_values(std::size_t count, F f) -> std::vector<decltype(f())>
}
return v;
}
#endif // BOOST_UNORDERED_TEST_CFOA_EXCEPTION_HELPERS_HPP
+43 -13
View File
@@ -1,10 +1,12 @@
// 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>
@@ -84,6 +86,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());
BOOST_TEST_EQ(raii::copy_constructor, 0u);
@@ -92,11 +97,19 @@ namespace {
rvalue_inserter_type::operator()(values, x);
if (std::is_same<T, typename X::value_type>::value) {
BOOST_TEST_EQ(raii::copy_constructor, x.size());
BOOST_TEST_EQ(raii::move_constructor, x.size());
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());
}
} else {
BOOST_TEST_EQ(raii::copy_constructor, 0u);
BOOST_TEST_EQ(raii::move_constructor, 2 * x.size());
BOOST_TEST_EQ(
raii::move_constructor, value_type_cardinality * x.size());
}
}
} norehash_rvalue_inserter;
@@ -246,6 +259,13 @@ 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();
@@ -253,7 +273,7 @@ namespace {
for (auto& r : s) {
try {
bool b =
x.insert_or_visit(r, [](typename X::value_type& v) { (void)v; });
x.insert_or_visit(r, [](arg_type& v) { (void)v; });
if (b) {
++num_inserts;
@@ -306,6 +326,13 @@ 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();
@@ -313,7 +340,7 @@ namespace {
for (auto& r : s) {
try {
bool b = x.insert_or_visit(
std::move(r), [](typename X::value_type& v) { (void)v; });
std::move(r), [](arg_type& v) { (void)v; });
if (b) {
++num_inserts;
@@ -377,14 +404,14 @@ namespace {
}
} iterator_range_insert_or_visit;
template <class X, class G, class F>
void insert(X*, G gen, F inserter, test::random_generator rg)
template <class X, class GF, class F>
void insert(X*, GF gen_factory, 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_map =
boost::unordered_flat_map<raii, raii>(values.begin(), values.end());
auto reference_cont = reference_container<X>(values.begin(), values.end());
raii::reset_counts();
{
@@ -392,13 +419,15 @@ namespace {
inserter(values, x);
test_fuzzy_matches_reference(x, reference_map, rg);
test_fuzzy_matches_reference(x, reference_cont, 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
@@ -409,8 +438,9 @@ using test::sequential;
// clang-format off
UNORDERED_TEST(
insert,
((map))
((exception_value_type_generator)(exception_init_type_generator))
((map)(set))
((exception_value_type_generator_factory)
(exception_init_type_generator_factory))
((lvalue_inserter)(rvalue_inserter)(iterator_range_inserter)
(norehash_lvalue_inserter)(norehash_rvalue_inserter)
(lvalue_insert_or_cvisit)(lvalue_insert_or_visit)
@@ -421,7 +451,7 @@ UNORDERED_TEST(
UNORDERED_TEST(
insert,
((map))
((exception_init_type_generator))
((exception_init_type_generator_factory))
((lvalue_insert_or_assign_copy_assign)(lvalue_insert_or_assign_move_assign)
(rvalue_insert_or_assign_copy_assign)(rvalue_insert_or_assign_move_assign))
((default_generator)(sequential)(limited_range)))
+21 -11
View File
@@ -1,29 +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 "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, allocator_type>;
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;
namespace {
test::seed_t initialize_seed(223333016);
template <class G> void merge(G gen, test::random_generator rg)
template <class X, class GF>
void merge(X*, GF gen_factory, 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_map =
boost::unordered_flat_map<raii, raii>(values.begin(), values.end());
auto reference_cont = reference_container<X>(values.begin(), values.end());
raii::reset_counts();
@@ -37,10 +46,10 @@ namespace {
for (unsigned i = 0; i < 5 * alloc_throw_threshold; ++i) {
disable_exceptions();
map_type x1(0, hasher(1), key_equal(2), allocator_type(3));
X x1(0, hasher(1), key_equal(2), allocator_type(3));
x1.insert(begin, mid);
map_type x2(0, hasher(2), key_equal(1), allocator_type(3));
X x2(0, hasher(2), key_equal(1), allocator_type(3));
x2.insert(mid, end);
enable_exceptions();
@@ -51,8 +60,8 @@ namespace {
}
disable_exceptions();
test_fuzzy_matches_reference(x1, reference_map, rg);
test_fuzzy_matches_reference(x2, reference_map, rg);
test_fuzzy_matches_reference(x1, reference_cont, rg);
test_fuzzy_matches_reference(x2, reference_cont, rg);
}
BOOST_TEST_GT(num_throws, 0u);
@@ -70,7 +79,8 @@ using test::sequential;
// clang-format off
UNORDERED_TEST(
merge,
((exception_value_type_generator))
((test_map)(test_set))
((exception_value_type_generator_factory))
((default_generator)(sequential)(limited_range)))
// clang-format on
+73 -19
View File
@@ -1,10 +1,12 @@
// 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};
@@ -34,37 +36,89 @@ bool unequal_call(boost::unordered::concurrent_flat_map<T, T>& x1,
return x1 != x2;
}
#include <boost/unordered/concurrent_flat_map.hpp>
using map_type = boost::unordered::concurrent_flat_map<int, int>;
#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
UNORDERED_AUTO_TEST (fwd_swap_call) {
map_type x1, x2;
swap_call(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);
}
#endif
template <class T>
bool equal_call(boost::unordered::concurrent_flat_set<T>& x1,
boost::unordered::concurrent_flat_set<T>& x2)
{
return x1 == x2;
}
UNORDERED_AUTO_TEST (fwd_equal_call) {
map_type 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) || \
BOOST_WORKAROUND(BOOST_CLANG_VERSION, >= 30800)
// clang-3.7 seems to have a codegen bug here so we workaround it
X x1, x2;
swap_call(x1, x2);
#endif
}
template <typename X>
void fwd_equal_call(X*)
{
X x1, x2;
BOOST_TEST(equal_call(x1, x2));
}
UNORDERED_AUTO_TEST (fwd_unequal_call) {
map_type x1, x2;
template <typename X>
void fwd_unequal_call(X*)
{
X 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
UNORDERED_AUTO_TEST (max_size) {
map_type x1;
template <typename X>
void max_size(X*)
{
X x1;
BOOST_TEST_EQ(
x1.max_size(), std::numeric_limits<typename map_type::size_type>::max());
x1.max_size(), std::numeric_limits<typename X::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()
+91 -36
View File
@@ -1,4 +1,5 @@
// 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)
@@ -7,11 +8,15 @@
#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>
@@ -123,6 +128,27 @@ struct stateful_key_equal
}
};
template <class T> struct cfoa_ptr
{
private:
template <class> friend struct stateful_allocator2;
T* p_ = nullptr;
cfoa_ptr(T* p) : p_(p) {}
public:
using element_type = T;
cfoa_ptr() = default;
cfoa_ptr(std::nullptr_t) : p_(nullptr){};
template <class U> using rebind = cfoa_ptr<U>;
T* operator->() const noexcept { return p_; }
static cfoa_ptr<T> pointer_to(element_type& r) { return {std::addressof(r)}; }
};
template <class T> struct stateful_allocator
{
int x_ = -1;
@@ -151,6 +177,36 @@ 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;
@@ -277,27 +333,48 @@ auto make_random_values(std::size_t count, F f) -> std::vector<decltype(f())>
return v;
}
struct value_type_generator_type
template <typename K>
struct value_generator
{
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;
using value_type = raii;
struct init_type_generator_type
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> >
{
std::pair<raii, raii> operator()(test::random_generator rg)
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)
{
int* p = nullptr;
int a = generate(p, rg);
int b = generate(p, rg);
return std::make_pair(raii{a}, raii{b});
}
} init_type_generator;
};
struct value_type_generator_factory_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;
template <class T>
std::vector<boost::span<T> > split(
@@ -357,29 +434,6 @@ 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()
@@ -458,6 +512,7 @@ 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_; }
@@ -590,4 +645,4 @@ public:
fancy_allocator& operator=(fancy_allocator const&) { return *this; }
};
#endif // BOOST_UNORDERED_TEST_CFOA_HELPERS_HPP
#endif // BOOST_UNORDERED_TEST_CFOA_HELPERS_HPP
+213 -84
View File
@@ -1,18 +1,32 @@
// 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, y;
raii_convertible(int x_, int y_) : x{x_}, y{y_} {}
int x = 0, y = 0 ;
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}; }
};
@@ -23,6 +37,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;
std::atomic<std::uint64_t> num_inserts{0};
thread_runner(values, [&x, &num_inserts](boost::span<T> s) {
for (auto const& r : s) {
@@ -33,7 +50,8 @@ namespace {
}
});
BOOST_TEST_EQ(num_inserts, x.size());
BOOST_TEST_EQ(raii::copy_constructor, 2 * x.size());
BOOST_TEST_EQ(
raii::copy_constructor, value_type_cardinality * x.size());
BOOST_TEST_EQ(raii::copy_assignment, 0u);
BOOST_TEST_EQ(raii::move_assignment, 0u);
}
@@ -43,9 +61,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;
x.reserve(values.size());
lvalue_inserter_type::operator()(values, x);
BOOST_TEST_EQ(raii::copy_constructor, 2 * x.size());
BOOST_TEST_EQ(
raii::copy_constructor, value_type_cardinality * x.size());
BOOST_TEST_EQ(raii::move_constructor, 0u);
}
} norehash_lvalue_inserter;
@@ -67,7 +89,8 @@ namespace {
});
BOOST_TEST_EQ(num_inserts, x.size());
if (std::is_same<T, typename X::value_type>::value) {
if (std::is_same<T, typename X::value_type>::value &&
!std::is_same<typename X::key_type, typename X::value_type>::value) {
BOOST_TEST_EQ(raii::copy_constructor, x.size());
} else {
BOOST_TEST_EQ(raii::copy_constructor, 0u);
@@ -82,6 +105,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());
BOOST_TEST_EQ(raii::copy_constructor, 0u);
@@ -90,11 +116,19 @@ namespace {
rvalue_inserter_type::operator()(values, x);
if (std::is_same<T, typename X::value_type>::value) {
BOOST_TEST_EQ(raii::copy_constructor, x.size());
BOOST_TEST_EQ(raii::move_constructor, x.size());
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());
}
} else {
BOOST_TEST_EQ(raii::copy_constructor, 0u);
BOOST_TEST_EQ(raii::move_constructor, 2 * x.size());
BOOST_TEST_EQ(
raii::move_constructor, value_type_cardinality * x.size());
}
}
} norehash_rvalue_inserter;
@@ -103,17 +137,21 @@ 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& p : values) {
values2.push_back(raii_convertible(p.first.x_, p.second.x_));
for (auto const& v : values) {
values2.push_back(raii_convertible(v));
}
thread_runner(values2, [&x](boost::span<raii_convertible> s) {
x.insert(s.begin(), s.end());
});
BOOST_TEST_EQ(raii::default_constructor, 2 * values2.size());
BOOST_TEST_EQ(
raii::default_constructor, value_type_cardinality * 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
@@ -253,6 +291,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;
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) {
@@ -273,7 +314,8 @@ namespace {
BOOST_TEST_EQ(num_invokes, values.size() - x.size());
BOOST_TEST_EQ(raii::default_constructor, 0u);
BOOST_TEST_EQ(raii::copy_constructor, 2 * x.size());
BOOST_TEST_EQ(
raii::copy_constructor, value_type_cardinality * x.size());
// don't check move construction count here because of rehashing
BOOST_TEST_GT(raii::move_constructor, 0u);
BOOST_TEST_EQ(raii::move_assignment, 0u);
@@ -284,12 +326,22 @@ 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](typename X::value_type& v) {
x.insert_or_visit(r, [&num_invokes](arg_type& v) {
(void)v;
++num_invokes;
});
@@ -304,7 +356,7 @@ namespace {
BOOST_TEST_EQ(num_invokes, values.size() - x.size());
BOOST_TEST_EQ(raii::default_constructor, 0u);
BOOST_TEST_EQ(raii::copy_constructor, 2 * x.size());
BOOST_TEST_EQ(raii::copy_constructor, value_type_cardinality * x.size());
// don't check move construction count here because of rehashing
BOOST_TEST_GT(raii::move_constructor, 0u);
BOOST_TEST_EQ(raii::move_assignment, 0u);
@@ -315,6 +367,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;
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) {
@@ -337,11 +392,19 @@ namespace {
BOOST_TEST_EQ(raii::default_constructor, 0u);
if (std::is_same<T, typename X::value_type>::value) {
BOOST_TEST_EQ(raii::copy_constructor, x.size());
BOOST_TEST_GE(raii::move_constructor, x.size());
if (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());
}
} else {
BOOST_TEST_EQ(raii::copy_constructor, 0u);
BOOST_TEST_GE(raii::move_constructor, 2 * x.size());
BOOST_TEST_GE(
raii::move_constructor, value_type_cardinality * x.size());
}
}
} rvalue_insert_or_cvisit;
@@ -350,12 +413,22 @@ 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](typename X::value_type& v) {
std::move(r), [&num_invokes](arg_type& v) {
(void)v;
++num_invokes;
});
@@ -371,11 +444,19 @@ namespace {
BOOST_TEST_EQ(raii::default_constructor, 0u);
if (std::is_same<T, typename X::value_type>::value) {
BOOST_TEST_EQ(raii::copy_constructor, x.size());
BOOST_TEST_GE(raii::move_constructor, x.size());
if (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());
}
} else {
BOOST_TEST_EQ(raii::copy_constructor, 0u);
BOOST_TEST_GE(raii::move_constructor, 2 * x.size());
BOOST_TEST_GE(
raii::move_constructor, value_type_cardinality * x.size());
}
}
} rvalue_insert_or_visit;
@@ -384,10 +465,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;
std::vector<raii_convertible> values2;
values2.reserve(values.size());
for (auto const& p : values) {
values2.push_back(raii_convertible(p.first.x_, p.second.x_));
for (auto const& v : values) {
values2.push_back(raii_convertible(v));
}
std::atomic<std::uint64_t> num_invokes{0};
@@ -402,7 +486,8 @@ namespace {
BOOST_TEST_EQ(num_invokes, values.size() - x.size());
BOOST_TEST_EQ(raii::default_constructor, 2 * values2.size());
BOOST_TEST_EQ(
raii::default_constructor, value_type_cardinality * values2.size());
#if BOOST_WORKAROUND(BOOST_GCC_VERSION, >= 50300) && \
BOOST_WORKAROUND(BOOST_GCC_VERSION, < 50500)
// skip test
@@ -417,10 +502,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;
std::vector<raii_convertible> values2;
values2.reserve(values.size());
for (auto const& p : values) {
values2.push_back(raii_convertible(p.first.x_, p.second.x_));
for (auto const& v : values) {
values2.push_back(raii_convertible(v));
}
std::atomic<std::uint64_t> num_invokes{0};
@@ -435,7 +523,8 @@ namespace {
BOOST_TEST_EQ(num_invokes, values.size() - x.size());
BOOST_TEST_EQ(raii::default_constructor, 2 * values2.size());
BOOST_TEST_EQ(
raii::default_constructor, value_type_cardinality * values2.size());
#if BOOST_WORKAROUND(BOOST_GCC_VERSION, >= 50300) && \
BOOST_WORKAROUND(BOOST_GCC_VERSION, < 50500)
// skip test
@@ -446,12 +535,12 @@ namespace {
}
} iterator_range_insert_or_visit;
template <class X, class G, class F>
void insert(X*, G gen, F inserter, test::random_generator rg)
template <class X, class GF, class F>
void insert(X*, GF gen_factory, 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_map =
boost::unordered_flat_map<raii, raii>(values.begin(), values.end());
auto reference_cont = reference_container<X>(values.begin(), values.end());
raii::reset_counts();
{
@@ -459,13 +548,13 @@ namespace {
inserter(values, x);
BOOST_TEST_EQ(x.size(), reference_map.size());
BOOST_TEST_EQ(x.size(), reference_cont.size());
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(x.size(), x.visit_all([&](value_type const& v) {
BOOST_TEST(reference_cont.contains(get_key(v)));
if (rg == test::sequential) {
BOOST_TEST_EQ(kv.second, reference_map[kv.first]);
BOOST_TEST_EQ(v, *reference_cont.find(get_key(v)));
}
}));
}
@@ -480,39 +569,21 @@ namespace {
raii::destructor);
}
template <class X> void insert_initializer_list(X*)
template <class X, class IL>
void insert_initializer_list(std::pair<X*, IL> p)
{
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::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}},
};
auto init_list = p.second;
std::vector<raii> dummy;
auto reference_map =
boost::unordered_flat_map<raii, raii>(values.begin(), values.end());
auto reference_cont = reference_container<X>(
init_list.begin(), init_list.end());
raii::reset_counts();
{
@@ -520,13 +591,13 @@ namespace {
X x;
thread_runner(
dummy, [&x, &values](boost::span<raii>) { x.insert(values); });
dummy, [&x, &init_list](boost::span<raii>) { x.insert(init_list); });
BOOST_TEST_EQ(x.size(), reference_map.size());
BOOST_TEST_EQ(x.size(), reference_cont.size());
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]);
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)));
}));
}
@@ -549,27 +620,27 @@ namespace {
X x;
thread_runner(dummy, [&x, &values, &num_invokes](boost::span<raii>) {
x.insert_or_visit(values, [&num_invokes](typename X::value_type& v) {
thread_runner(dummy, [&x, &init_list, &num_invokes](boost::span<raii>) {
x.insert_or_visit(init_list, [&num_invokes](arg_type& v) {
(void)v;
++num_invokes;
});
x.insert_or_cvisit(
values, [&num_invokes](typename X::value_type const& v) {
init_list, [&num_invokes](typename X::value_type const& v) {
(void)v;
++num_invokes;
});
});
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(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(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]);
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)));
}));
}
@@ -606,6 +677,64 @@ 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;
@@ -615,12 +744,12 @@ using test::sequential;
// clang-format off
UNORDERED_TEST(
insert_initializer_list,
((map)))
((map_and_init_list)(set_and_init_list)))
UNORDERED_TEST(
insert,
((map)(fancy_map))
((value_type_generator)(init_type_generator))
((map)(fancy_map)(set)(fancy_set))
((value_type_generator_factory)(init_type_generator_factory))
((lvalue_inserter)(rvalue_inserter)(iterator_range_inserter)
(norehash_lvalue_inserter)(norehash_rvalue_inserter)
(lvalue_insert_or_cvisit)(lvalue_insert_or_visit)
@@ -631,7 +760,7 @@ UNORDERED_TEST(
UNORDERED_TEST(
insert,
((map))
((init_type_generator))
((init_type_generator_factory))
((lvalue_insert_or_assign_copy_assign)(lvalue_insert_or_assign_move_assign)
(rvalue_insert_or_assign_copy_assign)(rvalue_insert_or_assign_move_assign))
((default_generator)(sequential)(limited_range)))
@@ -639,7 +768,7 @@ UNORDERED_TEST(
UNORDERED_TEST(
insert,
((trans_map))
((init_type_generator))
((init_type_generator_factory))
((trans_insert_or_assign_copy_assign)(trans_insert_or_assign_move_assign))
((default_generator)(sequential)(limited_range)))
// clang-format on
+57 -32
View File
@@ -1,10 +1,12 @@
// 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};
@@ -14,12 +16,25 @@ 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, allocator_type>;
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_value_type = typename map_type::value_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);
struct
{
@@ -40,18 +55,23 @@ struct
} rvalue_merge;
namespace {
template <class F, class G>
void merge_tests(F merger, G gen, test::random_generator rg)
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)
{
auto values = make_random_values(1024 * 8, [&] { return gen(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 ref_map =
boost::unordered_flat_map<raii, raii>(values.begin(), values.end());
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());
{
raii::reset_counts();
map_type x(values.size(), hasher(1), key_equal(2), allocator_type(3));
X x(values.size(), hasher(1), key_equal(2), allocator_type(3));
auto const old_cc = +raii::copy_constructor;
@@ -59,48 +79,50 @@ namespace {
std::atomic<unsigned long long> num_merged{0};
thread_runner(values, [&x, &expected_copies, &num_merged, merger](
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));
boost::span<value_type> s) {
Y y(s.size(), allocator_type(3));
for (auto const& v : s) {
y.insert(v);
}
expected_copies += 2 * y.size();
expected_copies += value_type_cardinality * 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, 2 * ref_map.size());
BOOST_TEST_EQ(+num_merged, ref_map.size());
BOOST_TEST_EQ(
raii::move_constructor,
value_type_cardinality * reference_cont.size());
BOOST_TEST_EQ(+num_merged, reference_cont.size());
test_fuzzy_matches_reference(x, ref_map, rg);
test_fuzzy_matches_reference(x, reference_cont, rg);
}
check_raii_counts();
}
template <class G>
void insert_and_merge_tests(G gen, test::random_generator rg)
template <typename X, typename Y, class GF>
void insert_and_merge_tests(
std::pair<X*, Y*>, GF gen_factory, test::random_generator rg)
{
using map2_type = boost::unordered::concurrent_flat_map<raii, raii,
std::hash<raii>, std::equal_to<raii>, allocator_type>;
static constexpr auto value_type_cardinality =
value_cardinality<typename X::value_type>::value;
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); });
auto ref_map = boost::unordered_flat_map<raii, raii>();
ref_map.insert(vals1.begin(), vals1.end());
ref_map.insert(vals2.begin(), vals2.end());
auto reference_cont = reference_container<X>();
reference_cont.insert(vals1.begin(), vals1.end());
reference_cont.insert(vals2.begin(), vals2.end());
{
raii::reset_counts();
map_type x1(2 * vals1.size(), hasher(1), key_equal(2), allocator_type(3));
X x1(2 * vals1.size(), hasher(1), key_equal(2), allocator_type(3));
map2_type x2(2 * vals1.size(), allocator_type(3));
Y x2(2 * vals1.size(), allocator_type(3));
std::thread t1, t2, t3;
boost::compat::latch l(2);
@@ -190,12 +212,13 @@ 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, 2 * num_merges);
BOOST_TEST_EQ(
+raii::move_constructor, value_type_cardinality * num_merges);
BOOST_TEST_GE(call_count, 1u);
}
x1.merge(x2);
test_fuzzy_matches_reference(x1, ref_map, rg);
test_fuzzy_matches_reference(x1, reference_cont, rg);
}
check_raii_counts();
@@ -206,13 +229,15 @@ namespace {
// clang-format off
UNORDERED_TEST(
merge_tests,
((test_maps)(test_sets))
((lvalue_merge)(rvalue_merge))
((value_type_generator))
((value_type_generator_factory))
((default_generator)(sequential)(limited_range)))
UNORDERED_TEST(
insert_and_merge_tests,
((value_type_generator))
((test_maps)(test_sets))
((value_type_generator_factory))
((default_generator)(sequential)(limited_range)))
// clang-format on
+113
View File
@@ -0,0 +1,113 @@
// 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()
+41 -16
View File
@@ -1,10 +1,12 @@
// 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;
@@ -12,18 +14,25 @@ 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, allocator_type>;
key_equal, stateful_allocator<std::pair<raii const, raii> > >;
using map_value_type = typename map_type::value_type;
using set_type = boost::unordered::concurrent_flat_set<raii, hasher,
key_equal, stateful_allocator<raii> >;
map_type* test_map;
set_type* test_set;
namespace {
test::seed_t initialize_seed{748775921};
UNORDERED_AUTO_TEST (rehash_no_insert) {
map_type x(0, hasher(1), key_equal(2), allocator_type(3));
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));
BOOST_TEST_EQ(x.bucket_count(), 0u);
x.rehash(1024);
@@ -37,10 +46,13 @@ namespace {
BOOST_TEST_EQ(x.bucket_count(), 0u);
}
UNORDERED_AUTO_TEST (reserve_no_insert) {
using size_type = map_type::size_type;
template <typename X>
void reserve_no_insert(X*)
{
using allocator_type = typename X::allocator_type;
using size_type = typename X::size_type;
map_type x(0, hasher(1), key_equal(2), allocator_type(3));
X 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()));
@@ -59,9 +71,13 @@ namespace {
BOOST_TEST_EQ(x.bucket_count(), f(0.0));
}
template <class G>
void insert_and_erase_with_rehash(G gen, test::random_generator rg)
template <class X, class GF>
void insert_and_erase_with_rehash(
X*, GF gen_factory, 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());
@@ -70,13 +86,13 @@ namespace {
}
shuffle_values(erase_indices);
auto ref_map = boost::unordered_flat_map<raii, raii>();
ref_map.insert(vals1.begin(), vals1.end());
auto reference_cont = reference_container<X>();
reference_cont.insert(vals1.begin(), vals1.end());
{
raii::reset_counts();
map_type x(0, hasher(1), key_equal(2), allocator_type(3));
X x(0, hasher(1), key_equal(2), allocator_type(3));
std::thread t1, t2, t3;
boost::compat::latch l(2);
@@ -121,7 +137,7 @@ namespace {
for (std::size_t idx = 0; idx < erase_indices.size(); ++idx) {
auto const& val = vals1[erase_indices[idx]];
x.erase(val.first);
x.erase(get_key(val));
if (idx % 100 == 0) {
std::this_thread::yield();
}
@@ -161,7 +177,7 @@ namespace {
BOOST_TEST_GE(call_count, 1u);
test_fuzzy_matches_reference(x, ref_map, rg);
test_fuzzy_matches_reference(x, reference_cont, rg);
}
check_raii_counts();
@@ -169,9 +185,18 @@ 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,
((value_type_generator))
((test_map)(test_set))
((value_type_generator_factory))
((default_generator)(sequential)(limited_range)))
// clang-format on
+82
View File
@@ -0,0 +1,82 @@
// 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()
+54 -41
View File
@@ -1,10 +1,12 @@
// 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};
@@ -56,17 +58,12 @@ 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, allocator_type>;
key_equal, stateful_allocator<std::pair<raii const, 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>;
using set_type = boost::unordered::concurrent_flat_set<raii, hasher,
key_equal, stateful_allocator<raii> >;
template <class T> struct is_nothrow_member_swappable
{
@@ -75,13 +72,21 @@ template <class T> struct is_nothrow_member_swappable
};
BOOST_STATIC_ASSERT(is_nothrow_member_swappable<
boost::unordered::concurrent_flat_map<int, int, std::hash<int>,
std::equal_to<int>, std::allocator<std::pair<int const, int> > > >::value);
replace_allocator<map_type, std::allocator> >::value);
BOOST_STATIC_ASSERT(is_nothrow_member_swappable<pocs_map_type>::value);
BOOST_STATIC_ASSERT(is_nothrow_member_swappable<
replace_allocator<map_type, pocs_allocator> >::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
{
@@ -97,31 +102,31 @@ namespace {
}
} free_fn_swap;
template <class X, class F, class G>
void swap_tests(X*, F swapper, G gen, test::random_generator rg)
template <class X, class F, class GF>
void swap_tests(X*, F swapper, GF gen_factory, test::random_generator rg)
{
using allocator = typename X::allocator_type;
using value_type = typename X::value_type;
using allocator_type = typename X::allocator_type;
bool const pocs =
boost::allocator_propagate_on_container_swap<allocator>::type::value;
boost::allocator_propagate_on_container_swap<
allocator_type>::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 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());
auto reference_cont1 = reference_container<X>(vals1.begin(), vals1.end());
auto reference_cont2 = reference_container<X>(vals2.begin(), vals2.end());
{
raii::reset_counts();
X x1(vals1.begin(), vals1.end(), vals1.size(), hasher(1), key_equal(2),
allocator(3));
allocator_type(3));
X x2(vals2.begin(), vals2.end(), vals2.size(), hasher(2), key_equal(1),
pocs ? allocator(4) : allocator(3));
pocs ? allocator_type(4) : allocator_type(3));
if (pocs) {
BOOST_TEST(x1.get_allocator() != x2.get_allocator());
@@ -132,7 +137,7 @@ namespace {
auto const old_cc = +raii::copy_constructor;
auto const old_mc = +raii::move_constructor;
thread_runner(vals1, [&x1, &x2, swapper](boost::span<map_value_type> s) {
thread_runner(vals1, [&x1, &x2, swapper](boost::span<value_type> s) {
(void)s;
swapper(x1, x2);
@@ -143,20 +148,20 @@ namespace {
BOOST_TEST_EQ(raii::move_constructor, old_mc);
if (pocs) {
if (x1.get_allocator() == allocator(3)) {
BOOST_TEST(x2.get_allocator() == allocator(4));
if (x1.get_allocator() == allocator_type(3)) {
BOOST_TEST(x2.get_allocator() == allocator_type(4));
} else {
BOOST_TEST(x1.get_allocator() == allocator(4));
BOOST_TEST(x2.get_allocator() == allocator(3));
BOOST_TEST(x1.get_allocator() == allocator_type(4));
BOOST_TEST(x2.get_allocator() == allocator_type(3));
}
} else {
BOOST_TEST(x1.get_allocator() == allocator(3));
BOOST_TEST(x1.get_allocator() == allocator_type(3));
BOOST_TEST(x1.get_allocator() == x2.get_allocator());
}
if (x1.size() == ref_map1.size()) {
test_matches_reference(x1, ref_map1);
test_matches_reference(x2, ref_map2);
if (x1.size() == reference_cont1.size()) {
test_matches_reference(x1, reference_cont1);
test_matches_reference(x2, reference_cont2);
BOOST_TEST_EQ(x1.hash_function(), hasher(1));
BOOST_TEST_EQ(x1.key_eq(), key_equal(2));
@@ -164,8 +169,8 @@ namespace {
BOOST_TEST_EQ(x2.hash_function(), hasher(2));
BOOST_TEST_EQ(x2.key_eq(), key_equal(1));
} else {
test_matches_reference(x2, ref_map1);
test_matches_reference(x1, ref_map2);
test_matches_reference(x2, reference_cont1);
test_matches_reference(x1, reference_cont2);
BOOST_TEST_EQ(x1.hash_function(), hasher(2));
BOOST_TEST_EQ(x1.key_eq(), key_equal(1));
@@ -177,17 +182,21 @@ namespace {
check_raii_counts();
}
template <class F, class G>
void insert_and_swap(F swapper, G gen, test::random_generator rg)
template <class X, class F, class GF>
void insert_and_swap(
X*, F swapper, GF gen_factory, 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();
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));
X x1(vals1.size(), hasher(1), key_equal(2), allocator_type(3));
X x2(vals2.size(), hasher(2), key_equal(1), allocator_type(3));
std::thread t1, t2, t3;
boost::compat::latch l(2);
@@ -282,21 +291,25 @@ namespace {
}
map_type* map;
pocs_map_type* pocs_map;
replace_allocator<map_type, pocs_allocator>* pocs_map;
set_type* set;
replace_allocator<set_type, pocs_allocator>* pocs_set;
} // namespace
// clang-format off
UNORDERED_TEST(
swap_tests,
((map)(pocs_map))
((map)(pocs_map)(set)(pocs_set))
((member_fn_swap)(free_fn_swap))
((value_type_generator))
((value_type_generator_factory))
((default_generator)(sequential)(limited_range)))
UNORDERED_TEST(insert_and_swap,
((map)(set))
((member_fn_swap)(free_fn_swap))
((value_type_generator))
((value_type_generator_factory))
((default_generator)(sequential)(limited_range)))
// clang-format on
+3
View File
@@ -373,6 +373,9 @@ 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,
+367 -92
View File
@@ -1,38 +1,65 @@
// 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_map)
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;
std::atomic<std::uint64_t> num_visits{0};
std::atomic<std::uint64_t> total_count{0};
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);
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)));
++num_visits;
};
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);
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)));
++num_visits;
};
@@ -40,14 +67,14 @@ namespace {
thread_runner(
values, [&x, &mut_visitor, &total_count](boost::span<T> s) {
for (auto const& val : s) {
auto r = val.first.x_;
auto r = get_key(val).x_;
BOOST_TEST(r >= 0);
auto count = x.visit(val.first, mut_visitor);
auto count = x.visit(get_key(val), mut_visitor);
BOOST_TEST_EQ(count, 1u);
total_count += count;
count = x.visit(val.second, mut_visitor);
count = x.visit(get_non_present_key(val), mut_visitor);
BOOST_TEST_EQ(count, 0u);
}
});
@@ -63,16 +90,16 @@ namespace {
thread_runner(
values, [&x, &const_visitor, &total_count](boost::span<T> s) {
for (auto const& val : s) {
auto r = val.first.x_;
auto r = get_key(val).x_;
BOOST_TEST(r >= 0);
auto const& y = x;
auto count = y.visit(val.first, const_visitor);
auto count = y.visit(get_key(val), const_visitor);
BOOST_TEST_EQ(count, 1u);
total_count += count;
count = y.visit(val.second, const_visitor);
count = y.visit(get_non_present_key(val), const_visitor);
BOOST_TEST_EQ(count, 0u);
}
});
@@ -88,15 +115,15 @@ namespace {
thread_runner(
values, [&x, &const_visitor, &total_count](boost::span<T> s) {
for (auto const& val : s) {
auto r = val.first.x_;
auto r = get_key(val).x_;
BOOST_TEST(r >= 0);
auto count = x.cvisit(val.first, const_visitor);
auto count = x.cvisit(get_key(val), const_visitor);
BOOST_TEST_EQ(count, 1u);
total_count += count;
count = x.cvisit(val.second, const_visitor);
count = x.cvisit(get_non_present_key(val), const_visitor);
BOOST_TEST_EQ(count, 0u);
}
});
@@ -111,14 +138,14 @@ namespace {
{
thread_runner(values, [&x, &total_count](boost::span<T> s) {
for (auto const& val : s) {
auto r = val.first.x_;
auto r = get_key(val).x_;
BOOST_TEST(r >= 0);
auto count = x.count(val.first);
auto count = x.count(get_key(val));
BOOST_TEST_EQ(count, 1u);
total_count += count;
count = x.count(val.second);
count = x.count(get_non_present_key(val));
BOOST_TEST_EQ(count, 0u);
}
});
@@ -132,13 +159,13 @@ namespace {
{
thread_runner(values, [&x](boost::span<T> s) {
for (auto const& val : s) {
auto r = val.first.x_;
auto r = get_key(val).x_;
BOOST_TEST(r >= 0);
auto contains = x.contains(val.first);
auto contains = x.contains(get_key(val));
BOOST_TEST(contains);
contains = x.contains(val.second);
contains = x.contains(get_non_present_key(val));
BOOST_TEST(!contains);
}
});
@@ -152,22 +179,29 @@ namespace {
struct transp_visitor_type
{
template <class T, class X, class M>
void operator()(std::vector<T>& values, X& x, M const& reference_map)
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;
std::atomic<std::uint64_t> num_visits{0};
std::atomic<std::uint64_t> total_count{0};
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);
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)));
++num_visits;
};
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);
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)));
++num_visits;
};
@@ -175,15 +209,15 @@ namespace {
thread_runner(
values, [&x, &mut_visitor, &total_count](boost::span<T> s) {
for (auto const& val : s) {
auto r = val.first.x_;
auto r = get_key(val).x_;
BOOST_TEST(r >= 0);
auto count = x.visit(val.first.x_, mut_visitor);
auto count = x.visit(get_key(val).x_, mut_visitor);
BOOST_TEST_EQ(count, 1u);
total_count += count;
count = x.visit(val.second.x_, mut_visitor);
count = x.visit(get_non_present_key(val).x_, mut_visitor);
BOOST_TEST_EQ(count, 0u);
}
});
@@ -199,16 +233,16 @@ namespace {
thread_runner(
values, [&x, &const_visitor, &total_count](boost::span<T> s) {
for (auto const& val : s) {
auto r = val.first.x_;
auto r = get_key(val).x_;
BOOST_TEST(r >= 0);
auto const& y = x;
auto count = y.visit(val.first.x_, const_visitor);
auto count = y.visit(get_key(val).x_, const_visitor);
BOOST_TEST_EQ(count, 1u);
total_count += count;
count = y.visit(val.second.x_, const_visitor);
count = y.visit(get_non_present_key(val).x_, const_visitor);
BOOST_TEST_EQ(count, 0u);
}
});
@@ -224,15 +258,15 @@ namespace {
thread_runner(
values, [&x, &const_visitor, &total_count](boost::span<T> s) {
for (auto const& val : s) {
auto r = val.first.x_;
auto r = get_key(val).x_;
BOOST_TEST(r >= 0);
auto count = x.cvisit(val.first.x_, const_visitor);
auto count = x.cvisit(get_key(val).x_, const_visitor);
BOOST_TEST_EQ(count, 1u);
total_count += count;
count = x.cvisit(val.second.x_, const_visitor);
count = x.cvisit(get_non_present_key(val).x_, const_visitor);
BOOST_TEST_EQ(count, 0u);
}
});
@@ -247,14 +281,14 @@ namespace {
{
thread_runner(values, [&x, &total_count](boost::span<T> s) {
for (auto const& val : s) {
auto r = val.first.x_;
auto r = get_key(val).x_;
BOOST_TEST(r >= 0);
auto count = x.count(val.first.x_);
auto count = x.count(get_key(val).x_);
BOOST_TEST_EQ(count, 1u);
total_count += count;
count = x.count(val.second.x_);
count = x.count(get_non_present_key(val).x_);
BOOST_TEST_EQ(count, 0u);
}
});
@@ -268,13 +302,13 @@ namespace {
{
thread_runner(values, [&x](boost::span<T> s) {
for (auto const& val : s) {
auto r = val.first.x_;
auto r = get_key(val).x_;
BOOST_TEST(r >= 0);
auto contains = x.contains(val.first.x_);
auto contains = x.contains(get_key(val).x_);
BOOST_TEST(contains);
contains = x.contains(val.second.x_);
contains = x.contains(get_non_present_key(val).x_);
BOOST_TEST(!contains);
}
});
@@ -288,24 +322,31 @@ namespace {
struct visit_all_type
{
template <class T, class X, class M>
void operator()(std::vector<T>& values, X& x, M const& reference_map)
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;
std::atomic<std::uint64_t> total_count{0};
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);
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)));
++num_visits;
};
};
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);
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)));
++num_visits;
};
};
@@ -349,26 +390,140 @@ 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_map)
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;
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);
// 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)));
++num_visits;
};
};
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);
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)));
++num_visits;
};
};
@@ -402,24 +557,138 @@ namespace {
#else
(void)values;
(void)x;
(void)reference_map;
(void)reference_cont;
#endif
}
} exec_policy_visit_all;
template <class X, class G, class F>
void visit(X*, G gen, F visitor, test::random_generator rg)
struct exec_policy_visit_while_type
{
auto values = make_random_values(1024 * 16, [&] { return gen(rg); });
for (auto& val : values) {
if (val.second.x_ == 0) {
val.second.x_ = 1;
}
val.second.x_ *= -1;
}
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;
auto reference_map =
boost::unordered_flat_map<raii, raii>(values.begin(), values.end());
// 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());
raii::reset_counts();
@@ -428,7 +697,7 @@ namespace {
for (auto const& v : values) {
x.insert(v);
}
BOOST_TEST_EQ(x.size(), reference_map.size());
BOOST_TEST_EQ(x.size(), reference_cont.size());
std::uint64_t old_default_constructor = raii::default_constructor;
std::uint64_t old_copy_constructor = raii::copy_constructor;
@@ -436,7 +705,7 @@ namespace {
std::uint64_t old_copy_assignment = raii::copy_assignment;
std::uint64_t old_move_assignment = raii::move_assignment;
visitor(values, x, reference_map);
visitor(values, x, reference_cont);
BOOST_TEST_EQ(old_default_constructor, raii::default_constructor);
BOOST_TEST_EQ(old_copy_constructor, raii::copy_constructor);
@@ -455,9 +724,10 @@ namespace {
raii::destructor);
}
template <class X, class G>
void empty_visit(X*, G gen, test::random_generator rg)
template <class X, class GF>
void empty_visit(X*, GF gen_factory, 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;
@@ -482,7 +752,7 @@ namespace {
BOOST_TEST_EQ(num_visits, 0u);
for (auto const& val : s) {
auto count = x.visit(val.first,
auto count = x.visit(get_key(val),
[&num_visits](typename X::value_type const&) { ++num_visits; });
BOOST_TEST_EQ(count, 0u);
}
@@ -502,8 +772,8 @@ namespace {
BOOST_TEST_EQ(raii::destructor, 0u);
}
template <class X, class G>
void insert_and_visit(X*, G gen, test::random_generator rg)
template <class X, class GF>
void insert_and_visit(X*, GF gen_factory, test::random_generator rg)
{
// here we attempt to ensure happens-before and synchronizes-with
// the visitation thread essentially chases the insertion one
@@ -512,6 +782,7 @@ namespace {
BOOST_TEST(rg == test::sequential);
auto gen = gen_factory.template get<X>();
auto const values = make_random_values(1024 * 16, [&] { return gen(rg); });
{
@@ -538,9 +809,9 @@ namespace {
for (std::size_t idx = 0; idx < values.size(); ++idx) {
std::atomic_bool b{false};
while (!b) {
x.cvisit(values[idx].first,
x.cvisit(get_key(values[idx]),
[&b, &strs, idx, &values](typename X::value_type const& v) {
BOOST_TEST_EQ(v.second, values[idx].second);
BOOST_TEST_EQ(get_value(v), get_value(values[idx]));
BOOST_TEST_EQ(strs[idx], "rawr");
b = true;
});
@@ -557,6 +828,9 @@ 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
@@ -568,29 +842,30 @@ using test::sequential;
UNORDERED_TEST(
visit,
((map))
((value_type_generator)(init_type_generator))
((lvalue_visitor)(visit_all)(exec_policy_visit_all))
((map)(set))
((value_type_generator_factory)(init_type_generator_factory))
((lvalue_visitor)(visit_all)(visit_while)(exec_policy_visit_all)
(exec_policy_visit_while))
((default_generator)(sequential)(limited_range)))
UNORDERED_TEST(
visit,
((transp_map))
((value_type_generator)(init_type_generator))
((transp_map)(transp_set))
((value_type_generator_factory)(init_type_generator_factory))
((transp_visitor))
((default_generator)(sequential)(limited_range)))
UNORDERED_TEST(
empty_visit,
((map)(transp_map))
((value_type_generator)(init_type_generator))
((map)(transp_map)(set)(transp_set))
((value_type_generator_factory)(init_type_generator_factory))
((default_generator)(sequential)(limited_range))
)
UNORDERED_TEST(
insert_and_visit,
((map))
((value_type_generator))
((map)(set))
((value_type_generator_factory))
((sequential))
)
@@ -0,0 +1,270 @@
/* 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
@@ -0,0 +1 @@
22 serialization::archive 19 0 0 0 0 0 0 0 0 0
+14
View File
@@ -0,0 +1,14 @@
<?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
@@ -0,0 +1 @@
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
@@ -0,0 +1,110 @@
<?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
@@ -0,0 +1 @@
22 serialization::archive 19 0 0 0 0 0 0 0 0 0
+14
View File
@@ -0,0 +1,14 @@
<?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
@@ -0,0 +1 @@
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
@@ -0,0 +1,110 @@
<?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
@@ -0,0 +1 @@
22 serialization::archive 19 0 0 0 0 0 0 0 0 0
+14
View File
@@ -0,0 +1,14 @@
<?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
@@ -0,0 +1 @@
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
@@ -0,0 +1,126 @@
<?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
@@ -0,0 +1 @@
22 serialization::archive 19 0 0 0 0 0 0 0 0 0
@@ -0,0 +1,14 @@
<?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>
@@ -0,0 +1 @@
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
@@ -0,0 +1,126 @@
<?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
@@ -0,0 +1 @@
22 serialization::archive 19 0 0 0 0 0 0 0
+14
View File
@@ -0,0 +1,14 @@
<?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
@@ -0,0 +1 @@
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
+42
View File
@@ -0,0 +1,42 @@
<?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>-938475945</item>
<item>-938475945</item>
<item>-1785808236</item>
<item>-1694006096</item>
<item>1021729225</item>
<item>1021729225</item>
<item>581989062</item>
<item>-1168265900</item>
<item>2097663454</item>
<item>1354395138</item>
<item>-743509129</item>
<item>-743509129</item>
<item>1832705803</item>
<item>1832705803</item>
</container>
<values>
<count>14</count>
<item_version>0</item_version>
<item>-938475945</item>
<item>-938475945</item>
<item>-1694006096</item>
<item>-1785808236</item>
<item>1021729225</item>
<item>1021729225</item>
<item>1832705803</item>
<item>1832705803</item>
<item>581989062</item>
<item>1354395138</item>
<item>2097663454</item>
<item>-743509129</item>
<item>-743509129</item>
<item>-1168265900</item>
</values>
</boost_serialization>
@@ -0,0 +1 @@
22 serialization::archive 19 0 0 152 193 0 1775667486 -948676228 -1108026676 -1866983349 -1866983349 258343568 -1068590402 -432548865 -432548865 177811234 5018725 5018725 190104374 1315739433 1315739433 -1995826660 -305386173 -305386173 287343763 287343763 1241175627 1241175627 109292002 1021729225 1021729225 -1426106865 -1426106865 918842470 -1338732474 669358715 669358715 183526573 183526573 -938475945 -938475945 -1694006096 1630722604 -1933552094 257760945 257760945 -1510933233 -1510933233 390937317 390937317 259868705 259868705 -38527737 -38527737 -551129560 1832705803 1832705803 607596425 607596425 -1520512970 -1674340339 -1674340339 -1873731147 -1873731147 1421489007 1421489007 -501526237 -501526237 -1877186230 -218590163 -218590163 647803737 647803737 -859679032 967560929 967560929 -815473344 -487119163 -487119163 -1546216506 1859681623 1859681623 -1040862600 -893024018 -1319261042 -1252588227 -1252588227 -1785808236 1198739057 1198739057 -1469084794 473010817 473010817 1481537722 2138619156 -1039351567 -1039351567 -493829536 411876340 -364116164 776215953 776215953 1568788209 1568788209 -1631204972 601527218 286273879 286273879 -232190041 -232190041 2075478837 2075478837 -1168265900 581989062 178482033 178482033 1354395138 -1530408968 -414767254 1970368353 1970368353 -1967815281 -1967815281 941660988 1786883066 670291604 -1803607012 1829132124 -432502583 -432502583 -1642734171 -1642734171 -425048532 2097663454 -1938999974 -1802192500 2056892401 2056892401 -917350225 -917350225 -55935255 -55935255 -418860175 -418860175 -2146202839 -2146202839 310524955 310524955 159865104 -265464733 -265464733 -1996442747 -1996442747 430136711 430136711 -743509129 -743509129 167237906 152 0 -938475945 -938475945 -1694006096 -1785808236 1021729225 1021729225 1832705803 1832705803 581989062 1354395138 2097663454 -743509129 -743509129 -1168265900 -414767254 -1039351567 -1039351567 167237906 177811234 -364116164 -1933552094 259868705 259868705 -501526237 -501526237 776215953 776215953 1630722604 310524955 310524955 473010817 473010817 1970368353 1970368353 647803737 647803737 -1252588227 -1252588227 669358715 669358715 2075478837 2075478837 -1995826660 109292002 1421489007 1421489007 287343763 287343763 -1108026676 1829132124 -1546216506 941660988 -305386173 -305386173 -815473344 -1877186230 601527218 -38527737 -38527737 -1068590402 1568788209 1568788209 1198739057 1198739057 2138619156 -218590163 -218590163 -1469084794 -493829536 -232190041 -232190041 -1674340339 -1674340339 1775667486 -948676228 -1338732474 178482033 178482033 918842470 -917350225 -917350225 390937317 390937317 -418860175 -418860175 670291604 1859681623 1859681623 1241175627 1241175627 -487119163 -487119163 1315739433 1315739433 -1938999974 1786883066 -55935255 -55935255 -2146202839 -2146202839 -1866983349 -1866983349 607596425 607596425 -425048532 -893024018 -1873731147 -1873731147 159865104 411876340 2056892401 2056892401 -1642734171 -1642734171 -1996442747 -1996442747 257760945 257760945 -1631204972 -1802192500 -432548865 -432548865 -551129560 -1319261042 -859679032 -1510933233 -1510933233 286273879 286273879 1481537722 258343568 -265464733 -265464733 -432502583 -432502583 -1967815281 -1967815281 -1530408968 967560929 967560929 -1426106865 -1426106865 190104374 430136711 430136711 -1803607012 -1520512970 5018725 5018725 183526573 183526573 -1040862600
+318
View File
@@ -0,0 +1,318 @@
<?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>152</count>
<bucket_count>193</bucket_count>
<item_version>0</item_version>
<item>1775667486</item>
<item>-948676228</item>
<item>-1108026676</item>
<item>-1866983349</item>
<item>-1866983349</item>
<item>258343568</item>
<item>-1068590402</item>
<item>-432548865</item>
<item>-432548865</item>
<item>177811234</item>
<item>5018725</item>
<item>5018725</item>
<item>190104374</item>
<item>1315739433</item>
<item>1315739433</item>
<item>-1995826660</item>
<item>-305386173</item>
<item>-305386173</item>
<item>287343763</item>
<item>287343763</item>
<item>1241175627</item>
<item>1241175627</item>
<item>109292002</item>
<item>1021729225</item>
<item>1021729225</item>
<item>-1426106865</item>
<item>-1426106865</item>
<item>918842470</item>
<item>-1338732474</item>
<item>669358715</item>
<item>669358715</item>
<item>183526573</item>
<item>183526573</item>
<item>-938475945</item>
<item>-938475945</item>
<item>-1694006096</item>
<item>1630722604</item>
<item>-1933552094</item>
<item>257760945</item>
<item>257760945</item>
<item>-1510933233</item>
<item>-1510933233</item>
<item>390937317</item>
<item>390937317</item>
<item>259868705</item>
<item>259868705</item>
<item>-38527737</item>
<item>-38527737</item>
<item>-551129560</item>
<item>1832705803</item>
<item>1832705803</item>
<item>607596425</item>
<item>607596425</item>
<item>-1520512970</item>
<item>-1674340339</item>
<item>-1674340339</item>
<item>-1873731147</item>
<item>-1873731147</item>
<item>1421489007</item>
<item>1421489007</item>
<item>-501526237</item>
<item>-501526237</item>
<item>-1877186230</item>
<item>-218590163</item>
<item>-218590163</item>
<item>647803737</item>
<item>647803737</item>
<item>-859679032</item>
<item>967560929</item>
<item>967560929</item>
<item>-815473344</item>
<item>-487119163</item>
<item>-487119163</item>
<item>-1546216506</item>
<item>1859681623</item>
<item>1859681623</item>
<item>-1040862600</item>
<item>-893024018</item>
<item>-1319261042</item>
<item>-1252588227</item>
<item>-1252588227</item>
<item>-1785808236</item>
<item>1198739057</item>
<item>1198739057</item>
<item>-1469084794</item>
<item>473010817</item>
<item>473010817</item>
<item>1481537722</item>
<item>2138619156</item>
<item>-1039351567</item>
<item>-1039351567</item>
<item>-493829536</item>
<item>411876340</item>
<item>-364116164</item>
<item>776215953</item>
<item>776215953</item>
<item>1568788209</item>
<item>1568788209</item>
<item>-1631204972</item>
<item>601527218</item>
<item>286273879</item>
<item>286273879</item>
<item>-232190041</item>
<item>-232190041</item>
<item>2075478837</item>
<item>2075478837</item>
<item>-1168265900</item>
<item>581989062</item>
<item>178482033</item>
<item>178482033</item>
<item>1354395138</item>
<item>-1530408968</item>
<item>-414767254</item>
<item>1970368353</item>
<item>1970368353</item>
<item>-1967815281</item>
<item>-1967815281</item>
<item>941660988</item>
<item>1786883066</item>
<item>670291604</item>
<item>-1803607012</item>
<item>1829132124</item>
<item>-432502583</item>
<item>-432502583</item>
<item>-1642734171</item>
<item>-1642734171</item>
<item>-425048532</item>
<item>2097663454</item>
<item>-1938999974</item>
<item>-1802192500</item>
<item>2056892401</item>
<item>2056892401</item>
<item>-917350225</item>
<item>-917350225</item>
<item>-55935255</item>
<item>-55935255</item>
<item>-418860175</item>
<item>-418860175</item>
<item>-2146202839</item>
<item>-2146202839</item>
<item>310524955</item>
<item>310524955</item>
<item>159865104</item>
<item>-265464733</item>
<item>-265464733</item>
<item>-1996442747</item>
<item>-1996442747</item>
<item>430136711</item>
<item>430136711</item>
<item>-743509129</item>
<item>-743509129</item>
<item>167237906</item>
</container>
<values>
<count>152</count>
<item_version>0</item_version>
<item>-938475945</item>
<item>-938475945</item>
<item>-1694006096</item>
<item>-1785808236</item>
<item>1021729225</item>
<item>1021729225</item>
<item>1832705803</item>
<item>1832705803</item>
<item>581989062</item>
<item>1354395138</item>
<item>2097663454</item>
<item>-743509129</item>
<item>-743509129</item>
<item>-1168265900</item>
<item>-414767254</item>
<item>-1039351567</item>
<item>-1039351567</item>
<item>167237906</item>
<item>177811234</item>
<item>-364116164</item>
<item>-1933552094</item>
<item>259868705</item>
<item>259868705</item>
<item>-501526237</item>
<item>-501526237</item>
<item>776215953</item>
<item>776215953</item>
<item>1630722604</item>
<item>310524955</item>
<item>310524955</item>
<item>473010817</item>
<item>473010817</item>
<item>1970368353</item>
<item>1970368353</item>
<item>647803737</item>
<item>647803737</item>
<item>-1252588227</item>
<item>-1252588227</item>
<item>669358715</item>
<item>669358715</item>
<item>2075478837</item>
<item>2075478837</item>
<item>-1995826660</item>
<item>109292002</item>
<item>1421489007</item>
<item>1421489007</item>
<item>287343763</item>
<item>287343763</item>
<item>-1108026676</item>
<item>1829132124</item>
<item>-1546216506</item>
<item>941660988</item>
<item>-305386173</item>
<item>-305386173</item>
<item>-815473344</item>
<item>-1877186230</item>
<item>601527218</item>
<item>-38527737</item>
<item>-38527737</item>
<item>-1068590402</item>
<item>1568788209</item>
<item>1568788209</item>
<item>1198739057</item>
<item>1198739057</item>
<item>2138619156</item>
<item>-218590163</item>
<item>-218590163</item>
<item>-1469084794</item>
<item>-493829536</item>
<item>-232190041</item>
<item>-232190041</item>
<item>-1674340339</item>
<item>-1674340339</item>
<item>1775667486</item>
<item>-948676228</item>
<item>-1338732474</item>
<item>178482033</item>
<item>178482033</item>
<item>918842470</item>
<item>-917350225</item>
<item>-917350225</item>
<item>390937317</item>
<item>390937317</item>
<item>-418860175</item>
<item>-418860175</item>
<item>670291604</item>
<item>1859681623</item>
<item>1859681623</item>
<item>1241175627</item>
<item>1241175627</item>
<item>-487119163</item>
<item>-487119163</item>
<item>1315739433</item>
<item>1315739433</item>
<item>-1938999974</item>
<item>1786883066</item>
<item>-55935255</item>
<item>-55935255</item>
<item>-2146202839</item>
<item>-2146202839</item>
<item>-1866983349</item>
<item>-1866983349</item>
<item>607596425</item>
<item>607596425</item>
<item>-425048532</item>
<item>-893024018</item>
<item>-1873731147</item>
<item>-1873731147</item>
<item>159865104</item>
<item>411876340</item>
<item>2056892401</item>
<item>2056892401</item>
<item>-1642734171</item>
<item>-1642734171</item>
<item>-1996442747</item>
<item>-1996442747</item>
<item>257760945</item>
<item>257760945</item>
<item>-1631204972</item>
<item>-1802192500</item>
<item>-432548865</item>
<item>-432548865</item>
<item>-551129560</item>
<item>-1319261042</item>
<item>-859679032</item>
<item>-1510933233</item>
<item>-1510933233</item>
<item>286273879</item>
<item>286273879</item>
<item>1481537722</item>
<item>258343568</item>
<item>-265464733</item>
<item>-265464733</item>
<item>-432502583</item>
<item>-432502583</item>
<item>-1967815281</item>
<item>-1967815281</item>
<item>-1530408968</item>
<item>967560929</item>
<item>967560929</item>
<item>-1426106865</item>
<item>-1426106865</item>
<item>190104374</item>
<item>430136711</item>
<item>430136711</item>
<item>-1803607012</item>
<item>-1520512970</item>
<item>5018725</item>
<item>5018725</item>
<item>183526573</item>
<item>183526573</item>
<item>-1040862600</item>
</values>
</boost_serialization>

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