Compare commits

..

194 Commits

Author SHA1 Message Date
joaquintides d67d58cb4b fine-tuned element prefetching in unprotected_bulk_visit 2023-10-18 09:23:24 +02:00
Peter Dimov 0e19223e5b Replace uses of boost::enable_if_t in deduction guides with std::enable_if_t 2023-10-12 13:13:56 +03:00
joaquintides 8ee48fe909 Feature/bulk visit (#217) 2023-10-11 12:50:28 +02:00
joaquintides ef0b3a0cd8 fixed #201 2023-10-04 17:38:33 +02:00
Christian Mazakas 44b669a71c Merge pull request #215 from cmazakas/cxx03_deprecation
cxx03 deprecation
2023-09-29 14:59:59 -07:00
Christian Mazakas 554ce65a69 Remove unnecessary forced inlining of bool conversion 2023-09-29 10:52:34 -07:00
Christian Mazakas c37d9a3bf1 Clean up tests to properly use boost::unordered::piecewise_construct and ensure it's a humble alias for its std counterpart 2023-09-29 08:29:06 -07:00
Christian Mazakas 4fcc6a9cab Manually implement explicit boolean conversion, removing header dependency on Core's polyfill 2023-09-29 08:03:49 -07:00
Christian Mazakas 0d61746fa3 Remove unnecessary requires_cxx11 header 2023-09-29 08:03:11 -07:00
Christian Mazakas 20559cbe5c Remove extraneous whitespace introduced by a clang-format bug 2023-09-29 07:44:52 -07:00
Christian Mazakas 6728ffcf6d Cleanup polyfilled type traits helpers to derive from integral constant 2023-09-29 07:42:58 -07:00
Christian Mazakas 9f5f8b1b48 Update maintainers list in libraries.json 2023-09-28 21:27:46 -07:00
Christian Mazakas aedebff4e1 Remove extraneous header include of Core's addressof impl 2023-09-28 21:27:46 -07:00
Christian Mazakas 47fa9cd17e Pull triviality type traits into internal type_traits header 2023-09-28 21:27:46 -07:00
Christian Mazakas a0e1bc295a Update libraries.json 2023-09-28 12:54:51 -07:00
Christian Mazakas f5bd5bc3d0 Cleanup default-constructiblity check for gcc-4 2023-09-28 12:52:13 -07:00
Christian Mazakas 630031706b Remove project test requirements and remove problematic CI entries 2023-09-28 09:51:47 -07:00
Christian Mazakas 9bc7876583 Update foa to be gcc-4.8 compliant 2023-09-28 08:31:14 -07:00
Christian Mazakas b482890453 Update testing requirements to re-enable gcc-4.8 and above 2023-09-28 08:27:12 -07:00
Christian Mazakas bc764d34cf Remove extraneous workarounds for gcc-4 in foa core header 2023-09-27 13:03:15 -07:00
Christian Mazakas a9da328e1d Add missing copyright to static_assert() header 2023-09-27 13:03:15 -07:00
Christian Mazakas 087f2052fc Update include paths to use ContainerHash instead of Functional for hash 2023-09-27 13:03:15 -07:00
Christian Mazakas 51a75c0fd0 Replace usage of make_void with void_t 2023-09-27 13:03:15 -07:00
Christian Mazakas ce5033c4a0 Update out-of-line definitions in prime_fmod to be conditional on pre-c++17 support 2023-09-27 13:03:15 -07:00
Christian Mazakas bcbb13e0cf Add missing licensing to opt_storage header 2023-09-27 13:03:15 -07:00
Christian Mazakas 7aac692162 Replace all usages of boost::addressof with their std counterpart 2023-09-27 13:03:15 -07:00
Christian Mazakas f1729bb8a1 Remove unused choice<n> helper structs 2023-09-27 12:21:15 -07:00
Christian Mazakas b4884b7201 Relocate piecewise_construct to impl file, remove unnecessary fwd header 2023-09-27 12:21:15 -07:00
Christian Mazakas b0a13f896f Remove problematic characters from clang GHA job names 2023-09-27 11:44:45 -07:00
joaquintides cb86544fcf updated docs to reflect C++03 support drop 2023-09-27 11:44:45 -07:00
Christian Mazakas 6c97eabbb3 Refactor prime_fmod_size policy to use static members in lieu of namespace-scope ones 2023-09-27 11:44:45 -07:00
Christian Mazakas 968c2e9c11 Split clang GHA jobs 2023-09-27 11:44:45 -07:00
Christian Mazakas 53a6566a98 Split up GHA gcc-12 sanitizer jobs 2023-09-27 11:44:45 -07:00
Christian Mazakas 9b1ec6cbce Reinstate boost::unordered::piecewise_construct
Maintain backwards-compatibility for users instead of forcing them to change to `std::`
2023-09-27 11:44:45 -07:00
Christian Mazakas 3f3b2128ec Update extract() SFINAE to be friendly to msvc-14.0
msvc-14.0 seems to require the parameter pack be explicitly mentioned though no other compilers seem to have this issue
2023-09-27 11:44:45 -07:00
Christian Mazakas d3e6be7356 Replace boost::addressof with std::addressof
For some reason, gcc-5 and 6 don't like Boost's impl here but the std one seems to work
2023-09-27 11:44:45 -07:00
Christian Mazakas 423eb08f00 Work around bugs in gcc's analysis
Under `-m32 -O3` builds, it seems like gcc gets confused by the usage of malloc and calloc so we opt into the C++ versions, operator new and delete
2023-09-27 11:44:45 -07:00
Christian Mazakas 777f2084a3 Remove dependency on TypeTraits 2023-09-27 11:44:45 -07:00
Christian Mazakas e5bf418c3d Add missing Boost.Tuple #include 2023-09-27 11:44:45 -07:00
Christian Mazakas 9d8beff688 Remove dependency on Boost.Tuple 2023-09-27 11:44:45 -07:00
Christian Mazakas 91f63697a7 Replace <boost/limits> with STL <limits> 2023-09-27 11:44:44 -07:00
Christian Mazakas 22344ab27f Add missing <algorithm> include for std::max 2023-09-27 11:44:44 -07:00
Christian Mazakas d742ee3a3b Remove unnecessary BOOST_UNORDERED_USE_MOVE tests 2023-09-27 11:44:44 -07:00
Christian Mazakas d881d4d5e2 Remove Boost.StaticAssert 2023-09-27 11:44:44 -07:00
Christian Mazakas 15318f0e46 Refactor prime_fmod_size impl to no longer use Boost.Preprocessor 2023-09-27 11:44:44 -07:00
Christian Mazakas e32f72d69c Remove dependency on Boost.Utility in implementation.hpp 2023-09-27 11:44:44 -07:00
Christian Mazakas e37e749b0b Remove Boost.PP from implementation.hpp 2023-09-27 11:44:44 -07:00
Christian Mazakas b02d013d44 Add missing <algorithm> include 2023-09-27 11:44:44 -07:00
Christian Mazakas 33dbd7cd76 Remove dependency on Boost.Move 2023-09-27 11:44:44 -07:00
Christian Mazakas 8f382e874d Remove BOOST_COPYABLE_AND_MOVABLE 2023-09-27 11:44:44 -07:00
Christian Mazakas bce9b5927a Remove BOOST_MOVABLE_BUT_NOT_COPYABLE 2023-09-27 11:44:44 -07:00
Christian Mazakas c6aa90651c Final cleanup of BOOST_NO_CXX11_* 2023-09-27 11:44:44 -07:00
Christian Mazakas cddb7cb8b1 Remove BOOST_NO_CXX11_NOEXCEPT 2023-09-27 11:44:44 -07:00
Christian Mazakas a36a7327eb Remove BOOST_NO_CXX11_ALLOCATOR 2023-09-27 11:44:44 -07:00
Christian Mazakas d2dc503e4a Remove BOOST_NO_CXX11_DEFAULTED_FUNCTIONS 2023-09-27 11:44:44 -07:00
Christian Mazakas 0c7b51cc16 Remove BOOST_NO_CXX11_RVALUE_REFERENCES 2023-09-27 11:44:44 -07:00
Christian Mazakas 3f0f8efbba Remove BOOST_NO_CXX11_VARIADIC_TEMPLATES 2023-09-27 11:44:44 -07:00
Christian Mazakas 8d59040ece Remove BOOST_COPY_ASSIGN_REF 2023-09-27 11:44:44 -07:00
Christian Mazakas 15d04295d6 Disable self-move warnings for gcc in node_handle_tests 2023-09-27 11:44:44 -07:00
Christian Mazakas 866ab46385 Remove BOOST_FWD_REF 2023-09-27 11:44:44 -07:00
Christian Mazakas 4cd1827104 Remove BOOST_RV_REF 2023-09-27 11:44:44 -07:00
Christian Mazakas 3bd7e93ac6 Remove BOOST_UNORDERED_TUPLE_ARGS 2023-09-27 11:44:44 -07:00
Christian Mazakas 1d991d065e Remove BOOST_UNORDERED_EMPLACE_LIMIT 2023-09-27 11:44:44 -07:00
Christian Mazakas 8782036a88 Remove BOOST_UNORDERED_HAVE_PIECEWISE_CONSTRUCT 2023-09-27 11:44:44 -07:00
Christian Mazakas f088a5b9b9 Remove usage of boost::forward 2023-09-27 11:44:44 -07:00
Christian Mazakas 16386f586f Remove usage of BOOST_UNORDERED_CXX11_CONSTRUCTION 2023-09-27 11:44:44 -07:00
Christian Mazakas 59a7fa09ba Remove usage of BOOST_NO_CXX11_HDR_TYPE_TRAITS 2023-09-27 11:44:44 -07:00
Christian Mazakas 8cbd9ad80a Remove BOOST_NO_CXX11_HDR_INITIALIZER_LIST checks 2023-09-27 11:44:44 -07:00
Christian Mazakas b0ee2b5116 Remove BOOST_UNORDERED_EMPLACE_TEMPLATE machinery 2023-09-27 11:44:44 -07:00
Christian Mazakas adb91ac06d Replace boost::move with std::move 2023-09-27 11:44:44 -07:00
Christian Mazakas 11322de29f Remove BOOST_CONSTEXPR 2023-09-27 11:44:44 -07:00
Christian Mazakas 0c90a84aa0 Add missing #include for Boost.Assert 2023-09-27 11:44:44 -07:00
Christian Mazakas aa16d1b8a9 Remove BOOST_NOEXCEPT 2023-09-27 11:44:44 -07: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
Christian Mazakas 5339bf67d9 Add missing #include's 2023-08-03 10:52:03 -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
joaquintides bd24dfd284 updated concurrent map benchmark plots 2023-07-18 18:19:13 +02:00
joaquintides faf54eef0d Merge pull request #196 from sdarwin/gha_runners
Self-hosted github actions runners
2023-07-13 10:12:44 +02:00
sdarwin 97b52879f5 Self-hosted github actions runners 2023-07-12 14:42:51 -06:00
Peter Dimov 9a7d1d336a Update ci.yml 2023-06-26 21:41:13 +03:00
Peter Dimov 3fc18d56ac Remove latch_tests.cpp 2023-06-26 16:13:20 +03:00
Peter Dimov 6bcf457b46 Use boost::compat::latch 2023-06-26 10:32:52 +03:00
Peter Dimov 01b2369700 Update .appveyor.yml 2023-06-26 02:45:47 +03:00
Peter Dimov fcb3f0e82a Decrease iterations in rw_spinlock_test8 to avoid timeouts 2023-06-26 02:42:12 +03:00
Peter Dimov 1e4deb10a1 Add rw_spinlock tests 2023-06-25 19:03:02 +03:00
226 changed files with 18361 additions and 6577 deletions
+10 -45
View File
@@ -31,14 +31,9 @@ environment:
B2_VARIANT: debug,release
matrix:
- FLAVOR: Visual Studio 2008, 2010, 2012
- FLAVOR: Visual Studio 2015
APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015
B2_TOOLSET: msvc-9.0,msvc-10.0,msvc-11.0
B2_ADDRESS_MODEL: 32 # No 64bit support
- FLAVOR: Visual Studio 2013, 2015
APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015
B2_TOOLSET: msvc-12.0,msvc-14.0
B2_TOOLSET: msvc-14.0
- FLAVOR: Visual Studio 2017, C++14
APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017
@@ -55,43 +50,27 @@ environment:
B2_CXXSTD: latest
B2_TOOLSET: msvc-14.1
- FLAVOR: cygwin (32-bit, C++03,11)
- FLAVOR: cygwin (32-bit, C++11)
APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017
ADDPATH: C:\cygwin\bin;
B2_ADDRESS_MODEL: 32
B2_CXXSTD: 03,11
B2_CXXSTD: 11
B2_TOOLSET: gcc
- FLAVOR: cygwin (32-bit, C++14,1z)
- FLAVOR: cygwin (32-bit, C++14)
APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017
ADDPATH: C:\cygwin\bin;
B2_ADDRESS_MODEL: 32
B2_CXXSTD: 14,1z
B2_CXXSTD: 14
B2_TOOLSET: gcc
- FLAVOR: cygwin (64-bit, C++03,11)
- FLAVOR: cygwin (32-bit, C++1z)
APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017
ADDPATH: C:\cygwin64\bin;
B2_ADDRESS_MODEL: 64
B2_CXXSTD: 03,11
ADDPATH: C:\cygwin\bin;
B2_ADDRESS_MODEL: 32
B2_CXXSTD: 1z
B2_TOOLSET: gcc
- FLAVOR: cygwin (64-bit, C++14,1z)
APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017
ADDPATH: C:\cygwin64\bin;
B2_ADDRESS_MODEL: 64
B2_CXXSTD: 14,1z
B2_TOOLSET: gcc
- FLAVOR: cygwin (64-bit, latest, C++03)
APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2022
ADDPATH: C:\cygwin64\bin;
B2_ADDRESS_MODEL: 64
B2_CXXSTD: 03
B2_TOOLSET: gcc
B2_FLAGS: "include=libs/unordered/test/unordered include=libs/unordered/test/exception"
B2_VARIANT: release
- FLAVOR: cygwin (64-bit, latest, C++11)
APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2022
ADDPATH: C:\cygwin64\bin;
@@ -119,13 +98,6 @@ environment:
B2_FLAGS: "include=libs/unordered/test/unordered include=libs/unordered/test/exception"
B2_VARIANT: release
- FLAVOR: mingw-w64, 32 bit, C++03
APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019
ADDPATH: C:\mingw-w64\i686-8.1.0-posix-dwarf-rt_v6-rev0\mingw32\bin;
B2_CXXSTD: 03
B2_TOOLSET: gcc
B2_ADDRESS_MODEL: 32
- FLAVOR: mingw-w64, 32 bit, C++11
APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019
ADDPATH: C:\mingw-w64\i686-8.1.0-posix-dwarf-rt_v6-rev0\mingw32\bin;
@@ -154,13 +126,6 @@ environment:
B2_TOOLSET: gcc
B2_ADDRESS_MODEL: 32
- FLAVOR: mingw-w64, 64 bit, C++03
APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019
ADDPATH: C:\mingw-w64\x86_64-8.1.0-posix-seh-rt_v6-rev0\mingw64\bin;
B2_CXXSTD: 03
B2_TOOLSET: gcc
B2_ADDRESS_MODEL: 64
- FLAVOR: mingw-w64, 64 bit, C++11
APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019
ADDPATH: C:\mingw-w64\x86_64-8.1.0-posix-seh-rt_v6-rev0\mingw64\bin;
+45 -69
View File
@@ -100,40 +100,16 @@ local windows_pipeline(name, image, environment, arch = "amd64") =
};
[
linux_pipeline(
"Linux 14.04 GCC 4.4 32/64",
"cppalliance/droneubuntu1404:1",
{ TOOLSET: 'gcc', COMPILER: 'g++-4.4', CXXSTD: '98,0x', ADDRMD: '32,64' },
"g++-4.4-multilib",
[ "ppa:ubuntu-toolchain-r/test" ],
),
linux_pipeline(
"Linux 14.04 GCC 4.6 32/64",
"cppalliance/droneubuntu1404:1",
{ TOOLSET: 'gcc', COMPILER: 'g++-4.6', CXXSTD: '98,0x', ADDRMD: '32,64' },
"g++-4.6-multilib",
[ "ppa:ubuntu-toolchain-r/test" ],
),
linux_pipeline(
"Linux 14.04 GCC 4.7 32/64",
"cppalliance/droneubuntu1404:1",
{ TOOLSET: 'gcc', COMPILER: 'g++-4.7', CXXSTD: '98,0x', ADDRMD: '32,64' },
"g++-4.7-multilib",
[ "ppa:ubuntu-toolchain-r/test" ],
),
linux_pipeline(
"Linux 14.04 GCC 4.8* 32/64",
"cppalliance/droneubuntu1404:1",
{ TOOLSET: 'gcc', COMPILER: 'g++', CXXSTD: '03,11', ADDRMD: '32,64' },
{ TOOLSET: 'gcc', COMPILER: 'g++', CXXSTD: '11', ADDRMD: '32,64' },
),
linux_pipeline(
"Linux 14.04 GCC 4.9 32/64",
"cppalliance/droneubuntu1404:1",
{ TOOLSET: 'gcc', COMPILER: 'g++-4.9', CXXSTD: '03,11', ADDRMD: '32,64' },
{ TOOLSET: 'gcc', COMPILER: 'g++-4.9', CXXSTD: '11', ADDRMD: '32,64' },
"g++-4.9-multilib",
[ "ppa:ubuntu-toolchain-r/test" ],
),
@@ -141,26 +117,26 @@ local windows_pipeline(name, image, environment, arch = "amd64") =
linux_pipeline(
"Linux 16.04 GCC 5* 32/64",
"cppalliance/droneubuntu1604:1",
{ TOOLSET: 'gcc', COMPILER: 'g++', CXXSTD: '03,11,14', ADDRMD: '32,64' },
{ TOOLSET: 'gcc', COMPILER: 'g++', CXXSTD: '11,14', ADDRMD: '32,64' },
),
linux_pipeline(
"Linux 18.04 GCC 6 32/64",
"cppalliance/droneubuntu1804:1",
{ TOOLSET: 'gcc', COMPILER: 'g++-6', CXXSTD: '03,11,14', ADDRMD: '32,64' },
{ TOOLSET: 'gcc', COMPILER: 'g++-6', CXXSTD: '11,14', ADDRMD: '32,64' },
"g++-6-multilib",
),
linux_pipeline(
"Linux 18.04 GCC 7* 32/64",
"cppalliance/droneubuntu1804:1",
{ TOOLSET: 'gcc', COMPILER: 'g++', CXXSTD: '03,11,14,17', ADDRMD: '32,64' },
{ TOOLSET: 'gcc', COMPILER: 'g++', CXXSTD: '11,14,17', ADDRMD: '32,64' },
),
linux_pipeline(
"Linux 18.04 GCC 8 32/64 (03,11)",
"Linux 18.04 GCC 8 32/64 (11)",
"cppalliance/droneubuntu1804:1",
{ TOOLSET: 'gcc', COMPILER: 'g++-8', CXXSTD: '03,11', ADDRMD: '32,64' },
{ TOOLSET: 'gcc', COMPILER: 'g++-8', CXXSTD: '11', ADDRMD: '32,64' },
"g++-8-multilib",
),
@@ -172,9 +148,9 @@ local windows_pipeline(name, image, environment, arch = "amd64") =
),
linux_pipeline(
"Linux 20.04 GCC 9* 32/64 (03,11,14)",
"Linux 20.04 GCC 9* 32/64 (11,14)",
"cppalliance/droneubuntu2004:1",
{ TOOLSET: 'gcc', COMPILER: 'g++', CXXSTD: '03,11,14', ADDRMD: '32,64' },
{ TOOLSET: 'gcc', COMPILER: 'g++', CXXSTD: '11,14', ADDRMD: '32,64' },
),
linux_pipeline(
@@ -186,14 +162,14 @@ local windows_pipeline(name, image, environment, arch = "amd64") =
linux_pipeline(
"Linux 20.04 GCC 9* ARM64",
"cppalliance/droneubuntu2004:multiarch",
{ TOOLSET: 'gcc', COMPILER: 'g++', CXXSTD: '03,11,14,17,2a' },
{ TOOLSET: 'gcc', COMPILER: 'g++', CXXSTD: '11,14,17,2a' },
arch="arm64",
),
linux_pipeline(
"Linux 20.04 GCC 9* S390x (03,11,14)",
"Linux 20.04 GCC 9* S390x (11,14)",
"cppalliance/droneubuntu2004:multiarch",
{ TOOLSET: 'gcc', COMPILER: 'g++', CXXSTD: '03,11,14' },
{ TOOLSET: 'gcc', COMPILER: 'g++', CXXSTD: '11,14' },
arch="s390x",
),
@@ -205,9 +181,9 @@ local windows_pipeline(name, image, environment, arch = "amd64") =
),
linux_pipeline(
"Linux 20.04 GCC 10 32/64 (03,11,14)",
"Linux 20.04 GCC 10 32/64 (11,14)",
"cppalliance/droneubuntu2004:1",
{ TOOLSET: 'gcc', COMPILER: 'g++-10', CXXSTD: '03,11,14', ADDRMD: '32,64' },
{ TOOLSET: 'gcc', COMPILER: 'g++-10', CXXSTD: '11,14', ADDRMD: '32,64' },
"g++-10-multilib",
),
@@ -219,9 +195,9 @@ local windows_pipeline(name, image, environment, arch = "amd64") =
),
linux_pipeline(
"Linux 22.04 GCC 11* 32/64 (03,11,14)",
"Linux 22.04 GCC 11* 32/64 (11,14)",
"cppalliance/droneubuntu2204:1",
{ TOOLSET: 'gcc', COMPILER: 'g++', CXXSTD: '03,11,14', ADDRMD: '32,64' },
{ TOOLSET: 'gcc', COMPILER: 'g++', CXXSTD: '11,14', ADDRMD: '32,64' },
),
linux_pipeline(
@@ -231,9 +207,9 @@ local windows_pipeline(name, image, environment, arch = "amd64") =
),
linux_pipeline(
"Linux 22.04 GCC 12 32 ASAN (03,11,14)",
"Linux 22.04 GCC 12 32 ASAN (11,14)",
"cppalliance/droneubuntu2204:1",
{ TOOLSET: 'gcc', COMPILER: 'g++-12', CXXSTD: '03,11', ADDRMD: '32' } + asan,
{ TOOLSET: 'gcc', COMPILER: 'g++-12', CXXSTD: '11', ADDRMD: '32' } + asan,
"g++-12-multilib",
),
@@ -266,9 +242,9 @@ local windows_pipeline(name, image, environment, arch = "amd64") =
),
linux_pipeline(
"Linux 22.04 GCC 12 64 ASAN (03,11,14)",
"Linux 22.04 GCC 12 64 ASAN (11,14)",
"cppalliance/droneubuntu2204:1",
{ TOOLSET: 'gcc', COMPILER: 'g++-12', CXXSTD: '03,11,14', ADDRMD: '64' } + asan,
{ TOOLSET: 'gcc', COMPILER: 'g++-12', CXXSTD: '11,14', ADDRMD: '64' } + asan,
"g++-12-multilib",
),
@@ -301,9 +277,9 @@ local windows_pipeline(name, image, environment, arch = "amd64") =
),
linux_pipeline(
"Linux 23.04 GCC 13 32/64 (03,11,14)",
"Linux 23.04 GCC 13 32/64 (11,14)",
"cppalliance/droneubuntu2304:1",
{ TOOLSET: 'gcc', COMPILER: 'g++-13', CXXSTD: '03,11,14', ADDRMD: '32,64' },
{ TOOLSET: 'gcc', COMPILER: 'g++-13', CXXSTD: '11,14', ADDRMD: '32,64' },
"g++-13 g++-13-multilib",
),
@@ -317,112 +293,112 @@ local windows_pipeline(name, image, environment, arch = "amd64") =
linux_pipeline(
"Linux 16.04 Clang 3.5",
"cppalliance/droneubuntu1604:1",
{ TOOLSET: 'clang', COMPILER: 'clang++-3.5', CXXSTD: '03,11' },
{ TOOLSET: 'clang', COMPILER: 'clang++-3.5', CXXSTD: '11' },
"clang-3.5",
),
linux_pipeline(
"Linux 16.04 Clang 3.6",
"cppalliance/droneubuntu1604:1",
{ TOOLSET: 'clang', COMPILER: 'clang++-3.6', CXXSTD: '03,11,14' },
{ TOOLSET: 'clang', COMPILER: 'clang++-3.6', CXXSTD: '11,14' },
"clang-3.6",
),
linux_pipeline(
"Linux 16.04 Clang 3.7",
"cppalliance/droneubuntu1604:1",
{ TOOLSET: 'clang', COMPILER: 'clang++-3.7', CXXSTD: '03,11,14' },
{ TOOLSET: 'clang', COMPILER: 'clang++-3.7', CXXSTD: '11,14' },
"clang-3.7",
),
linux_pipeline(
"Linux 16.04 Clang 3.8",
"cppalliance/droneubuntu1604:1",
{ TOOLSET: 'clang', COMPILER: 'clang++-3.8', CXXSTD: '03,11,14' },
{ TOOLSET: 'clang', COMPILER: 'clang++-3.8', CXXSTD: '11,14' },
"clang-3.8",
),
linux_pipeline(
"Linux 18.04 Clang 3.9",
"cppalliance/droneubuntu1804:1",
{ TOOLSET: 'clang', COMPILER: 'clang++-3.9', CXXSTD: '03,11,14' },
{ TOOLSET: 'clang', COMPILER: 'clang++-3.9', CXXSTD: '11,14' },
"clang-3.9",
),
linux_pipeline(
"Linux 18.04 Clang 4.0",
"cppalliance/droneubuntu1804:1",
{ TOOLSET: 'clang', COMPILER: 'clang++-4.0', CXXSTD: '03,11,14' },
{ TOOLSET: 'clang', COMPILER: 'clang++-4.0', CXXSTD: '11,14' },
"clang-4.0",
),
linux_pipeline(
"Linux 18.04 Clang 5.0",
"cppalliance/droneubuntu1804:1",
{ TOOLSET: 'clang', COMPILER: 'clang++-5.0', CXXSTD: '03,11,14,1z' },
{ TOOLSET: 'clang', COMPILER: 'clang++-5.0', CXXSTD: '11,14,1z' },
"clang-5.0",
),
linux_pipeline(
"Linux 18.04 Clang 6.0",
"cppalliance/droneubuntu1804:1",
{ TOOLSET: 'clang', COMPILER: 'clang++-6.0', CXXSTD: '03,11,14,17' },
{ TOOLSET: 'clang', COMPILER: 'clang++-6.0', CXXSTD: '11,14,17' },
"clang-6.0",
),
linux_pipeline(
"Linux 20.04 Clang 7",
"cppalliance/droneubuntu2004:1",
{ TOOLSET: 'clang', COMPILER: 'clang++-7', CXXSTD: '03,11,14,17' },
{ TOOLSET: 'clang', COMPILER: 'clang++-7', CXXSTD: '11,14,17' },
"clang-7",
),
linux_pipeline(
"Linux 20.04 Clang 8",
"cppalliance/droneubuntu2004:1",
{ TOOLSET: 'clang', COMPILER: 'clang++-8', CXXSTD: '03,11,14,17' },
{ TOOLSET: 'clang', COMPILER: 'clang++-8', CXXSTD: '11,14,17' },
"clang-8",
),
linux_pipeline(
"Linux 20.04 Clang 9",
"cppalliance/droneubuntu2004:1",
{ TOOLSET: 'clang', COMPILER: 'clang++-9', CXXSTD: '03,11,14,17,2a' },
{ TOOLSET: 'clang', COMPILER: 'clang++-9', CXXSTD: '11,14,17,2a' },
"clang-9",
),
linux_pipeline(
"Linux 20.04 Clang 10",
"cppalliance/droneubuntu2004:1",
{ TOOLSET: 'clang', COMPILER: 'clang++-10', CXXSTD: '03,11,14,17,2a' },
{ TOOLSET: 'clang', COMPILER: 'clang++-10', CXXSTD: '11,14,17,2a' },
"clang-10",
),
linux_pipeline(
"Linux 20.04 Clang 11",
"cppalliance/droneubuntu2004:1",
{ TOOLSET: 'clang', COMPILER: 'clang++-11', CXXSTD: '03,11,14,17,2a' },
{ TOOLSET: 'clang', COMPILER: 'clang++-11', CXXSTD: '11,14,17,2a' },
"clang-11",
),
linux_pipeline(
"Linux 20.04 Clang 12",
"cppalliance/droneubuntu2004:1",
{ TOOLSET: 'clang', COMPILER: 'clang++-12', CXXSTD: '03,11,14,17,2a' },
{ TOOLSET: 'clang', COMPILER: 'clang++-12', CXXSTD: '11,14,17,2a' },
"clang-12",
),
linux_pipeline(
"Linux 22.04 Clang 13",
"cppalliance/droneubuntu2204:1",
{ TOOLSET: 'clang', COMPILER: 'clang++-13', CXXSTD: '03,11,14,17,20' },
{ TOOLSET: 'clang', COMPILER: 'clang++-13', CXXSTD: '11,14,17,20' },
"clang-13",
),
linux_pipeline(
"Linux 22.04 Clang 14 UBSAN (03,11,14)",
"Linux 22.04 Clang 14 UBSAN (11,14)",
"cppalliance/droneubuntu2204:1",
{ TOOLSET: 'clang', COMPILER: 'clang++-14', CXXSTD: '03,11,14' } + ubsan,
{ TOOLSET: 'clang', COMPILER: 'clang++-14', CXXSTD: '11,14' } + ubsan,
"clang-14",
),
@@ -434,9 +410,9 @@ local windows_pipeline(name, image, environment, arch = "amd64") =
),
linux_pipeline(
"Linux 22.04 Clang 14 ASAN (03,11,14)",
"Linux 22.04 Clang 14 ASAN (11,14)",
"cppalliance/droneubuntu2204:1",
{ TOOLSET: 'clang', COMPILER: 'clang++-14', CXXSTD: '03,11,14' } + asan,
{ TOOLSET: 'clang', COMPILER: 'clang++-14', CXXSTD: '11,14' } + asan,
"clang-14",
),
@@ -457,14 +433,14 @@ local windows_pipeline(name, image, environment, arch = "amd64") =
linux_pipeline(
"Linux 22.04 Clang 15",
"cppalliance/droneubuntu2204:1",
{ TOOLSET: 'clang', COMPILER: 'clang++-15', CXXSTD: '03,11,14,17,20,2b' },
{ TOOLSET: 'clang', COMPILER: 'clang++-15', CXXSTD: '11,14,17,20,2b' },
"clang-15",
["deb http://apt.llvm.org/jammy/ llvm-toolchain-jammy-15 main"],
),
macos_pipeline(
"MacOS 10.15 Xcode 12.2 UBSAN (03,11)",
{ TOOLSET: 'clang', COMPILER: 'clang++', CXXSTD: '03,11' } + ubsan,
"MacOS 10.15 Xcode 12.2 UBSAN (11)",
{ TOOLSET: 'clang', COMPILER: 'clang++', CXXSTD: '11' } + ubsan,
),
macos_pipeline(
@@ -479,7 +455,7 @@ local windows_pipeline(name, image, environment, arch = "amd64") =
macos_pipeline(
"MacOS 12.4 Xcode 13.4.1 ASAN",
{ TOOLSET: 'clang', COMPILER: 'clang++', CXXSTD: '03,11,14,1z' } + asan,
{ TOOLSET: 'clang', COMPILER: 'clang++', CXXSTD: '11,14,1z' } + asan,
xcode_version = "13.4.1", osx_version = "monterey", arch = "arm64",
),
+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
+81 -47
View File
@@ -32,6 +32,16 @@ env:
CODECOV_NAME: Github Actions
jobs:
runner-selection:
# runs-on: ubuntu-latest
runs-on: ${{ github.repository_owner == 'boostorg' && fromJSON('[ "self-hosted", "linux", "x64", "ubuntu-latest-aws" ]') || 'ubuntu-latest' }}
outputs:
labelmatrix: ${{ steps.aws_hosted_runners.outputs.labelmatrix }}
steps:
- name: AWS Hosted Runners
id: aws_hosted_runners
uses: cppalliance/aws-hosted-runners@v1.0.0
posix:
defaults:
run:
@@ -42,50 +52,71 @@ jobs:
matrix:
include:
# Linux, gcc
- { compiler: gcc-7, cxxstd: '03,11,14,17', os: ubuntu-20.04, install: 'g++-7' }
- { compiler: gcc-8, cxxstd: '03,11,14,17', os: ubuntu-20.04, install: 'g++-8' }
- { compiler: gcc-9, cxxstd: '03,11,14,17', os: ubuntu-20.04, install: 'g++-9' }
- { compiler: gcc-10, cxxstd: '03,11,14,17,20', os: ubuntu-22.04, install: 'g++-10' }
- { compiler: gcc-11, cxxstd: '03,11,14,17,20', os: ubuntu-22.04, install: 'g++-11' }
- { name: "gcc-12 w/ sanitizers (03,11,14)", sanitize: yes,
compiler: gcc-12, cxxstd: '03,11,14', os: ubuntu-22.04, ccache_key: "san1" }
- { name: "gcc-12 w/ sanitizers (17,20,2b)", sanitize: yes,
compiler: gcc-12, cxxstd: '17,20,2b', os: ubuntu-22.04, ccache_key: "san2" }
- { compiler: gcc-7, cxxstd: '11,14,17', os: 'ubuntu-20.04', install: 'g++-7' }
- { compiler: gcc-8, cxxstd: '11,14,17', os: 'ubuntu-20.04', install: 'g++-8' }
- { compiler: gcc-9, cxxstd: '11,14,17', os: 'ubuntu-22.04', install: 'g++-9' }
- { compiler: gcc-10, cxxstd: '11,14,17,20', os: 'ubuntu-22.04', install: 'g++-10' }
- { compiler: gcc-11, cxxstd: '11,14,17,20', os: 'ubuntu-22.04', install: 'g++-11' }
- { name: "gcc-12 w/ sanitizers (11)", sanitize: yes,
compiler: gcc-12, cxxstd: '11', os: 'ubuntu-22.04', ccache_key: "san1" }
- { name: "gcc-12 w/ sanitizers (14)", sanitize: yes,
compiler: gcc-12, cxxstd: '14', os: 'ubuntu-22.04', ccache_key: "san1" }
- { name: "gcc-12 w/ sanitizers (17)", sanitize: yes,
compiler: gcc-12, cxxstd: '17', os: 'ubuntu-22.04', ccache_key: "san2" }
- { name: "gcc-12 w/ sanitizers (20)", sanitize: yes,
compiler: gcc-12, cxxstd: '20', os: 'ubuntu-22.04', ccache_key: "san2" }
- { name: "gcc-12 w/ sanitizers (2b)", sanitize: yes,
compiler: gcc-12, cxxstd: '2b', os: 'ubuntu-22.04', ccache_key: "san2" }
- { name: Collect coverage, coverage: yes,
compiler: gcc-12, cxxstd: '03,20', os: ubuntu-22.04, install: 'g++-12-multilib', address-model: '32,64', ccache_key: "cov" }
compiler: gcc-12, cxxstd: '20', os: 'ubuntu-22.04', install: 'g++-12-multilib', address-model: '32,64', ccache_key: "cov" }
- { name: "cfoa tsan (gcc)", cxxstd: '11,14,17,20,2b', os: ubuntu-22.04, compiler: gcc-12,
targets: 'libs/unordered/test//cfoa_tests', thread-sanitize: yes }
- { name: "cfoa tsan (gcc-12)", cxxstd: '11,14,17,20,2b', os: 'ubuntu-22.04', compiler: gcc-12,
targets: 'libs/unordered/test//cfoa_tests', thread-sanitize: yes, ccache_key: "tsan" }
# Linux, clang, libc++
- { compiler: clang-7, cxxstd: '03,11,14,17', os: ubuntu-20.04, stdlib: libc++, install: 'clang-7 libc++-7-dev libc++abi-7-dev' }
- { compiler: clang-10, cxxstd: '03,11,14,17,20', os: ubuntu-20.04, stdlib: libc++, install: 'clang-10 libc++-10-dev libc++abi-10-dev' }
- { compiler: clang-11, cxxstd: '03,11,14,17,20', os: ubuntu-22.04, stdlib: libc++, install: 'clang-11 libc++-11-dev libc++abi-11-dev' }
# clang-12 doesn't work on ubuntu-22.04, the linker can't find -lunwind for some reason
- { name: "clang-12 w/ sanitizers (03,11,14)", sanitize: yes,
compiler: clang-12, cxxstd: '03,11,14', os: ubuntu-20.04, stdlib: libc++, install: 'clang-12 libc++-12-dev libc++abi-12-dev', ccache_key: "san1" }
- { name: "clang-12 w/ sanitizers (17,20,2b)", sanitize: yes,
compiler: clang-12, cxxstd: '17,20,2b', os: ubuntu-20.04, stdlib: libc++, install: 'clang-12 libc++-12-dev libc++abi-12-dev', ccache_key: "san2" }
- { compiler: clang-13, cxxstd: '03,11,14,17,20,2b', os: ubuntu-22.04, stdlib: libc++, install: 'clang-13 libc++-13-dev libc++abi-13-dev' }
- { compiler: clang-14, cxxstd: '03,11,14,17,20,2b', os: ubuntu-22.04, stdlib: libc++, install: 'clang-14 libc++-14-dev libc++abi-14-dev' }
- { compiler: clang-7, cxxstd: '11,14,17', os: 'ubuntu-20.04', stdlib: libc++, install: 'clang-7 libc++-7-dev libc++abi-7-dev' }
- { compiler: clang-10, cxxstd: '11,14,17,20', os: 'ubuntu-20.04', stdlib: libc++, install: 'clang-10 libc++-10-dev libc++abi-10-dev' }
- { compiler: clang-11, cxxstd: '11,14,17,20', os: 'ubuntu-22.04', stdlib: libc++, install: 'clang-11 libc++-11-dev libc++abi-11-dev' }
# clang-12 doesn't work on 'ubuntu-22.04', the linker can't find -lunwind for some reason
- { name: "clang-12 w/ sanitizers (11,14)", sanitize: yes,
compiler: clang-12, cxxstd: '11,14', os: 'ubuntu-20.04', stdlib: libc++, install: 'clang-12 libc++-12-dev libc++abi-12-dev', ccache_key: "san1" }
- { name: "clang-12 w/ sanitizers (17)", sanitize: yes,
compiler: clang-12, cxxstd: '17', os: 'ubuntu-20.04', stdlib: libc++, install: 'clang-12 libc++-12-dev libc++abi-12-dev', ccache_key: "san2" }
- { name: "clang-12 w/ sanitizers (20)", sanitize: yes,
compiler: clang-12, cxxstd: '20', os: 'ubuntu-20.04', stdlib: libc++, install: 'clang-12 libc++-12-dev libc++abi-12-dev', ccache_key: "san2" }
- { name: "clang-12 w/ sanitizers (2b)", sanitize: yes,
compiler: clang-12, cxxstd: '2b', os: 'ubuntu-20.04', stdlib: libc++, install: 'clang-12 libc++-12-dev libc++abi-12-dev', ccache_key: "san2" }
- { compiler: 'clang-13', cxxstd: '11,14', os: 'ubuntu-22.04', stdlib: libc++, install: 'clang-13 libc++-13-dev libc++abi-13-dev' }
- { compiler: 'clang-13', cxxstd: '17,20,2b', os: 'ubuntu-22.04', stdlib: libc++, install: 'clang-13 libc++-13-dev libc++abi-13-dev' }
# not using libc++ because of https://github.com/llvm/llvm-project/issues/52771
- { name: "clang-14 w/ sanitizers (03,11,14)", sanitize: yes,
compiler: clang-14, cxxstd: '03,11,14', os: ubuntu-22.04, ccache_key: "san1" }
- { name: "clang-14 w/ sanitizers (17,20,2b)", sanitize: yes,
compiler: clang-14, cxxstd: '17,20,2b', os: ubuntu-22.04, ccache_key: "san2" }
- { name: "clang-14 w/ sanitizers (11,14)", sanitize: yes,
compiler: clang-14, cxxstd: '11,14', os: 'ubuntu-22.04', ccache_key: "san1" }
- { name: "clang-14 w/ sanitizers (17)", sanitize: yes,
compiler: clang-14, cxxstd: '17', os: 'ubuntu-22.04', ccache_key: "san2" }
- { name: "clang-14 w/ sanitizers (20)", sanitize: yes,
compiler: clang-14, cxxstd: '20', os: 'ubuntu-22.04', ccache_key: "san2" }
- { name: "clang-14 w/ sanitizers (2b)", sanitize: yes,
compiler: clang-14, cxxstd: '2b', os: 'ubuntu-22.04', ccache_key: "san2" }
- { name: "cfoa tsan (clang)", cxxstd: '11,14,17,20,2b', os: ubuntu-22.04, compiler: clang-14,
- { name: "cfoa tsan (clang-14)", cxxstd: '11,14,17,20,2b', os: 'ubuntu-22.04', compiler: clang-14,
targets: 'libs/unordered/test//cfoa_tests', thread-sanitize: yes,
stdlib: libc++, install: 'clang-14 libc++-14-dev libc++abi-14-dev' }
stdlib: libc++, install: 'clang-14 libc++-14-dev libc++abi-14-dev', ccache_key: "tsan" }
- { compiler: 'clang-15', cxxstd: '11,14', os: 'ubuntu-22.04', stdlib: libc++, install: 'clang-15 libc++-15-dev libc++abi-15-dev' }
- { compiler: 'clang-15', cxxstd: '17,20,2b', os: 'ubuntu-22.04', stdlib: libc++, install: 'clang-15 libc++-15-dev libc++abi-15-dev' }
# OSX, clang
- { compiler: clang, cxxstd: '03,11,14,17,2a', os: macos-11, }
- { compiler: clang, cxxstd: '03,11,14,17,2a', os: macos-12, sanitize: yes }
- { compiler: clang, cxxstd: '11,14,17,2a', os: macos-12, thread-sanitize: yes, targets: 'libs/unordered/test//cfoa_tests' }
- { compiler: clang, cxxstd: '11,14,17,2a', os: 'macos-11' }
- { compiler: clang, cxxstd: '11,14,17,2a', os: 'macos-12', sanitize: yes, ccache_key: "san1" }
- { compiler: clang, cxxstd: '11,14,17,2a', os: 'macos-12', thread-sanitize: yes, targets: 'libs/unordered/test//cfoa_tests', ccache_key: "tsan" }
- { compiler: clang, cxxstd: '11,14,17,20,2b', os: 'macos-13' }
timeout-minutes: 180
runs-on: ${{matrix.os}}
timeout-minutes: 360
# posix (gcc-12 w/ sanitizers is taking longer than 210 minutes
# timeout-minutes: 210
needs: [runner-selection]
runs-on: ${{ fromJSON(needs.runner-selection.outputs.labelmatrix)[matrix.os] }}
container: ${{matrix.container}}
env: {B2_USE_CCACHE: 1}
@@ -229,15 +260,16 @@ jobs:
fail-fast: false
matrix:
include:
- { toolset: msvc-14.0, cxxstd: '14,latest', addrmd: '32,64', os: windows-2019, variant: 'debug,release' }
- { toolset: msvc-14.2, cxxstd: '14,17,20,latest', addrmd: '32,64', os: windows-2019, variant: 'debug,release' }
- { toolset: msvc-14.3, cxxstd: '14,17,20,latest', addrmd: '32,64', os: windows-2022, variant: 'debug,release' }
- { toolset: msvc-14.3, cxxstd: '14', addrmd: '64', os: windows-2022, variant: 'debug', defines: '_ALLOW_RTCc_IN_STL', cxxflags: '/RTCc' }
- { toolset: msvc-14.3, cxxstd: '14', addrmd: '32', os: windows-2022, variant: 'debug', defines: '_ALLOW_RTCc_IN_STL', cxxflags: '"/RTCc /arch:IA32"' }
- { toolset: clang-win, cxxstd: '14,17,latest', addrmd: '32,64', os: windows-2022, variant: 'debug,release' }
- { toolset: gcc, cxxstd: '03,11,14,17,2a', addrmd: '64', os: windows-2019, variant: 'debug,release' }
- { toolset: msvc-14.0, cxxstd: '14,latest', addrmd: '32,64', os: 'windows-2019', variant: 'debug,release' }
- { toolset: msvc-14.2, cxxstd: '14,17,20,latest', addrmd: '32,64', os: 'windows-2019', variant: 'debug,release' }
- { toolset: msvc-14.3, cxxstd: '14,17,20,latest', addrmd: '32,64', os: 'windows-2022', variant: 'debug,release' }
- { toolset: msvc-14.3, cxxstd: '14', addrmd: '64', os: 'windows-2022', variant: 'debug', defines: '_ALLOW_RTCc_IN_STL', cxxflags: '/RTCc' }
- { toolset: msvc-14.3, cxxstd: '14', addrmd: '32', os: 'windows-2022', variant: 'debug', defines: '_ALLOW_RTCc_IN_STL', cxxflags: '"/RTCc /arch:IA32"' }
- { toolset: clang-win, cxxstd: '14,17,latest', addrmd: '32,64', os: 'windows-2022', variant: 'debug,release' }
- { toolset: gcc, cxxstd: '11,14,17,2a', addrmd: '64', os: 'windows-2019', variant: 'debug,release' }
runs-on: ${{matrix.os}}
needs: [runner-selection]
runs-on: ${{ fromJSON(needs.runner-selection.outputs.labelmatrix)[matrix.os] }}
steps:
- uses: actions/checkout@v3
@@ -291,10 +323,11 @@ jobs:
fail-fast: false
matrix:
include:
- { sys: MINGW32, compiler: gcc, cxxstd: '03,11,17,20' }
- { sys: MINGW64, compiler: gcc, cxxstd: '03,11,17,20' }
- { sys: MINGW32, compiler: gcc, cxxstd: '11,17,20' }
- { sys: MINGW64, compiler: gcc, cxxstd: '11,17,20' }
runs-on: windows-latest
needs: [runner-selection]
runs-on: ${{ fromJSON(needs.runner-selection.outputs.labelmatrix)['windows-latest'] }}
steps:
- uses: actions/checkout@v3
@@ -348,11 +381,12 @@ jobs:
fail-fast: false
matrix:
include:
- { os: ubuntu-20.04, build_shared: ON, build_type: Debug, generator: 'Unix Makefiles' }
- { os: windows-2019, build_shared: ON, build_type: Debug, generator: 'Visual Studio 16 2019' }
- { os: 'ubuntu-20.04', build_shared: ON, build_type: Debug, generator: 'Unix Makefiles' }
- { os: 'windows-2019', build_shared: ON, build_type: Debug, generator: 'Visual Studio 16 2019' }
timeout-minutes: 120
runs-on: ${{matrix.os}}
needs: [runner-selection]
runs-on: ${{ fromJSON(needs.runner-selection.outputs.labelmatrix)[matrix.os] }}
steps:
- uses: actions/checkout@v3
Binary file not shown.

Before

Width:  |  Height:  |  Size: 27 KiB

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 27 KiB

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 23 KiB

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 23 KiB

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 23 KiB

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 23 KiB

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 23 KiB

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 23 KiB

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 27 KiB

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 27 KiB

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

After

Width:  |  Height:  |  Size: 25 KiB

+4
View File
@@ -447,6 +447,10 @@ operations follow a https://en.wikipedia.org/wiki/Zipf%27s_law#Formal_definition
with different _skew_ parameters: the higher the skew, the more concentrated are the keys in the lower values
of the covered range.
`boost::concurrent_flat_map` is exercised using both regular and xref:#concurrent_bulk_visitation[bulk visitation]:
in the latter case, lookup keys are buffered in a local array and then processed at
once each time the buffer reaches xref:#concurrent_flat_map_constants[`bulk_visit_size`].
=== GCC 12, x64
+16
View File
@@ -6,6 +6,22 @@
: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 bulk visitation to concurrent containers for increased lookup performance.
* 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.
* Starting with this release, `boost::unordered_[multi]set` and `boost::unordered_[multi]map`
only work with C++11 onwards.
== Release 1.83.0 - Major update
* Added `boost::concurrent_flat_map`, a fast, thread-safe hashmap based on open addressing.
+48 -106
View File
@@ -7,116 +7,54 @@
== Closed-addressing Containers
`unordered_[multi]set` and `unordered_[multi]map` are intended to provide a conformant
implementation of the {cpp}20 standard that will work with {cpp}98 upwards.
This wide compatibility does mean some compromises have to be made.
With a compiler and library that fully support {cpp}11, the differences should
be minor.
`boost::unordered_[multi]set` and `boost::unordered_[multi]map` provide a conformant
implementation for {cpp}11 (or later) compilers of the latest standard revision of
{cpp} unordered associative containers, with very minor deviations as noted.
The containers are fully https://en.cppreference.com/w/cpp/named_req/AllocatorAwareContainer[AllocatorAware^]
and support https://en.cppreference.com/w/cpp/named_req/Allocator#Fancy_pointers[fancy pointers^].
=== Move Emulation
=== Deduction Guides
Support for move semantics is implemented using Boost.Move. If rvalue
references are available it will use them, but if not it uses a close,
but imperfect emulation. On such compilers:
Deduction guides for
https://en.cppreference.com/w/cpp/language/class_template_argument_deduction[class template argument deduction (CTAD)^]
are only available on {cpp}17 (or later) compilers.
* Non-copyable objects can be stored in the containers.
They can be constructed in place using `emplace`, or if they support
Boost.Move, moved into place.
* The containers themselves are not movable.
* Argument forwarding is not perfect.
=== Piecewise Pair Emplacement
=== Use of Allocators
{cpp}11 introduced a new allocator system. It's backwards compatible due to
the lax requirements for allocators in the old standard, but might need
some changes for allocators which worked with the old versions of the
unordered containers.
It uses a traits class, `allocator_traits` to handle the allocator
adding extra functionality, and making some methods and types optional.
During development a stable release of
`allocator_traits` wasn't available so an internal partial implementation
is always used in this version. Hopefully a future version will use the
standard implementation where available.
The member functions `construct`, `destroy` and `max_size` are now
optional, if they're not available a fallback is used.
A full implementation of `allocator_traits` requires sophisticated
member function detection so that the fallback is used whenever the
member function call is not well formed.
This requires support for SFINAE expressions, which are available on
GCC from version 4.4 and Clang.
On other compilers, there's just a test to see if the allocator has
a member, but no check that it can be called. So rather than using a
fallback there will just be a compile error.
`propagate_on_container_copy_assignment`,
`propagate_on_container_move_assignment`,
`propagate_on_container_swap` and
`select_on_container_copy_construction` are also supported.
Due to imperfect move emulation, some assignments might check
`propagate_on_container_copy_assignment` on some compilers and
`propagate_on_container_move_assignment` on others.
=== Construction/Destruction Using Allocators
The following support is required for full use of {cpp}11 style
construction/destruction:
* Variadic templates.
* Piecewise construction of `std::pair`.
* Either `std::allocator_traits` or expression SFINAE.
This is detected using Boost.Config. The macro
`BOOST_UNORDERED_CXX11_CONSTRUCTION` will be set to 1 if it is found, or 0
otherwise.
When this is the case `allocator_traits::construct` and
`allocator_traits::destroy` will always be used, apart from when piecewise
constructing a `std::pair` using `boost::tuple` (see <<compliance_pairs,below>>), but that should be easily avoided.
When support is not available `allocator_traits::construct` and
`allocator_traits::destroy` are never called.
=== Pointer Traits
`pointer_traits` aren't used. Instead, pointer types are obtained from
rebound allocators, this can cause problems if the allocator can't be
used with incomplete types. If `const_pointer` is not defined in the
allocator, `boost::pointer_to_other<pointer, const value_type>::type`
is used to obtain a const pointer.
=== Pairs
Since the containers use `std::pair` they're limited to the version
from the current standard library. But since {cpp}11 ``std::pair``'s
`piecewise_construct` based constructor is very useful, `emplace`
emulates it with a `piecewise_construct` in the `boost::unordered`
namespace. So for example, the following will work:
In accordance with the standard specification,
`boost::unordered_[multi]map::emplace` supports piecewise pair construction:
[source,c++]
----
boost::unordered_multimap<std::string, std::complex> x;
x.emplace(
std::piecewise_construct,
std::make_tuple("key"), std::make_tuple(1, 2));
----
Additionally, the same
functionality is provided via non-standard `boost::unordered::piecewise_construct`
and Boost.Tuple:
[source,c++]
----
x.emplace(
boost::unordered::piecewise_construct,
boost::make_tuple("key"), boost::make_tuple(1, 2));
----
Older drafts of the standard also supported variadic constructors
for `std::pair`, where the first argument would be used for the
first part of the pair, and the remaining for the second part.
This feature has been retained for backwards compatibility with
previous versions of Boost.Unordered: users are encouraged to
update their code to use `std::piecewise_construct` and
``std::tuple``s instead.
=== Miscellaneous
=== Swap
When swapping, `Pred` and `Hash` are not currently swapped by calling
`swap`, their copy constructors are used. As a consequence when swapping
`swap`, their copy constructors are used. As a consequence, when swapping
an exception may be thrown from their copy constructor.
Variadic constructor arguments for `emplace` are only used when both
rvalue references and variadic template parameters are available.
Otherwise `emplace` can only take up to 10 constructors arguments.
== Open-addressing Containers
The C++ standard does not currently provide any open-addressing container
@@ -129,7 +67,9 @@ radically different from that imposed by the standard (closed addressing).
Open-addressing containers provided by Boost.Unordered only work with reasonably
compliant C++11 (or later) compilers. Language-level features such as move semantics
and variadic template parameters are then not emulated.
The containers are fully https://en.cppreference.com/w/cpp/named_req/AllocatorAwareContainer[AllocatorAware^].
The containers are fully https://en.cppreference.com/w/cpp/named_req/AllocatorAwareContainer[AllocatorAware^]
and support https://en.cppreference.com/w/cpp/named_req/Allocator#Fancy_pointers[fancy pointers^].
The main differences with C++ unordered associative containers are:
@@ -148,22 +88,24 @@ 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^]
containers except those implying iterators.
they meet all the requirements of https://en.cppreference.com/w/cpp/named_req/AllocatorAwareContainer[AllocatorAware^]
containers (including
https://en.cppreference.com/w/cpp/named_req/Allocator#Fancy_pointers[fancy pointer^] support)
except those implying iterators.
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 +133,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`].
+120 -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,64 @@ 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.
== Bulk visitation
Suppose you have an `std::array` of keys you want to look up for in a concurrent map:
[source,c++]
----
std::array<int, N> keys;
...
for(const auto& key: keys) {
m.visit(key, [](auto& x) { ++x.second; });
}
----
_Bulk visitation_ allows us to pass all the keys in one operation:
[source,c++]
----
m.visit(keys.begin(), keys.end(), [](auto& x) { ++x.second; });
----
This functionality is not provided for mere syntactic convenience, though: by processing all the
keys at once, some internal optimizations can be applied that increase
performance over the regular, one-at-a-time case (consult the
xref:#benchmarks_boostconcurrent_flat_map[benchmarks]). In fact, it may be beneficial
to buffer incoming keys so that they can be bulk visited in chunks:
[source,c++]
----
static constexpr auto bulk_visit_size = boost::concurrent_flat_map<int,int>::bulk_visit_size;
std::array<int, bulk_visit_size> buffer;
std::size_t i=0;
while(...) { // processing loop
...
buffer[i++] = k;
if(i == bulk_visit_size) {
map.visit(buffer.begin(), buffer.end(), [](auto& x) { ++x.second; });
i = 0;
}
...
}
// flush remaining keys
map.visit(buffer.begin(), buffer.begin() + i, [](auto& x) { ++x.second; });
----
There's a latency/throughput tradeoff here: it will take longer for incoming keys to
be processed (since they are buffered), but the number of processed keys per second
is higher. `bulk_visit_size` is the recommended chunk size —smaller buffers
may yield worse performance.
== 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 +254,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);
----
+186 -20
View File
@@ -50,6 +50,9 @@ namespace boost {
using size_type = std::size_t;
using difference_type = std::ptrdiff_t;
// constants
static constexpr size_type xref:#concurrent_flat_map_constants[bulk_visit_size] = _implementation-defined_;
// construct/copy/destroy
xref:#concurrent_flat_map_default_constructor[concurrent_flat_map]();
explicit xref:#concurrent_flat_map_bucket_count_constructor[concurrent_flat_map](size_type n,
@@ -69,6 +72,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 +93,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;
@@ -104,6 +109,13 @@ namespace boost {
template<class K, class F> size_t xref:#concurrent_flat_map_cvisit[visit](const K& k, F f) const;
template<class K, class F> size_t xref:#concurrent_flat_map_cvisit[cvisit](const K& k, F f) const;
template<class FwdIterator, class F>
size_t xref:concurrent_flat_map_bulk_visit[visit](FwdIterator first, FwdIterator last, F f);
template<class FwdIterator, class F>
size_t xref:concurrent_flat_map_bulk_visit[visit](FwdIterator first, FwdIterator last, F f) const;
template<class FwdIterator, class F>
size_t xref:concurrent_flat_map_bulk_visit[cvisit](FwdIterator first, FwdIterator last, F f) const;
template<class F> size_t xref:#concurrent_flat_map_cvisit_all[visit_all](F f);
template<class F> size_t xref:#concurrent_flat_map_cvisit_all[visit_all](F f) const;
template<class F> size_t xref:#concurrent_flat_map_cvisit_all[cvisit_all](F f) const;
@@ -114,6 +126,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 +327,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 +385,25 @@ 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.
=== Constants
```cpp
static constexpr size_type bulk_visit_size;
```
Chunk size internally used in xref:concurrent_flat_map_bulk_visit[bulk visit] operations.
=== Constructors
==== Default Constructor
@@ -491,6 +531,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 +690,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`.
@@ -683,6 +739,42 @@ Notes:;; The `template<class K, class F>` overloads only participate in overload
---
==== Bulk visit
```c++
template<class FwdIterator, class F>
size_t visit(FwdIterator first, FwdIterator last, F f);
template<class FwdIterator, class F>
size_t visit(FwdIterator first, FwdIterator last, F f) const;
template<class FwdIterator, class F>
size_t cvisit(FwdIterator first, FwdIterator last, F f) const;
```
For each element `k` in the range [`first`, `last`),
if there is an element `x` in the container with key equivalent to `k`,
invokes `f` with a reference to `x`.
Such reference is const iff `*this` is const.
Although functionally equivalent to individually invoking
xref:concurrent_flat_map_cvisit[`[c\]visit`] for each key, bulk visitation
performs generally faster due to internal streamlining optimizations.
It is advisable that `std::distance(first,last)` be at least
xref:#concurrent_flat_map_constants[`bulk_visit_size`] to enjoy
a performance gain: beyond this size, performance is not expected
to increase further.
[horizontal]
Requires:;; `FwdIterator` is a https://en.cppreference.com/w/cpp/named_req/ForwardIterator[LegacyForwardIterator^]
({cpp}11 to {cpp}17),
or satisfies https://en.cppreference.com/w/cpp/iterator/forward_iterator[std::forward_iterator^] ({cpp}20 and later).
For `K` = `std::iterator_traits<FwdIterator>::value_type`, either `K` is `key_type` or
else `Hash::is_transparent` and `Pred::is_transparent` are valid member typedefs.
In the latter case, the library assumes that `Hash` is callable with both `K` and `Key` and that `Pred` is transparent.
This enables heterogeneous lookup which avoids the cost of instantiating an instance of the `Key` type.
Returns:;; The number of elements visited.
---
==== [c]visit_all
```c++
@@ -720,6 +812,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
@@ -962,13 +1098,13 @@ if there is an element with an equivalent key; otherwise, the construction is of
```c++
// first two overloads
value_type(std::piecewise_construct,
std::forward_as_tuple(boost::forward<Key>(k)),
std::forward_as_tuple(boost::forward<Args>(args)...))
std::forward_as_tuple(std::forward<Key>(k)),
std::forward_as_tuple(std::forward<Args>(args)...))
// third overload
value_type(std::piecewise_construct,
std::forward_as_tuple(boost::forward<K>(k)),
std::forward_as_tuple(boost::forward<Args>(args)...))
std::forward_as_tuple(std::forward<K>(k)),
std::forward_as_tuple(std::forward<Args>(args)...))
```
unlike xref:#concurrent_flat_map_emplace[emplace], which simply forwards all arguments to ``value_type``'s constructor.
@@ -1010,13 +1146,13 @@ if there is an element with an equivalent key; otherwise, the construction is of
```c++
// first four overloads
value_type(std::piecewise_construct,
std::forward_as_tuple(boost::forward<Key>(k)),
std::forward_as_tuple(boost::forward<Args>(args)...))
std::forward_as_tuple(std::forward<Key>(k)),
std::forward_as_tuple(std::forward<Args>(args)...))
// last two overloads
value_type(std::piecewise_construct,
std::forward_as_tuple(boost::forward<K>(k)),
std::forward_as_tuple(boost::forward<Args>(args)...))
std::forward_as_tuple(std::forward<K>(k)),
std::forward_as_tuple(std::forward<Args>(args)...))
```
Invalidates pointers and references to elements if a rehashing is issued.
@@ -1038,19 +1174,19 @@ template<class K, class M> bool insert_or_assign(K&& k, M&& obj);
Inserts a new element into the table or updates an existing one by assigning to the contained value.
If there is an element with key `k`, then it is updated by assigning `boost::forward<M>(obj)`.
If there is an element with key `k`, then it is updated by assigning `std::forward<M>(obj)`.
If there is no such element, it is added to the table as:
```c++
// first two overloads
value_type(std::piecewise_construct,
std::forward_as_tuple(boost::forward<Key>(k)),
std::forward_as_tuple(boost::forward<M>(obj)))
std::forward_as_tuple(std::forward<Key>(k)),
std::forward_as_tuple(std::forward<M>(obj)))
// third overload
value_type(std::piecewise_construct,
std::forward_as_tuple(boost::forward<K>(k)),
std::forward_as_tuple(boost::forward<M>(obj)))
std::forward_as_tuple(std::forward<K>(k)),
std::forward_as_tuple(std::forward<M>(obj)))
```
[horizontal]
@@ -1436,3 +1572,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.
+94 -26
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`.
@@ -891,13 +909,13 @@ if there is an element with an equivalent key; otherwise, the construction is of
```c++
// first two overloads
value_type(std::piecewise_construct,
std::forward_as_tuple(boost::forward<Key>(k)),
std::forward_as_tuple(boost::forward<Args>(args)...))
std::forward_as_tuple(std::forward<Key>(k)),
std::forward_as_tuple(std::forward<Args>(args)...))
// third overload
value_type(std::piecewise_construct,
std::forward_as_tuple(boost::forward<K>(k)),
std::forward_as_tuple(boost::forward<Args>(args)...))
std::forward_as_tuple(std::forward<K>(k)),
std::forward_as_tuple(std::forward<Args>(args)...))
```
unlike xref:#unordered_flat_map_emplace[emplace], which simply forwards all arguments to ``value_type``'s constructor.
@@ -936,13 +954,13 @@ if there is an element with an equivalent key; otherwise, the construction is of
```c++
// first two overloads
value_type(std::piecewise_construct,
std::forward_as_tuple(boost::forward<Key>(k)),
std::forward_as_tuple(boost::forward<Args>(args)...))
std::forward_as_tuple(std::forward<Key>(k)),
std::forward_as_tuple(std::forward<Args>(args)...))
// third overload
value_type(std::piecewise_construct,
std::forward_as_tuple(boost::forward<K>(k)),
std::forward_as_tuple(boost::forward<Args>(args)...))
std::forward_as_tuple(std::forward<K>(k)),
std::forward_as_tuple(std::forward<Args>(args)...))
```
unlike xref:#unordered_flat_map_emplace_hint[emplace_hint], which simply forwards all arguments to ``value_type``'s constructor.
@@ -967,19 +985,19 @@ template<class K, class M>
Inserts a new element into the container or updates an existing one by assigning to the contained value.
If there is an element with key `k`, then it is updated by assigning `boost::forward<M>(obj)`.
If there is an element with key `k`, then it is updated by assigning `std::forward<M>(obj)`.
If there is no such element, it is added to the container as:
```c++
// first two overloads
value_type(std::piecewise_construct,
std::forward_as_tuple(boost::forward<Key>(k)),
std::forward_as_tuple(boost::forward<M>(obj)))
std::forward_as_tuple(std::forward<Key>(k)),
std::forward_as_tuple(std::forward<M>(obj)))
// third overload
value_type(std::piecewise_construct,
std::forward_as_tuple(boost::forward<K>(k)),
std::forward_as_tuple(boost::forward<M>(obj)))
std::forward_as_tuple(std::forward<K>(k)),
std::forward_as_tuple(std::forward<M>(obj)))
```
[horizontal]
@@ -1005,19 +1023,19 @@ template<class K, class M>
Inserts a new element into the container or updates an existing one by assigning to the contained value.
If there is an element with key `k`, then it is updated by assigning `boost::forward<M>(obj)`.
If there is an element with key `k`, then it is updated by assigning `std::forward<M>(obj)`.
If there is no such element, it is added to the container as:
```c++
// first two overloads
value_type(std::piecewise_construct,
std::forward_as_tuple(boost::forward<Key>(k)),
std::forward_as_tuple(boost::forward<M>(obj)))
std::forward_as_tuple(std::forward<Key>(k)),
std::forward_as_tuple(std::forward<M>(obj)))
// third overload
value_type(std::piecewise_construct,
std::forward_as_tuple(boost::forward<K>(k)),
std::forward_as_tuple(boost::forward<M>(obj)))
std::forward_as_tuple(std::forward<K>(k)),
std::forward_as_tuple(std::forward<M>(obj)))
```
`hint` is a suggestion to where the element should be inserted. This implementation ignores it.
@@ -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`.
+105 -95
View File
@@ -26,12 +26,12 @@ namespace boost {
using hasher = Hash;
using key_equal = Pred;
using allocator_type = Allocator;
using pointer = typename boost::allocator_traits<Allocator>::pointer;
using const_pointer = typename boost::allocator_traits<Allocator>::const_pointer;
using pointer = typename std::allocator_traits<Allocator>::pointer;
using const_pointer = typename std::allocator_traits<Allocator>::const_pointer;
using reference = value_type&;
using const_reference = const value_type&;
using size_type = _implementation-defined_;
using difference_type = _implementation-defined_;
using size_type = std::size_t;
using difference_type = std::ptrdiff_t;
using iterator = _implementation-defined_;
using const_iterator = _implementation-defined_;
@@ -316,6 +316,7 @@ namespace boost {
|_Allocator_
|An allocator whose value type is the same as the container's value type.
Allocators using https://en.cppreference.com/w/cpp/named_req/Allocator#Fancy_pointers[fancy pointers] are supported.
|===
@@ -323,51 +324,15 @@ 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
[source,c++,subs=+quotes]
----
typedef typename allocator_type::pointer pointer;
----
`value_type*` if `allocator_type::pointer` is not defined.
---
[source,c++,subs=+quotes]
----
typedef typename allocator_type::const_pointer const_pointer;
----
`boost::pointer_to_other<pointer, value_type>::type` if `allocator_type::const_pointer` is not defined.
---
[source,c++,subs=+quotes]
----
typedef _implementation-defined_ size_type;
----
An unsigned integral type.
`size_type` can represent any non-negative value of `difference_type`.
---
[source,c++,subs=+quotes]
----
typedef _implementation-defined_ difference_type;
----
A signed integral type.
Is identical to the difference type of `iterator` and `const_iterator`.
---
[source,c++,subs=+quotes]
----
typedef _implementation-defined_ iterator;
@@ -419,7 +384,8 @@ A const_local_iterator object can be used to iterate through a single bucket.
typedef _implementation-defined_ node_type;
----
See node_handle_map for details.
A class for holding extracted container elements, modelling
https://en.cppreference.com/w/cpp/container/node_handle[NodeHandle].
---
@@ -428,7 +394,20 @@ See node_handle_map for details.
typedef _implementation-defined_ insert_return_type;
----
Structure returned by inserting node_type.
A specialization of an internal class template:
[source,c++,subs=+quotes]
----
template<class Iterator, class NodeType>
struct _insert_return_type_ // name is exposition only
{
Iterator position;
bool inserted;
NodeType node;
};
----
with `Iterator` = `iterator` and `NodeType` = `node_type`.
---
@@ -508,10 +487,7 @@ The move constructor.
[horizontal]
Notes:;; This is implemented using Boost.Move.
Requires:;; `value_type` is move-constructible. +
+
On compilers without rvalue reference support the emulation does not support moving without calling `boost::move` if `value_type` is not copyable.
So, for example, you can't return the container from a function.
Requires:;; `value_type` is move-constructible.
---
@@ -711,7 +687,6 @@ The move assignment operator.
If `Alloc::propagate_on_container_move_assignment` exists and `Alloc::propagate_on_container_move_assignment::value` is `true`, the allocator is overwritten, if not the moved elements are created using the existing allocator.
[horizontal]
Notes:;; On compilers without rvalue references, this is emulated using Boost.Move. Note that on some compilers the copy assignment operator may be used in some circumstances.
Requires:;; `value_type` is move constructible.
---
@@ -822,11 +797,7 @@ If an insert took place, then the iterator points to the newly inserted element.
Throws:;; If an exception is thrown by an operation other than a call to `hasher` the function has no effect.
Notes:;; Can invalidate iterators, but only if the insert causes the load factor to be greater to or equal to the maximum load factor. +
+
Pointers and references to elements are never invalidated. +
+
If the compiler doesn't support variadic template arguments or rvalue references, this is emulated for up to `10` arguments, with no support for rvalue references or move semantics. +
+
Since existing `std::pair` implementations don't support `std::piecewise_construct` this emulates it, but using `boost::unordered::piecewise_construct`.
Pointers and references to elements are never invalidated.
---
@@ -847,11 +818,7 @@ Notes:;; The standard is fairly vague on the meaning of the hint. But the only p
+
Can invalidate iterators, but only if the insert causes the load factor to be greater to or equal to the maximum load factor. +
+
Pointers and references to elements are never invalidated. +
+
If the compiler doesn't support variadic template arguments or rvalue references, this is emulated for up to 10 arguments, with no support for rvalue references or move semantics. +
+
Since existing `std::pair` implementations don't support `std::piecewise_construct` this emulates it, but using `boost::unordered::piecewise_construct`.
Pointers and references to elements are never invalidated.
---
@@ -1029,13 +996,13 @@ Notes:;; This function is similiar to xref:#unordered_map_emplace[emplace] excep
```c++
// first two overloads
value_type(std::piecewise_construct,
std::forward_as_tuple(boost::forward<Key>(k)),
std::forward_as_tuple(boost::forward<Args>(args)...))
std::forward_as_tuple(std::forward<Key>(k)),
std::forward_as_tuple(std::forward<Args>(args)...))
// third overload
value_type(std::piecewise_construct,
std::forward_as_tuple(boost::forward<K>(k)),
std::forward_as_tuple(boost::forward<Args>(args)...))
std::forward_as_tuple(std::forward<K>(k)),
std::forward_as_tuple(std::forward<Args>(args)...))
```
instead of xref:#unordered_map_emplace[emplace] which simply forwards all arguments to ``value_type``'s constructor.
@@ -1045,10 +1012,6 @@ Can invalidate iterators, but only if the insert causes the load factor to be gr
Pointers and references to elements are never invalidated.
The `template<class K, class\... Args>` overload only participates in overload resolution if `Hash::is_transparent` and `Pred::is_transparent` are valid member typedefs and neither `iterator` nor `const_iterator` are implicitly convertible from `K`. The library assumes that `Hash` is callable with both `K` and `Key` and that `Pred` is transparent. This enables heterogeneous lookup which avoids the cost of instantiating an instance of the `Key` type.
If the compiler doesn't support variadic template arguments or rvalue references, this is emulated for up to `10` arguments, with no support for rvalue references or move semantics.
Since existing `std::pair` implementations don't support `std::piecewise_construct` this emulates it, but using `boost::unordered::piecewise_construct`.
--
---
@@ -1078,13 +1041,13 @@ Notes:;; This function is similiar to xref:#unordered_map_emplace_hint[emplace_h
```c++
// first two overloads
value_type(std::piecewise_construct,
std::forward_as_tuple(boost::forward<Key>(k)),
std::forward_as_tuple(boost::forward<Args>(args)...))
std::forward_as_tuple(std::forward<Key>(k)),
std::forward_as_tuple(std::forward<Args>(args)...))
// third overload
value_type(std::piecewise_construct,
std::forward_as_tuple(boost::forward<K>(k)),
std::forward_as_tuple(boost::forward<Args>(args)...))
std::forward_as_tuple(std::forward<K>(k)),
std::forward_as_tuple(std::forward<Args>(args)...))
```
instead of xref:#unordered_map_emplace_hint[emplace_hint] which simply forwards all arguments to ``value_type``'s constructor.
@@ -1096,10 +1059,6 @@ Can invalidate iterators, but only if the insert causes the load factor to be gr
Pointers and references to elements are never invalidated.
The `template<class K, class\... Args>` overload only participates in overload resolution if `Hash::is_transparent` and `Pred::is_transparent` are valid member typedefs and neither `iterator` nor `const_iterator` are implicitly convertible from `K`. The library assumes that `Hash` is callable with both `K` and `Key` and that `Pred` is transparent. This enables heterogeneous lookup which avoids the cost of instantiating an instance of the `Key` type.
If the compiler doesn't support variadic template arguments or rvalue references, this is emulated for up to `10` arguments, with no support for rvalue references or move semantics.
Since existing `std::pair` implementations don't support `std::piecewise_construct` this emulates it, but using `boost::unordered::piecewise_construct`.
--
---
@@ -1116,19 +1075,19 @@ template<class K, class M>
Inserts a new element into the container or updates an existing one by assigning to the contained value.
If there is an element with key `k`, then it is updated by assigning `boost::forward<M>(obj)`.
If there is an element with key `k`, then it is updated by assigning `std::forward<M>(obj)`.
If there is no such element, it is added to the container as:
```c++
// first two overloads
value_type(std::piecewise_construct,
std::forward_as_tuple(boost::forward<Key>(k)),
std::forward_as_tuple(boost::forward<M>(obj)))
std::forward_as_tuple(std::forward<Key>(k)),
std::forward_as_tuple(std::forward<M>(obj)))
// third overload
value_type(std::piecewise_construct,
std::forward_as_tuple(boost::forward<K>(k)),
std::forward_as_tuple(boost::forward<M>(obj)))
std::forward_as_tuple(std::forward<K>(k)),
std::forward_as_tuple(std::forward<M>(obj)))
```
[horizontal]
@@ -1156,19 +1115,19 @@ template<class K, class M>
Inserts a new element into the container or updates an existing one by assigning to the contained value.
If there is an element with key `k`, then it is updated by assigning `boost::forward<M>(obj)`.
If there is an element with key `k`, then it is updated by assigning `std::forward<M>(obj)`.
If there is no such element, it is added to the container as:
```c++
// first two overloads
value_type(std::piecewise_construct,
std::forward_as_tuple(boost::forward<Key>(k)),
std::forward_as_tuple(boost::forward<M>(obj)))
std::forward_as_tuple(std::forward<Key>(k)),
std::forward_as_tuple(std::forward<M>(obj)))
// third overload
value_type(std::piecewise_construct,
std::forward_as_tuple(boost::forward<K>(k)),
std::forward_as_tuple(boost::forward<M>(obj)))
std::forward_as_tuple(std::forward<K>(k)),
std::forward_as_tuple(std::forward<M>(obj)))
```
`hint` is a suggestion to where the element should be inserted.
@@ -1761,9 +1720,7 @@ template<class Key, class T, class Hash, class Pred, class Alloc>
Return `true` if `x.size() == y.size()` and for every element in `x`, there is an element in `y` with the same key, with an equal value (using `operator==` to compare the value types).
[horizontal]
Notes:;; The behavior of this function was changed to match the C++11 standard in Boost 1.48. +
+
Behavior is undefined if the two containers don't have equivalent equality predicates.
Notes:;; Behavior is undefined if the two containers don't have equivalent equality predicates.
---
@@ -1777,9 +1734,7 @@ template<class Key, class T, class Hash, class Pred, class Alloc>
Return `false` if `x.size() == y.size()` and for every element in `x`, there is an element in `y` with the same key, with an equal value (using `operator==` to compare the value types).
[horizontal]
Notes:;; The behavior of this function was changed to match the C++11 standard in Boost 1.48. +
+
Behavior is undefined if the two containers don't have equivalent equality predicates.
Notes:;; Behavior is undefined if the two containers don't have equivalent equality predicates.
=== Swap
```c++
@@ -1825,4 +1780,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`.
+72 -66
View File
@@ -26,12 +26,12 @@ namespace boost {
using hasher = Hash;
using key_equal = Pred;
using allocator_type = Allocator;
using pointer = typename boost::allocator_traits<Allocator>::pointer;
using const_pointer = typename boost::allocator_traits<Allocator>::const_pointer;
using pointer = typename std::allocator_traits<Allocator>::pointer;
using const_pointer = typename std::allocator_traits<Allocator>::const_pointer;
using reference = value_type&;
using const_reference = const value_type&;
using size_type = _implementation-defined_;
using difference_type = _implementation-defined_;
using size_type = std::size_t;
using difference_type = std::ptrdiff_t;
using iterator = _implementation-defined_;
using const_iterator = _implementation-defined_;
@@ -283,6 +283,7 @@ namespace boost {
|_Allocator_
|An allocator whose value type is the same as the container's value type.
Allocators using https://en.cppreference.com/w/cpp/named_req/Allocator#Fancy_pointers[fancy pointers] are supported.
|===
@@ -290,49 +291,15 @@ 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]
----
typedef typename allocator_type::pointer pointer;
----
`value_type*` if `allocator_type::pointer` is not defined.
---
[source,c++,subs=+quotes]
----
typedef typename allocator_type::const_pointer const_pointer;
----
`boost::pointer_to_other<pointer, value_type>::type` if `allocator_type::const_pointer` is not defined.
---
[source,c++,subs=+quotes]
----
typedef _implementation-defined_ size_type;
----
An unsigned integral type.
`size_type` can represent any non-negative value of `difference_type`.
---
[source,c++,subs=+quotes]
----
typedef _implementation-defined_ difference_type;
----
A signed integral type.
Is identical to the difference type of `iterator` and `const_iterator`.
---
[source,c++,subs=+quotes]
----
typedef _implementation-defined_ iterator;
@@ -464,10 +431,7 @@ The move constructor.
[horizontal]
Notes:;; This is implemented using Boost.Move.
Requires:;; `value_type` is move-constructible. +
+
On compilers without rvalue reference support the emulation does not support moving without calling `boost::move` if `value_type` is not copyable.
So, for example, you can't return the container from a function.
Requires:;; `value_type` is move-constructible.
---
@@ -666,7 +630,6 @@ The move assignment operator.
If `Alloc::propagate_on_container_move_assignment` exists and `Alloc::propagate_on_container_move_assignment::value` is `true`, the allocator is overwritten, if not the moved elements are created using the existing allocator.
[horizontal]
Notes:;; On compilers without rvalue references, this is emulated using Boost.Move. Note that on some compilers the copy assignment operator may be used in some circumstances.
Requires:;; `value_type` is move constructible.
---
@@ -775,11 +738,7 @@ Returns:;; An iterator pointing to the inserted element.
Throws:;; If an exception is thrown by an operation other than a call to `hasher` the function has no effect.
Notes:;; Can invalidate iterators, but only if the insert causes the load factor to be greater to or equal to the maximum load factor. +
+
Pointers and references to elements are never invalidated. +
+
If the compiler doesn't support variadic template arguments or rvalue references, this is emulated for up to `10` arguments, with no support for rvalue references or move semantics. +
+
Since existing `std::pair` implementations don't support `std::piecewise_construct` this emulates it, but using `boost::unordered::piecewise_construct`.
Pointers and references to elements are never invalidated.
---
@@ -800,11 +759,7 @@ Notes:;; The standard is fairly vague on the meaning of the hint. But the only p
+
Can invalidate iterators, but only if the insert causes the load factor to be greater to or equal to the maximum load factor. +
+
Pointers and references to elements are never invalidated. +
+
If the compiler doesn't support variadic template arguments or rvalue references, this is emulated for up to 10 arguments, with no support for rvalue references or move semantics. +
+
Since existing `std::pair` implementations don't support `std::piecewise_construct` this emulates it, but using `boost::unordered::piecewise_construct`.
Pointers and references to elements are never invalidated.
---
@@ -1486,9 +1441,7 @@ template<class Key, class T, class Hash, class Pred, class Alloc>
Return `true` if `x.size() == y.size()` and for every equivalent key group in `x`, there is a group in `y` for the same key, which is a permutation (using `operator==` to compare the value types).
[horizontal]
Notes:;; The behavior of this function was changed to match the C++11 standard in Boost 1.48. +
+
Behavior is undefined if the two containers don't have equivalent equality predicates.
Notes:;; Behavior is undefined if the two containers don't have equivalent equality predicates.
---
@@ -1502,9 +1455,7 @@ template<class Key, class T, class Hash, class Pred, class Alloc>
Return `false` if `x.size() == y.size()` and for every equivalent key group in `x`, there is a group in `y` for the same key, which is a permutation (using `operator==` to compare the value types).
[horizontal]
Notes:;; The behavior of this function was changed to match the C++11 standard in Boost 1.48. +
+
Behavior is undefined if the two containers don't have equivalent equality predicates.
Notes:;; Behavior is undefined if the two containers don't have equivalent equality predicates.
---
@@ -1552,4 +1503,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`.
+70 -66
View File
@@ -24,12 +24,12 @@ namespace boost {
using hasher = Hash;
using key_equal = Pred;
using allocator_type = Allocator;
using pointer = typename boost::allocator_traits<Allocator>::pointer;
using const_pointer = typename boost::allocator_traits<Allocator>::const_pointer;
using pointer = typename std::allocator_traits<Allocator>::pointer;
using const_pointer = typename std::allocator_traits<Allocator>::const_pointer;
using reference = value_type&;
using const_reference = const value_type&;
using size_type = _implementation-defined_;
using difference_type = _implementation-defined_;
using size_type = std::size_t;
using difference_type = std::ptrdiff_t;
using iterator = _implementation-defined_;
using const_iterator = _implementation-defined_;
@@ -271,6 +271,7 @@ namespace boost {
|_Allocator_
|An allocator whose value type is the same as the container's value type.
Allocators using https://en.cppreference.com/w/cpp/named_req/Allocator#Fancy_pointers[fancy pointers] are supported.
|===
@@ -278,50 +279,15 @@ 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
[source,c++,subs=+quotes]
----
typedef typename allocator_type::pointer pointer;
----
`value_type*` if `allocator_type::pointer` is not defined.
---
[source,c++,subs=+quotes]
----
typedef typename allocator_type::const_pointer const_pointer;
----
`boost::pointer_to_other<pointer, value_type>::type` if `allocator_type::const_pointer` is not defined.
---
[source,c++,subs=+quotes]
----
typedef _implementation-defined_ size_type;
----
An unsigned integral type.
`size_type` can represent any non-negative value of `difference_type`.
---
[source,c++,subs=+quotes]
----
typedef _implementation-defined_ difference_type;
----
A signed integral type.
Is identical to the difference type of `iterator` and `const_iterator`.
---
[source,c++,subs=+quotes]
----
typedef _implementation-defined_ iterator;
@@ -453,10 +419,7 @@ The move constructor.
[horizontal]
Notes:;; This is implemented using Boost.Move.
Requires:;; `value_type` is move-constructible. +
+
On compilers without rvalue reference support the emulation does not support moving without calling `boost::move` if `value_type` is not copyable.
So, for example, you can't return the container from a function.
Requires:;; `value_type` is move-constructible.
---
@@ -656,7 +619,6 @@ The move assignment operator.
If `Alloc::propagate_on_container_move_assignment` exists and `Alloc::propagate_on_container_move_assignment::value` is `true`, the allocator is overwritten, if not the moved elements are created using the existing allocator.
[horizontal]
Notes:;; On compilers without rvalue references, this is emulated using Boost.Move. Note that on some compilers the copy assignment operator may be used in some circumstances.
Requires:;; `value_type` is move constructible.
---
@@ -767,11 +729,7 @@ Returns:;; An iterator pointing to the inserted element.
Throws:;; If an exception is thrown by an operation other than a call to `hasher` the function has no effect.
Notes:;; Can invalidate iterators, but only if the insert causes the load factor to be greater to or equal to the maximum load factor. +
+
Pointers and references to elements are never invalidated. +
+
If the compiler doesn't support variadic template arguments or rvalue references, this is emulated for up to `10` arguments, with no support for rvalue references or move semantics. +
+
Since existing `std::pair` implementations don't support `std::piecewise_construct` this emulates it, but using `boost::unordered::piecewise_construct`.
Pointers and references to elements are never invalidated.
---
@@ -792,11 +750,7 @@ Notes:;; The standard is fairly vague on the meaning of the hint. But the only p
+
Can invalidate iterators, but only if the insert causes the load factor to be greater to or equal to the maximum load factor. +
+
Pointers and references to elements are never invalidated. +
+
If the compiler doesn't support variadic template arguments or rvalue references, this is emulated for up to 10 arguments, with no support for rvalue references or move semantics. +
+
Since existing `std::pair` implementations don't support `std::piecewise_construct` this emulates it, but using `boost::unordered::piecewise_construct`.
Pointers and references to elements are never invalidated.
---
@@ -1419,9 +1373,7 @@ template<class Key, class Hash, class Pred, class Alloc>
Return `true` if `x.size() == y.size()` and for every element in `x`, there is an element in `y` with the same key, with an equal value (using `operator==` to compare the value types).
[horizontal]
Notes:;; The behavior of this function was changed to match the C++11 standard in Boost 1.48. +
+
Behavior is undefined if the two containers don't have equivalent equality predicates.
Notes:;; Behavior is undefined if the two containers don't have equivalent equality predicates.
---
@@ -1435,9 +1387,7 @@ template<class Key, class Hash, class Pred, class Alloc>
Return `false` if `x.size() == y.size()` and for every element in `x`, there is an element in `y` with the same key, with an equal value (using `operator==` to compare the value types).
[horizontal]
Notes:;; The behavior of this function was changed to match the C++11 standard in Boost 1.48. +
+
Behavior is undefined if the two containers don't have equivalent equality predicates.
Notes:;; Behavior is undefined if the two containers don't have equivalent equality predicates.
---
@@ -1485,4 +1435,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`.
+80 -26
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`.
@@ -961,13 +962,13 @@ if there is an element with an equivalent key; otherwise, the construction is of
```c++
// first two overloads
value_type(std::piecewise_construct,
std::forward_as_tuple(boost::forward<Key>(k)),
std::forward_as_tuple(boost::forward<Args>(args)...))
std::forward_as_tuple(std::forward<Key>(k)),
std::forward_as_tuple(std::forward<Args>(args)...))
// third overload
value_type(std::piecewise_construct,
std::forward_as_tuple(boost::forward<K>(k)),
std::forward_as_tuple(boost::forward<Args>(args)...))
std::forward_as_tuple(std::forward<K>(k)),
std::forward_as_tuple(std::forward<Args>(args)...))
```
unlike xref:#unordered_node_map_emplace[emplace], which simply forwards all arguments to ``value_type``'s constructor.
@@ -1006,13 +1007,13 @@ if there is an element with an equivalent key; otherwise, the construction is of
```c++
// first two overloads
value_type(std::piecewise_construct,
std::forward_as_tuple(boost::forward<Key>(k)),
std::forward_as_tuple(boost::forward<Args>(args)...))
std::forward_as_tuple(std::forward<Key>(k)),
std::forward_as_tuple(std::forward<Args>(args)...))
// third overload
value_type(std::piecewise_construct,
std::forward_as_tuple(boost::forward<K>(k)),
std::forward_as_tuple(boost::forward<Args>(args)...))
std::forward_as_tuple(std::forward<K>(k)),
std::forward_as_tuple(std::forward<Args>(args)...))
```
unlike xref:#unordered_node_map_emplace_hint[emplace_hint], which simply forwards all arguments to ``value_type``'s constructor.
@@ -1037,19 +1038,19 @@ template<class K, class M>
Inserts a new element into the container or updates an existing one by assigning to the contained value.
If there is an element with key `k`, then it is updated by assigning `boost::forward<M>(obj)`.
If there is an element with key `k`, then it is updated by assigning `std::forward<M>(obj)`.
If there is no such element, it is added to the container as:
```c++
// first two overloads
value_type(std::piecewise_construct,
std::forward_as_tuple(boost::forward<Key>(k)),
std::forward_as_tuple(boost::forward<M>(obj)))
std::forward_as_tuple(std::forward<Key>(k)),
std::forward_as_tuple(std::forward<M>(obj)))
// third overload
value_type(std::piecewise_construct,
std::forward_as_tuple(boost::forward<K>(k)),
std::forward_as_tuple(boost::forward<M>(obj)))
std::forward_as_tuple(std::forward<K>(k)),
std::forward_as_tuple(std::forward<M>(obj)))
```
[horizontal]
@@ -1075,19 +1076,19 @@ template<class K, class M>
Inserts a new element into the container or updates an existing one by assigning to the contained value.
If there is an element with key `k`, then it is updated by assigning `boost::forward<M>(obj)`.
If there is an element with key `k`, then it is updated by assigning `std::forward<M>(obj)`.
If there is no such element, it is added to the container as:
```c++
// first two overloads
value_type(std::piecewise_construct,
std::forward_as_tuple(boost::forward<Key>(k)),
std::forward_as_tuple(boost::forward<M>(obj)))
std::forward_as_tuple(std::forward<Key>(k)),
std::forward_as_tuple(std::forward<M>(obj)))
// third overload
value_type(std::piecewise_construct,
std::forward_as_tuple(boost::forward<K>(k)),
std::forward_as_tuple(boost::forward<M>(obj)))
std::forward_as_tuple(std::forward<K>(k)),
std::forward_as_tuple(std::forward<M>(obj)))
```
`hint` is a suggestion to where the element should be inserted. This implementation ignores it.
@@ -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`.
+86 -69
View File
@@ -24,12 +24,12 @@ namespace boost {
using hasher = Hash;
using key_equal = Pred;
using allocator_type = Allocator;
using pointer = typename boost::allocator_traits<Allocator>::pointer;
using const_pointer = typename boost::allocator_traits<Allocator>::const_pointer;
using pointer = typename std::allocator_traits<Allocator>::pointer;
using const_pointer = typename std::allocator_traits<Allocator>::const_pointer;
using reference = value_type&;
using const_reference = const value_type&;
using size_type = _implementation-defined_;
using difference_type = _implementation-defined_;
using size_type = std::size_t;
using difference_type = std::ptrdiff_t;
using iterator = _implementation-defined_;
using const_iterator = _implementation-defined_;
@@ -272,6 +272,7 @@ namespace boost {
|_Allocator_
|An allocator whose value type is the same as the container's value type.
Allocators using https://en.cppreference.com/w/cpp/named_req/Allocator#Fancy_pointers[fancy pointers] are supported.
|===
@@ -279,51 +280,15 @@ 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
[source,c++,subs=+quotes]
----
typedef typename allocator_type::pointer pointer;
----
`value_type*` if `allocator_type::pointer` is not defined.
---
[source,c++,subs=+quotes]
----
typedef typename allocator_type::const_pointer const_pointer;
----
`boost::pointer_to_other<pointer, value_type>::type` if `allocator_type::const_pointer` is not defined.
---
[source,c++,subs=+quotes]
----
typedef _implementation-defined_ size_type;
----
An unsigned integral type.
`size_type` can represent any non-negative value of `difference_type`.
---
[source,c++,subs=+quotes]
----
typedef _implementation-defined_ difference_type;
----
A signed integral type.
Is identical to the difference type of `iterator` and `const_iterator`.
---
[source,c++,subs=+quotes]
----
typedef _implementation-defined_ iterator;
@@ -375,7 +340,8 @@ A const_local_iterator object can be used to iterate through a single bucket.
typedef _implementation-defined_ node_type;
----
See node_handle_set for details.
A class for holding extracted container elements, modelling
https://en.cppreference.com/w/cpp/container/node_handle[NodeHandle].
---
@@ -384,7 +350,20 @@ See node_handle_set for details.
typedef _implementation-defined_ insert_return_type;
----
Structure returned by inserting node_type.
A specialization of an internal class template:
[source,c++,subs=+quotes]
----
template<class Iterator, class NodeType>
struct _insert_return_type_ // name is exposition only
{
Iterator position;
bool inserted;
NodeType node;
};
----
with `Iterator` = `iterator` and `NodeType` = `node_type`.
---
@@ -464,10 +443,7 @@ The move constructor.
[horizontal]
Notes:;; This is implemented using Boost.Move.
Requires:;; `value_type` is move-constructible. +
+
On compilers without rvalue reference support the emulation does not support moving without calling `boost::move` if `value_type` is not copyable.
So, for example, you can't return the container from a function.
Requires:;; `value_type` is move-constructible.
---
@@ -667,7 +643,6 @@ The move assignment operator.
If `Alloc::propagate_on_container_move_assignment` exists and `Alloc::propagate_on_container_move_assignment::value` is `true`, the allocator is overwritten, if not the moved elements are created using the existing allocator.
[horizontal]
Notes:;; On compilers without rvalue references, this is emulated using Boost.Move. Note that on some compilers the copy assignment operator may be used in some circumstances.
Requires:;; `value_type` is move constructible.
---
@@ -780,11 +755,7 @@ If an insert took place, then the iterator points to the newly inserted element.
Throws:;; If an exception is thrown by an operation other than a call to `hasher` the function has no effect.
Notes:;; Can invalidate iterators, but only if the insert causes the load factor to be greater to or equal to the maximum load factor. +
+
Pointers and references to elements are never invalidated. +
+
If the compiler doesn't support variadic template arguments or rvalue references, this is emulated for up to `10` arguments, with no support for rvalue references or move semantics. +
+
Since existing `std::pair` implementations don't support `std::piecewise_construct` this emulates it, but using `boost::unordered::piecewise_construct`.
Pointers and references to elements are never invalidated.
---
@@ -805,11 +776,7 @@ Notes:;; The standard is fairly vague on the meaning of the hint. But the only p
+
Can invalidate iterators, but only if the insert causes the load factor to be greater to or equal to the maximum load factor. +
+
Pointers and references to elements are never invalidated. +
+
If the compiler doesn't support variadic template arguments or rvalue references, this is emulated for up to 10 arguments, with no support for rvalue references or move semantics. +
+
Since existing `std::pair` implementations don't support `std::piecewise_construct` this emulates it, but using `boost::unordered::piecewise_construct`.
Pointers and references to elements are never invalidated.
---
@@ -1484,9 +1451,7 @@ template<class Key, class Hash, class Pred, class Alloc>
Return `true` if `x.size() == y.size()` and for every element in `x`, there is an element in `y` with the same key, with an equal value (using `operator==` to compare the value types).
[horizontal]
Notes:;; The behavior of this function was changed to match the C++11 standard in Boost 1.48. +
+
Behavior is undefined if the two containers don't have equivalent equality predicates.
Notes:;; Behavior is undefined if the two containers don't have equivalent equality predicates.
---
@@ -1500,9 +1465,7 @@ template<class Key, class Hash, class Pred, class Alloc>
Return `false` if `x.size() == y.size()` and for every element in `x`, there is an element in `y` with the same key, with an equal value (using `operator==` to compare the value types).
[horizontal]
Notes:;; The behavior of this function was changed to match the C++11 standard in Boost 1.48. +
+
Behavior is undefined if the two containers don't have equivalent equality predicates.
Notes:;; Behavior is undefined if the two containers don't have equivalent equality predicates.
---
@@ -1550,4 +1513,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`.
+1 -1
View File
@@ -14,7 +14,7 @@
#define BOOST_HASH_EXAMPLES_CASE_INSENSITIVE_HEADER
#include <boost/algorithm/string/predicate.hpp>
#include <boost/functional/hash.hpp>
#include <boost/container_hash/hash.hpp>
namespace hash_examples
{
+145 -101
View File
@@ -1,6 +1,7 @@
/* Fast open-addressing concurrent hash table.
/* Fast open-addressing concurrent hashmap.
*
* 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)
@@ -12,71 +13,20 @@
#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/type_traits/type_identity.hpp>
#include <boost/core/serialization.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;
@@ -104,14 +65,15 @@ namespace boost {
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 hasher = typename boost::unordered::detail::type_identity<Hash>::type;
using key_equal = typename boost::unordered::detail::type_identity<Pred>::type;
using allocator_type = typename boost::unordered::detail::type_identity<Allocator>::type;
using reference = value_type&;
using const_reference = value_type const&;
using pointer = typename boost::allocator_pointer<allocator_type>::type;
using const_pointer =
typename boost::allocator_const_pointer<allocator_type>::type;
static constexpr size_type bulk_visit_size = table_type::bulk_visit_size;
concurrent_flat_map()
: concurrent_flat_map(detail::foa::default_bucket_count)
@@ -223,6 +185,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 +200,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;
@@ -305,6 +272,33 @@ namespace boost {
return table_.visit(std::forward<K>(k), f);
}
template<class FwdIterator, class F>
BOOST_FORCEINLINE
size_t visit(FwdIterator first, FwdIterator last, F f)
{
BOOST_UNORDERED_STATIC_ASSERT_BULK_VISIT_ITERATOR(FwdIterator)
BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(F)
return table_.visit(first, last, f);
}
template<class FwdIterator, class F>
BOOST_FORCEINLINE
size_t visit(FwdIterator first, FwdIterator last, F f) const
{
BOOST_UNORDERED_STATIC_ASSERT_BULK_VISIT_ITERATOR(FwdIterator)
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
return table_.visit(first, last, f);
}
template<class FwdIterator, class F>
BOOST_FORCEINLINE
size_t cvisit(FwdIterator first, FwdIterator last, F f) const
{
BOOST_UNORDERED_STATIC_ASSERT_BULK_VISIT_ITERATOR(FwdIterator)
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
return table_.visit(first, last, f);
}
template <class F> size_type visit_all(F f)
{
BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(F)
@@ -355,6 +349,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 +455,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 +510,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 +756,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,
@@ -720,10 +772,10 @@ namespace boost {
std::equal_to<boost::unordered::detail::iter_key_t<InputIterator> >,
class Allocator = std::allocator<
boost::unordered::detail::iter_to_alloc_t<InputIterator> >,
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> > >
class = std::enable_if_t<detail::is_input_iterator_v<InputIterator> >,
class = std::enable_if_t<detail::is_hash_v<Hash> >,
class = std::enable_if_t<detail::is_pred_v<Pred> >,
class = std::enable_if_t<detail::is_allocator_v<Allocator> > >
concurrent_flat_map(InputIterator, InputIterator,
std::size_t = boost::unordered::detail::foa::default_bucket_count,
Hash = Hash(), Pred = Pred(), Allocator = Allocator())
@@ -733,21 +785,21 @@ namespace boost {
Allocator>;
template <class Key, class T,
class Hash = boost::hash<boost::remove_const_t<Key> >,
class Pred = std::equal_to<boost::remove_const_t<Key> >,
class Hash = boost::hash<std::remove_const_t<Key> >,
class Pred = std::equal_to<std::remove_const_t<Key> >,
class Allocator = std::allocator<std::pair<const Key, T> >,
class = 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> > >
class = std::enable_if_t<detail::is_hash_v<Hash> >,
class = std::enable_if_t<detail::is_pred_v<Pred> >,
class = std::enable_if_t<detail::is_allocator_v<Allocator> > >
concurrent_flat_map(std::initializer_list<std::pair<Key, T> >,
std::size_t = boost::unordered::detail::foa::default_bucket_count,
Hash = Hash(), Pred = Pred(), Allocator = Allocator())
-> concurrent_flat_map<boost::remove_const_t<Key>, T, Hash, Pred,
-> concurrent_flat_map<std::remove_const_t<Key>, 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> > >
class = std::enable_if_t<detail::is_input_iterator_v<InputIterator> >,
class = std::enable_if_t<detail::is_allocator_v<Allocator> > >
concurrent_flat_map(InputIterator, InputIterator, std::size_t, Allocator)
-> concurrent_flat_map<
boost::unordered::detail::iter_key_t<InputIterator>,
@@ -757,8 +809,8 @@ namespace boost {
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> > >
class = std::enable_if_t<detail::is_input_iterator_v<InputIterator> >,
class = std::enable_if_t<detail::is_allocator_v<Allocator> > >
concurrent_flat_map(InputIterator, InputIterator, Allocator)
-> concurrent_flat_map<
boost::unordered::detail::iter_key_t<InputIterator>,
@@ -768,9 +820,9 @@ namespace boost {
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> > >
class = std::enable_if_t<detail::is_hash_v<Hash> >,
class = std::enable_if_t<detail::is_input_iterator_v<InputIterator> >,
class = std::enable_if_t<detail::is_allocator_v<Allocator> > >
concurrent_flat_map(
InputIterator, InputIterator, std::size_t, Hash, Allocator)
-> concurrent_flat_map<
@@ -780,25 +832,25 @@ namespace boost {
Allocator>;
template <class Key, class T, class Allocator,
class = boost::enable_if_t<detail::is_allocator_v<Allocator> > >
class = std::enable_if_t<detail::is_allocator_v<Allocator> > >
concurrent_flat_map(std::initializer_list<std::pair<Key, T> >, std::size_t,
Allocator) -> concurrent_flat_map<boost::remove_const_t<Key>, T,
boost::hash<boost::remove_const_t<Key> >,
std::equal_to<boost::remove_const_t<Key> >, Allocator>;
Allocator) -> concurrent_flat_map<std::remove_const_t<Key>, T,
boost::hash<std::remove_const_t<Key> >,
std::equal_to<std::remove_const_t<Key> >, Allocator>;
template <class Key, class T, class Allocator,
class = boost::enable_if_t<detail::is_allocator_v<Allocator> > >
class = std::enable_if_t<detail::is_allocator_v<Allocator> > >
concurrent_flat_map(std::initializer_list<std::pair<Key, T> >, Allocator)
-> concurrent_flat_map<boost::remove_const_t<Key>, T,
boost::hash<boost::remove_const_t<Key> >,
std::equal_to<boost::remove_const_t<Key> >, Allocator>;
-> concurrent_flat_map<std::remove_const_t<Key>, T,
boost::hash<std::remove_const_t<Key> >,
std::equal_to<std::remove_const_t<Key> >, Allocator>;
template <class Key, class T, class Hash, class Allocator,
class = boost::enable_if_t<detail::is_hash_v<Hash> >,
class = boost::enable_if_t<detail::is_allocator_v<Allocator> > >
class = std::enable_if_t<detail::is_hash_v<Hash> >,
class = std::enable_if_t<detail::is_allocator_v<Allocator> > >
concurrent_flat_map(std::initializer_list<std::pair<Key, T> >, std::size_t,
Hash, Allocator) -> concurrent_flat_map<boost::remove_const_t<Key>, T,
Hash, std::equal_to<boost::remove_const_t<Key> >, Allocator>;
Hash, Allocator) -> concurrent_flat_map<std::remove_const_t<Key>, T,
Hash, std::equal_to<std::remove_const_t<Key> >, Allocator>;
#endif
@@ -807,12 +859,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,718 @@
/* 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 <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>;
using table_type =
detail::foa::concurrent_table<type_policy, Hash, Pred, Allocator>;
table_type table_;
template <class K, class H, class KE, class A>
bool friend operator==(concurrent_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::unordered::detail::type_identity<Hash>::type;
using key_equal = typename boost::unordered::detail::type_identity<Pred>::type;
using allocator_type = typename boost::unordered::detail::type_identity<Allocator>::type;
using reference = value_type&;
using const_reference = value_type const&;
using pointer = typename boost::allocator_pointer<allocator_type>::type;
using const_pointer =
typename boost::allocator_const_pointer<allocator_type>::type;
static constexpr size_type bulk_visit_size = table_type::bulk_visit_size;
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 FwdIterator, class F>
BOOST_FORCEINLINE
size_t visit(FwdIterator first, FwdIterator last, F f) const
{
BOOST_UNORDERED_STATIC_ASSERT_BULK_VISIT_ITERATOR(FwdIterator)
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
return table_.visit(first, last, f);
}
template<class FwdIterator, class F>
BOOST_FORCEINLINE
size_t cvisit(FwdIterator first, FwdIterator last, F f) const
{
BOOST_UNORDERED_STATIC_ASSERT_BULK_VISIT_ITERATOR(FwdIterator)
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
return table_.visit(first, last, f);
}
template <class F> size_type visit_all(F f) 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 = std::enable_if_t<detail::is_input_iterator_v<InputIterator> >,
class = std::enable_if_t<detail::is_hash_v<Hash> >,
class = std::enable_if_t<detail::is_pred_v<Pred> >,
class = std::enable_if_t<detail::is_allocator_v<Allocator> > >
concurrent_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 = std::enable_if_t<detail::is_hash_v<Hash> >,
class = std::enable_if_t<detail::is_pred_v<Pred> >,
class = std::enable_if_t<detail::is_allocator_v<Allocator> > >
concurrent_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 = std::enable_if_t<detail::is_input_iterator_v<InputIterator> >,
class = std::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 = std::enable_if_t<detail::is_input_iterator_v<InputIterator> >,
class = std::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 = std::enable_if_t<detail::is_hash_v<Hash> >,
class = std::enable_if_t<detail::is_input_iterator_v<InputIterator> >,
class = std::enable_if_t<detail::is_allocator_v<Allocator> > >
concurrent_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 = std::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 = std::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 = std::enable_if_t<detail::is_hash_v<Hash> >,
class = std::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,71 @@
/* 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/unordered/detail/opt_storage.hpp>
#include <boost/config.hpp>
#include <boost/core/no_exceptions_support.hpp>
#include <boost/core/noncopyable.hpp>
#include <boost/core/serialization.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,std::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 *space.address();}
#if defined(BOOST_UNORDERED_IGNORE_WSTRICT_ALIASING)
#pragma GCC diagnostic pop
#undef BOOST_UNORDERED_IGNORE_WSTRICT_ALIASING
#endif
private:
opt_storage<T> 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,105 @@
/* 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/config.hpp>
#include <boost/mp11/algorithm.hpp>
#include <boost/mp11/list.hpp>
#include <functional>
#include <iterator>
#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
#if defined(BOOST_NO_CXX20_HDR_CONCEPTS)
#define BOOST_UNORDERED_STATIC_ASSERT_FWD_ITERATOR(Iterator) \
static_assert( \
std::is_base_of< \
std::forward_iterator_tag, \
typename std::iterator_traits<Iterator>::iterator_category>::value, \
"The provided iterator must be at least forward");
#else
#define BOOST_UNORDERED_STATIC_ASSERT_FWD_ITERATOR(Iterator) \
static_assert(std::forward_iterator<Iterator>, \
"The provided iterator must be at least forward");
#endif
#define BOOST_UNORDERED_STATIC_ASSERT_KEY_COMPATIBLE_ITERATOR(Iterator) \
static_assert( \
std::is_same< \
typename std::iterator_traits<Iterator>::value_type, \
key_type>::value || \
detail::are_transparent< \
typename std::iterator_traits<Iterator>::value_type, \
hasher, key_equal>::value, \
"The provided iterator must dereference to a compatible key value");
#define BOOST_UNORDERED_STATIC_ASSERT_BULK_VISIT_ITERATOR(Iterator) \
BOOST_UNORDERED_STATIC_ASSERT_FWD_ITERATOR(Iterator) \
BOOST_UNORDERED_STATIC_ASSERT_KEY_COMPATIBLE_ITERATOR(Iterator)
#endif // BOOST_UNORDERED_DETAIL_CONCURRENT_STATIC_ASSERTS_HPP
+110 -87
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,18 +114,17 @@ to normal separate chaining implementations.
*/
#include <boost/unordered/detail/prime_fmod.hpp>
#include <boost/unordered/detail/serialize_tracked_address.hpp>
#include <boost/unordered/detail/opt_storage.hpp>
#include <boost/core/addressof.hpp>
#include <boost/assert.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>
#include <boost/config.hpp>
@@ -142,19 +141,18 @@ namespace boost {
node>::type node_pointer;
node_pointer next;
typename boost::aligned_storage<sizeof(value_type),
boost::alignment_of<value_type>::value>::type buf;
opt_storage<value_type> buf;
node() BOOST_NOEXCEPT : next(), buf() {}
node() noexcept : next(), buf() {}
value_type* value_ptr() BOOST_NOEXCEPT
value_type* value_ptr() noexcept
{
return reinterpret_cast<value_type*>(buf.address());
return buf.address();
}
value_type& value() BOOST_NOEXCEPT
value_type& value() noexcept
{
return *reinterpret_cast<value_type*>(buf.address());
return *buf.address();
}
};
@@ -168,7 +166,7 @@ namespace boost {
node_pointer next;
bucket() BOOST_NOEXCEPT : next() {}
bucket() noexcept : next() {}
};
template <class Bucket> struct bucket_group
@@ -184,7 +182,7 @@ namespace boost {
std::size_t bitmask;
bucket_group_pointer next, prev;
bucket_group() BOOST_NOEXCEPT : buckets(), bitmask(0), next(), prev() {}
bucket_group() noexcept : buckets(), bitmask(0), next(), prev() {}
~bucket_group() {}
};
@@ -222,33 +220,28 @@ namespace boost {
public:
grouped_bucket_iterator() : p(), pbg() {}
reference operator*() const BOOST_NOEXCEPT { return dereference(); }
pointer operator->() const BOOST_NOEXCEPT
{
return boost::to_address(p);
}
reference operator*() const noexcept { return dereference(); }
pointer operator->() const noexcept { return boost::to_address(p); }
grouped_bucket_iterator& operator++() BOOST_NOEXCEPT
grouped_bucket_iterator& operator++() noexcept
{
increment();
return *this;
}
grouped_bucket_iterator operator++(int) BOOST_NOEXCEPT
grouped_bucket_iterator operator++(int) noexcept
{
grouped_bucket_iterator old = *this;
increment();
return old;
}
bool operator==(
grouped_bucket_iterator const& other) const BOOST_NOEXCEPT
bool operator==(grouped_bucket_iterator const& other) const noexcept
{
return equal(other);
}
bool operator!=(
grouped_bucket_iterator const& other) const BOOST_NOEXCEPT
bool operator!=(grouped_bucket_iterator const& other) const noexcept
{
return !equal(other);
}
@@ -264,14 +257,14 @@ namespace boost {
{
}
Bucket& dereference() const BOOST_NOEXCEPT { return *p; }
Bucket& dereference() const noexcept { return *p; }
bool equal(const grouped_bucket_iterator& x) const BOOST_NOEXCEPT
bool equal(const grouped_bucket_iterator& x) const noexcept
{
return p == x.p;
}
void increment() BOOST_NOEXCEPT
void increment() noexcept
{
std::size_t const offset = static_cast<std::size_t>(p - pbg->buckets);
@@ -287,6 +280,24 @@ 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;
@@ -305,20 +316,17 @@ namespace boost {
grouped_local_bucket_iterator() : p() {}
reference operator*() const BOOST_NOEXCEPT { return dereference(); }
reference operator*() const noexcept { return dereference(); }
pointer operator->() const BOOST_NOEXCEPT
{
return boost::to_address(p);
}
pointer operator->() const noexcept { return boost::to_address(p); }
grouped_local_bucket_iterator& operator++() BOOST_NOEXCEPT
grouped_local_bucket_iterator& operator++() noexcept
{
increment();
return *this;
}
grouped_local_bucket_iterator operator++(int) BOOST_NOEXCEPT
grouped_local_bucket_iterator operator++(int) noexcept
{
grouped_local_bucket_iterator old = *this;
increment();
@@ -326,13 +334,13 @@ namespace boost {
}
bool operator==(
grouped_local_bucket_iterator const& other) const BOOST_NOEXCEPT
grouped_local_bucket_iterator const& other) const noexcept
{
return equal(other);
}
bool operator!=(
grouped_local_bucket_iterator const& other) const BOOST_NOEXCEPT
grouped_local_bucket_iterator const& other) const noexcept
{
return !equal(other);
}
@@ -345,14 +353,14 @@ namespace boost {
grouped_local_bucket_iterator(node_pointer p_) : p(p_) {}
value_type& dereference() const BOOST_NOEXCEPT { return p->value(); }
value_type& dereference() const noexcept { return p->value(); }
bool equal(const grouped_local_bucket_iterator& x) const BOOST_NOEXCEPT
bool equal(const grouped_local_bucket_iterator& x) const noexcept
{
return p == x.p;
}
void increment() BOOST_NOEXCEPT { p = p->next; }
void increment() noexcept { p = p->next; }
node_pointer p;
};
@@ -376,20 +384,17 @@ namespace boost {
{
}
reference operator*() const BOOST_NOEXCEPT { return dereference(); }
reference operator*() const noexcept { return dereference(); }
pointer operator->() const BOOST_NOEXCEPT
{
return boost::to_address(p);
}
pointer operator->() const noexcept { return boost::to_address(p); }
const_grouped_local_bucket_iterator& operator++() BOOST_NOEXCEPT
const_grouped_local_bucket_iterator& operator++() noexcept
{
increment();
return *this;
}
const_grouped_local_bucket_iterator operator++(int) BOOST_NOEXCEPT
const_grouped_local_bucket_iterator operator++(int) noexcept
{
const_grouped_local_bucket_iterator old = *this;
increment();
@@ -397,13 +402,13 @@ namespace boost {
}
bool operator==(
const_grouped_local_bucket_iterator const& other) const BOOST_NOEXCEPT
const_grouped_local_bucket_iterator const& other) const noexcept
{
return equal(other);
}
bool operator!=(
const_grouped_local_bucket_iterator const& other) const BOOST_NOEXCEPT
const_grouped_local_bucket_iterator const& other) const noexcept
{
return !equal(other);
}
@@ -414,23 +419,22 @@ namespace boost {
const_grouped_local_bucket_iterator(node_pointer p_) : p(p_) {}
value_type& dereference() const BOOST_NOEXCEPT { return p->value(); }
value_type& dereference() const noexcept { return p->value(); }
bool equal(
const const_grouped_local_bucket_iterator& x) const BOOST_NOEXCEPT
bool equal(const const_grouped_local_bucket_iterator& x) const noexcept
{
return p == x.p;
}
void increment() BOOST_NOEXCEPT { p = p->next; }
void increment() noexcept { p = p->next; }
node_pointer p;
};
template <class T> struct span
{
T* begin() const BOOST_NOEXCEPT { return data; }
T* end() const BOOST_NOEXCEPT { return data + size; }
T* begin() const noexcept { return data; }
T* end() const noexcept { return data + size; }
T* data;
std::size_t size;
@@ -445,8 +449,6 @@ namespace boost {
typename boost::allocator_void_pointer<Allocator>::type> >::
type>
{
BOOST_MOVABLE_BUT_NOT_COPYABLE(grouped_bucket_array)
typedef typename boost::allocator_value_type<Allocator>::type
allocator_value_type;
typedef
@@ -511,8 +513,7 @@ namespace boost {
grouped_bucket_array(size_type n, const Allocator& al)
: empty_value<node_allocator_type>(empty_init_t(), al),
size_index_(0),
size_(0), buckets(), groups()
size_index_(0), size_(0), buckets(), groups()
{
if (n == 0) {
return;
@@ -561,8 +562,10 @@ namespace boost {
~grouped_bucket_array() { this->deallocate(); }
grouped_bucket_array(
BOOST_RV_REF(grouped_bucket_array) other) BOOST_NOEXCEPT
grouped_bucket_array(grouped_bucket_array const&) = delete;
grouped_bucket_array& operator=(grouped_bucket_array const&) = delete;
grouped_bucket_array(grouped_bucket_array&& other) noexcept
: empty_value<node_allocator_type>(
empty_init_t(), other.get_node_allocator()),
size_index_(other.size_index_),
@@ -576,13 +579,12 @@ namespace boost {
other.groups = group_pointer();
}
grouped_bucket_array& operator=(
BOOST_RV_REF(grouped_bucket_array) other) BOOST_NOEXCEPT
grouped_bucket_array& operator=(grouped_bucket_array&& other) noexcept
{
BOOST_ASSERT(
this->get_node_allocator() == other.get_node_allocator());
if (this == boost::addressof(other)) {
if (this == std::addressof(other)) {
return *this;
}
@@ -601,25 +603,48 @@ namespace boost {
return *this;
}
void deallocate() BOOST_NOEXCEPT
#if defined(BOOST_MSVC)
#pragma warning(push)
#pragma warning(disable : 4100) // unreferenced formal parameter (dtor calls)
#endif
void deallocate() noexcept
{
if (buckets) {
size_type const num_buckets = buckets_len();
bucket_type* pb = boost::to_address(buckets);
(void)pb; // VS complains when dtor is trivial
for (size_type i = 0; i < num_buckets; ++i) {
(pb + i)->~bucket_type();
}
bucket_allocator_type bucket_alloc = this->get_bucket_allocator();
boost::allocator_deallocate(
bucket_alloc, buckets, this->buckets_len());
boost::allocator_deallocate(bucket_alloc, buckets, num_buckets);
buckets = bucket_pointer();
}
if (groups) {
size_type const num_groups = groups_len();
group* pg = boost::to_address(groups);
(void)pg; // VS complains when dtor is trivial
for (size_type i = 0; i < num_groups; ++i) {
(pg + i)->~group();
}
group_allocator_type group_alloc = this->get_group_allocator();
boost::allocator_deallocate(
group_alloc, groups, this->groups_len());
boost::allocator_deallocate(group_alloc, groups, num_groups);
groups = group_pointer();
}
}
#if defined(BOOST_MSVC)
#pragma warning(pop)
#endif
void swap(grouped_bucket_array& other)
{
std::swap(size_index_, other.size_index_);
@@ -630,7 +655,8 @@ 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());
}
}
@@ -654,12 +680,9 @@ namespace boost {
return this->get_node_allocator();
}
size_type buckets_len() const BOOST_NOEXCEPT { return size_ + 1; }
size_type buckets_len() const noexcept { return size_ + 1; }
size_type groups_len() const BOOST_NOEXCEPT
{
return size_ / group::N + 1;
}
size_type groups_len() const noexcept { return size_ / group::N + 1; }
void reset_allocator(Allocator const& allocator_)
{
@@ -692,7 +715,7 @@ namespace boost {
local_iterator end(size_type) const { return local_iterator(); }
size_type capacity() const BOOST_NOEXCEPT { return size_; }
size_type capacity() const noexcept { return size_; }
iterator at(size_type n) const
{
@@ -726,7 +749,7 @@ namespace boost {
size_ = 0;
}
void append_bucket_group(iterator itb) BOOST_NOEXCEPT
void append_bucket_group(iterator itb) noexcept
{
std::size_t const N = group::N;
@@ -756,7 +779,7 @@ namespace boost {
}
}
void insert_node(iterator itb, node_pointer p) BOOST_NOEXCEPT
void insert_node(iterator itb, node_pointer p) noexcept
{
this->append_bucket_group(itb);
@@ -765,7 +788,7 @@ namespace boost {
}
void insert_node_hint(
iterator itb, node_pointer p, node_pointer hint) BOOST_NOEXCEPT
iterator itb, node_pointer p, node_pointer hint) noexcept
{
this->append_bucket_group(itb);
@@ -778,24 +801,24 @@ namespace boost {
}
}
void extract_node(iterator itb, node_pointer p) BOOST_NOEXCEPT
void extract_node(iterator itb, node_pointer p) noexcept
{
node_pointer* pp = boost::addressof(itb->next);
node_pointer* pp = std::addressof(itb->next);
while ((*pp) != p)
pp = boost::addressof((*pp)->next);
pp = std::addressof((*pp)->next);
*pp = p->next;
if (!itb->next)
unlink_bucket(itb);
}
void extract_node_after(iterator itb, node_pointer* pp) BOOST_NOEXCEPT
void extract_node_after(iterator itb, node_pointer* pp) noexcept
{
*pp = (*pp)->next;
if (!itb->next)
unlink_bucket(itb);
}
void unlink_empty_buckets() BOOST_NOEXCEPT
void unlink_empty_buckets() noexcept
{
std::size_t const N = group::N;
@@ -843,7 +866,7 @@ namespace boost {
}
};
} // namespace detail
} // namespace unordered
} // namespace unordered
} // namespace boost
#endif // BOOST_UNORDERED_DETAIL_FCA_HPP
@@ -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 <boost/unordered/detail/static_assert.hpp>
#include <cstddef>
#include <functional>
#include <initializer_list>
#include <iterator>
#include <memory>
#include <new>
#include <type_traits>
@@ -82,7 +89,7 @@ private:
static constexpr std::size_t element_offset=
(sizeof(T)+cacheline_size-1)/cacheline_size*cacheline_size;
BOOST_STATIC_ASSERT(alignof(T)<=cacheline_size);
BOOST_UNORDERED_STATIC_ASSERT(alignof(T)<=cacheline_size);
T* data(std::size_t pos)noexcept
{
@@ -120,7 +127,7 @@ template<typename Mutex>
class shared_lock
{
public:
shared_lock(Mutex& m_)noexcept:m{m_}{m.lock_shared();}
shared_lock(Mutex& m_)noexcept:m(m_){m.lock_shared();}
~shared_lock()noexcept{if(owns)m.unlock_shared();}
/* not used but VS in pre-C++17 mode needs to see it for RVO */
@@ -142,7 +149,7 @@ template<typename Mutex>
class lock_guard
{
public:
lock_guard(Mutex& m_)noexcept:m{m_}{m.lock();}
lock_guard(Mutex& m_)noexcept:m(m_){m.lock();}
~lock_guard()noexcept{m.unlock();}
/* not used but VS in pre-C++17 mode needs to see it for RVO */
@@ -241,73 +248,105 @@ 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
{
static constexpr auto atomic_size_t_size=sizeof(std::atomic<std::size_t>);
BOOST_STATIC_ASSERT(atomic_size_t_size<cacheline_size);
BOOST_UNORDERED_STATIC_ASSERT(atomic_size_t_size<cacheline_size);
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;
@@ -426,6 +466,7 @@ public:
using key_equal=typename super::key_equal;
using allocator_type=typename super::allocator_type;
using size_type=typename super::size_type;
static constexpr std::size_t bulk_visit_size=16;
private:
template<typename Value,typename T>
@@ -450,6 +491,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 +524,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 +566,27 @@ public:
return visit(x,std::forward<F>(f));
}
template<typename FwdIterator,typename F>
BOOST_FORCEINLINE
std::size_t visit(FwdIterator first,FwdIterator last,F&& f)
{
return bulk_visit_impl(group_exclusive{},first,last,std::forward<F>(f));
}
template<typename FwdIterator,typename F>
BOOST_FORCEINLINE
std::size_t visit(FwdIterator first,FwdIterator last,F&& f)const
{
return bulk_visit_impl(group_shared{},first,last,std::forward<F>(f));
}
template<typename FwdIterator,typename F>
BOOST_FORCEINLINE
std::size_t cvisit(FwdIterator first,FwdIterator last,F&& f)const
{
return visit(first,last,std::forward<F>(f));
}
template<typename F> std::size_t visit_all(F&& f)
{
return visit_all_impl(group_exclusive{},std::forward<F>(f));
@@ -539,6 +626,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 +962,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 +989,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 +1008,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 +1018,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
@@ -922,7 +1052,7 @@ private:
erase_on_exit(
concurrent_table& x_,
group_type* pg_,unsigned int pos_,element_type* p_):
x{x_},pg{pg_},pos{pos_},p{p_}{}
x(x_),pg(pg_),pos(pos_),p(p_){}
~erase_on_exit(){if(!rollback_)x.super::erase(pg,pos,p);}
void rollback(){rollback_=true;}
@@ -944,6 +1074,26 @@ private:
access_mode,x,this->position_for(hash),hash,std::forward<F>(f));
}
template<typename GroupAccessMode,typename FwdIterator,typename F>
BOOST_FORCEINLINE
std::size_t bulk_visit_impl(
GroupAccessMode access_mode,FwdIterator first,FwdIterator last,F&& f)const
{
auto lck=shared_access();
std::size_t res=0;
auto n=static_cast<std::size_t>(std::distance(first,last));
while(n){
auto m=n<2*bulk_visit_size?n:bulk_visit_size;
res+=unprotected_bulk_visit(access_mode,first,m,std::forward<F>(f));
n-=m;
std::advance(
first,
static_cast<
typename std::iterator_traits<FwdIterator>::difference_type>(m));
}
return res;
}
template<typename GroupAccessMode,typename F>
std::size_t visit_all_impl(GroupAccessMode access_mode,F&& f)const
{
@@ -970,6 +1120,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 +1168,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 +1192,77 @@ private:
return 0;
}
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(m<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);
BOOST_UNORDERED_PREFETCH(this->arrays.elements()+pos*N+unchecked_countr_zero(mask));
}
}
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:
do{
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]);
}while(!mask);
p=this->arrays.elements()+pos*N;
BOOST_UNORDERED_PREFETCH_ELEMENTS(p,N);
}
next_key:;
}
return res;
}
#if defined(BOOST_MSVC)
#pragma warning(pop) /* C4800 */
#endif
@@ -1153,7 +1397,7 @@ private:
struct reserve_size
{
reserve_size(concurrent_table& x_):x{x_}
reserve_size(concurrent_table& x_):x(x_)
{
size_=++x.size_ctrl.size;
}
@@ -1211,7 +1455,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 +1465,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 +1498,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 +1547,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 +1564,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;
};
+288 -181
View File
@@ -22,13 +22,10 @@
#include <boost/core/pointer_traits.hpp>
#include <boost/cstdint.hpp>
#include <boost/predef.h>
#include <boost/static_assert.hpp>
#include <boost/type_traits/has_trivial_constructor.hpp>
#include <boost/type_traits/has_trivial_copy.hpp>
#include <boost/type_traits/has_trivial_assign.hpp>
#include <boost/type_traits/is_nothrow_swappable.hpp>
#include <boost/unordered/detail/narrow_cast.hpp>
#include <boost/unordered/detail/mulx.hpp>
#include <boost/unordered/detail/static_assert.hpp>
#include <boost/unordered/detail/type_traits.hpp>
#include <boost/unordered/hash_traits.hpp>
#include <climits>
#include <cmath>
@@ -95,7 +92,7 @@
#elif defined(BOOST_UNORDERED_SSE2)
#define BOOST_UNORDERED_PREFETCH(p) _mm_prefetch((const char*)(p),_MM_HINT_T0)
#else
#define BOOST_UNORDERED_PREFETCH(p) ((void)0)
#define BOOST_UNORDERED_PREFETCH(p) ((void)(p))
#endif
/* We have experimentally confirmed that ARM architectures get a higher
@@ -134,9 +131,9 @@
#endif
#define BOOST_UNORDERED_STATIC_ASSERT_HASH_PRED(Hash, Pred) \
static_assert(boost::is_nothrow_swappable<Hash>::value, \
static_assert(boost::unordered::detail::is_nothrow_swappable<Hash>::value, \
"Template parameter Hash is required to be nothrow Swappable."); \
static_assert(boost::is_nothrow_swappable<Pred>::value, \
static_assert(boost::unordered::detail::is_nothrow_swappable<Pred>::value, \
"Template parameter Pred is required to be nothrow Swappable");
namespace boost{
@@ -148,7 +145,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
@@ -311,7 +308,7 @@ struct group15
private:
using slot_type=IntegralWrapper<unsigned char>;
BOOST_STATIC_ASSERT(sizeof(slot_type)==1);
BOOST_UNORDERED_STATIC_ASSERT(sizeof(slot_type)==1);
static constexpr unsigned char available_=0,
sentinel_=1;
@@ -337,38 +334,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)];
@@ -503,7 +511,7 @@ struct group15
private:
using slot_type=IntegralWrapper<unsigned char>;
BOOST_STATIC_ASSERT(sizeof(slot_type)==1);
BOOST_UNORDERED_STATIC_ASSERT(sizeof(slot_type)==1);
static constexpr unsigned char available_=0,
sentinel_=1;
@@ -549,7 +557,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 +637,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;
}
@@ -694,7 +704,7 @@ struct group15
private:
using word_type=IntegralWrapper<uint64_t>;
BOOST_STATIC_ASSERT(sizeof(word_type)==8);
BOOST_UNORDERED_STATIC_ASSERT(sizeof(word_type)==8);
static constexpr unsigned char available_=0,
sentinel_=1;
@@ -787,8 +797,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 +948,134 @@ 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,
is_trivially_default_constructible<group_type>{});
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 +1094,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 +1103,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 +1250,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 +1261,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 +1328,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 +1348,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 +1358,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 +1432,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 +1464,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 +1475,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 +1497,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 +1519,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 +1597,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 +1651,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 +1663,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 +1674,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 +1893,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 +1929,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 +1958,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 +2001,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);
}
}
@@ -1883,13 +2015,7 @@ private:
x,
std::integral_constant<
bool,
#if BOOST_WORKAROUND(BOOST_LIBSTDCXX_VERSION,<50000)
/* std::is_trivially_copy_constructible not provided */
boost::has_trivial_copy<element_type>::value
#else
std::is_trivially_copy_constructible<element_type>::value
#endif
&&(
is_trivially_copy_constructible<element_type>::value&&(
is_std_allocator<Allocator>::value||
!alloc_has_construct<Allocator,value_type*,const value_type&>::value)
>{}
@@ -1903,8 +2029,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 +2040,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;
});
}
@@ -1931,37 +2057,31 @@ private:
}
void copy_groups_array_from(const table_core& x) {
copy_groups_array_from(x, std::integral_constant<bool,
#if BOOST_WORKAROUND(BOOST_LIBSTDCXX_VERSION,<50000)
/* std::is_trivially_copy_assignable not provided */
boost::has_trivial_assign<group_type>::value
#else
std::is_trivially_copy_assignable<group_type>::value
#endif
>{}
);
copy_groups_array_from(x,is_trivially_copy_assignable<group_type>{});
}
void copy_groups_array_from(
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 +2094,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 +2191,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;
@@ -9,6 +9,8 @@
#ifndef BOOST_UNORDERED_DETAIL_FOA_NODE_HANDLE_HPP
#define BOOST_UNORDERED_DETAIL_FOA_NODE_HANDLE_HPP
#include <boost/unordered/detail/opt_storage.hpp>
#include <boost/config.hpp>
#include <boost/core/allocator_access.hpp>
@@ -25,14 +27,6 @@ struct insert_return_type
NodeType node;
};
template <class T>
union opt_storage {
BOOST_ATTRIBUTE_NO_UNIQUE_ADDRESS T t_;
opt_storage(){}
~opt_storage(){}
};
template <class TypePolicy,class Allocator>
struct node_handle_base
{
@@ -5,14 +5,17 @@
#ifndef BOOST_UNORDERED_DETAIL_FOA_NODE_MAP_TYPES_HPP
#define BOOST_UNORDERED_DETAIL_FOA_NODE_MAP_TYPES_HPP
#include <boost/unordered/detail/foa/element_type.hpp>
#include <boost/core/allocator_access.hpp>
#include <boost/core/no_exceptions_support.hpp>
#include <boost/core/pointer_traits.hpp>
namespace boost {
namespace unordered {
namespace detail {
namespace foa {
template <class Key, class T> struct node_map_types
template <class Key, class T, class VoidPtr> struct node_map_types
{
using key_type = Key;
using mapped_type = T;
@@ -23,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)
{
@@ -82,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
@@ -113,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,7 +5,10 @@
#ifndef BOOST_UNORDERED_DETAIL_FOA_NODE_SET_TYPES_HPP
#define BOOST_UNORDERED_DETAIL_FOA_NODE_SET_TYPES_HPP
#include <boost/unordered/detail/foa/element_type.hpp>
#include <boost/core/allocator_access.hpp>
#include <boost/core/no_exceptions_support.hpp>
#include <boost/core/pointer_traits.hpp>
namespace boost {
@@ -13,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;
@@ -21,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; }
@@ -52,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
@@ -77,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
+124 -54
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;
@@ -99,24 +106,24 @@ public:
using element_type=
typename std::conditional<Const,value_type const,value_type>::type;
table_iterator()=default;
table_iterator():pc_{nullptr},p_{nullptr}{};
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* ptet):
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*>(ptet))}
{}
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,9 +544,31 @@ 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_}{}
erase_on_exit(table& x_,const_iterator it_):x(x_),it(it_){}
~erase_on_exit(){if(!rollback_)x.erase(it);}
void rollback(){rollback_=true;}
-150
View File
@@ -1,150 +0,0 @@
// Copyright (C) 2008-2016 Daniel James.
// Copyright (C) 2022 Christian Mazakas
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
#ifndef BOOST_UNORDERED_FWD_HPP_INCLUDED
#define BOOST_UNORDERED_FWD_HPP_INCLUDED
#include <boost/config.hpp>
#if defined(BOOST_HAS_PRAGMA_ONCE)
#pragma once
#endif
#include <boost/predef.h>
#if defined(BOOST_UNORDERED_HAVE_PIECEWISE_CONSTRUCT)
// Already defined.
#elif defined(BOOST_LIBSTDCXX11)
// https://github.com/gcc-mirror/gcc/blob/gcc-4_6-branch/libstdc++-v3/include/bits/stl_pair.h#L70
#if BOOST_LIBSTDCXX_VERSION > 40600
#define BOOST_UNORDERED_HAVE_PIECEWISE_CONSTRUCT 1
#endif
#elif BOOST_LIB_STD_CXX
// https://github.com/llvm-mirror/libcxx/blob/release_30/include/utility#L206
#if BOOST_LIB_STD_CXX >= BOOST_VERSION_NUMBER(3, 0, 0)
#define BOOST_UNORDERED_HAVE_PIECEWISE_CONSTRUCT 1
#endif
#elif defined(BOOST_LIB_STD_DINKUMWARE)
// Apparently C++11 standard supported in Visual Studio 2012
// https://msdn.microsoft.com/en-us/library/hh567368.aspx#stl
// 2012 = VC+11 = BOOST_MSVC 1700 Hopefully!
// I have no idea when Dinkumware added it, probably a lot
// earlier than this check.
#if BOOST_LIB_STD_DINKUMWARE >= BOOST_VERSION_NUMBER(6, 10, 0) || \
BOOST_COMP_MSVC >= BOOST_VERSION_NUMBER(17, 0, 0)
#define BOOST_UNORDERED_HAVE_PIECEWISE_CONSTRUCT 1
#endif
#endif
// Assume that an unknown library does not support piecewise construction.
#if !defined(BOOST_UNORDERED_HAVE_PIECEWISE_CONSTRUCT)
#define BOOST_UNORDERED_HAVE_PIECEWISE_CONSTRUCT 0
#endif
#if BOOST_UNORDERED_HAVE_PIECEWISE_CONSTRUCT
#include <utility>
#endif
namespace boost {
namespace unordered {
#if BOOST_UNORDERED_HAVE_PIECEWISE_CONSTRUCT
using std::piecewise_construct_t;
using std::piecewise_construct;
#else
struct piecewise_construct_t
{
};
const piecewise_construct_t piecewise_construct = piecewise_construct_t();
#endif
}
}
// BOOST_UNORDERED_EMPLACE_LIMIT = The maximum number of parameters in
// emplace (not including things like hints). Don't set it to a lower value, as
// that might break something.
#if !defined BOOST_UNORDERED_EMPLACE_LIMIT
#define BOOST_UNORDERED_EMPLACE_LIMIT 10
#endif
////////////////////////////////////////////////////////////////////////////////
// Configuration
//
// Unless documented elsewhere these configuration macros should be considered
// an implementation detail, I'll try not to break them, but you never know.
// Use Sun C++ workarounds
// I'm not sure which versions of the compiler require these workarounds, so
// I'm just using them of everything older than the current test compilers
// (as of May 2017).
#if !defined(BOOST_UNORDERED_SUN_WORKAROUNDS1)
#if BOOST_COMP_SUNPRO && BOOST_COMP_SUNPRO < BOOST_VERSION_NUMBER(5, 20, 0)
#define BOOST_UNORDERED_SUN_WORKAROUNDS1 1
#else
#define BOOST_UNORDERED_SUN_WORKAROUNDS1 0
#endif
#endif
// BOOST_UNORDERED_TUPLE_ARGS
//
// Maximum number of std::tuple members to support, or 0 if std::tuple
// isn't avaiable. More are supported when full C++11 is used.
// Already defined, so do nothing
#if defined(BOOST_UNORDERED_TUPLE_ARGS)
// Assume if we have C++11 tuple it's properly variadic,
// and just use a max number of 10 arguments.
#elif !defined(BOOST_NO_CXX11_HDR_TUPLE)
#define BOOST_UNORDERED_TUPLE_ARGS 10
// Visual C++ has a decent enough tuple for piecewise construction,
// so use that if available, using _VARIADIC_MAX for the maximum
// number of parameters. Note that this comes after the check
// for a full C++11 tuple.
#elif defined(BOOST_MSVC)
#if !BOOST_UNORDERED_HAVE_PIECEWISE_CONSTRUCT
#define BOOST_UNORDERED_TUPLE_ARGS 0
#elif defined(_VARIADIC_MAX)
#define BOOST_UNORDERED_TUPLE_ARGS _VARIADIC_MAX
#else
#define BOOST_UNORDERED_TUPLE_ARGS 5
#endif
// Assume that we don't have std::tuple
#else
#define BOOST_UNORDERED_TUPLE_ARGS 0
#endif
#if BOOST_UNORDERED_TUPLE_ARGS
#include <tuple>
#endif
// BOOST_UNORDERED_CXX11_CONSTRUCTION
//
// Use C++11 construction, requires variadic arguments, good construct support
// in allocator_traits and piecewise construction of std::pair
// Otherwise allocators aren't used for construction/destruction
#if BOOST_UNORDERED_HAVE_PIECEWISE_CONSTRUCT && \
!defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES) && BOOST_UNORDERED_TUPLE_ARGS
#if BOOST_COMP_SUNPRO && BOOST_LIB_STD_GNU
// Sun C++ std::pair piecewise construction doesn't seem to be exception safe.
// (At least for Sun C++ 12.5 using libstdc++).
#define BOOST_UNORDERED_CXX11_CONSTRUCTION 0
#elif BOOST_COMP_GNUC && BOOST_COMP_GNUC < BOOST_VERSION_NUMBER(4, 7, 0)
// Piecewise construction in GCC 4.6 doesn't work for uncopyable types.
#define BOOST_UNORDERED_CXX11_CONSTRUCTION 0
#elif !defined(BOOST_NO_CXX11_ALLOCATOR)
#define BOOST_UNORDERED_CXX11_CONSTRUCTION 1
#endif
#endif
#if !defined(BOOST_UNORDERED_CXX11_CONSTRUCTION)
#define BOOST_UNORDERED_CXX11_CONSTRUCTION 0
#endif
#endif
File diff suppressed because it is too large Load Diff
@@ -9,21 +9,21 @@
#ifndef BOOST_UNORDERED_DETAIL_NARROW_CAST_HPP
#define BOOST_UNORDERED_DETAIL_NARROW_CAST_HPP
#include <boost/unordered/detail/static_assert.hpp>
#include <boost/config.hpp>
#include <boost/static_assert.hpp>
#include <boost/type_traits/is_integral.hpp>
#include <boost/type_traits/make_unsigned.hpp>
#include <type_traits>
namespace boost{
namespace unordered{
namespace detail{
template<typename To,typename From>
BOOST_CONSTEXPR To narrow_cast(From x) BOOST_NOEXCEPT
constexpr To narrow_cast(From x) noexcept
{
BOOST_STATIC_ASSERT(boost::is_integral<From>::value);
BOOST_STATIC_ASSERT(boost::is_integral<To>::value);
BOOST_STATIC_ASSERT(sizeof(From)>=sizeof(To));
BOOST_UNORDERED_STATIC_ASSERT(std::is_integral<From>::value);
BOOST_UNORDERED_STATIC_ASSERT(std::is_integral<To>::value);
BOOST_UNORDERED_STATIC_ASSERT(sizeof(From)>=sizeof(To));
return static_cast<To>(
x
@@ -32,7 +32,7 @@ BOOST_CONSTEXPR To narrow_cast(From x) BOOST_NOEXCEPT
/* Avoids VS's "Run-Time Check Failure #1 - A cast to a smaller data type
* has caused a loss of data."
*/
&static_cast<typename boost::make_unsigned<To>::type>(~static_cast<To>(0))
&static_cast<typename std::make_unsigned<To>::type>(~static_cast<To>(0))
#endif
);
}
@@ -0,0 +1,30 @@
// Copyright (C) 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)
#ifndef BOOST_UNORDERED_DETAIL_OPT_STORAGE_HPP
#define BOOST_UNORDERED_DETAIL_OPT_STORAGE_HPP
#include <boost/config.hpp>
#include <memory>
namespace boost {
namespace unordered {
namespace detail {
template <class T> union opt_storage
{
BOOST_ATTRIBUTE_NO_UNIQUE_ADDRESS T t_;
opt_storage() {}
~opt_storage() {}
T* address() noexcept { return std::addressof(t_); }
T const* address() const noexcept { return std::addressof(t_); }
};
} // namespace detail
} // namespace unordered
} // namespace boost
#endif // BOOST_UNORDERED_DETAIL_OPT_STORAGE_HPP
+96 -145
View File
@@ -1,5 +1,5 @@
// Copyright (C) 2022 Joaquin M Lopez Munoz.
// 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)
@@ -7,13 +7,10 @@
#ifndef BOOST_UNORDERED_DETAIL_PRIME_FMOD_HPP
#define BOOST_UNORDERED_DETAIL_PRIME_FMOD_HPP
#include <boost/cstdint.hpp>
#include <boost/preprocessor/seq/enum.hpp>
#include <boost/preprocessor/seq/for_each.hpp>
#include <boost/preprocessor/seq/size.hpp>
#include <boost/unordered/detail/narrow_cast.hpp>
#include <boost/config.hpp>
#include <boost/cstdint.hpp>
#include <climits>
#include <cstddef>
@@ -37,21 +34,90 @@ namespace boost {
namespace detail {
template <class = void> struct prime_fmod_size
{
// Because we compile for C++03, we don't have access to any inline
// initialization for array data members so the definitions must exist
// out-of-line. To keep the library header-only, we introduce a dummy
// template parameter which permits the definition to be included in
// multiple TUs without conflict.
//
static std::size_t sizes[];
static std::size_t const sizes_len;
static std::size_t (*positions[])(std::size_t);
constexpr static std::size_t const sizes[] = {13ul, 29ul, 53ul, 97ul,
193ul, 389ul, 769ul, 1543ul, 3079ul, 6151ul, 12289ul, 24593ul,
49157ul, 98317ul, 196613ul, 393241ul, 786433ul, 1572869ul, 3145739ul,
6291469ul, 12582917ul, 25165843ul, 50331653ul, 100663319ul,
201326611ul, 402653189ul, 805306457ul, 1610612741ul, 3221225473ul,
#if !defined(BOOST_UNORDERED_FCA_HAS_64B_SIZE_T)
4294967291ul
#else
6442450939ull, 12884901893ull, 25769803751ull, 51539607551ull,
103079215111ull, 206158430209ull, 412316860441ull, 824633720831ull,
1649267441651ull
#endif
};
constexpr static std::size_t const sizes_len =
sizeof(sizes) / sizeof(sizes[0]);
#if defined(BOOST_UNORDERED_FCA_HAS_64B_SIZE_T)
static boost::uint64_t inv_sizes32[];
static std::size_t const inv_sizes32_len;
constexpr static boost::uint64_t const inv_sizes32[] = {
1418980313362273202ull, 636094623231363849ull, 348051774975651918ull,
190172619316593316ull, 95578984837873325ull, 47420935922132524ull,
23987963684927896ull, 11955116055547344ull, 5991147799191151ull,
2998982941588287ull, 1501077717772769ull, 750081082979285ull,
375261795343686ull, 187625172388393ull, 93822606204624ull,
46909513691883ull, 23456218233098ull, 11728086747027ull,
5864041509391ull, 2932024948977ull, 1466014921160ull, 733007198436ull,
366503839517ull, 183251896093ull, 91625960335ull, 45812983922ull,
22906489714ull, 11453246088ull, 5726623060ull};
constexpr static std::size_t const inv_sizes32_len =
sizeof(inv_sizes32) / sizeof(inv_sizes32[0]);
#endif /* defined(BOOST_UNORDERED_FCA_HAS_64B_SIZE_T) */
template <std::size_t SizeIndex, std::size_t Size = sizes[SizeIndex]>
static std::size_t position(std::size_t hash)
{
return hash % Size;
}
constexpr static std::size_t (*positions[])(std::size_t) = {
#if !defined(BOOST_UNORDERED_FCA_HAS_64B_SIZE_T)
position<0, sizes[0]>,
position<1, sizes[1]>,
position<2, sizes[2]>,
position<3, sizes[3]>,
position<4, sizes[4]>,
position<5, sizes[5]>,
position<6, sizes[6]>,
position<7, sizes[7]>,
position<8, sizes[8]>,
position<9, sizes[9]>,
position<10, sizes[10]>,
position<11, sizes[11]>,
position<12, sizes[12]>,
position<13, sizes[13]>,
position<14, sizes[14]>,
position<15, sizes[15]>,
position<16, sizes[16]>,
position<17, sizes[17]>,
position<18, sizes[18]>,
position<19, sizes[19]>,
position<20, sizes[20]>,
position<21, sizes[21]>,
position<22, sizes[22]>,
position<23, sizes[23]>,
position<24, sizes[24]>,
position<25, sizes[25]>,
position<26, sizes[26]>,
position<27, sizes[27]>,
position<28, sizes[28]>,
position<29, sizes[29]>,
#else
position<29, sizes[29]>,
position<30, sizes[30]>,
position<31, sizes[31]>,
position<32, sizes[32]>,
position<33, sizes[33]>,
position<34, sizes[34]>,
position<35, sizes[35]>,
position<36, sizes[36]>,
position<37, sizes[37]>,
#endif
};
static inline std::size_t size_index(std::size_t n)
{
std::size_t i = 0;
@@ -68,11 +134,6 @@ namespace boost {
return sizes[size_index];
}
template <std::size_t Size> static std::size_t modulo(std::size_t hash)
{
return hash % Size;
}
#if defined(BOOST_UNORDERED_FCA_HAS_64B_SIZE_T)
// We emulate the techniques taken from:
// Faster Remainder by Direct Computation: Applications to Compilers and
@@ -118,8 +179,8 @@ namespace boost {
#if defined(BOOST_UNORDERED_FCA_HAS_64B_SIZE_T)
std::size_t sizes_under_32bit = inv_sizes32_len;
if (BOOST_LIKELY(size_index < sizes_under_32bit)) {
return fast_modulo(
narrow_cast<boost::uint32_t>(hash) + narrow_cast<boost::uint32_t>(hash >> 32),
return fast_modulo(narrow_cast<boost::uint32_t>(hash) +
narrow_cast<boost::uint32_t>(hash >> 32),
inv_sizes32[size_index], boost::uint32_t(sizes[size_index]));
} else {
return positions[size_index - sizes_under_32bit](hash);
@@ -130,134 +191,24 @@ namespace boost {
}
}; // prime_fmod_size
#define BOOST_UNORDERED_PRIME_FMOD_SIZES_32BIT_INCOMPLETE \
(13ul)(29ul)(53ul)(97ul)(193ul)(389ul)(769ul)(1543ul)(3079ul)(6151ul)( \
12289ul)(24593ul)(49157ul)(98317ul)(196613ul)(393241ul)(786433ul)( \
1572869ul)(3145739ul)(6291469ul)(12582917ul)(25165843ul)(50331653ul)( \
100663319ul)(201326611ul)(402653189ul)(805306457ul)(1610612741ul)( \
3221225473ul)
#if defined(BOOST_NO_CXX17_INLINE_VARIABLES)
// https://en.cppreference.com/w/cpp/language/static#Constant_static_members
// If a const non-inline (since C++17) static data member or a constexpr
// static data member (since C++11)(until C++17) is odr-used, a definition
// at namespace scope is still required, but it cannot have an
// initializer.
template <class T> constexpr std::size_t prime_fmod_size<T>::sizes[];
#if !defined(BOOST_UNORDERED_FCA_HAS_64B_SIZE_T)
#define BOOST_UNORDERED_PRIME_FMOD_SIZES_32BIT \
BOOST_UNORDERED_PRIME_FMOD_SIZES_32BIT_INCOMPLETE(4294967291ul)
#define BOOST_UNORDERED_PRIME_FMOD_SIZES_64BIT
#else
#define BOOST_UNORDERED_PRIME_FMOD_SIZES_32BIT \
BOOST_UNORDERED_PRIME_FMOD_SIZES_32BIT_INCOMPLETE
// The original sequence here is this:
// (6442450939ul)
// (12884901893ul)
// (25769803751ul)
// (51539607551ul)
// (103079215111ul)
// (206158430209ul)
// (412316860441ul)
// (824633720831ul)
// (1649267441651ul)
//
// but this causes problems on versions of mingw where the `long` type is 32
// bits, even for 64-bit targets. We work around this by replacing the literals
// with compile-time arithmetic, using bitshifts to reconstruct the number.
//
// clang-format off
#define BOOST_UNORDERED_PRIME_FMOD_SIZES_64BIT \
((boost::ulong_long_type(1ul) << 32) + boost::ulong_long_type(2147483643ul)) \
((boost::ulong_long_type(3ul) << 32) + boost::ulong_long_type(5ul)) \
((boost::ulong_long_type(5ul) << 32) + boost::ulong_long_type(4294967271ul)) \
((boost::ulong_long_type(11ul) << 32) + boost::ulong_long_type(4294967295ul)) \
((boost::ulong_long_type(24ul) << 32) + boost::ulong_long_type(7ul)) \
((boost::ulong_long_type(48ul) << 32) + boost::ulong_long_type(1ul)) \
((boost::ulong_long_type(96ul) << 32) + boost::ulong_long_type(25ul)) \
((boost::ulong_long_type(191ul) << 32) + boost::ulong_long_type(4294967295ul)) \
((boost::ulong_long_type(383ul) << 32) + boost::ulong_long_type(4294967283ul))
// clang-format on
#endif /* BOOST_UNORDERED_FCA_HAS_64B_SIZE_T */
#define BOOST_UNORDERED_PRIME_FMOD_SIZES \
BOOST_UNORDERED_PRIME_FMOD_SIZES_32BIT BOOST_UNORDERED_PRIME_FMOD_SIZES_64BIT
template <class T>
std::size_t prime_fmod_size<T>::sizes[] = {
BOOST_PP_SEQ_ENUM(BOOST_UNORDERED_PRIME_FMOD_SIZES)};
template <class T>
std::size_t const prime_fmod_size<T>::sizes_len = BOOST_PP_SEQ_SIZE(
BOOST_UNORDERED_PRIME_FMOD_SIZES);
// Similarly here, we have to re-express the integer initialization using
// arithmetic such that each literal can fit in a 32-bit value.
//
#if defined(BOOST_UNORDERED_FCA_HAS_64B_SIZE_T)
// clang-format off
template <class T>
boost::uint64_t prime_fmod_size<T>::inv_sizes32[] = {
(boost::ulong_long_type(330382099ul) << 32) + boost::ulong_long_type(2973438898ul) /* = 1418980313362273202 */,
(boost::ulong_long_type(148102320ul) << 32) + boost::ulong_long_type(2369637129ul) /* = 636094623231363849 */,
(boost::ulong_long_type(81037118ul) << 32) + boost::ulong_long_type(3403558990ul) /* = 348051774975651918 */,
(boost::ulong_long_type(44278013ul) << 32) + boost::ulong_long_type(1549730468ul) /* = 190172619316593316 */,
(boost::ulong_long_type(22253716ul) << 32) + boost::ulong_long_type(2403401389ul) /* = 95578984837873325 */,
(boost::ulong_long_type(11041047ul) << 32) + boost::ulong_long_type(143533612ul) /* = 47420935922132524 */,
(boost::ulong_long_type(5585133ul) << 32) + boost::ulong_long_type(106117528ul) /* = 23987963684927896 */,
(boost::ulong_long_type(2783517ul) << 32) + boost::ulong_long_type(1572687312ul) /* = 11955116055547344 */,
(boost::ulong_long_type(1394922ul) << 32) + boost::ulong_long_type(3428720239ul) /* = 5991147799191151 */,
(boost::ulong_long_type(698255ul) << 32) + boost::ulong_long_type(552319807ul) /* = 2998982941588287 */,
(boost::ulong_long_type(349496ul) << 32) + boost::ulong_long_type(3827689953ul) /* = 1501077717772769 */,
(boost::ulong_long_type(174641ul) << 32) + boost::ulong_long_type(3699438549ul) /* = 750081082979285 */,
(boost::ulong_long_type(87372ul) << 32) + boost::ulong_long_type(1912757574ul) /* = 375261795343686 */,
(boost::ulong_long_type(43684ul) << 32) + boost::ulong_long_type(3821029929ul) /* = 187625172388393 */,
(boost::ulong_long_type(21844ul) << 32) + boost::ulong_long_type(3340590800ul) /* = 93822606204624 */,
(boost::ulong_long_type(10921ul) << 32) + boost::ulong_long_type(4175852267ul) /* = 46909513691883 */,
(boost::ulong_long_type(5461ul) << 32) + boost::ulong_long_type(1401829642ul) /* = 23456218233098 */,
(boost::ulong_long_type(2730ul) << 32) + boost::ulong_long_type(2826028947ul) /* = 11728086747027 */,
(boost::ulong_long_type(1365ul) << 32) + boost::ulong_long_type(1411150351ul) /* = 5864041509391 */,
(boost::ulong_long_type(682ul) << 32) + boost::ulong_long_type(2857253105ul) /* = 2932024948977 */,
(boost::ulong_long_type(341ul) << 32) + boost::ulong_long_type(1431073224ul) /* = 1466014921160 */,
(boost::ulong_long_type(170ul) << 32) + boost::ulong_long_type(2862758116ul) /* = 733007198436 */,
(boost::ulong_long_type(85ul) << 32) + boost::ulong_long_type(1431619357ul) /* = 366503839517 */,
(boost::ulong_long_type(42ul) << 32) + boost::ulong_long_type(2863269661ul) /* = 183251896093 */,
(boost::ulong_long_type(21ul) << 32) + boost::ulong_long_type(1431647119ul) /* = 91625960335 */,
(boost::ulong_long_type(10ul) << 32) + boost::ulong_long_type(2863310962ul) /* = 45812983922 */,
(boost::ulong_long_type(5ul) << 32) + boost::ulong_long_type(1431653234ul) /* = 22906489714 */,
(boost::ulong_long_type(2ul) << 32) + boost::ulong_long_type(2863311496ul) /* = 11453246088 */,
(boost::ulong_long_type(1ul) << 32) + boost::ulong_long_type(1431655764ul) /* = 5726623060 */,
};
// clang-format on
template <class T>
std::size_t const
prime_fmod_size<T>::inv_sizes32_len = sizeof(inv_sizes32) /
sizeof(inv_sizes32[0]);
#endif /* defined(BOOST_UNORDERED_FCA_HAS_64B_SIZE_T) */
#define BOOST_UNORDERED_PRIME_FMOD_POSITIONS_ELEMENT(z, _, n) \
prime_fmod_size<T>::template modulo<n>,
template <class T>
std::size_t (*prime_fmod_size<T>::positions[])(std::size_t) = {
#if !defined(BOOST_UNORDERED_FCA_HAS_64B_SIZE_T)
BOOST_PP_SEQ_FOR_EACH(BOOST_UNORDERED_PRIME_FMOD_POSITIONS_ELEMENT, ~,
BOOST_UNORDERED_PRIME_FMOD_SIZES_32BIT)
#else
BOOST_PP_SEQ_FOR_EACH(BOOST_UNORDERED_PRIME_FMOD_POSITIONS_ELEMENT, ~,
BOOST_UNORDERED_PRIME_FMOD_SIZES_64BIT)
constexpr boost::uint64_t prime_fmod_size<T>::inv_sizes32[];
#endif
};
#undef BOOST_UNORDERED_PRIME_FMOD_POSITIONS_ELEMENT
#undef BOOST_UNORDERED_PRIME_FMOD_SIZES
#undef BOOST_UNORDERED_PRIME_FMOD_SIZES_64BIT
#undef BOOST_UNORDERED_PRIME_FMOD_SIZES_32BIT
#undef BOOST_UNORDERED_PRIME_FMOD_SIZES_32BIT_INCOMPLETE
template <class T>
constexpr std::size_t (*prime_fmod_size<T>::positions[])(std::size_t);
#endif
} // namespace detail
} // namespace unordered
} // namespace unordered
} // namespace boost
#endif // BOOST_UNORDERED_DETAIL_PRIME_FMOD_HPP
@@ -1,21 +0,0 @@
#ifndef BOOST_UNORDERED_DETAIL_REQUIRES_CXX11_HPP_INCLUDED
#define BOOST_UNORDERED_DETAIL_REQUIRES_CXX11_HPP_INCLUDED
// Copyright 2023 Peter Dimov
// Distributed under the Boost Software License, Version 1.0.
// https://www.boost.org/LICENSE_1_0.txt
#include <boost/config.hpp>
#include <boost/config/pragma_message.hpp>
#if defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES) || \
defined(BOOST_NO_CXX11_RVALUE_REFERENCES) || \
defined(BOOST_NO_CXX11_DECLTYPE) || \
defined(BOOST_NO_CXX11_CONSTEXPR) || \
defined(BOOST_NO_CXX11_NOEXCEPT)
BOOST_PRAGMA_MESSAGE("C++03 support is deprecated in Boost.Unordered 1.82 and will be removed in Boost.Unordered 1.84.")
#endif
#endif // #ifndef BOOST_UNORDERED_DETAIL_REQUIRES_CXX11_HPP_INCLUDED
@@ -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,204 @@
/* 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/serialization.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/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,std::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(std::move(value.get())));
if(!p.second)throw_exception(bad_archive_exception());
ar.reset_object_address(
std::addressof(*p.first),std::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 std::remove_const<
typename Map::key_type>::type key_type;
typedef typename std::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,std::addressof(first->first),key_version);
ar<<core::make_nvp("key",first->first);
core::save_construct_data_adl(
ar,std::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 std::remove_const<
typename Map::key_type>::type key_type;
typedef typename std::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(std::move(key.get()),std::move(mapped.get())));
if(!p.second)throw_exception(bad_archive_exception());
ar.reset_object_address(
std::addressof(p.first->first),std::addressof(key.get()));
ar.reset_object_address(
std::addressof(p.first->second),std::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,
std::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,104 @@
/* 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/unordered/detail/bad_archive_exception.hpp>
#include <boost/core/pointer_traits.hpp>
#include <boost/core/serialization.hpp>
#include <boost/throw_exception.hpp>
#include <type_traits>
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 std::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,std::true_type /* save */)
{
typedef typename boost::pointer_traits<Ptr> ptr_traits;
typedef typename std::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,std::false_type /* load */)
{
typedef typename boost::pointer_traits<Ptr> ptr_traits;
typedef typename std::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,
std::integral_constant<bool,Archive::is_saving::value>());
}
} /* namespace detail */
} /* namespace unordered */
} /* namespace boost */
#endif
@@ -0,0 +1,16 @@
// Copyright 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)
#ifndef BOOST_UNORDERED_DETAIL_STATIC_ASSERT_HPP
#define BOOST_UNORDERED_DETAIL_STATIC_ASSERT_HPP
#include <boost/config.hpp>
#if defined(BOOST_HAS_PRAGMA_ONCE)
#pragma once
#endif
#define BOOST_UNORDERED_STATIC_ASSERT(...) \
static_assert(__VA_ARGS__, #__VA_ARGS__)
#endif // BOOST_UNORDERED_DETAIL_STATIC_ASSERT_HPP
+91 -19
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)
@@ -11,20 +11,15 @@
#pragma once
#endif
#include <boost/type_traits/integral_constant.hpp>
#include <boost/type_traits/is_convertible.hpp>
#include <boost/type_traits/make_void.hpp>
#include <boost/type_traits/type_identity.hpp>
#include <boost/config/workaround.hpp>
#if !defined(BOOST_NO_CXX17_DEDUCTION_GUIDES)
#include <boost/type_traits/enable_if.hpp>
#include <boost/type_traits/is_integral.hpp>
#include <boost/type_traits/remove_const.hpp>
#include <iterator>
#include <utility>
#endif
#include <type_traits>
#include <utility>
// BOOST_UNORDERED_TEMPLATE_DEDUCTION_GUIDES
#if !defined(BOOST_UNORDERED_TEMPLATE_DEDUCTION_GUIDES)
@@ -40,19 +35,96 @@
namespace boost {
namespace unordered {
namespace detail {
template <class T> struct type_identity
{
using type = T;
};
template <typename... Ts> struct make_void
{
typedef void type;
};
template <typename... Ts> using void_t = typename make_void<Ts...>::type;
#if BOOST_WORKAROUND(BOOST_LIBSTDCXX_VERSION, < 50000)
/* std::is_trivially_default_constructible not provided */
template <class T>
struct is_trivially_default_constructible
: public std::integral_constant<bool,
std::is_default_constructible<T>::value &&
std::has_trivial_default_constructor<T>::value>
{
};
#else
using std::is_trivially_default_constructible;
#endif
#if BOOST_WORKAROUND(BOOST_LIBSTDCXX_VERSION, < 50000)
/* std::is_trivially_copy_constructible not provided */
template <class T>
struct is_trivially_copy_constructible
: public std::integral_constant<bool,
std::is_copy_constructible<T>::value &&
std::has_trivial_copy_constructor<T>::value>
{
};
#else
using std::is_trivially_copy_constructible;
#endif
#if BOOST_WORKAROUND(BOOST_LIBSTDCXX_VERSION, < 50000)
/* std::is_trivially_copy_assignable not provided */
template <class T>
struct is_trivially_copy_assignable
: public std::integral_constant<bool,
std::is_copy_assignable<T>::value &&
std::has_trivial_copy_assign<T>::value>
{
};
#else
using std::is_trivially_copy_assignable;
#endif
namespace type_traits_detail {
using std::swap;
template <class T, class = void> struct is_nothrow_swappable_helper
{
constexpr static bool const value = false;
};
template <class T>
struct is_nothrow_swappable_helper<T,
void_t<decltype(swap(std::declval<T&>(), std::declval<T&>()))> >
{
constexpr static bool const value =
noexcept(swap(std::declval<T&>(), std::declval<T&>()));
};
} // namespace type_traits_detail
template <class T>
struct is_nothrow_swappable
: public std::integral_constant<bool,
type_traits_detail::is_nothrow_swappable_helper<T>::value>
{
};
////////////////////////////////////////////////////////////////////////////
// Type checkers used for the transparent member functions added by C++20
// and up
template <class, class = void>
struct is_transparent : public boost::false_type
struct is_transparent : public std::false_type
{
};
template <class T>
struct is_transparent<T,
typename boost::make_void<typename T::is_transparent>::type>
: public boost::true_type
boost::unordered::detail::void_t<typename T::is_transparent> >
: public std::true_type
{
};
@@ -71,8 +143,8 @@ namespace boost {
static bool const value =
are_transparent<Key, hash, key_equal>::value &&
!boost::is_convertible<Key, iterator>::value &&
!boost::is_convertible<Key, const_iterator>::value;
!std::is_convertible<Key, iterator>::value &&
!std::is_convertible<Key, const_iterator>::value;
};
#if BOOST_UNORDERED_TEMPLATE_DEDUCTION_GUIDES
@@ -81,7 +153,7 @@ namespace boost {
template <class InputIterator>
constexpr bool const is_input_iterator_v =
!boost::is_integral<InputIterator>::value;
!std::is_integral<InputIterator>::value;
template <class A, class = void> struct is_allocator
{
@@ -90,7 +162,7 @@ namespace boost {
template <class A>
struct is_allocator<A,
boost::void_t<typename A::value_type,
boost::unordered::detail::void_t<typename A::value_type,
decltype(std::declval<A&>().allocate(std::size_t{}))> >
{
constexpr static bool const value = true;
@@ -101,7 +173,7 @@ namespace boost {
template <class H>
constexpr bool const is_hash_v =
!boost::is_integral<H>::value && !is_allocator_v<H>;
!std::is_integral<H>::value && !is_allocator_v<H>;
template <class P> constexpr bool const is_pred_v = !is_allocator_v<P>;
@@ -116,7 +188,7 @@ namespace boost {
typename std::pair<iter_key_t<T> const, iter_val_t<T> >;
#endif
} // namespace detail
} // namespace unordered
} // namespace unordered
} // namespace boost
#endif // BOOST_UNORDERED_DETAIL_TYPE_TRAITS_HPP
+4 -5
View File
@@ -11,8 +11,7 @@
#ifndef BOOST_UNORDERED_HASH_TRAITS_HPP
#define BOOST_UNORDERED_HASH_TRAITS_HPP
#include <boost/type_traits/make_void.hpp>
#include <boost/type_traits/integral_constant.hpp>
#include <boost/unordered/detail/type_traits.hpp>
namespace boost{
namespace unordered{
@@ -20,12 +19,12 @@ namespace unordered{
namespace detail{
template<typename Hash,typename=void>
struct hash_is_avalanching_impl: boost::false_type{};
struct hash_is_avalanching_impl: std::false_type{};
template<typename Hash>
struct hash_is_avalanching_impl<Hash,
typename boost::make_void<typename Hash::is_avalanching>::type>:
boost::true_type{};
boost::unordered::detail::void_t<typename Hash::is_avalanching> >:
std::true_type{};
} /* namespace detail */
+56 -37
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,13 +10,15 @@
#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>
#include <boost/core/allocator_access.hpp>
#include <boost/functional/hash.hpp>
#include <boost/container_hash/hash.hpp>
#include <boost/throw_exception.hpp>
#include <initializer_list>
@@ -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,
@@ -59,9 +65,9 @@ namespace boost {
using init_type = typename map_types::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<KeyEqual>::type;
using allocator_type = typename boost::type_identity<Allocator>::type;
using hasher = typename boost::unordered::detail::type_identity<Hash>::type;
using key_equal = typename boost::unordered::detail::type_identity<KeyEqual>::type;
using allocator_type = typename boost::unordered::detail::type_identity<Allocator>::type;
using reference = value_type&;
using const_reference = value_type const&;
using pointer = typename boost::allocator_pointer<allocator_type>::type;
@@ -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
@@ -698,10 +717,10 @@ namespace boost {
std::equal_to<boost::unordered::detail::iter_key_t<InputIterator> >,
class Allocator = std::allocator<
boost::unordered::detail::iter_to_alloc_t<InputIterator> >,
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> > >
class = std::enable_if_t<detail::is_input_iterator_v<InputIterator> >,
class = std::enable_if_t<detail::is_hash_v<Hash> >,
class = std::enable_if_t<detail::is_pred_v<Pred> >,
class = std::enable_if_t<detail::is_allocator_v<Allocator> > >
unordered_flat_map(InputIterator, InputIterator,
std::size_t = boost::unordered::detail::foa::default_bucket_count,
Hash = Hash(), Pred = Pred(), Allocator = Allocator())
@@ -710,21 +729,21 @@ namespace boost {
Allocator>;
template <class Key, class T,
class Hash = boost::hash<boost::remove_const_t<Key> >,
class Pred = std::equal_to<boost::remove_const_t<Key> >,
class Hash = boost::hash<std::remove_const_t<Key> >,
class Pred = std::equal_to<std::remove_const_t<Key> >,
class Allocator = std::allocator<std::pair<const Key, T> >,
class = 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> > >
class = std::enable_if_t<detail::is_hash_v<Hash> >,
class = std::enable_if_t<detail::is_pred_v<Pred> >,
class = std::enable_if_t<detail::is_allocator_v<Allocator> > >
unordered_flat_map(std::initializer_list<std::pair<Key, T> >,
std::size_t = boost::unordered::detail::foa::default_bucket_count,
Hash = Hash(), Pred = Pred(), Allocator = Allocator())
-> unordered_flat_map<boost::remove_const_t<Key>, T, Hash, Pred,
-> unordered_flat_map<std::remove_const_t<Key>, 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> > >
class = std::enable_if_t<detail::is_input_iterator_v<InputIterator> >,
class = std::enable_if_t<detail::is_allocator_v<Allocator> > >
unordered_flat_map(InputIterator, InputIterator, std::size_t, Allocator)
-> unordered_flat_map<boost::unordered::detail::iter_key_t<InputIterator>,
boost::unordered::detail::iter_val_t<InputIterator>,
@@ -733,8 +752,8 @@ namespace boost {
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> > >
class = std::enable_if_t<detail::is_input_iterator_v<InputIterator> >,
class = std::enable_if_t<detail::is_allocator_v<Allocator> > >
unordered_flat_map(InputIterator, InputIterator, Allocator)
-> unordered_flat_map<boost::unordered::detail::iter_key_t<InputIterator>,
boost::unordered::detail::iter_val_t<InputIterator>,
@@ -743,9 +762,9 @@ namespace boost {
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> > >
class = std::enable_if_t<detail::is_hash_v<Hash> >,
class = std::enable_if_t<detail::is_input_iterator_v<InputIterator> >,
class = std::enable_if_t<detail::is_allocator_v<Allocator> > >
unordered_flat_map(
InputIterator, InputIterator, std::size_t, Hash, Allocator)
-> unordered_flat_map<boost::unordered::detail::iter_key_t<InputIterator>,
@@ -754,25 +773,25 @@ namespace boost {
Allocator>;
template <class Key, class T, class Allocator,
class = boost::enable_if_t<detail::is_allocator_v<Allocator> > >
class = std::enable_if_t<detail::is_allocator_v<Allocator> > >
unordered_flat_map(std::initializer_list<std::pair<Key, T> >, std::size_t,
Allocator) -> unordered_flat_map<boost::remove_const_t<Key>, T,
boost::hash<boost::remove_const_t<Key> >,
std::equal_to<boost::remove_const_t<Key> >, Allocator>;
Allocator) -> unordered_flat_map<std::remove_const_t<Key>, T,
boost::hash<std::remove_const_t<Key> >,
std::equal_to<std::remove_const_t<Key> >, Allocator>;
template <class Key, class T, class Allocator,
class = boost::enable_if_t<detail::is_allocator_v<Allocator> > >
class = std::enable_if_t<detail::is_allocator_v<Allocator> > >
unordered_flat_map(std::initializer_list<std::pair<Key, T> >, Allocator)
-> unordered_flat_map<boost::remove_const_t<Key>, T,
boost::hash<boost::remove_const_t<Key> >,
std::equal_to<boost::remove_const_t<Key> >, Allocator>;
-> unordered_flat_map<std::remove_const_t<Key>, T,
boost::hash<std::remove_const_t<Key> >,
std::equal_to<std::remove_const_t<Key> >, Allocator>;
template <class Key, class T, class Hash, class Allocator,
class = boost::enable_if_t<detail::is_hash_v<Hash> >,
class = boost::enable_if_t<detail::is_allocator_v<Allocator> > >
class = std::enable_if_t<detail::is_hash_v<Hash> >,
class = std::enable_if_t<detail::is_allocator_v<Allocator> > >
unordered_flat_map(std::initializer_list<std::pair<Key, T> >, std::size_t,
Hash, Allocator) -> unordered_flat_map<boost::remove_const_t<Key>, T,
Hash, std::equal_to<boost::remove_const_t<Key> >, Allocator>;
Hash, Allocator) -> unordered_flat_map<std::remove_const_t<Key>, T,
Hash, std::equal_to<std::remove_const_t<Key> >, Allocator>;
#endif
} // namespace unordered
@@ -11,7 +11,7 @@
#pragma once
#endif
#include <boost/functional/hash_fwd.hpp>
#include <boost/container_hash/hash_fwd.hpp>
#include <functional>
#include <memory>
+41 -23
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,13 +10,15 @@
#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>
#include <boost/core/allocator_access.hpp>
#include <boost/functional/hash.hpp>
#include <boost/container_hash/hash.hpp>
#include <initializer_list>
#include <iterator>
@@ -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
@@ -517,10 +535,10 @@ namespace boost {
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> > >
class = std::enable_if_t<detail::is_input_iterator_v<InputIterator> >,
class = std::enable_if_t<detail::is_hash_v<Hash> >,
class = std::enable_if_t<detail::is_pred_v<Pred> >,
class = std::enable_if_t<detail::is_allocator_v<Allocator> > >
unordered_flat_set(InputIterator, InputIterator,
std::size_t = boost::unordered::detail::foa::default_bucket_count,
Hash = Hash(), Pred = Pred(), Allocator = Allocator())
@@ -530,17 +548,17 @@ namespace boost {
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> > >
class = std::enable_if_t<detail::is_hash_v<Hash> >,
class = std::enable_if_t<detail::is_pred_v<Pred> >,
class = std::enable_if_t<detail::is_allocator_v<Allocator> > >
unordered_flat_set(std::initializer_list<T>,
std::size_t = boost::unordered::detail::foa::default_bucket_count,
Hash = Hash(), Pred = Pred(), Allocator = Allocator())
-> unordered_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> > >
class = std::enable_if_t<detail::is_input_iterator_v<InputIterator> >,
class = std::enable_if_t<detail::is_allocator_v<Allocator> > >
unordered_flat_set(InputIterator, InputIterator, std::size_t, Allocator)
-> unordered_flat_set<
typename std::iterator_traits<InputIterator>::value_type,
@@ -549,9 +567,9 @@ namespace boost {
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> > >
class = std::enable_if_t<detail::is_hash_v<Hash> >,
class = std::enable_if_t<detail::is_input_iterator_v<InputIterator> >,
class = std::enable_if_t<detail::is_allocator_v<Allocator> > >
unordered_flat_set(
InputIterator, InputIterator, std::size_t, Hash, Allocator)
-> unordered_flat_set<
@@ -560,19 +578,19 @@ namespace boost {
Allocator>;
template <class T, class Allocator,
class = boost::enable_if_t<detail::is_allocator_v<Allocator> > >
class = std::enable_if_t<detail::is_allocator_v<Allocator> > >
unordered_flat_set(std::initializer_list<T>, std::size_t, Allocator)
-> unordered_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> > >
class = std::enable_if_t<detail::is_hash_v<Hash> >,
class = std::enable_if_t<detail::is_allocator_v<Allocator> > >
unordered_flat_set(std::initializer_list<T>, std::size_t, Hash, Allocator)
-> unordered_flat_set<T, Hash, std::equal_to<T>, 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> > >
class = std::enable_if_t<detail::is_input_iterator_v<InputIterator> >,
class = std::enable_if_t<detail::is_allocator_v<Allocator> > >
unordered_flat_set(InputIterator, InputIterator, Allocator)
-> unordered_flat_set<
typename std::iterator_traits<InputIterator>::value_type,
@@ -581,7 +599,7 @@ namespace boost {
Allocator>;
template <class T, class Allocator,
class = boost::enable_if_t<detail::is_allocator_v<Allocator> > >
class = std::enable_if_t<detail::is_allocator_v<Allocator> > >
unordered_flat_set(std::initializer_list<T>, Allocator)
-> unordered_flat_set<T, boost::hash<T>, std::equal_to<T>, Allocator>;
#endif
@@ -11,7 +11,7 @@
#pragma once
#endif
#include <boost/functional/hash_fwd.hpp>
#include <boost/container_hash/hash_fwd.hpp>
#include <functional>
#include <memory>
File diff suppressed because it is too large Load Diff

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