Compare commits

...

301 Commits

Author SHA1 Message Date
Peter Dimov 64f60542ac Update ci.yml 2023-04-23 22:05:34 +03:00
Peter Dimov a757f3737b Update ci.yml 2023-04-23 18:51:48 +03:00
Christian Mazakas 26ab9ff584 Add prototype copy assignment operator 2023-04-21 15:56:22 -07:00
Christian Mazakas 1e92d9f545 Refactor stateful_allocator to test helpers 2023-04-21 13:33:18 -07:00
Christian Mazakas 2cf72093b1 Update test to spin until the container is non-empty 2023-04-21 10:44:06 -07:00
Christian Mazakas 88d4d64edf Add last remaining constructors 2023-04-21 10:33:21 -07:00
joaquintides 7f7e577e77 polished description 2023-04-21 09:13:28 +02:00
Christian Mazakas 80d7203d78 Add more iterator constructor overloads 2023-04-20 15:35:04 -07:00
Christian Mazakas 5dfed4deec Update thread_runner to block on hand-rolled barrier, add yield points to hash, key_equal 2023-04-20 14:34:01 -07:00
Christian Mazakas e08f9f11a1 Add more constructors 2023-04-20 12:58:29 -07:00
Christian Mazakas c304284773 Update tests to use condition variables for work synchronization instead of sleeps 2023-04-20 12:58:29 -07:00
joaquintides 0e8affcc07 [skip ci] editorial 2023-04-20 20:40:10 +02:00
joaquintides ba00dbd65a Merge branch 'feature/cfoa' of https://github.com/boostorg/unordered into feature/cfoa 2023-04-20 18:50:29 +02:00
joaquintides c214fb44a3 completed internal code documentation 2023-04-20 18:49:42 +02:00
Christian Mazakas c704788718 Fix flaky test by starting insertion thread earlier 2023-04-20 09:25:33 -07:00
joaquintides a91efeb237 refactored size_/ml swap 2023-04-20 17:29:21 +02:00
Christian Mazakas 7812b26d3a Add allocator aware move constructors 2023-04-19 15:18:53 -07:00
Christian Mazakas 8bd07e17c3 Add forced inlining to keep msvc benchmarks on par with raw concurrent_table 2023-04-19 10:52:41 -07:00
Christian Mazakas b3c0368ab5 Clean up typedef names to avoid conflicts 2023-04-19 09:35:47 -07:00
Christian Mazakas 37edc392a5 Add allocator-aware copy constructor 2023-04-19 09:35:38 -07:00
Christian Mazakas fb403bc233 Add explicit allocator constructor 2023-04-18 15:36:12 -07:00
Christian Mazakas 910b8de697 Add iterator range + allocator constructor, continue to clean up constructor_tests 2023-04-18 15:11:53 -07:00
Christian Mazakas 3aff995ae0 Clean up constructor_tests 2023-04-18 13:38:24 -07:00
Christian Mazakas 68c018fda6 Clean up usage of decltype to help msvc-14.3 2023-04-18 12:30:34 -07:00
Christian Mazakas a9ad06139e Add move constructor 2023-04-18 12:00:08 -07:00
joaquintides 9a0e5e9ea8 dispensed with std::array 2023-04-15 12:13:40 +02:00
joaquintides b1a52d0236 explicitly declared guards' copy ctors as it prevents VS from doing spurious copies (!) 2023-04-15 09:51:01 +02:00
joaquintides 1723358298 refactored to deICE Clang<=3.8 and for clarity 2023-04-14 10:48:26 +02:00
Christian Mazakas 6bf0e93a41 Remove size check as the insertion thread may not have started by the time the check happens 2023-04-13 13:30:56 -07:00
joaquintides 0e5ef25a69 added workaround for GCC<=5 having problems with return {...} syntax 2023-04-13 19:31:20 +02:00
joaquintides ab84a922cf added workaround for lack of std::is_trivially_copy_assignable in GCC<5.0 2023-04-13 13:00:29 +02:00
Christian Mazakas d8bc38f154 Clean up tag dispatch for group_type copy assignment 2023-04-12 14:41:36 -07:00
Christian Mazakas ecd4a82ed5 Add test for select_on_container_copy_construction Allocator requirement 2023-04-12 13:58:01 -07:00
Christian Mazakas 0898219edc Test empty visitation 2023-04-12 12:23:16 -07:00
Christian Mazakas d9b62d24dc Begin work on constructors 2023-04-12 12:22:59 -07:00
Christian Mazakas b771278813 Fix copy assignment warning in gcc 2023-04-12 12:22:38 -07:00
joaquintides 2226a7238d adjusted arg constness in erase and erase_if 2023-04-11 16:32:47 +02:00
joaquintides 00c6a93a56 restored invocability static asserts 2023-04-10 11:03:11 +02:00
joaquintides 8ea1cbbd64 s/BOOST_ASSERT/BOOST_TEST to avoid unused var warnings 2023-04-10 10:17:05 +02:00
joaquintides 81a47ca88d updated more tests affected by change in position of f 2023-04-10 09:05:19 +02:00
joaquintides c54151769f updated insert_or_assign impls to f-last interface 2023-04-09 21:56:12 +02:00
joaquintides e73d5ecdd1 updated tests affected by change in position of f 2023-04-09 20:41:15 +02:00
joaquintides 8a4e987030 made f the last argument in all *_or_[c]visit functions 2023-04-09 20:20:55 +02:00
Christian Mazakas 8544d9f3c8 Clean up test/Jamfile 2023-04-07 12:40:06 -07:00
Christian Mazakas ed80cb14e2 Add visit_all() 2023-04-07 12:39:39 -07:00
Christian Mazakas 91eb2ddbd1 Fix sign comparison warning 2023-04-07 12:39:13 -07:00
Christian Mazakas 3bd28dc3d7 Implement basic version of single element visitation 2023-04-06 13:42:57 -07:00
Christian Mazakas e4d2da40f2 Add missing static_assert()s 2023-04-06 09:36:08 -07:00
Christian Mazakas a004e71dd0 Implement emplace, emplace_or_[c]visit 2023-04-05 13:21:18 -07:00
Christian Mazakas ddcab1c171 Add impl of try_emplace, try_emplace_or_[c]visit 2023-04-04 15:16:52 -07:00
Christian Mazakas e3cbf03f47 Commit missing changes to erase_if() 2023-04-03 14:11:51 -07:00
Christian Mazakas f8fbbc3b76 Add ExecutionPolicy overload of erase_if 2023-04-03 13:15:15 -07:00
Christian Mazakas 52bf2bf7a2 Add insert_or_cvisit() overloads 2023-04-03 08:14:21 -07:00
Christian Mazakas cf39fc4c38 Implement transparent erase_if, unary erase_if 2023-04-03 08:14:21 -07:00
Christian Mazakas 1309361a02 Remove erroneous move() calls 2023-04-03 08:14:21 -07:00
joaquintides 946491489e added workaround for lack of std::is_trivially_constructible in GCC<5.0 2023-04-01 12:06:39 +02:00
joaquintides 19202d2a8d avoided memset-related GCC warning 2023-04-01 09:09:22 +02:00
joaquintides 3be466966d avoided copy elision for the initialization of an atomic 2023-03-31 19:46:59 +02:00
joaquintides d22787c218 use memset for group array initialization only when permissible 2023-03-31 19:36:31 +02:00
joaquintides b86dee9a18 removed BOOST_UNORDERED_EMBEDDED_GROUP_ACCESS support 2023-03-31 18:52:00 +02:00
joaquintides 863984a7c8 fixed erase_if(x,f) return value calculation 2023-03-31 17:28:21 +02:00
joaquintides e9b3ad4a5f reverted 2023-03-31 13:07:02 +02:00
joaquintides e00d700457 experiment with unaligned embedded group 2023-03-31 11:32:29 +02:00
Christian Mazakas 53e20a2a1b Add transparent impl of erase() 2023-03-30 12:41:31 -07:00
Christian Mazakas af4cdf8fab Shorten "transparent" to "transp" 2023-03-30 12:41:18 -07:00
Christian Mazakas 7003e91d44 Harden erase() tests 2023-03-30 11:43:12 -07:00
joaquintides e86bb5cce2 stylistic 2023-03-30 14:50:19 +02:00
joaquintides 6a4728add0 removed implementation of scoped_bilock copy ctor 2023-03-30 12:39:10 +02:00
joaquintides f02afbc815 simplified scoped_bilock impl 2023-03-30 12:13:58 +02:00
joaquintides 6399dfd1e4 made erase_if returned count exact 2023-03-30 10:08:38 +02:00
Christian Mazakas f468fb77e0 Hopefully fix sign-conversion warnings 2023-03-29 15:55:22 -07:00
Christian Mazakas 5e316ebc88 Add initial draft of erase() 2023-03-29 15:00:54 -07:00
Christian Mazakas 0c90585511 Refactor myriad test helpers into separate file 2023-03-29 15:00:32 -07:00
Christian Mazakas 13b4fd7133 Fix sign-compare warnings 2023-03-29 12:16:09 -07:00
joaquintides 27b4c62bd2 fixed initialization of concurrent_table::thread:counter 2023-03-29 20:35:48 +02:00
joaquintides 846de7ca82 added cvisit overloads to concurrent_table 2023-03-29 19:17:57 +02:00
joaquintides 62cf58d1ca sprinkled some inlines 2023-03-29 18:29:15 +02:00
joaquintides 4482031329 made concurrent_table::thread_counter static 2023-03-29 16:45:15 +02:00
Christian Mazakas 7b1af37b9c Update RAII counters to use unsigned integers instead of signed 2023-03-28 14:37:52 -07:00
Christian Mazakas 1c48f665ea Add alias target for foa container tests 2023-03-28 14:30:29 -07:00
Christian Mazakas e4072747bb Implement insert_or_visit() 2023-03-28 14:28:42 -07:00
Christian Mazakas ce674c8007 Add transparent insert_or_assign 2023-03-27 15:06:28 -07:00
joaquintides 45ebc1e555 fixed call ambiguities in insert_or_visit 2023-03-27 13:39:14 +02:00
joaquintides 02c3bd4072 stylistic 2023-03-27 11:32:07 +02:00
joaquintides 5f4172c13f refactored visitation an const-protection of args to visitation functions 2023-03-27 10:17:25 +02:00
joaquintides 40c5c6907a fixed const-casting in erase_if(foa::table&,...) 2023-03-25 18:50:25 +01:00
joaquintides 8dbe380d6c changed predicate arg to an lvalue ref to avoid ADL ambiguities 2023-03-25 17:37:02 +01:00
joaquintides 37ff9de0db fixed remaining errors in erase_if(foa::table&,...) 2023-03-25 17:35:37 +01:00
joaquintides ae9f35073b avoided sign conversion warning 2023-03-25 17:17:00 +01:00
joaquintides a85df27d4c fixed previous commit 2023-03-25 12:47:19 +01:00
joaquintides 88c70b885d fixed previous commit 2023-03-25 12:37:10 +01:00
joaquintides 240f5f94b1 moved erase_if_impl from foa::table_core into foa::table 2023-03-25 12:31:49 +01:00
joaquintides 7415721f7d completed shared/exclusive visitation 2023-03-25 12:28:44 +01:00
Christian Mazakas 8b5539756b Partially implement insert_or_assign() 2023-03-24 14:35:01 -07:00
joaquintides 49ac1035bb (partially) implemented shared/exclusive visitation 2023-03-24 19:49:59 +01:00
joaquintides d085c40541 relaxed all ops of atomic_integral 2023-03-24 18:37:53 +01:00
Christian Mazakas 99d5b9b1bb Avoid unsupported typedef on earlier compilers 2023-03-24 08:57:51 -07:00
Christian Mazakas ad86f9211b Flesh out tests for insertion 2023-03-23 10:59:50 -07:00
Christian Mazakas d9515fdc2f Add insert(initializer_list) 2023-03-23 10:59:50 -07:00
Christian Mazakas eedbc6bcf7 Add missing stdlib= to drone.sh 2023-03-23 10:59:50 -07:00
joaquintides 297fb2e8c4 abode by TSan on Neon 2023-03-23 09:41:36 +01:00
Christian Mazakas 99eab2dfb1 Fix missing quotes in jsonnet file 2023-03-22 15:14:16 -07:00
Christian Mazakas 1070c9b69e Attempt to add tsan to Drone CI 2023-03-22 15:11:55 -07:00
Christian Mazakas 9e6b5a7e43 Add tsan OSX job 2023-03-22 15:07:36 -07:00
Christian Mazakas e1f11e840c update clang tsan job to use libc++ 2023-03-22 13:55:42 -07:00
Christian Mazakas f2d4f1968b Explicitly add B2_TARGETS to script invocation 2023-03-22 12:49:05 -07:00
joaquintides 4a7116b996 partially restored prior commit to nail down problem with Clang x86 2023-03-22 20:16:09 +01:00
joaquintides 41584e73d9 partially reverted prior commit to nail down problem with Clang x86 2023-03-22 20:09:53 +01:00
joaquintides 52453cb5a4 Merge branch 'feature/cfoa' of https://github.com/boostorg/unordered into feature/cfoa 2023-03-22 19:25:10 +01:00
joaquintides 44b918896d tried relaxing atomic operations 2023-03-22 19:24:46 +01:00
Christian Mazakas ff356ac083 Fix typo in GHA ci.yml 2023-03-22 11:20:03 -07:00
Christian Mazakas 3064801a89 Attempt to add tsan to GHA 2023-03-22 11:19:13 -07:00
Christian Mazakas e9d9f19e76 Flesh out insert_tests 2023-03-22 10:47:01 -07:00
Christian Mazakas 9b85c38e37 Add rehash, add missing move in insert() 2023-03-22 10:46:19 -07:00
joaquintides 0b2829c793 hardened TSan detection 2023-03-21 20:03:35 +01:00
joaquintides a9c2d1daa5 abode by TSan on SSE2 2023-03-21 19:41:55 +01:00
joaquintides d29815b3a8 group-synchronized earlier in unprotected_norehash_emplace_or_visit 2023-03-21 14:47:43 +01:00
joaquintides 7ed7878278 fixed 3df435d4d3 fix 2023-03-21 11:50:21 +01:00
Christian Mazakas 4c927cd2f2 Implement myriad insert() overloads, get first threaded test working 2023-03-20 16:00:54 -07:00
joaquintides 2fae05ed31 made concurrent_table use table_core's automatic capacity growth formula 2023-03-20 12:43:57 -07:00
joaquintides 41abb96d83 avoided sign conversion warning 2023-03-20 12:43:57 -07:00
joaquintides 63f51e7c41 fixed concurrent_table_arrays::delete_ 2023-03-20 12:43:57 -07:00
joaquintides ba4f321934 removed unused typedef 2023-03-20 12:43:57 -07:00
joaquintides 68563fab72 removed unused typedef 2023-03-20 12:43:57 -07:00
joaquintides d8e9503869 made table_arrays non-aggregate for the benefit of older compilers 2023-03-20 12:43:57 -07:00
joaquintides 86e998cbc7 fixed UB in dummy_group_accesses 2023-03-20 12:43:57 -07:00
joaquintides ce722e4d56 avoided shadowing 2023-03-20 12:43:57 -07:00
joaquintides 77eaab4803 provided concurrent_table_arrays ctor to satisfy older compilers 2023-03-20 12:43:57 -07:00
joaquintides 96562f4c3b hardened non-embedded group_access 2023-03-20 12:43:57 -07:00
joaquintides d66ceaf7fe avoided std::atomic_* aliases 2023-03-20 12:43:57 -07:00
joaquintides 2e44769247 added preliminary version of non-embedded group_access 2023-03-20 12:43:57 -07:00
joaquintides e64fd45760 removed inefficient group15::match_really_occupied 2023-03-20 12:43:57 -07:00
joaquintides 454fc7501e extended shutting down VS C4324 warning 2023-03-20 12:43:57 -07:00
joaquintides a89c62728a removed unnecessary mutable 2023-03-20 12:43:57 -07:00
joaquintides 1d8c0f91f8 avoided name hiding 2023-03-20 12:43:57 -07:00
joaquintides 25ba8e4dfc shut down VS C4324 warning 2023-03-20 12:43:57 -07:00
joaquintides fb4437219f fixed memory ordering in atomic_integral 2023-03-20 12:43:57 -07:00
joaquintides 0d1fdae84c implemented safe concurrent_table::load_factor 2023-03-20 12:43:57 -07:00
joaquintides c5debf11cf added explicit disabling/enabling of parallel algorithms 2023-03-20 12:43:57 -07:00
Christian Mazakas 9e4f89aa43 Continue fleshing out insert_tests 2023-03-20 12:43:57 -07:00
Christian Mazakas 12935eb695 Add minimal prototype of concurrent_flat_map 2023-03-20 12:43:57 -07:00
Christian Mazakas 2386188d69 Relocate macros needed by test suite into detail/fwd.hpp 2023-03-20 12:43:57 -07:00
Christian Mazakas e4b32f7f5e Update rw_spinlock impl to mirror github.com/pdimov's impl 2023-03-20 12:43:57 -07:00
Christian Mazakas 3df435d4d3 Temporarily suppress warning about memset'ing non-trivial types by using a constructor loop 2023-03-20 12:43:57 -07:00
Christian Mazakas ed076facc4 Stabilize ABI of cacheline_protected by hardcoding its alignment to 64 2023-03-20 12:43:57 -07:00
Christian Mazakas b09c5bdcf0 Add missing <execution> header 2023-03-20 12:43:57 -07:00
Christian Mazakas 6abb94bb68 Silence unused variable warnings in for_all_elements_exec 2023-03-20 12:43:57 -07:00
Christian Mazakas d2b1f095c8 Update atomic_uint nsdmi to eschew potentially deleted copy constructor 2023-03-20 12:43:57 -07:00
joaquintides 3d34571654 tried yest another variation for for_all_elements_while 2023-03-20 12:43:57 -07:00
joaquintides d683b3ac3e disabled use of match_really_occupied in for_all_elements_while 2023-03-20 12:43:57 -07:00
joaquintides e16a8737f4 shut down GCC Winterference-size 2023-03-20 12:43:57 -07:00
joaquintides 8f348aea26 temporarily disabled parallel algorithms 2023-03-20 12:43:57 -07:00
joaquintides 9a8ba2bdff re-enabled match_really_occupied in for_all_elements_while 2023-03-20 12:43:57 -07:00
joaquintides 3aaefdcc78 completed concurrent_table's API 2023-03-20 12:43:57 -07:00
joaquintides ca59ed8c50 avoided usage of match_really_occupied on for_all_elements_while (used by rehash) 2023-03-20 12:43:57 -07:00
joaquintides ff78ef25c0 removed unnecessary forwards and captures 2023-03-20 12:43:57 -07:00
joaquintides ec487b5c6c stylistic 2023-03-20 12:43:57 -07:00
joaquintides 0a03ed8170 weakened atomic_integral ops, fixed some trivial bugs 2023-03-20 12:43:57 -07:00
joaquintides 5881dcc2b2 done reverse unlock in multimutex 2023-03-20 12:43:57 -07:00
joaquintides 2070cbe2cc fixed regression in table_core(table_core&&) 2023-03-20 12:43:57 -07:00
joaquintides 8198b9c57c dropped erroneous call to user-provided f 2023-03-20 12:43:57 -07:00
joaquintides c35e9fc631 made slot setting on insertion exception safe 2023-03-20 12:43:57 -07:00
joaquintides f244ba55de switched from available to size_ 2023-03-20 12:43:57 -07:00
joaquintides 5e225fe46c micro-optimized reserve_available 2023-03-20 12:43:57 -07:00
joaquintides 06535e518e uploaded first draft of foa::concurrent_table 2023-03-20 12:43:57 -07:00
joaquintides 81584c22cf swapped member order in alloc_cted_insert_type to suppress padding warnings 2023-03-20 12:43:57 -07:00
joaquintides 83040211e8 recovered warninng supression pragmas 2023-03-20 12:43:56 -07:00
joaquintides 4ed301df97 refactored insert_type creation in emplace 2023-03-20 12:43:56 -07:00
joaquintides 8010f506a6 added SizeImpl and fixed some size arithmetic bugs 2023-03-20 12:43:56 -07:00
joaquintides 840ea1ce5c refactored some more for future concurrent_table 2023-03-20 12:43:56 -07:00
joaquintides 447306bfb9 stylistic 2023-03-20 12:43:56 -07:00
joaquintides 4569c1bec0 changed type of N to std::size_t 2023-03-20 12:43:56 -07:00
joaquintides 37ad547e18 more fixed sign issues in Neon ops 2023-03-20 12:43:56 -07:00
joaquintides e037ee8d82 fixed syntax error 2023-03-20 12:43:56 -07:00
joaquintides 9bbadfdb34 fixed sign issues in Neon ops 2023-03-20 12:43:56 -07:00
joaquintides c1b63f68c8 added missing op to plain_integral 2023-03-20 12:43:56 -07:00
joaquintides 0590e3bf8c typo 2023-03-20 12:43:56 -07:00
joaquintides 8f17f5f548 fixed static_assert 2023-03-20 12:43:56 -07:00
joaquintides 3a8bea9e1d parameterized group15 and core 2023-03-20 12:43:56 -07:00
joaquintides b33b354818 extended caae8eb9ac9a47dd224b81f22efe8ea34b084d54 to rest of "using typename"s 2023-03-20 12:43:56 -07:00
joaquintides b08837b93e added missing template keyword 2023-03-20 12:43:56 -07:00
joaquintides b3b840df30 extended Wshadow supression to foa::table 2023-03-20 12:43:56 -07:00
joaquintides 2ef47f0813 reformulated using to workaround VS problem 2023-03-20 12:43:56 -07:00
joaquintides 27f5513911 published max_load_factor 2023-03-20 12:43:56 -07:00
joaquintides a5100a9d35 fixed incomplete porting of PR187 2023-03-20 12:43:56 -07:00
joaquintides ccbc639677 made table_core protected interface public 2023-03-20 12:43:56 -07:00
joaquintides 18725a682d fixed previous fix 2023-03-20 12:43:56 -07:00
joaquintides dac1135199 fixed usage syntax for destroy_on_exit 2023-03-20 12:43:56 -07:00
joaquintides ead55a8938 s/noexcept(...)/noexcept(noexcept(...)) 2023-03-20 12:43:56 -07:00
joaquintides 8f39001ff0 fixed trivial syntax error 2023-03-20 12:43:56 -07:00
joaquintides 8b056b902e split foa::table in table and core in preparation for foa::concurrent_table 2023-03-20 12:43:56 -07:00
Christian Mazakas 5724adbbe9 Merge pull request #187 from boostorg/feature/foa_fast_iteration
Feature/foa fast iteration
2023-03-20 12:43:29 -07:00
joaquintides 1c5640cfbe s/BOOST_UNORDERED_ASSUME/BOOST_ASSERT 2023-03-08 19:12:46 +01:00
joaquintides 4d982e133e added is-not-end assumption on table_iterator::increment 2023-03-08 18:29:55 +01:00
joaquintides 1bf193b8fc updated release notes 2023-03-06 21:46:42 +01:00
joaquintides 7faec20f26 Merge branch 'feature/foa_fast_iteration' of https://github.com/boostorg/unordered into feature/foa_fast_iteration 2023-03-06 21:44:55 +01:00
joaquintides ced8b45add fixed UB with begin when using fast iteration 2023-03-06 12:44:10 -08:00
joaquintides 15c9bc40f7 added fast table_iterator::increment variant for regular-layout groups 2023-03-06 12:44:10 -08:00
joaquintides 9aedb95296 Merge pull request #185 from boostorg/fix/uses-allocator 2023-03-06 21:38:13 +01:00
Christian Mazakas e56cb4418c Remove extraneous information from TypePolicy's construct, destroy docs 2023-03-06 09:48:26 -08:00
Christian Mazakas 1ae21c6d1a Remove extraneous typedef from destroy_on_exit 2023-03-06 09:47:33 -08:00
Peter Dimov 82acad3794 Increase GHA timeout to 180 minutes 2023-03-06 17:04:06 +02:00
joaquintides c50461528c fixed UB with begin when using fast iteration 2023-03-06 13:50:25 +01:00
joaquintides bd08e49255 added fast table_iterator::increment variant for regular-layout groups 2023-03-06 11:56:49 +01:00
Peter Dimov dbba3cec7b Add C++03 deprecation notice 2023-03-06 09:43:45 +02:00
Christian Mazakas 6c5b5b3186 Update comments on type policy to include changes to construct, destroy and move 2023-03-03 11:33:32 -08:00
Christian Mazakas 47dd02a4b3 Add bugfix notes to release notes 2023-03-03 11:18:41 -08:00
Christian Mazakas 8429d1a6aa Refactor node_handle to directly store element_type by modularizing and extending it 2023-03-03 11:17:38 -08:00
Christian Mazakas 86d3f9f632 Refactor move(init_type&) to handle defects in C++20 versions of libstdc++ for gcc versions <=12 2023-03-03 11:17:38 -08:00
Christian Mazakas 48e92afd92 Refactor drop_guard into destroy_on_exit 2023-03-03 11:17:38 -08:00
Christian Mazakas 5a5c31de35 Rename storage to uninitialized_storage 2023-03-03 11:17:38 -08:00
Christian Mazakas 088941dc7b Clean up usage of Allocators for flat containers 2023-03-03 11:17:38 -08:00
Christian Mazakas 5f6f1691e5 Format node_map 2023-03-03 11:17:38 -08:00
Christian Mazakas 45d6c07be6 Update emplace() to no longer needlessly rebind its allocator 2023-03-03 11:17:38 -08:00
Christian Mazakas 5c63cd2bdb Update drone to run using the same branches as listed in GHA's ci.yml 2023-03-03 11:17:38 -08:00
Christian Mazakas 86318c1e88 Update foa to construct stack-locals with the user's Allocator during emplace() 2023-03-03 11:17:38 -08:00
Christian Mazakas 0ff1fa0f6e Add test that ensures proper uses-allocator construction is followed 2023-03-03 11:17:38 -08:00
Christian Mazakas d90bf0c438 Merge pull request #186 from boostorg/cxx03-deprecation
Update changelog to reflect planned C++03 deprecation
2023-03-01 09:53:24 -08:00
Christian Mazakas dc63438e22 Add missing end-of-list marker to compliance section 2023-03-01 09:37:49 -08:00
Christian Mazakas 0a3ac26f00 Update changelog to reflect planned C++03 deprecation 2023-02-28 15:44:44 -08:00
joaquintides c64ed1caa3 Feature/SIMD config (#184)
* added BOOST_UNORDERED_[ENABLE|DISABLE]_[SSE2|LITTLE_ENDIAN_NEON]
* renamed and refactored
* removed #undefs of SIMD usage macros
2023-02-26 16:39:50 +01:00
joaquintides efc33b18be Merge pull request #182 from boostorg/feature/unordered_node_map_docs
Feature/unordered node map docs
2023-02-25 10:20:05 +01:00
joaquintides c189d8d5e8 updated boost::unordered_(flat|node)_map benchmarks 2023-02-25 10:03:10 +01:00
joaquintides 886227db3b updated link to P2363 2023-02-24 09:58:51 +01:00
joaquintides 719eb90d03 fixed copy&paste error 2023-02-24 08:59:45 +01:00
joaquintides fadb686bc5 added boost::unordered_node_map to benchmarks 2023-02-23 20:17:01 +01:00
Christian Mazakas aebefaef0d Merge pull request #183 from boostorg/unordered_node_map_rc
Node Map Release Candidate
2023-02-22 08:39:59 -08:00
Christian Mazakas 63a564e8ff Update foa comments for changes in TypePolicy 2023-02-21 11:12:33 -08:00
Christian Mazakas 16c1593368 Remove extraneous copy constructor from flat set's type policy 2023-02-21 07:53:09 -08:00
Christian Mazakas 53580a3070 Fix bug in hint-based overload for node_type insertion where elements were erroneously reallocated 2023-02-19 21:26:06 -08:00
Christian Mazakas d26aeed68f Update node_handle tests to cover address stability for inserted nodes when using the hint-based overloads 2023-02-19 21:26:06 -08:00
Christian Mazakas 73eefd2b5d Rename Node{X}Types to TypePolicy in node-based foa containers 2023-02-19 21:26:06 -08:00
Christian Mazakas 3df600d069 Remove extraneous construct() overloads from the flat foa-based containers 2023-02-19 21:26:06 -08:00
Christian Mazakas a1adacdfe2 Refactor erase_on_exit to use a const_iterator so callsites can avoid a const cast 2023-02-19 21:26:06 -08:00
Christian Mazakas f405fa9118 Rename foa's emplace_dispatch to emplace_value 2023-02-19 21:26:06 -08:00
Christian Mazakas 7b5d6e7c29 Fix typos in code comments in foa 2023-02-19 21:26:06 -08:00
Christian Mazakas 7d0ba7178e Clean up naming in delete_ impl to use storage_traits for foa 2023-02-19 21:26:06 -08:00
Christian Mazakas df5e2c3aca Remove extraneous commented out code from foa 2023-02-19 21:26:06 -08:00
Christian Mazakas 343e344582 Reify implementation of node_handle's swap 2023-02-19 21:26:06 -08:00
Christian Mazakas 34ed463bea Reify implementation of node_handle's move assignment operator 2023-02-19 21:26:06 -08:00
Christian Mazakas 5986f75d16 Refactor node_handle's clear() member function to reset() 2023-02-19 21:26:06 -08:00
Christian Mazakas b08810c0c4 Add missing copyright notice to foa.hpp 2023-02-19 21:26:06 -08:00
Christian Mazakas 7c608ae7bc Include missing extract tests in transparent_tests 2023-02-19 21:25:25 -08:00
joaquintides d51cee35d5 updated open-addressing benchmark info 2023-02-18 12:31:26 +01:00
joaquintides d0289b4c7b editorial 2023-02-18 12:13:14 +01:00
Christian Mazakas bc7ba9cb3b Implement internal foa updates + node_handle for node-based containers 2023-02-14 10:55:35 -08:00
Christian Mazakas 6b10c8a4d3 Update unordered_flat_map for new type traits 2023-02-14 10:55:35 -08:00
Christian Mazakas b9805c7309 Update unordered_flat_set for new type traits 2023-02-14 10:55:35 -08:00
Christian Mazakas 844460fd22 Add unordered_node_map 2023-02-14 10:55:35 -08:00
Christian Mazakas a600278d27 Add unordered_node_set 2023-02-14 10:55:35 -08:00
Christian Mazakas 4398ffe8c9 Add foa-based node containers to node_handle_tests 2023-02-14 10:55:35 -08:00
Christian Mazakas eed1eeedeb Add foa-based node containers to extract_tests 2023-02-14 10:55:35 -08:00
Christian Mazakas 8c0c230f78 Add foa-based node containers to the exception tests 2023-02-14 10:55:35 -08:00
Christian Mazakas e96ec1e79a Add foa-based node containers to transparent_tests 2023-02-14 10:55:35 -08:00
Christian Mazakas be7dd51fab Add foa-based node containers to swap_tests 2023-02-14 10:55:35 -08:00
Christian Mazakas 4dec95840b Add foa-based node containers to simple_tests 2023-02-14 10:55:35 -08:00
Christian Mazakas d44fb36581 Add foa-based node containers to scoped_allocator tests 2023-02-14 10:55:35 -08:00
Christian Mazakas 9d3bbe27db Add foa-based node containers to scary_tests 2023-02-14 10:55:35 -08:00
Christian Mazakas 13ed4afc95 Add foa-based node containers to reserve_tests 2023-02-14 10:55:35 -08:00
Christian Mazakas b3f54e74a7 Add foa-based node containers to rehash_tests 2023-02-14 10:55:35 -08:00
Christian Mazakas d6374e2718 Add foa-based node containers to post_move_tests 2023-02-14 10:55:35 -08:00
Christian Mazakas 74156b6b06 Add foa-based node containers to noexcept_tests 2023-02-14 10:55:35 -08:00
Christian Mazakas 965048d3e6 Add foa-based node containers to move_tests 2023-02-14 10:55:35 -08:00
Christian Mazakas 36dfe6b98b Add foa-based node containers to merge_tests 2023-02-14 10:55:35 -08:00
Christian Mazakas e4e89bdbde Add foa-based node containers to max_load_tests 2023-02-14 10:55:35 -08:00
Christian Mazakas 85a9d8d81d Add foa-based node containers to load_factor_tests 2023-02-14 10:55:35 -08:00
Christian Mazakas b21ad13bbb Add foa-based node containers to link tests 2023-02-14 10:55:35 -08:00
Christian Mazakas f0d10308e3 Add foa-based node containers to insert_tests 2023-02-14 10:55:35 -08:00
Christian Mazakas f240e587cb Add foa-based node containers to insert_hint_tests 2023-02-14 10:55:35 -08:00
Christian Mazakas 2a7dc9e40e Add foa-based node containers to init_type_insert_tests 2023-02-14 10:55:35 -08:00
Christian Mazakas 99b8d0063b Add foa-based node containers to incomplete_test 2023-02-14 10:55:35 -08:00
Christian Mazakas c6aff9278a Add foa-based node containers to fwd_set_test 2023-02-14 10:55:35 -08:00
Christian Mazakas 129497133e Add foa-based node containers to fwd_map_test 2023-02-14 10:55:35 -08:00
Christian Mazakas eaa9ff0cec Add foa-based node containers to find_tests 2023-02-14 10:55:35 -08:00
Christian Mazakas fa735864a3 Add foa-based node containers to erase_tests 2023-02-14 10:55:35 -08:00
Christian Mazakas 0ce180e19b Add foa-based node containers to erase_if tests 2023-02-14 10:55:35 -08:00
Christian Mazakas e578b7eee3 Add foa-based node containers to equivalent_keys_tests 2023-02-14 10:55:35 -08:00
Christian Mazakas 1844695b0c Add foa-based node containers to equality_tests 2023-02-14 10:55:35 -08:00
Christian Mazakas 54b9adbdbc Add foa-based node containers to emplace_tests 2023-02-14 10:55:35 -08:00
Christian Mazakas 8b71656cd7 Add foa-based node containers to copy_tests 2023-02-14 10:55:35 -08:00
Christian Mazakas a858ef5688 Add foa-based node containers to contains_tests 2023-02-14 10:55:35 -08:00
Christian Mazakas 18395c8d21 Add foa-based node containers to constructor_tests 2023-02-14 10:55:35 -08:00
Christian Mazakas e68ec5c9a9 Add foa-based node containers to compile_set tests 2023-02-14 10:55:35 -08:00
Christian Mazakas d3b264345d Add foa-based node containers to compile_map tests 2023-02-14 10:55:35 -08:00
Christian Mazakas f1e1733c06 Add foa-based node containers to at_tests 2023-02-14 10:55:35 -08:00
Christian Mazakas 66ffbdb881 Add foa-based node containers to assign_tests 2023-02-14 10:55:35 -08:00
Christian Mazakas f7f5466ed2 Add necessary #include's and typedefs for test suite to use foa-based node containers 2023-02-14 10:55:35 -08:00
Christian Mazakas 83410fcabe Add -Wunused-template to tests run with the clang-14 toolset 2023-02-14 10:55:35 -08:00
Christian Mazakas 1ee91d494d Fix bug in impl of test::exception::less::operator() 2023-02-14 10:55:35 -08:00
Christian Mazakas e8fe550d6c Add less_tests to track bug in test::exception::less::operator() impl 2023-02-14 10:55:35 -08:00
Christian Mazakas 5b0fe7f655 Update test::compare_range to use BOOST_TEST_ALL_WITH 2023-02-14 10:55:35 -08:00
Christian Mazakas ba439437bf Add osx 12 to gha ci matrix 2023-02-14 10:55:35 -08:00
joaquintides aa6eef135e editorial 2023-02-13 18:40:36 +01:00
joaquintides 1dd085daaa added unordered_node_[map|set] containers to the tutorial 2023-02-13 13:29:52 +01:00
joaquintides 2a8f50bcb7 added reference for unordered_node_set 2023-02-12 18:32:26 +01:00
joaquintides 866c58a879 typo 2023-02-12 17:35:02 +01:00
joaquintides c9a75a56c2 added reference for unordered_node_map 2023-02-11 13:23:34 +01:00
joaquintides e1861aa3d9 updated copyright notes 2023-02-11 13:23:12 +01:00
joaquintides 242c7cb93a updated release notes 2023-02-11 13:22:55 +01:00
joaquintides 0597463902 Merge pull request #181 from boostorg/feature/mulx_docs
Documented the new mulx-based mixing algorithm
2023-02-09 09:06:51 +01:00
joaquintides 14d80725eb documented the new mulx-based mixing algorithm 2023-02-08 20:07:23 +01:00
125 changed files with 15121 additions and 2329 deletions
+22 -1
View File
@@ -6,11 +6,12 @@ local library = "unordered";
local triggers =
{
branch: [ "master", "develop", "feature/*", "bugfix/*" ]
branch: [ "master", "develop", "feature/*", "bugfix/*", "fix/*", "pr/*" ]
};
local ubsan = { UBSAN: '1', UBSAN_OPTIONS: 'print_stacktrace=1' };
local asan = { ASAN: '1' };
local tsan = { TSAN: '1' };
local linux_pipeline(name, image, environment, packages = "", sources = [], arch = "amd64") =
{
@@ -224,6 +225,13 @@ local windows_pipeline(name, image, environment, arch = "amd64") =
"g++-12-multilib",
),
linux_pipeline(
"Linux 22.04 GCC 12 64 TSAN (11,14,17,20,2b)",
"cppalliance/droneubuntu2204:1",
{ TOOLSET: 'gcc', COMPILER: 'g++-12', CXXSTD: '11,14,17,20,2b', ADDRMD: '64', TARGET: 'libs/unordered/test//cfoa_tests' } + tsan,
"g++-12-multilib",
),
linux_pipeline(
"Linux 16.04 Clang 3.5",
"cppalliance/droneubuntu1604:1",
@@ -343,6 +351,13 @@ local windows_pipeline(name, image, environment, arch = "amd64") =
"clang-14",
),
linux_pipeline(
"Linux 22.04 Clang 14 libc++ 64 TSAN",
"cppalliance/droneubuntu2204:1",
{ TOOLSET: 'clang', COMPILER: 'clang++-14', ADDRMD: '64', TARGET: 'libs/unordered/test//cfoa_tests', CXXSTD: '11,14,17,20', STDLIB: 'libc++' } + tsan,
"clang-14 libc++-14-dev libc++abi-14-dev",
),
linux_pipeline(
"Linux 22.04 Clang 15",
"cppalliance/droneubuntu2204:1",
@@ -362,6 +377,12 @@ local windows_pipeline(name, image, environment, arch = "amd64") =
xcode_version = "13.4.1", osx_version = "monterey", arch = "arm64",
),
macos_pipeline(
"MacOS 12.4 Xcode 13.4.1 TSAN",
{ TOOLSET: 'clang', COMPILER: 'clang++', CXXSTD: '11,14,1z', TARGET: 'libs/unordered/test//cfoa_tests' } + tsan,
xcode_version = "13.4.1", osx_version = "monterey", arch = "arm64",
),
windows_pipeline(
"Windows VS2015 msvc-14.0",
"cppalliance/dronevs2015",
+3 -1
View File
@@ -7,6 +7,8 @@
set -ex
export PATH=~/.local/bin:/usr/local/bin:$PATH
: ${TARGET:="libs/$LIBRARY/test"}
DRONE_BUILD_DIR=$(pwd)
BOOST_BRANCH=develop
@@ -22,4 +24,4 @@ python tools/boostdep/depinst/depinst.py $LIBRARY
./b2 -d0 headers
echo "using $TOOLSET : : $COMPILER ;" > ~/user-config.jam
./b2 -j3 libs/$LIBRARY/test toolset=$TOOLSET cxxstd=$CXXSTD variant=debug,release ${ADDRMD:+address-model=$ADDRMD} ${UBSAN:+undefined-sanitizer=norecover debug-symbols=on} ${ASAN:+address-sanitizer=norecover debug-symbols=on} ${LINKFLAGS:+linkflags=$LINKFLAGS}
./b2 -j3 $TARGET toolset=$TOOLSET cxxstd=$CXXSTD variant=debug,release ${ADDRMD:+address-model=$ADDRMD} ${STDLIB:+stdlib=$STDLIB} ${UBSAN:+undefined-sanitizer=norecover debug-symbols=on} ${ASAN:+address-sanitizer=norecover debug-symbols=on} ${TSAN:+thread-sanitizer=norecover debug-symbols=on} ${LINKFLAGS:+linkflags=$LINKFLAGS}
+45 -214
View File
@@ -19,9 +19,9 @@ on:
- fix/**
- pr/**
concurrency:
group: ${{format('{0}:{1}', github.repository, github.ref)}}
cancel-in-progress: true
#concurrency:
# group: ${{format('{0}:{1}', github.repository, github.ref)}}
# cancel-in-progress: true
env:
GIT_FETCH_JOBS: 8
@@ -42,39 +42,52 @@ jobs:
matrix:
include:
# Linux, gcc
- { compiler: gcc-7, cxxstd: '03,11,14,17', os: ubuntu-20.04, install: 'g++-7' }
- { compiler: gcc-8, cxxstd: '03,11,14,17', os: ubuntu-20.04, install: 'g++-8' }
- { compiler: gcc-9, cxxstd: '03,11,14,17', os: ubuntu-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" }
- { name: Collect coverage, coverage: yes,
compiler: gcc-8, cxxstd: '03,11', os: ubuntu-20.04, install: 'g++-8-multilib', address-model: '32,64', ccache_key: "cov" }
# - { 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" }
# - { name: Collect coverage, coverage: yes,
# compiler: gcc-8, cxxstd: '03,11', os: ubuntu-20.04, install: 'g++-8-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, ccache_key: "tsan-gcc" }
# 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' }
# - { 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' }
# - { 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' }
# 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 (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: "cfoa tsan (clang)", cxxstd: '11,14,17,20,2b', os: ubuntu-22.04, compiler: clang-14,
targets: 'libs/unordered/test//cfoa_tests', thread-sanitize: yes,
stdlib: libc++, install: 'clang-14 libc++-14-dev libc++abi-14-dev', ccache_key: "tsan-clang" }
# OSX, clang
- { compiler: clang, cxxstd: '03,11,14,17,2a', os: macos-11, sanitize: yes }
- { compiler: clang, cxxstd: '11,14', os: macos-11, ccache_key: "11-14" }
- { compiler: clang, cxxstd: '17,2a', os: macos-11, ccache_key: "17-2a" }
- { compiler: clang, cxxstd: '11,14', os: macos-12, sanitize: yes, ccache_key: "11-14" }
- { compiler: clang, cxxstd: '17,2a', os: macos-12, sanitize: yes, ccache_key: "17-2a" }
- { compiler: clang, cxxstd: '11,14', os: macos-12, thread-sanitize: yes, targets: 'libs/unordered/test//cfoa_tests', ccache_key: "tsan-macos-1" }
- { compiler: clang, cxxstd: '17,2a', os: macos-12, thread-sanitize: yes, targets: 'libs/unordered/test//cfoa_tests', ccache_key: "tsan-macos-2" }
timeout-minutes: 120
timeout-minutes: 240
runs-on: ${{matrix.os}}
container: ${{matrix.container}}
env: {B2_USE_CCACHE: 1}
@@ -183,6 +196,8 @@ jobs:
B2_COMPILER: ${{matrix.compiler}}
B2_CXXSTD: ${{matrix.cxxstd}}
B2_SANITIZE: ${{matrix.sanitize}}
B2_TSAN: ${{matrix.thread-sanitize}}
B2_TARGETS: ${{matrix.targets}}
B2_STDLIB: ${{matrix.stdlib}}
# More entries can be added in the same way, see the B2_ARGS assignment in ci/enforce.sh for the possible keys.
# B2_DEFINES: ${{matrix.defines}}
@@ -196,7 +211,7 @@ jobs:
- name: Run tests
if: '!matrix.coverity'
run: ci/build.sh
run: B2_TARGETS=${{matrix.targets}} B2_FLAGS='-l 3600' ci/build.sh
- name: Upload coverage
if: matrix.coverage
@@ -208,187 +223,3 @@ jobs:
env:
COVERITY_SCAN_NOTIFICATION_EMAIL: ${{ secrets.COVERITY_SCAN_NOTIFICATION_EMAIL }}
COVERITY_SCAN_TOKEN: ${{ secrets.COVERITY_SCAN_TOKEN }}
windows:
defaults:
run:
shell: cmd
strategy:
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' }
runs-on: ${{matrix.os}}
steps:
- uses: actions/checkout@v3
- name: Fetch Boost.CI
uses: actions/checkout@v3
with:
repository: boostorg/boost-ci
ref: master
path: boost-ci-cloned
- name: Get CI scripts folder
run: |
REM Copy ci folder if not testing Boost.CI
if "%GITHUB_REPOSITORY%" == "%GITHUB_REPOSITORY:boost-ci=%" xcopy /s /e /q /i /y boost-ci-cloned\ci .\ci
rmdir /s /q boost-ci-cloned
- name: Setup Boost
run: ci\github\install.bat
- name: Run tests
if: '!matrix.coverage'
run: ci\build.bat
env:
B2_TOOLSET: ${{matrix.toolset}}
B2_CXXSTD: ${{matrix.cxxstd}}
B2_ADDRESS_MODEL: ${{matrix.addrmd}}
B2_DEFINES: ${{matrix.defines}}
B2_VARIANT: ${{matrix.variant}}
B2_CXXFLAGS: ${{matrix.cxxflags}}
- name: Collect coverage
shell: powershell
if: matrix.coverage
run: ci\opencppcoverage.ps1
env:
B2_TOOLSET: ${{matrix.toolset}}
B2_CXXSTD: ${{matrix.cxxstd}}
B2_ADDRESS_MODEL: ${{matrix.addrmd}}
- name: Upload coverage
if: matrix.coverage
uses: codecov/codecov-action@v2
with:
files: __out/cobertura.xml
MSYS2:
defaults:
run:
shell: msys2 {0}
strategy:
fail-fast: false
matrix:
include:
- { sys: MINGW32, compiler: gcc, cxxstd: '03,11,17,20' }
- { sys: MINGW64, compiler: gcc, cxxstd: '03,11,17,20' }
runs-on: windows-latest
steps:
- uses: actions/checkout@v3
- name: Setup MSYS2 environment
uses: msys2/setup-msys2@v2
with:
msystem: ${{matrix.sys}}
update: true
install: git python
pacboy: gcc:p cmake:p ninja:p
- name: Fetch Boost.CI
uses: actions/checkout@v3
with:
repository: boostorg/boost-ci
ref: master
path: boost-ci-cloned
- name: Get CI scripts folder
run: |
# Copy ci folder if not testing Boost.CI
[[ "$GITHUB_REPOSITORY" =~ "boost-ci" ]] || cp -r boost-ci-cloned/ci .
rm -rf boost-ci-cloned
- name: Setup Boost
env:
B2_COMPILER: ${{matrix.compiler}}
B2_CXXSTD: ${{matrix.cxxstd}}
B2_SANITIZE: ${{matrix.sanitize}}
B2_STDLIB: ${{matrix.stdlib}}
run: ci/github/install.sh
- name: Run tests
run: ci/build.sh
# Run also the CMake tests to avoid having to setup another matrix for CMake on MSYS
- name: Run CMake tests
run: |
cd "$BOOST_ROOT"
mkdir __build_cmake_test__ && cd __build_cmake_test__
cmake -G Ninja -DCMAKE_BUILD_TYPE=Debug -DBOOST_INCLUDE_LIBRARIES=$SELF -DBUILD_SHARED_LIBS=ON -DBUILD_TESTING=ON -DBoost_VERBOSE=ON ..
cmake --build . --target tests --config Debug -j$B2_JOBS
ctest --output-on-failure --build-config Debug
CMake:
defaults:
run:
shell: bash
strategy:
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' }
timeout-minutes: 120
runs-on: ${{matrix.os}}
steps:
- uses: actions/checkout@v3
- name: Fetch Boost.CI
uses: actions/checkout@v3
with:
repository: boostorg/boost-ci
ref: master
path: boost-ci-cloned
- name: Get CI scripts folder
run: |
# Copy ci folder if not testing Boost.CI
[[ "$GITHUB_REPOSITORY" =~ "boost-ci" ]] || cp -r boost-ci-cloned/ci .
rm -rf boost-ci-cloned
- name: Setup Boost
env: {B2_DONT_BOOTSTRAP: 1}
run: source ci/github/install.sh
- name: Run CMake tests
run: |
cd "$BOOST_ROOT"
mkdir __build_cmake_test__ && cd __build_cmake_test__
cmake -G "${{matrix.generator}}" -DCMAKE_BUILD_TYPE=${{matrix.build_type}} -DBOOST_INCLUDE_LIBRARIES=$SELF -DBUILD_SHARED_LIBS=${{matrix.build_shared}} -DBUILD_TESTING=ON -DBoost_VERBOSE=ON ..
cmake --build . --target tests --config ${{matrix.build_type}} -j$B2_JOBS
ctest --output-on-failure --build-config ${{matrix.build_type}}
- name: Run CMake subdir tests
run: |
cmake_test_folder="$BOOST_ROOT/libs/$SELF/test/cmake_test" # New unified folder
[ -d "$cmake_test_folder" ] || cmake_test_folder="$BOOST_ROOT/libs/$SELF/test/cmake_subdir_test"
cd "$cmake_test_folder"
mkdir __build_cmake_subdir_test__ && cd __build_cmake_subdir_test__
cmake -G "${{matrix.generator}}" -DBOOST_CI_INSTALL_TEST=OFF -DCMAKE_BUILD_TYPE=${{matrix.build_type}} -DBUILD_SHARED_LIBS=${{matrix.build_shared}} ..
cmake --build . --config ${{matrix.build_type}} -j$B2_JOBS
ctest --output-on-failure --build-config ${{matrix.build_type}}
- name: Install Library
run: |
cd "$BOOST_ROOT"
mkdir __build_cmake_install_test__ && cd __build_cmake_install_test__
cmake -G "${{matrix.generator}}" -DCMAKE_BUILD_TYPE=${{matrix.build_type}} -DBOOST_INCLUDE_LIBRARIES=$SELF -DBUILD_SHARED_LIBS=${{matrix.build_shared}} -DCMAKE_INSTALL_PREFIX=~/.local -DBoost_VERBOSE=ON -DBoost_DEBUG=ON ..
cmake --build . --target install --config ${{matrix.build_type}} -j$B2_JOBS
- name: Run CMake install tests
run: |
cmake_test_folder="$BOOST_ROOT/libs/$SELF/test/cmake_test" # New unified folder
[ -d "$cmake_test_folder" ] || cmake_test_folder="$BOOST_ROOT/libs/$SELF/test/cmake_install_test"
cd "$cmake_test_folder"
mkdir __build_cmake_install_test__ && cd __build_cmake_install_test__
cmake -G "${{matrix.generator}}" -DBOOST_CI_INSTALL_TEST=ON -DCMAKE_BUILD_TYPE=${{matrix.build_type}} -DBUILD_SHARED_LIBS=${{matrix.build_shared}} -DCMAKE_PREFIX_PATH=~/.local ..
cmake --build . --config ${{matrix.build_type}} -j$B2_JOBS
ctest --output-on-failure --build-config ${{matrix.build_type}}
+10
View File
@@ -1,4 +1,5 @@
// Copyright 2021 Peter Dimov.
// Copyright 2023 Joaquin M Lopez Munoz.
// Distributed under the Boost Software License, Version 1.0.
// https://www.boost.org/LICENSE_1_0.txt
@@ -6,6 +7,7 @@
#define _SILENCE_CXX20_CISO646_REMOVED_WARNING
#include <boost/unordered_map.hpp>
#include <boost/unordered/unordered_node_map.hpp>
#include <boost/unordered/unordered_flat_map.hpp>
#include <boost/core/detail/splitmix64.hpp>
#include <boost/config.hpp>
@@ -272,6 +274,9 @@ template<class K, class V> using std_unordered_map =
template<class K, class V> using boost_unordered_map =
boost::unordered_map<K, V, boost::hash<K>, std::equal_to<K>, allocator_for<K, V>>;
template<class K, class V> using boost_unordered_node_map =
boost::unordered_node_map<K, V, boost::hash<K>, std::equal_to<K>, allocator_for<K, V>>;
template<class K, class V> using boost_unordered_flat_map =
boost::unordered_flat_map<K, V, boost::hash<K>, std::equal_to<K>, allocator_for<K, V>>;
@@ -345,6 +350,9 @@ std::unordered_map<K, V, fnv1a_hash, std::equal_to<K>, allocator_for<K, V>>;
template<class K, class V> using boost_unordered_map_fnv1a =
boost::unordered_map<K, V, fnv1a_hash, std::equal_to<K>, allocator_for<K, V>>;
template<class K, class V> using boost_unordered_node_map_fnv1a =
boost::unordered_node_map<K, V, fnv1a_hash, std::equal_to<K>, allocator_for<K, V>>;
template<class K, class V> using boost_unordered_flat_map_fnv1a =
boost::unordered_flat_map<K, V, fnv1a_hash, std::equal_to<K>, allocator_for<K, V>>;
@@ -373,6 +381,7 @@ int main()
test<std_unordered_map>( "std::unordered_map" );
test<boost_unordered_map>( "boost::unordered_map" );
test<boost_unordered_node_map>( "boost::unordered_node_map" );
test<boost_unordered_flat_map>( "boost::unordered_flat_map" );
#ifdef HAVE_ANKERL_UNORDERED_DENSE
@@ -390,6 +399,7 @@ int main()
test<std_unordered_map_fnv1a>( "std::unordered_map, FNV-1a" );
test<boost_unordered_map_fnv1a>( "boost::unordered_map, FNV-1a" );
test<boost_unordered_node_map_fnv1a>( "boost::unordered_node_map, FNV-1a" );
test<boost_unordered_flat_map_fnv1a>( "boost::unordered_flat_map, FNV-1a" );
#ifdef HAVE_ANKERL_UNORDERED_DENSE
+10
View File
@@ -1,4 +1,5 @@
// Copyright 2021 Peter Dimov.
// Copyright 2023 Joaquin M Lopez Munoz.
// Distributed under the Boost Software License, Version 1.0.
// https://www.boost.org/LICENSE_1_0.txt
@@ -6,6 +7,7 @@
#define _SILENCE_CXX20_CISO646_REMOVED_WARNING
#include <boost/unordered_map.hpp>
#include <boost/unordered/unordered_node_map.hpp>
#include <boost/unordered/unordered_flat_map.hpp>
#include <boost/core/detail/splitmix64.hpp>
#include <boost/config.hpp>
@@ -273,6 +275,9 @@ template<class K, class V> using std_unordered_map =
template<class K, class V> using boost_unordered_map =
boost::unordered_map<K, V, boost::hash<K>, std::equal_to<K>, allocator_for<K, V>>;
template<class K, class V> using boost_unordered_node_map =
boost::unordered_node_map<K, V, boost::hash<K>, std::equal_to<K>, allocator_for<K, V>>;
template<class K, class V> using boost_unordered_flat_map =
boost::unordered_flat_map<K, V, boost::hash<K>, std::equal_to<K>, allocator_for<K, V>>;
@@ -346,6 +351,9 @@ std::unordered_map<K, V, fnv1a_hash, std::equal_to<K>, allocator_for<K, V>>;
template<class K, class V> using boost_unordered_map_fnv1a =
boost::unordered_map<K, V, fnv1a_hash, std::equal_to<K>, allocator_for<K, V>>;
template<class K, class V> using boost_unordered_node_map_fnv1a =
boost::unordered_node_map<K, V, fnv1a_hash, std::equal_to<K>, allocator_for<K, V>>;
template<class K, class V> using boost_unordered_flat_map_fnv1a =
boost::unordered_flat_map<K, V, fnv1a_hash, std::equal_to<K>, allocator_for<K, V>>;
@@ -374,6 +382,7 @@ int main()
test<std_unordered_map>( "std::unordered_map" );
test<boost_unordered_map>( "boost::unordered_map" );
test<boost_unordered_node_map>( "boost::unordered_node_map" );
test<boost_unordered_flat_map>( "boost::unordered_flat_map" );
#ifdef HAVE_ANKERL_UNORDERED_DENSE
@@ -391,6 +400,7 @@ int main()
test<std_unordered_map_fnv1a>( "std::unordered_map, FNV-1a" );
test<boost_unordered_map_fnv1a>( "boost::unordered_map, FNV-1a" );
test<boost_unordered_node_map_fnv1a>( "boost::unordered_node_map, FNV-1a" );
test<boost_unordered_flat_map_fnv1a>( "boost::unordered_flat_map, FNV-1a" );
#ifdef HAVE_ANKERL_UNORDERED_DENSE
+6
View File
@@ -1,4 +1,5 @@
// Copyright 2021 Peter Dimov.
// Copyright 2023 Joaquin M Lopez Munoz.
// Distributed under the Boost Software License, Version 1.0.
// https://www.boost.org/LICENSE_1_0.txt
@@ -6,6 +7,7 @@
#define _SILENCE_CXX20_CISO646_REMOVED_WARNING
#include <boost/unordered_map.hpp>
#include <boost/unordered/unordered_node_map.hpp>
#include <boost/unordered/unordered_flat_map.hpp>
#include <boost/endian/conversion.hpp>
#include <boost/core/detail/splitmix64.hpp>
@@ -289,6 +291,9 @@ template<class K, class V> using std_unordered_map =
template<class K, class V> using boost_unordered_map =
boost::unordered_map<K, V, boost::hash<K>, std::equal_to<K>, allocator_for<K, V>>;
template<class K, class V> using boost_unordered_node_map =
boost::unordered_node_map<K, V, boost::hash<K>, std::equal_to<K>, allocator_for<K, V>>;
template<class K, class V> using boost_unordered_flat_map =
boost::unordered_flat_map<K, V, boost::hash<K>, std::equal_to<K>, allocator_for<K, V>>;
@@ -315,6 +320,7 @@ int main()
test<std_unordered_map>( "std::unordered_map" );
test<boost_unordered_map>( "boost::unordered_map" );
test<boost_unordered_node_map>( "boost::unordered_node_map" );
test<boost_unordered_flat_map>( "boost::unordered_flat_map" );
#ifdef HAVE_ANKERL_UNORDERED_DENSE
+6
View File
@@ -1,4 +1,5 @@
// Copyright 2021 Peter Dimov.
// Copyright 2023 Joaquin M Lopez Munoz.
// Distributed under the Boost Software License, Version 1.0.
// https://www.boost.org/LICENSE_1_0.txt
@@ -6,6 +7,7 @@
#define _SILENCE_CXX20_CISO646_REMOVED_WARNING
#include <boost/unordered_map.hpp>
#include <boost/unordered/unordered_node_map.hpp>
#include <boost/unordered/unordered_flat_map.hpp>
#include <boost/endian/conversion.hpp>
#include <boost/core/detail/splitmix64.hpp>
@@ -289,6 +291,9 @@ template<class K, class V> using std_unordered_map =
template<class K, class V> using boost_unordered_map =
boost::unordered_map<K, V, boost::hash<K>, std::equal_to<K>, allocator_for<K, V>>;
template<class K, class V> using boost_unordered_node_map =
boost::unordered_node_map<K, V, boost::hash<K>, std::equal_to<K>, allocator_for<K, V>>;
template<class K, class V> using boost_unordered_flat_map =
boost::unordered_flat_map<K, V, boost::hash<K>, std::equal_to<K>, allocator_for<K, V>>;
@@ -325,6 +330,7 @@ int main()
#endif
test<boost_unordered_map>( "boost::unordered_map" );
test<boost_unordered_node_map>( "boost::unordered_node_map" );
test<boost_unordered_flat_map>( "boost::unordered_flat_map" );
#ifdef HAVE_ANKERL_UNORDERED_DENSE
+6
View File
@@ -1,4 +1,5 @@
// Copyright 2021, 2022 Peter Dimov.
// Copyright 2023 Joaquin M Lopez Munoz.
// Distributed under the Boost Software License, Version 1.0.
// https://www.boost.org/LICENSE_1_0.txt
@@ -6,6 +7,7 @@
#define _SILENCE_CXX20_CISO646_REMOVED_WARNING
#include <boost/unordered_map.hpp>
#include <boost/unordered/unordered_node_map.hpp>
#include <boost/unordered/unordered_flat_map.hpp>
#include <boost/endian/conversion.hpp>
#include <boost/core/detail/splitmix64.hpp>
@@ -340,6 +342,9 @@ template<class K, class V> using std_unordered_map =
template<class K, class V> using boost_unordered_map =
boost::unordered_map<K, V, boost::hash<K>, std::equal_to<K>, allocator_for<K, V>>;
template<class K, class V> using boost_unordered_node_map =
boost::unordered_node_map<K, V, boost::hash<K>, std::equal_to<K>, allocator_for<K, V>>;
template<class K, class V> using boost_unordered_flat_map =
boost::unordered_flat_map<K, V, boost::hash<K>, std::equal_to<K>, allocator_for<K, V>>;
@@ -366,6 +371,7 @@ int main()
test<std_unordered_map>( "std::unordered_map" );
test<boost_unordered_map>( "boost::unordered_map" );
test<boost_unordered_node_map>( "boost::unordered_node_map" );
test<boost_unordered_flat_map>( "boost::unordered_flat_map" );
#ifdef HAVE_ANKERL_UNORDERED_DENSE
+10
View File
@@ -1,4 +1,5 @@
// Copyright 2021, 2022 Peter Dimov.
// Copyright 2023 Joaquin M Lopez Munoz.
// Distributed under the Boost Software License, Version 1.0.
// https://www.boost.org/LICENSE_1_0.txt
@@ -6,6 +7,7 @@
#define _SILENCE_CXX20_CISO646_REMOVED_WARNING
#include <boost/unordered_map.hpp>
#include <boost/unordered/unordered_node_map.hpp>
#include <boost/unordered/unordered_flat_map.hpp>
#include <boost/regex.hpp>
#ifdef HAVE_ABSEIL
@@ -227,6 +229,9 @@ template<class K, class V> using std_unordered_map =
template<class K, class V> using boost_unordered_map =
boost::unordered_map<K, V, boost::hash<K>, std::equal_to<K>, allocator_for<K, V>>;
template<class K, class V> using boost_unordered_node_map =
boost::unordered_node_map<K, V, boost::hash<K>, std::equal_to<K>, allocator_for<K, V>>;
template<class K, class V> using boost_unordered_flat_map =
boost::unordered_flat_map<K, V, boost::hash<K>, std::equal_to<K>, allocator_for<K, V>>;
@@ -300,6 +305,9 @@ std::unordered_map<K, V, fnv1a_hash, std::equal_to<K>, allocator_for<K, V>>;
template<class K, class V> using boost_unordered_map_fnv1a =
boost::unordered_map<K, V, fnv1a_hash, std::equal_to<K>, allocator_for<K, V>>;
template<class K, class V> using boost_unordered_node_map_fnv1a =
boost::unordered_node_map<K, V, fnv1a_hash, std::equal_to<K>, allocator_for<K, V>>;
template<class K, class V> using boost_unordered_flat_map_fnv1a =
boost::unordered_flat_map<K, V, fnv1a_hash, std::equal_to<K>, allocator_for<K, V>>;
@@ -328,6 +336,7 @@ int main()
test<std_unordered_map>( "std::unordered_map" );
test<boost_unordered_map>( "boost::unordered_map" );
test<boost_unordered_node_map>( "boost::unordered_node_map" );
test<boost_unordered_flat_map>( "boost::unordered_flat_map" );
#ifdef HAVE_ANKERL_UNORDERED_DENSE
@@ -345,6 +354,7 @@ int main()
test<std_unordered_map_fnv1a>( "std::unordered_map, FNV-1a" );
test<boost_unordered_map_fnv1a>( "boost::unordered_map, FNV-1a" );
test<boost_unordered_node_map_fnv1a>( "boost::unordered_node_map, FNV-1a" );
test<boost_unordered_flat_map_fnv1a>( "boost::unordered_flat_map, FNV-1a" );
#ifdef HAVE_ANKERL_UNORDERED_DENSE
+6
View File
@@ -1,4 +1,5 @@
// Copyright 2021, 2022 Peter Dimov.
// Copyright 2023 Joaquin M Lopez Munoz.
// Distributed under the Boost Software License, Version 1.0.
// https://www.boost.org/LICENSE_1_0.txt
@@ -6,6 +7,7 @@
#define _SILENCE_CXX20_CISO646_REMOVED_WARNING
#include <boost/unordered_map.hpp>
#include <boost/unordered/unordered_node_map.hpp>
#include <boost/unordered/unordered_flat_map.hpp>
#include <boost/regex.hpp>
#ifdef HAVE_ABSEIL
@@ -181,6 +183,9 @@ template<class K, class V> using std_unordered_map =
template<class K, class V> using boost_unordered_map =
boost::unordered_map<K, V, boost::hash<K>, std::equal_to<K>, allocator_for<K, V>>;
template<class K, class V> using boost_unordered_node_map =
boost::unordered_node_map<K, V, boost::hash<K>, std::equal_to<K>, allocator_for<K, V>>;
template<class K, class V> using boost_unordered_flat_map =
boost::unordered_flat_map<K, V, boost::hash<K>, std::equal_to<K>, allocator_for<K, V>>;
@@ -207,6 +212,7 @@ int main()
test<std_unordered_map>( "std::unordered_map" );
test<boost_unordered_map>( "boost::unordered_map" );
test<boost_unordered_node_map>( "boost::unordered_node_map" );
test<boost_unordered_flat_map>( "boost::unordered_flat_map" );
#ifdef HAVE_ANKERL_UNORDERED_DENSE
Binary file not shown.

Before

Width:  |  Height:  |  Size: 36 KiB

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 37 KiB

After

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 40 KiB

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 49 KiB

After

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 37 KiB

After

Width:  |  Height:  |  Size: 51 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 35 KiB

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 40 KiB

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 45 KiB

After

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 38 KiB

After

Width:  |  Height:  |  Size: 51 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 41 KiB

After

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 40 KiB

After

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 43 KiB

After

Width:  |  Height:  |  Size: 53 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 37 KiB

After

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 36 KiB

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 39 KiB

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 44 KiB

After

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 37 KiB

After

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 40 KiB

After

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 41 KiB

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 44 KiB

After

Width:  |  Height:  |  Size: 55 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 37 KiB

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 38 KiB

After

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 44 KiB

After

Width:  |  Height:  |  Size: 51 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 40 KiB

After

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 37 KiB

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 38 KiB

After

Width:  |  Height:  |  Size: 51 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 41 KiB

After

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 43 KiB

After

Width:  |  Height:  |  Size: 51 KiB

+3 -2
View File
@@ -278,13 +278,14 @@ max load factor 5
|===
== boost::unordered_flat_map
== boost::unordered_(flat|node)_map
All benchmarks were created using:
* `https://abseil.io/docs/cpp/guides/container[absl::flat_hash_map^]<uint64_t, uint64_t>`
* `boost::unordered_flat_map<uint64_t, uint64_t>`
* `boost::unordered_map<uint64_t, uint64_t>`
* `boost::unordered_flat_map<uint64_t, uint64_t>`
* `boost::unordered_node_map<uint64_t, uint64_t>`
The source code can be https://github.com/boostorg/boost_unordered_benchmarks/tree/boost_unordered_flat_map[found here^].
+9 -6
View File
@@ -134,7 +134,8 @@ h|*Method* h|*Description*
|Changes the number of buckets so that there at least `n` buckets, and so that the load factor is less than the maximum load factor.
2+^h| *Open-addressing containers only* +
`boost::unordered_flat_set`, `boost::unordered_flat_map`
`boost::unordered_flat_set`, `boost::unordered_flat_map` +
`boost::unordered_node_set`, `boost::unordered_node_map` +
h|*Method* h|*Description*
|`size_type max_load() const`
@@ -160,8 +161,9 @@ change the number of buckets when this happens. Iterators can be
invalidated by calls to `insert`, `rehash` and `reserve`.
As for pointers and references,
they are never invalidated for closed-addressing containers (`boost::unordered_[multi]set`, `boost::unordered_[multi]map`),
but they will when rehashing occurs for open-addressing
they are never invalidated for node-based containers
(`boost::unordered_[multi]set`, `boost::unordered_[multi]map`, `boost::unordered_node_set`, `boost::unordered_node_map`),
but they will when rehashing occurs for
`boost::unordered_flat_set` and `boost::unordered_flat_map`: this is because
these containers store elements directly into their holding buckets, so
when allocating a new bucket array the elements must be transferred by means of move construction.
@@ -252,15 +254,16 @@ xref:#rationale_boostunordered_multiset_and_boostunordered_multimap[correspondin
== Open Addressing Implementation
The diagram shows the basic internal layout of `boost::unordered_flat_map` and
`boost:unordered_flat_set`.
The diagram shows the basic internal layout of `boost::unordered_flat_map`/`unordered_node_map` and
`boost:unordered_flat_set`/`unordered_node_set`.
[#img-foa-layout]
.Open-addressing layout used by Boost.Unordered.
image::foa.png[align=center]
As with all open-addressing containers, elements are stored directly in the bucket array.
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.
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.
+15 -2
View File
@@ -6,10 +6,23 @@
:github-pr-url: https://github.com/boostorg/unordered/pull
:cpp: C++
== Release 1.82.0
== Release 1.83.0
* Sped up iteration of open-addressing containers.
== Release 1.82.0 - Major update
* {cpp}03 support is planned for deprecation. Boost 1.84.0 will no longer support
{cpp}03 mode and {cpp}11 will become the new minimum for using the library.
* Added node-based, open-addressing containers
`boost::unordered_node_map` and `boost::unordered_node_set`.
* Extended heterogeneous lookup to more member functions as specified in
https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2022/p2363r3.html[P2363].
https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2023/p2363r5.html[P2363].
* Replaced the previous post-mixing process for open-addressing containers with
a new algorithm based on extended multiplication by a constant.
* Fixed bug in internal emplace() impl where stack-local types were not properly
constructed using the Allocator of the container which breaks uses-allocator
construction.
== Release 1.81.0 - Major update
+2 -2
View File
@@ -33,8 +33,8 @@
|Iterators, pointers and references to the container's elements are never invalidated.
|<<buckets_iterator_invalidation,Iterators can be invalidated by calls to insert or rehash>>. +
**Closed-addressing containers:** Pointers and references to the container's elements are never invalidated. +
**Open-addressing containers:** Pointers and references to the container's elements are invalidated when rehashing occurs.
**Node-based containers:** Pointers and references to the container's elements are never invalidated. +
**Flat containers:** Pointers and references to the container's elements are invalidated when rehashing occurs.
|Iterators iterate through the container in the order defined by the comparison object.
|Iterators iterate through the container in an arbitrary order, that can change as elements are inserted, although equivalent elements are always adjacent.
+19 -15
View File
@@ -5,9 +5,9 @@
:cpp: C++
== Closed-addressing containers: unordered_[multi]set, unordered_[multi]map
== Closed-addressing containers
The intent of Boost.Unordered is to provide a conformant
`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
@@ -117,27 +117,31 @@ 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: unordered_flat_set, unordered_flat_map
== Open-addressing containers
The C++ standard does not currently provide any open-addressing container
specification to adhere to, so `boost::unordered_flat_set` and
`boost::unordered_flat_map` take inspiration from `std::unordered_set` and
specification to adhere to, so `boost::unordered_flat_set`/`unordered_node_set` and
`boost::unordered_flat_map`/`unordered_node_map` take inspiration from `std::unordered_set` and
`std::unordered_map`, respectively, and depart from their interface where
convenient or as dictated by their internal data structure, which is
radically different from that imposed by the standard (closed addressing, node based).
radically different from that imposed by the standard (closed addressing).
`unordered_flat_set` and `unordered_flat_map` only work with reasonably
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.
`unordered_flat_set` and `unordered_flat_map` 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^].
The main differences with C++ unordered associative containers are:
* `value_type` must be move-constructible.
* Pointer stability is not kept under rehashing.
* `begin()` is not constant-time.
* `erase(iterator)` returns `void` instead of an iterator to the following element.
* There is no API for bucket handling (except `bucket_count`) or node extraction/insertion.
* The maximum load factor of the container is managed internally and can't be set by the user. The maximum load,
exposed through the public function `max_load`, may decrease on erasure under high-load conditions.
* In general:
** `begin()` is not constant-time.
** `erase(iterator)` returns `void` instead of an iterator to the following element.
** There is no API for bucket handling (except `bucket_count`).
** The maximum load factor of the container is managed internally and can't be set by the user. The maximum load,
exposed through the public function `max_load`, may decrease on erasure under high-load conditions.
* Flat containers (`boost::unordered_flat_set` and `boost::unordered_flat_map`):
** `value_type` must be move-constructible.
** Pointer stability is not kept under rehashing.
** There is no API for node extraction/insertion.
//-
+3 -3
View File
@@ -9,10 +9,10 @@ Copyright (C) 2003, 2004 Jeremy B. Maitin-Shepard
Copyright (C) 2005-2008 Daniel James
Copyright (C) 2022 Christian Mazakas
Copyright (C) 2022-2023 Christian Mazakas
Copyright (C) 2022 Joaqu&iacute;n M L&oacute;pez Mu&ntilde;oz
Copyright (C) 2022-2023 Joaqu&iacute;n M L&oacute;pez Mu&ntilde;oz
Copyright (C) 2022 Peter Dimov
Copyright (C) 2022-2023 Peter Dimov
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)
+63 -2
View File
@@ -106,8 +106,69 @@ namespace boost {
}
----
`boost::unordered_flat_set` and `boost::unordered_flat_map` require a
reasonably compliant C++11 compiler.
Starting in Boost 1.82, the containers `boost::unordered_node_set` and `boost::unordered_node_map`
are introduced: they use open addressing like `boost::unordered_flat_set` and `boost::unordered_flat_map`,
but internally store element _nodes_, like `boost::unordered_set` and `boost::unordered_map`,
which provide stability of pointers and references to the elements:
[source,c++]
----
// #include <boost/unordered/unordered_node_set.hpp>
//
// Note: no multiset version
namespace boost {
template <
class Key,
class Hash = boost::hash<Key>,
class Pred = std::equal_to<Key>,
class Alloc = std::allocator<Key> >
class unordered_node_set;
}
----
[source,c++]
----
// #include <boost/unordered/unordered_node_map.hpp>
//
// Note: no multimap version
namespace boost {
template <
class Key, class Mapped,
class Hash = boost::hash<Key>,
class Pred = std::equal_to<Key>,
class Alloc = std::allocator<std::pair<Key const, Mapped> > >
class unordered_node_map;
}
----
These are all the containers provided by Boost.Unordered:
[caption=, title='Table {counter:table-counter}. Boost.Unordered containers']
[cols="1,1,.^1", frame=all, grid=rows]
|===
^h|
^h|*Node-based*
^h|*Flat*
^.^h|*Closed addressing*
^| `boost::unordered_set` +
`boost::unordered_map` +
`boost::unordered_multiset` +
`boost::unordered_multimap`
^|
^.^h|*Open addressing*
^| `boost::unordered_node_set` +
`boost::unordered_node_map`
^| `boost::unordered_flat_set` +
`boost::unordered_flat_map`
|===
Closed-addressing containers are pass:[C++]98-compatible. Open-addressing containers require a
reasonably compliant pass:[C++]11 compiler.
Boost.Unordered containers are used in a similar manner to the normal associative
containers:
+16 -12
View File
@@ -4,9 +4,10 @@
= Implementation Rationale
== boost::unordered_[multi]set and boost::unordered_[multi]map
== Closed-addressing containers
These containers adhere to the standard requirements for unordered associative
`boost::unordered_[multi]set` and `boost::unordered_[multi]map`
adhere to the standard requirements for unordered associative
containers, so the interface was fixed. But there are
still some implementation decisions to make. The priorities are
conformance to the standard and portability.
@@ -64,8 +65,8 @@ of bits in the hash value, so it was only used when `size_t` was 64 bit.
Since release 1.79.0, https://en.wikipedia.org/wiki/Hash_function#Fibonacci_hashing[Fibonacci hashing]
is used instead. With this implementation, the bucket number is determined
by using `(h * m) >> (w - k)`, where `h` is the hash value, `m` is the golden
ratio multiplied by `2^w`, `w` is the word size (32 or 64), and `2^k` is the
by using `(h * m) >> (w - k)`, where `h` is the hash value, `m` is `2^w` divided
by the golden ratio, `w` is the word size (32 or 64), and `2^k` is the
number of buckets. This provides a good compromise between speed and
distribution.
@@ -73,7 +74,7 @@ Since release 1.80.0, prime numbers are chosen for the number of buckets in
tandem with sophisticated modulo arithmetic. This removes the need for "mixing"
the result of the user's hash function as was used for release 1.79.0.
== boost::unordered_flat_set and boost::unordered_flat_map
== Open-addresing containers
The C++ standard specification of unordered associative containers impose
severe limitations on permissible implementations, the most important being
@@ -81,29 +82,32 @@ that closed addressing is implicitly assumed. Slightly relaxing this specificati
opens up the possibility of providing container variations taking full
advantage of open-addressing techniques.
The design of `boost::unordered_flat_set` and `boost::unordered_flat_map` has been
The design of `boost::unordered_flat_set`/`unordered_node_set` and `boost::unordered_flat_map`/`unordered_node_map` has been
guided by Peter Dimov's https://pdimov.github.io/articles/unordered_dev_plan.html[Development Plan for Boost.Unordered^].
We discuss here the most relevant principles.
=== Hash function
Given its rich functionality and cross-platform interoperability,
`boost::hash` remains the default hash function of `boost::unordered_flat_set` and `boost::unordered_flat_map`.
`boost::hash` remains the default hash function of open-addressing containers.
As it happens, `boost::hash` for integral and other basic types does not possess
the statistical properties required by open addressing; to cope with this,
we implement a post-mixing stage:
* 64-bit architectures: we use the `xmx` function defined in
Jon Maiga's http://jonkagstrom.com/bit-mixer-construction/index.html[The construct of a bit mixer^].
* 32-bit architectures: the mixer used was selected from a set generated with https://github.com/skeeto/hash-prospector[Hash Function Prospector^]
as the best overall performer in our internal benchmarks. Score assigned by Hash Prospector is 333.7934929677524.
{nbsp}{nbsp}{nbsp}{nbsp} _a_ <- _h_ *mulx* _C_, +
{nbsp}{nbsp}{nbsp}{nbsp} _h_ <- *high*(_a_) *xor* *low*(_a_),
where *mulx* is an _extended multiplication_ (128 bits in 64-bit architectures, 64 bits in 32-bit environments),
and *high* and *low* are the upper and lower halves of an extended word, respectively.
In 64-bit architectures, _C_ is the integer part of 2^64^&#8725;https://en.wikipedia.org/wiki/Golden_ratio[_&phi;_],
whereas in 32 bits _C_ = 0xE817FB2Du has been obtained from https://arxiv.org/abs/2001.05304[Steele and Vigna (2021)^].
When using a hash function directly suitable for open addressing, post-mixing can be opted out by via a dedicated <<hash_traits_hash_is_avalanching,`hash_is_avalanching`>>trait.
`boost::hash` specializations for string types are marked as avalanching.
=== Platform interoperability
The observable behavior of `boost::unordered_flat_set` and `boost::unordered_flat_map` is deterministically
The observable behavior of `boost::unordered_flat_set`/`unordered_node_set` and `boost::unordered_flat_map`/`unordered_node_map` is deterministically
identical across different compilers as long as their ``std::size_type``s are the same size and the user-provided
hash function and equality predicate are also interoperable
&#8212;this includes elements being ordered in exactly the same way for the same sequence of
+2
View File
@@ -8,3 +8,5 @@ include::unordered_multiset.adoc[]
include::hash_traits.adoc[]
include::unordered_flat_map.adoc[]
include::unordered_flat_set.adoc[]
include::unordered_node_map.adoc[]
include::unordered_node_set.adoc[]
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,685 @@
/* Fast open-addressing concurrent hash table.
*
* 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)
*
* See https://www.boost.org/libs/unordered for library home page.
*/
/* Reference:
* https://github.com/joaquintides/concurrent_hashmap_api#proposed-synopsis
*/
#ifndef BOOST_UNORDERED_CONCURRENT_FLAT_MAP_HPP
#define BOOST_UNORDERED_CONCURRENT_FLAT_MAP_HPP
#include <boost/unordered/detail/foa/concurrent_table.hpp>
#include <boost/unordered/detail/type_traits.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 <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&`");
#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> >
{
};
template <class Key, class T> struct concurrent_map_types
{
using key_type = Key;
using raw_key_type = typename std::remove_const<Key>::type;
using raw_mapped_type = typename std::remove_const<T>::type;
using init_type = std::pair<raw_key_type, raw_mapped_type>;
using moved_type = std::pair<raw_key_type&&, raw_mapped_type&&>;
using value_type = std::pair<Key const, T>;
using element_type = value_type;
static value_type& value_from(element_type& x) { return x; }
template <class K, class V>
static raw_key_type const& extract(std::pair<K, V> const& kv)
{
return kv.first;
}
static moved_type move(init_type& x)
{
return {std::move(x.first), std::move(x.second)};
}
static moved_type move(element_type& x)
{
// TODO: we probably need to launder here
return {std::move(const_cast<raw_key_type&>(x.first)),
std::move(const_cast<raw_mapped_type&>(x.second))};
}
template <class A, class... Args>
static void construct(A& al, init_type* p, Args&&... args)
{
boost::allocator_construct(al, p, std::forward<Args>(args)...);
}
template <class A, class... Args>
static void construct(A& al, value_type* p, Args&&... args)
{
boost::allocator_construct(al, p, std::forward<Args>(args)...);
}
template <class A> static void destroy(A& al, init_type* p) noexcept
{
boost::allocator_destroy(al, p);
}
template <class A> static void destroy(A& al, value_type* p) noexcept
{
boost::allocator_destroy(al, p);
}
};
} // namespace detail
template <class Key, class T, class Hash = boost::hash<Key>,
class Pred = std::equal_to<Key>,
class Allocator = std::allocator<std::pair<Key const, T> > >
class concurrent_flat_map
{
private:
using type_policy = detail::concurrent_map_types<Key, T>;
detail::foa::concurrent_table<type_policy, Hash, Pred, Allocator> table_;
public:
using key_type = Key;
using mapped_type = T;
using value_type = typename type_policy::value_type;
using init_type = typename type_policy::init_type;
using size_type = std::size_t;
using difference_type = std::ptrdiff_t;
using hasher = typename boost::type_identity<Hash>::type;
using key_equal = typename boost::type_identity<Pred>::type;
using allocator_type = typename boost::type_identity<Allocator>::type;
using reference = value_type&;
using const_reference = value_type const&;
using pointer = typename boost::allocator_pointer<allocator_type>::type;
using const_pointer =
typename boost::allocator_const_pointer<allocator_type>::type;
concurrent_flat_map()
: concurrent_flat_map(detail::foa::default_bucket_count)
{
}
explicit concurrent_flat_map(size_type n, const hasher& hf = hasher(),
const key_equal& eql = key_equal(),
const allocator_type& a = allocator_type())
: table_(n, hf, eql, a)
{
}
template <class InputIterator>
concurrent_flat_map(InputIterator f, InputIterator l,
size_type n = detail::foa::default_bucket_count,
const hasher& hf = hasher(), const key_equal& eql = key_equal(),
const allocator_type& a = allocator_type())
: table_(n, hf, eql, a)
{
this->insert(f, l);
}
concurrent_flat_map(concurrent_flat_map const& rhs)
: table_(rhs.table_,
boost::allocator_select_on_container_copy_construction(
rhs.get_allocator()))
{
}
concurrent_flat_map(concurrent_flat_map&& rhs)
: table_(std::move(rhs.table_))
{
}
template <class InputIterator>
concurrent_flat_map(InputIterator f, InputIterator l, allocator_type a)
: concurrent_flat_map(f, l, 0, hasher(), key_equal(), a)
{
}
explicit concurrent_flat_map(allocator_type a)
: table_(detail::foa::default_bucket_count, hasher(), key_equal(), a)
{
}
concurrent_flat_map(concurrent_flat_map const& rhs, allocator_type a)
: table_(rhs.table_, a)
{
}
concurrent_flat_map(concurrent_flat_map&& rhs, allocator_type a)
: table_(std::move(rhs.table_), a)
{
}
concurrent_flat_map(std::initializer_list<value_type> il,
size_type n = detail::foa::default_bucket_count,
const hasher& hf = hasher(), const key_equal& eql = key_equal(),
const allocator_type& a = allocator_type())
: concurrent_flat_map(n, hf, eql, a)
{
this->insert(il.begin(), il.end());
}
concurrent_flat_map(size_type n, const allocator_type& a)
: concurrent_flat_map(n, hasher(), key_equal(), a)
{
}
concurrent_flat_map(
size_type n, const hasher& hf, const allocator_type& a)
: concurrent_flat_map(n, hf, key_equal(), a)
{
}
template <typename InputIterator>
concurrent_flat_map(
InputIterator f, InputIterator l, size_type n, const allocator_type& a)
: concurrent_flat_map(f, l, n, hasher(), key_equal(), a)
{
}
template <typename InputIterator>
concurrent_flat_map(InputIterator f, InputIterator l, size_type n,
const hasher& hf, const allocator_type& a)
: concurrent_flat_map(f, l, n, hf, key_equal(), a)
{
}
concurrent_flat_map(
std::initializer_list<value_type> il, const allocator_type& a)
: concurrent_flat_map(
il, detail::foa::default_bucket_count, hasher(), key_equal(), a)
{
}
concurrent_flat_map(std::initializer_list<value_type> il, size_type n,
const allocator_type& a)
: concurrent_flat_map(il, n, hasher(), key_equal(), a)
{
}
concurrent_flat_map(std::initializer_list<value_type> il, size_type n,
const hasher& hf, const allocator_type& a)
: concurrent_flat_map(il, n, hf, key_equal(), a)
{
}
~concurrent_flat_map() = default;
concurrent_flat_map& operator=(concurrent_flat_map const& rhs)
{
table_ = rhs.table_;
return *this;
}
/// Capacity
///
size_type size() const noexcept { return table_.size(); }
BOOST_ATTRIBUTE_NODISCARD bool empty() const noexcept
{
return size() == 0;
}
template <class F>
BOOST_FORCEINLINE std::size_t visit(key_type const& k, F f)
{
BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(F)
return table_.visit(k, f);
}
template <class F>
BOOST_FORCEINLINE std::size_t 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 std::size_t 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, std::size_t>::type
visit(K&& k, F f)
{
BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(F)
return table_.visit(std::forward<K>(k), f);
}
template <class K, class F>
BOOST_FORCEINLINE typename std::enable_if<
detail::are_transparent<K, hasher, key_equal>::value, std::size_t>::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, std::size_t>::type
cvisit(K&& k, F f) const
{
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
return table_.visit(std::forward<K>(k), f);
}
template <class F> BOOST_FORCEINLINE std::size_t visit_all(F f)
{
BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(F)
return table_.visit_all(f);
}
template <class F> BOOST_FORCEINLINE std::size_t visit_all(F f) const
{
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
return table_.visit_all(f);
}
template <class F> BOOST_FORCEINLINE std::size_t 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>
BOOST_FORCEINLINE
typename std::enable_if<detail::is_execution_policy<ExecPolicy>::value,
void>::type
visit_all(ExecPolicy p, F f)
{
BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(F)
table_.visit_all(p, f);
}
template <class ExecPolicy, class F>
BOOST_FORCEINLINE
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)
table_.visit_all(p, f);
}
template <class ExecPolicy, class F>
BOOST_FORCEINLINE
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)
table_.cvisit_all(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));
}
BOOST_FORCEINLINE bool insert(init_type const& obj)
{
return table_.insert(obj);
}
BOOST_FORCEINLINE bool insert(init_type&& obj)
{
return table_.insert(std::move(obj));
}
template <class InputIterator>
BOOST_FORCEINLINE void insert(InputIterator begin, InputIterator end)
{
for (auto pos = begin; pos != end; ++pos) {
table_.insert(*pos);
}
}
BOOST_FORCEINLINE void insert(std::initializer_list<value_type> ilist)
{
this->insert(ilist.begin(), ilist.end());
}
template <class M>
BOOST_FORCEINLINE bool insert_or_assign(key_type const& k, M&& obj)
{
return table_.try_emplace_or_visit(k, std::forward<M>(obj),
[&](value_type& m) { m.second = std::forward<M>(obj); });
}
template <class M>
BOOST_FORCEINLINE bool insert_or_assign(key_type&& k, M&& obj)
{
return table_.try_emplace_or_visit(std::move(k), std::forward<M>(obj),
[&](value_type& m) { m.second = std::forward<M>(obj); });
}
template <class K, class M>
BOOST_FORCEINLINE typename std::enable_if<
detail::are_transparent<K, hasher, key_equal>::value, bool>::type
insert_or_assign(K&& k, M&& obj)
{
return table_.try_emplace_or_visit(std::forward<K>(k),
std::forward<M>(obj),
[&](value_type& m) { m.second = std::forward<M>(obj); });
}
template <class F>
BOOST_FORCEINLINE bool insert_or_visit(value_type const& obj, F f)
{
BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(F)
return table_.insert_or_visit(obj, f);
}
template <class F>
BOOST_FORCEINLINE bool insert_or_visit(value_type&& obj, F f)
{
BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(F)
return table_.insert_or_visit(std::move(obj), f);
}
template <class F>
BOOST_FORCEINLINE bool insert_or_visit(init_type const& obj, F f)
{
BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(F)
return table_.insert_or_visit(obj, f);
}
template <class F>
BOOST_FORCEINLINE bool insert_or_visit(init_type&& obj, F f)
{
BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(F)
return table_.insert_or_visit(std::move(obj), f);
}
template <class InputIterator, class F>
BOOST_FORCEINLINE void insert_or_visit(
InputIterator first, InputIterator last, F f)
{
BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(F)
for (; first != last; ++first) {
table_.insert_or_visit(*first, f);
}
}
template <class F>
BOOST_FORCEINLINE void insert_or_visit(
std::initializer_list<value_type> ilist, F f)
{
BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(F)
this->insert_or_visit(ilist.begin(), ilist.end(), f);
}
template <class F>
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 F>
BOOST_FORCEINLINE bool insert_or_cvisit(init_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(init_type&& obj, F f)
{
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
return table_.insert_or_cvisit(std::move(obj), f);
}
template <class InputIterator, class F>
BOOST_FORCEINLINE void insert_or_cvisit(
InputIterator first, InputIterator last, F f)
{
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
for (; first != last; ++first) {
table_.insert_or_cvisit(*first, f);
}
}
template <class F>
BOOST_FORCEINLINE 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);
}
template <class... Args> BOOST_FORCEINLINE bool emplace(Args&&... args)
{
return table_.emplace(std::forward<Args>(args)...);
}
template <class Arg, class... Args>
BOOST_FORCEINLINE bool emplace_or_visit(Arg&& arg, Args&&... args)
{
BOOST_UNORDERED_STATIC_ASSERT_LAST_ARG_INVOCABLE(Arg, Args...)
return table_.emplace_or_visit(
std::forward<Arg>(arg), std::forward<Args>(args)...);
}
template <class Arg, class... Args>
BOOST_FORCEINLINE bool emplace_or_cvisit(Arg&& arg, Args&&... args)
{
BOOST_UNORDERED_STATIC_ASSERT_LAST_ARG_CONST_INVOCABLE(Arg, Args...)
return table_.emplace_or_cvisit(
std::forward<Arg>(arg), std::forward<Args>(args)...);
}
template <class... Args>
BOOST_FORCEINLINE bool try_emplace(key_type const& k, Args&&... args)
{
return table_.try_emplace(k, std::forward<Args>(args)...);
}
template <class... Args>
BOOST_FORCEINLINE bool try_emplace(key_type&& k, Args&&... args)
{
return table_.try_emplace(std::move(k), std::forward<Args>(args)...);
}
template <class K, class... Args>
BOOST_FORCEINLINE typename std::enable_if<
detail::are_transparent<K, hasher, key_equal>::value, bool>::type
try_emplace(K&& k, Args&&... args)
{
return table_.try_emplace(
std::forward<K>(k), std::forward<Args>(args)...);
}
template <class Arg, class... Args>
BOOST_FORCEINLINE bool try_emplace_or_visit(
key_type const& k, Arg&& arg, Args&&... args)
{
BOOST_UNORDERED_STATIC_ASSERT_LAST_ARG_INVOCABLE(Arg, Args...)
return table_.try_emplace_or_visit(
k, std::forward<Arg>(arg), std::forward<Args>(args)...);
}
template <class Arg, class... Args>
BOOST_FORCEINLINE bool try_emplace_or_cvisit(
key_type const& k, Arg&& arg, Args&&... args)
{
BOOST_UNORDERED_STATIC_ASSERT_LAST_ARG_CONST_INVOCABLE(Arg, Args...)
return table_.try_emplace_or_cvisit(
k, std::forward<Arg>(arg), std::forward<Args>(args)...);
}
template <class Arg, class... Args>
BOOST_FORCEINLINE bool try_emplace_or_visit(
key_type&& k, Arg&& arg, Args&&... args)
{
BOOST_UNORDERED_STATIC_ASSERT_LAST_ARG_INVOCABLE(Arg, Args...)
return table_.try_emplace_or_visit(
std::move(k), std::forward<Arg>(arg), std::forward<Args>(args)...);
}
template <class Arg, class... Args>
BOOST_FORCEINLINE bool try_emplace_or_cvisit(
key_type&& k, Arg&& arg, Args&&... args)
{
BOOST_UNORDERED_STATIC_ASSERT_LAST_ARG_CONST_INVOCABLE(Arg, Args...)
return table_.try_emplace_or_cvisit(
std::move(k), std::forward<Arg>(arg), std::forward<Args>(args)...);
}
template <class K, class Arg, class... Args>
BOOST_FORCEINLINE bool try_emplace_or_visit(
K&& k, Arg&& arg, Args&&... args)
{
BOOST_UNORDERED_STATIC_ASSERT_LAST_ARG_INVOCABLE(Arg, Args...)
return table_.try_emplace_or_visit(std::forward<K>(k),
std::forward<Arg>(arg), std::forward<Args>(args)...);
}
template <class K, class Arg, class... Args>
BOOST_FORCEINLINE bool try_emplace_or_cvisit(
K&& k, Arg&& arg, Args&&... args)
{
BOOST_UNORDERED_STATIC_ASSERT_LAST_ARG_CONST_INVOCABLE(Arg, Args...)
return table_.try_emplace_or_cvisit(std::forward<K>(k),
std::forward<Arg>(arg), std::forward<Args>(args)...);
}
BOOST_FORCEINLINE size_type erase(key_type const& k)
{
return table_.erase(k);
}
template <class K>
BOOST_FORCEINLINE typename std::enable_if<
detail::are_transparent<K, hasher, key_equal>::value, size_type>::type
erase(K&& k)
{
return table_.erase(std::forward<K>(k));
}
template <class F>
BOOST_FORCEINLINE size_type erase_if(key_type const& k, F f)
{
return table_.erase_if(k, f);
}
template <class K, class F>
BOOST_FORCEINLINE typename std::enable_if<
detail::are_transparent<K, hasher, key_equal>::value &&
!detail::is_execution_policy<K>::value,
size_type>::type
erase_if(K&& k, F f)
{
return table_.erase_if(std::forward<K>(k), f);
}
#if defined(BOOST_UNORDERED_PARALLEL_ALGORITHMS)
template <class ExecPolicy, class F>
BOOST_FORCEINLINE
typename std::enable_if<detail::is_execution_policy<ExecPolicy>::value,
void>::type
erase_if(ExecPolicy p, F f)
{
table_.erase_if(p, f);
}
#endif
template <class F> BOOST_FORCEINLINE size_type erase_if(F f)
{
return table_.erase_if(f);
}
/// Hash Policy
///
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(); }
};
} // namespace unordered
} // namespace boost
#undef BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE
#undef BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE
#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
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,60 @@
/* 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)
*
* See https://www.boost.org/libs/unordered for library home page.
*/
#ifndef BOOST_UNORDERED_DETAIL_FOA_ELEMENT_TYPE_HPP
#define BOOST_UNORDERED_DETAIL_FOA_ELEMENT_TYPE_HPP
namespace boost{
namespace unordered{
namespace detail{
namespace foa{
template<class T>
struct element_type
{
using value_type=T;
value_type* p;
/*
* we use a deleted copy constructor here so the type is no longer
* trivially copy-constructible which inhibits our memcpy
* optimizations when copying the tables
*/
element_type() = default;
element_type(value_type* p_):p(p_){}
element_type(element_type const&) = delete;
element_type(element_type&& rhs) noexcept
{
p = rhs.p;
rhs.p = nullptr;
}
element_type& operator=(element_type const&)=delete;
element_type& operator=(element_type&& rhs)noexcept
{
if (this!=&rhs){
p=rhs.p;
rhs.p=nullptr;
}
return *this;
}
void swap(element_type& rhs)noexcept
{
auto tmp=p;
p=rhs.p;
rhs.p=tmp;
}
};
}
}
}
}
#endif // BOOST_UNORDERED_DETAIL_FOA_ELEMENT_TYPE_HPP
@@ -0,0 +1,35 @@
/* 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.
*/
#include <boost/config.hpp>
#if defined(BOOST_GCC)
#if !defined(BOOST_UNORDERED_DETAIL_RESTORE_WSHADOW)
/* GCC's -Wshadow triggers at scenarios like this:
*
* struct foo{};
* template<typename Base>
* struct derived:Base
* {
* void f(){int foo;}
* };
*
* derived<foo>x;
* x.f(); // declaration of "foo" in derived::f shadows base type "foo"
*
* This makes shadowing warnings unavoidable in general when a class template
* derives from user-provided classes, as is the case with foa::table_core
* deriving from empty_value.
*/
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wshadow"
#else
#pragma GCC diagnostic pop
#endif
#endif
@@ -0,0 +1,210 @@
/* 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)
*
* See https://www.boost.org/libs/unordered for library home page.
*/
#ifndef BOOST_UNORDERED_DETAIL_FOA_NODE_HANDLE_HPP
#define BOOST_UNORDERED_DETAIL_FOA_NODE_HANDLE_HPP
#include <boost/config.hpp>
#include <boost/core/allocator_access.hpp>
namespace boost{
namespace unordered{
namespace detail{
namespace foa{
template <class Iterator,class NodeType>
struct insert_return_type
{
Iterator position;
bool inserted;
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
{
protected:
using type_policy=TypePolicy;
using element_type=typename type_policy::element_type;
public:
using allocator_type = Allocator;
private:
using node_value_type=typename type_policy::value_type;
element_type p_;
BOOST_ATTRIBUTE_NO_UNIQUE_ADDRESS opt_storage<Allocator> a_;
protected:
node_value_type& data()noexcept
{
return *(p_.p);
}
node_value_type const& data()const noexcept
{
return *(p_.p);
}
element_type& element()noexcept
{
BOOST_ASSERT(!empty());
return p_;
}
element_type const& element()const noexcept
{
BOOST_ASSERT(!empty());
return p_;
}
Allocator& al()noexcept
{
BOOST_ASSERT(!empty());
return a_.t_;
}
Allocator const& al()const noexcept
{
BOOST_ASSERT(!empty());
return a_.t_;
}
void emplace(element_type&& x,Allocator a)
{
BOOST_ASSERT(empty());
auto* p=x.p;
p_.p=p;
new(&a_.t_)Allocator(a);
x.p=nullptr;
}
void reset()
{
a_.t_.~Allocator();
p_.p=nullptr;
}
public:
constexpr node_handle_base()noexcept:p_{nullptr}{}
node_handle_base(node_handle_base&& nh) noexcept
{
p_.p = nullptr;
if (!nh.empty()){
emplace(std::move(nh.p_),nh.al());
nh.reset();
}
}
node_handle_base& operator=(node_handle_base&& nh)noexcept
{
if(this!=&nh){
if(empty()){
if(nh.empty()){ /* empty(), nh.empty() */
/* nothing to do */
}else{ /* empty(), !nh.empty() */
emplace(std::move(nh.p_),std::move(nh.al()));
nh.reset();
}
}else{
if(nh.empty()){ /* !empty(), nh.empty() */
type_policy::destroy(al(),&p_);
reset();
}else{ /* !empty(), !nh.empty() */
bool const pocma=
boost::allocator_propagate_on_container_move_assignment<
Allocator>::type::value;
BOOST_ASSERT(pocma||al()==nh.al());
type_policy::destroy(al(),&p_);
if(pocma){
al()=std::move(nh.al());
}
p_=std::move(nh.p_);
nh.reset();
}
}
}else{
if(empty()){ /* empty(), nh.empty() */
/* nothing to do */
}else{ /* !empty(), !nh.empty() */
type_policy::destroy(al(),&p_);
reset();
}
}
return *this;
}
~node_handle_base()
{
if(!empty()){
type_policy::destroy(al(),&p_);
reset();
}
}
allocator_type get_allocator()const noexcept{return al();}
explicit operator bool()const noexcept{ return !empty();}
BOOST_ATTRIBUTE_NODISCARD bool empty()const noexcept{return p_.p==nullptr;}
void swap(node_handle_base& nh) noexcept(
boost::allocator_is_always_equal<Allocator>::type::value||
boost::allocator_propagate_on_container_swap<Allocator>::type::value)
{
if(this!=&nh){
if(empty()){
if(nh.empty()) {
/* nothing to do here */
} else {
emplace(std::move(nh.p_), nh.al());
nh.reset();
}
}else{
if(nh.empty()){
nh.emplace(std::move(p_),al());
reset();
}else{
bool const pocs=
boost::allocator_propagate_on_container_swap<
Allocator>::type::value;
BOOST_ASSERT(pocs || al()==nh.al());
using std::swap;
p_.swap(nh.p_);
if(pocs)swap(al(),nh.al());
}
}
}
}
friend
void swap(node_handle_base& lhs,node_handle_base& rhs)
noexcept(noexcept(lhs.swap(rhs)))
{
return lhs.swap(rhs);
}
};
}
}
}
}
#endif // BOOST_UNORDERED_DETAIL_FOA_NODE_HANDLE_HPP
@@ -0,0 +1,11 @@
/* 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.
*/
#define BOOST_UNORDERED_DETAIL_RESTORE_WSHADOW
#include <boost/unordered/detail/foa/ignore_wshadow.hpp>
#undef BOOST_UNORDERED_DETAIL_RESTORE_WSHADOW
@@ -0,0 +1,188 @@
#ifndef BOOST_UNORDERED_DETAIL_FOA_RW_SPINLOCK_HPP_INCLUDED
#define BOOST_UNORDERED_DETAIL_FOA_RW_SPINLOCK_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/smart_ptr/detail/sp_thread_pause.hpp>
#include <boost/smart_ptr/detail/sp_thread_sleep.hpp>
#include <atomic>
#include <cstdint>
namespace boost{
namespace unordered{
namespace detail{
namespace foa{
class rw_spinlock
{
private:
// bit 31: locked exclusive
// bit 30: writer pending
// bit 29..0: reader lock count
static constexpr std::uint32_t locked_exclusive_mask = 1u << 31; // 0x8000'0000
static constexpr std::uint32_t writer_pending_mask = 1u << 30; // 0x4000'0000
static constexpr std::uint32_t reader_lock_count_mask = writer_pending_mask - 1; // 0x3FFF'FFFF
std::atomic<std::uint32_t> state_ = {};
private:
// number of times to spin before sleeping
static constexpr int spin_count = 24576;
public:
bool try_lock_shared() noexcept
{
std::uint32_t st = state_.load( std::memory_order_relaxed );
if( st >= reader_lock_count_mask )
{
// either bit 31 set, bit 30 set, or reader count is max
return false;
}
std::uint32_t newst = st + 1;
return state_.compare_exchange_strong( st, newst, std::memory_order_acquire, std::memory_order_relaxed );
}
void lock_shared() noexcept
{
for( ;; )
{
for( int k = 0; k < spin_count; ++k )
{
std::uint32_t st = state_.load( std::memory_order_relaxed );
if( st < reader_lock_count_mask )
{
std::uint32_t newst = st + 1;
if( state_.compare_exchange_weak( st, newst, std::memory_order_acquire, std::memory_order_relaxed ) ) return;
}
boost::detail::sp_thread_pause();
}
boost::detail::sp_thread_sleep();
}
}
void unlock_shared() noexcept
{
// pre: locked shared, not locked exclusive
state_.fetch_sub( 1, std::memory_order_release );
// if the writer pending bit is set, there's a writer waiting
// let it acquire the lock; it will clear the bit on unlock
}
bool try_lock() noexcept
{
std::uint32_t st = state_.load( std::memory_order_relaxed );
if( st & locked_exclusive_mask )
{
// locked exclusive
return false;
}
if( st & reader_lock_count_mask )
{
// locked shared
return false;
}
std::uint32_t newst = locked_exclusive_mask;
return state_.compare_exchange_strong( st, newst, std::memory_order_acquire, std::memory_order_relaxed );
}
void lock() noexcept
{
for( ;; )
{
for( int k = 0; k < spin_count; ++k )
{
std::uint32_t st = state_.load( std::memory_order_relaxed );
if( st & locked_exclusive_mask )
{
// locked exclusive, spin
}
else if( ( st & reader_lock_count_mask ) == 0 )
{
// not locked exclusive, not locked shared, try to lock
std::uint32_t newst = locked_exclusive_mask;
if( state_.compare_exchange_weak( st, newst, std::memory_order_acquire, std::memory_order_relaxed ) ) return;
}
else if( st & writer_pending_mask )
{
// writer pending bit already set, nothing to do
}
else
{
// locked shared, set writer pending bit
std::uint32_t newst = st | writer_pending_mask;
state_.compare_exchange_weak( st, newst, std::memory_order_relaxed, std::memory_order_relaxed );
}
boost::detail::sp_thread_pause();
}
// clear writer pending bit before going to sleep
{
std::uint32_t st = state_.load( std::memory_order_relaxed );
for( ;; )
{
if( st & locked_exclusive_mask )
{
// locked exclusive, nothing to do
break;
}
else if( ( st & reader_lock_count_mask ) == 0 )
{
// lock free, try to take it
std::uint32_t newst = locked_exclusive_mask;
if( state_.compare_exchange_weak( st, newst, std::memory_order_acquire, std::memory_order_relaxed ) ) return;
}
else if( ( st & writer_pending_mask ) == 0 )
{
// writer pending bit already clear, nothing to do
break;
}
else
{
// clear writer pending bit
std::uint32_t newst = st & ~writer_pending_mask;
if( state_.compare_exchange_weak( st, newst, std::memory_order_relaxed, std::memory_order_relaxed ) ) break;
}
}
}
boost::detail::sp_thread_sleep();
}
}
void unlock() noexcept
{
// pre: locked exclusive, not locked shared
state_.store( 0, std::memory_order_release );
}
};
} /* namespace foa */
} /* namespace detail */
} /* namespace unordered */
} /* namespace boost */
#endif // BOOST_UNORDERED_DETAIL_FOA_RW_SPINLOCK_HPP_INCLUDED
@@ -0,0 +1,540 @@
/* Fast open-addressing hash table.
*
* Copyright 2022-2023 Joaquin M Lopez Munoz.
* 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)
*
* See https://www.boost.org/libs/unordered for library home page.
*/
#ifndef BOOST_UNORDERED_DETAIL_FOA_TABLE_HPP
#define BOOST_UNORDERED_DETAIL_FOA_TABLE_HPP
#include <boost/assert.hpp>
#include <boost/config.hpp>
#include <boost/config/workaround.hpp>
#include <boost/unordered/detail/foa/core.hpp>
#include <cstddef>
#include <iterator>
#include <memory>
#include <type_traits>
#include <utility>
namespace boost{
namespace unordered{
namespace detail{
namespace foa{
/* use plain integrals for group metadata storage */
template<typename Integral>
struct plain_integral
{
operator Integral()const{return n;}
void operator=(Integral m){n=m;}
#if BOOST_WORKAROUND(BOOST_GCC,>=50000 && BOOST_GCC<60000)
void operator|=(Integral m){n=static_cast<Integral>(n|m);}
void operator&=(Integral m){n=static_cast<Integral>(n&m);}
#else
void operator|=(Integral m){n|=m;}
void operator&=(Integral m){n&=m;}
#endif
Integral n;
};
template<typename,typename,typename,typename>
class table;
/* table_iterator keeps two pointers:
*
* - A pointer p to the element slot.
* - A pointer pc to the n-th byte of the associated group metadata, where n
* is the position of the element in the group.
*
* A simpler solution would have been to keep a pointer p to the element, a
* pointer pg to the group, and the position n, but that would increase
* sizeof(table_iterator) by 4/8 bytes. In order to make this compact
* representation feasible, it is required that group objects are aligned
* to their size, so that we can recover pg and n as
*
* - n = pc%sizeof(group)
* - pg = pc-n
*
* (for explanatory purposes pg and pc are treated above as if they were memory
* addresses rather than pointers).
*
* p = nullptr is conventionally used to mark end() iterators.
*/
/* internal conversion from const_iterator to iterator */
struct const_iterator_cast_tag{};
template<typename TypePolicy,typename Group,bool Const>
class table_iterator
{
using type_policy=TypePolicy;
using table_element_type=typename type_policy::element_type;
using group_type=Group;
static constexpr auto N=group_type::N;
static constexpr auto regular_layout=group_type::regular_layout;
public:
using difference_type=std::ptrdiff_t;
using value_type=typename type_policy::value_type;
using pointer=
typename std::conditional<Const,value_type const*,value_type*>::type;
using reference=
typename std::conditional<Const,value_type const&,value_type&>::type;
using iterator_category=std::forward_iterator_tag;
using element_type=
typename std::conditional<Const,value_type const,value_type>::type;
table_iterator()=default;
template<bool Const2,typename std::enable_if<!Const2>::type* =nullptr>
table_iterator(const table_iterator<TypePolicy,Group,Const2>& x):
pc{x.pc},p{x.p}{}
table_iterator(
const_iterator_cast_tag, const table_iterator<TypePolicy,Group,true>& x):
pc{x.pc},p{x.p}{}
inline reference operator*()const noexcept
{return type_policy::value_from(*p);}
inline pointer operator->()const noexcept
{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;}
friend inline bool operator!=(
const table_iterator& x,const table_iterator& y)
{return !(x==y);}
private:
template<typename,typename,bool> friend class table_iterator;
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_)}
{}
inline void increment()noexcept
{
BOOST_ASSERT(p!=nullptr);
increment(std::integral_constant<bool,regular_layout>{});
}
inline void increment(std::true_type /* regular layout */)noexcept
{
for(;;){
++p;
if(reinterpret_cast<uintptr_t>(pc)%sizeof(group_type)==N-1){
pc+=sizeof(group_type)-(N-1);
break;
}
++pc;
if(!group_type::is_occupied(pc))continue;
if(BOOST_UNLIKELY(group_type::is_sentinel(pc)))p=nullptr;
return;
}
for(;;){
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;
}
else{
pc+=n;
p+=n;
}
return;
}
pc+=sizeof(group_type);
p+=N;
}
}
inline void increment(std::false_type /* interleaved */)noexcept
{
std::size_t n0=reinterpret_cast<uintptr_t>(pc)%sizeof(group_type);
pc-=n0;
int mask=(
reinterpret_cast<group_type*>(pc)->match_occupied()>>(n0+1))<<(n0+1);
if(!mask){
do{
pc+=sizeof(group_type);
p+=N;
}
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;
}
else{
pc+=n;
p-=n0;
p+=n;
}
}
unsigned char *pc=nullptr;
table_element_type *p=nullptr;
};
/* foa::table interface departs in a number of ways from that of C++ unordered
* associative containers because it's not for end-user consumption
* (boost::unordered_[flat|node]_[map|set] wrappers complete it as
* appropriate).
*
* The table supports two main modes of operation: flat and node-based. In the
* flat case, buckets directly store elements. For node-based, buckets store
* pointers to individually heap-allocated elements.
*
* For both flat and node-based:
*
* - begin() is not O(1).
* - No bucket API.
* - Load factor is fixed and can't be set by the user.
*
* For flat only:
*
* - value_type must be moveable.
* - Pointer stability is not kept under rehashing.
* - No extract API.
*
* try_emplace, erase and find support heterogeneous lookup by default,
* that is, without checking for any ::is_transparent typedefs --the
* checking is done by boost::unordered_[flat|node]_[map|set].
*/
template <typename TypePolicy,typename Hash,typename Pred,typename Allocator>
using table_core_impl=
table_core<TypePolicy,group15<plain_integral>,table_arrays,
std::size_t,Hash,Pred,Allocator>;
#include <boost/unordered/detail/foa/ignore_wshadow.hpp>
#if defined(BOOST_MSVC)
#pragma warning(push)
#pragma warning(disable:4714) /* marked as __forceinline not inlined */
#endif
template<typename TypePolicy,typename Hash,typename Pred,typename Allocator>
class table:table_core_impl<TypePolicy,Hash,Pred,Allocator>
{
using super=table_core_impl<TypePolicy,Hash,Pred,Allocator>;
using type_policy=typename super::type_policy;
using group_type=typename super::group_type;
using super::N;
using prober=typename super::prober;
using locator=typename super::locator;
public:
using key_type=typename super::key_type;
using init_type=typename super::init_type;
using value_type=typename super::value_type;
using element_type=typename super::element_type;
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;
using allocator_type=typename super::allocator_type;
using pointer=typename super::pointer;
using const_pointer=typename super::const_pointer;
using reference=typename super::reference;
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 iterator=typename std::conditional<
has_mutable_iterator,
table_iterator<type_policy,group_type,false>,
const_iterator>::type;
table(
std::size_t n=default_bucket_count,const Hash& h_=Hash(),
const Pred& pred_=Pred(),const Allocator& al_=Allocator()):
super{n,h_,pred_,al_}
{}
table(const table& x)=default;
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()=default;
table& operator=(const table& x)=default;
table& operator=(table&& x)=default;
using super::get_allocator;
iterator begin()noexcept
{
iterator it{this->arrays.groups,0,this->arrays.elements};
if(this->arrays.elements&&
!(this->arrays.groups[0].match_occupied()&0x1))++it;
return it;
}
const_iterator begin()const noexcept
{return const_cast<table*>(this)->begin();}
iterator end()noexcept{return {};}
const_iterator end()const noexcept{return const_cast<table*>(this)->end();}
const_iterator cbegin()const noexcept{return begin();}
const_iterator cend()const noexcept{return end();}
using super::empty;
using super::size;
using super::max_size;
template<typename... Args>
BOOST_FORCEINLINE std::pair<iterator,bool> emplace(Args&&... args)
{
auto x=alloc_make_insert_type<type_policy>(
this->al(),std::forward<Args>(args)...);
return emplace_impl(type_policy::move(x.value()));
}
template<typename Key,typename... Args>
BOOST_FORCEINLINE std::pair<iterator,bool> try_emplace(
Key&& x,Args&&... args)
{
return emplace_impl(
try_emplace_args_t{},std::forward<Key>(x),std::forward<Args>(args)...);
}
BOOST_FORCEINLINE std::pair<iterator,bool>
insert(const init_type& x){return emplace_impl(x);}
BOOST_FORCEINLINE std::pair<iterator,bool>
insert(init_type&& x){return emplace_impl(std::move(x));}
/* template<typename=void> tilts call ambiguities in favor of init_type */
template<typename=void>
BOOST_FORCEINLINE std::pair<iterator,bool>
insert(const value_type& x){return emplace_impl(x);}
template<typename=void>
BOOST_FORCEINLINE std::pair<iterator,bool>
insert(value_type&& x){return emplace_impl(std::move(x));}
template<typename T=element_type>
BOOST_FORCEINLINE
typename std::enable_if<
!std::is_same<T,value_type>::value,
std::pair<iterator,bool>
>::type
insert(element_type&& x){return emplace_impl(std::move(x));}
template<
bool dependent_value=false,
typename std::enable_if<
has_mutable_iterator||dependent_value>::type* =nullptr
>
void erase(iterator pos)noexcept{return erase(const_iterator(pos));}
BOOST_FORCEINLINE
void erase(const_iterator pos)noexcept
{
super::erase(pos.pc,pos.p);
}
template<typename Key>
BOOST_FORCEINLINE
auto erase(Key&& x) -> typename std::enable_if<
!std::is_convertible<Key,iterator>::value&&
!std::is_convertible<Key,const_iterator>::value, std::size_t>::type
{
auto it=find(x);
if(it!=end()){
erase(it);
return 1;
}
else return 0;
}
void swap(table& x)
noexcept(noexcept(std::declval<super&>().swap(std::declval<super&>())))
{
super::swap(x);
}
using super::clear;
element_type extract(const_iterator pos)
{
BOOST_ASSERT(pos!=end());
erase_on_exit e{*this,pos};
(void)e;
return std::move(*pos.p);
}
// TODO: should we accept different allocator too?
template<typename Hash2,typename Pred2>
void merge(table<TypePolicy,Hash2,Pred2,Allocator>& x)
{
x.for_all_elements([&,this](group_type* pg,unsigned int n,element_type* p){
erase_on_exit e{x,{pg,n,p}};
if(!emplace_impl(type_policy::move(*p)).second)e.rollback();
});
}
template<typename Hash2,typename Pred2>
void merge(table<TypePolicy,Hash2,Pred2,Allocator>&& x){merge(x);}
using super::hash_function;
using super::key_eq;
template<typename Key>
BOOST_FORCEINLINE iterator find(const Key& x)
{
auto hash=this->hash_for(x);
return find_impl(x,this->position_for(hash),hash);
}
template<typename Key>
BOOST_FORCEINLINE const_iterator find(const Key& x)const
{
return const_cast<table*>(this)->find(x);
}
using super::capacity;
using super::load_factor;
using super::max_load_factor;
using super::max_load;
using super::rehash;
using super::reserve;
template<typename Predicate>
friend std::size_t erase_if(table& x,Predicate& pr)
{
using value_reference=typename std::conditional<
std::is_same<key_type,value_type>::value,
const_reference,
reference
>::type;
std::size_t s=x.size();
x.for_all_elements(
[&](group_type* pg,unsigned int n,element_type* p){
if(pr(const_cast<value_reference>(type_policy::value_from(*p)))){
x.super::erase(pg,n,p);
}
});
return std::size_t(s-x.size());
}
private:
struct erase_on_exit
{
erase_on_exit(table& x_,const_iterator it_):x{x_},it{it_}{}
~erase_on_exit(){if(!rollback_)x.erase(it);}
void rollback(){rollback_=true;}
table& x;
const_iterator it;
bool rollback_=false;
};
static inline iterator make_iterator(const locator& l)noexcept
{
return {l.pg,l.n,l.p};
}
#if defined(BOOST_MSVC)
/* warning: forcing value to bool 'true' or 'false' in bool(pred()...) */
#pragma warning(push)
#pragma warning(disable:4800)
#endif
template<typename Key>
BOOST_FORCEINLINE iterator find_impl(
const Key& x,std::size_t pos0,std::size_t hash)const
{
prober pb(pos0);
do{
auto pos=pb.get();
auto pg=this->arrays.groups+pos;
auto mask=pg->match(hash);
if(mask){
BOOST_UNORDERED_ASSUME(this->arrays.elements!=nullptr);
auto p=this->arrays.elements+pos*N;
this->prefetch_elements(p);
do{
auto n=unchecked_countr_zero(mask);
if(BOOST_LIKELY(bool(this->pred()(x,this->key_from(p[n]))))){
return {pg,n,p+n};
}
mask&=mask-1;
}while(mask);
}
if(BOOST_LIKELY(pg->is_not_overflowed(hash))){
return {}; /* end() */
}
}
while(BOOST_LIKELY(pb.next(this->arrays.groups_size_mask)));
return {}; /* end() */
}
#if defined(BOOST_MSVC)
#pragma warning(pop) /* C4800 */
#endif
template<typename... Args>
BOOST_FORCEINLINE std::pair<iterator,bool> emplace_impl(Args&&... args)
{
const auto &k=this->key_from(std::forward<Args>(args)...);
auto hash=this->hash_for(k);
auto pos0=this->position_for(hash);
auto it=find_impl(k,pos0,hash);
if(it!=end()){
return {it,false};
}
if(BOOST_LIKELY(this->size_<this->ml)){
return {
make_iterator(
this->unchecked_emplace_at(pos0,hash,std::forward<Args>(args)...)),
true
};
}
else{
return {
make_iterator(
this->unchecked_emplace_with_rehash(
hash,std::forward<Args>(args)...)),
true
};
}
}
};
#if defined(BOOST_MSVC)
#pragma warning(pop) /* C4714 */
#endif
#include <boost/unordered/detail/foa/restore_wshadow.hpp>
} /* namespace foa */
} /* namespace detail */
} /* namespace unordered */
} /* namespace boost */
#endif
@@ -0,0 +1,52 @@
/* 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_TUPLE_ROTATE_RIGHT_HPP
#define BOOST_UNORDERED_DETAIL_FOA_TUPLE_ROTATE_RIGHT_HPP
#include <boost/mp11/algorithm.hpp>
#include <boost/mp11/integer_sequence.hpp>
#include <tuple>
#include <utility>
namespace boost{
namespace unordered{
namespace detail{
namespace foa{
template<typename Tuple>
using tuple_rotate_right_return_type=mp11::mp_rotate_right_c<
typename std::remove_cv<typename std::remove_reference<Tuple>::type>::type,
1
>;
template<std::size_t... Is,typename Tuple>
tuple_rotate_right_return_type<Tuple>
tuple_rotate_right_aux(mp11::index_sequence<Is...>,Tuple&& x)
{
return tuple_rotate_right_return_type<Tuple>{
std::get<(Is+sizeof...(Is)-1)%sizeof...(Is)>(std::forward<Tuple>(x))...};
}
template<typename Tuple>
tuple_rotate_right_return_type<Tuple> tuple_rotate_right(Tuple&& x)
{
using RawTuple=typename std::remove_cv<
typename std::remove_reference<Tuple>::type>::type;
return tuple_rotate_right_aux(
mp11::make_index_sequence<std::tuple_size<RawTuple>::value>{},
std::forward<Tuple>(x));
}
} /* namespace foa */
} /* namespace detail */
} /* namespace unordered */
} /* namespace boost */
#endif
+86
View File
@@ -61,4 +61,90 @@ namespace boost {
}
}
// 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
@@ -60,92 +60,6 @@
#include <type_traits>
#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_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
// 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
#if BOOST_UNORDERED_CXX11_CONSTRUCTION
#include <boost/mp11/list.hpp>
#include <boost/mp11/algorithm.hpp>
@@ -0,0 +1,21 @@
#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
+41 -6
View File
@@ -10,7 +10,7 @@
#pragma once
#endif
#include <boost/unordered/detail/foa.hpp>
#include <boost/unordered/detail/foa/table.hpp>
#include <boost/unordered/detail/type_traits.hpp>
#include <boost/unordered/unordered_flat_map_fwd.hpp>
@@ -32,10 +32,8 @@ namespace boost {
#pragma warning(disable : 4714) /* marked as __forceinline not inlined */
#endif
template <class Key, class T, class Hash, class KeyEqual, class Allocator>
class unordered_flat_map
{
struct map_types
namespace detail {
template <class Key, class T> struct flat_map_types
{
using key_type = Key;
using raw_key_type = typename std::remove_const<Key>::type;
@@ -45,19 +43,56 @@ namespace boost {
using moved_type = std::pair<raw_key_type&&, raw_mapped_type&&>;
using value_type = std::pair<Key const, T>;
using element_type = value_type;
static value_type& value_from(element_type& x) { return x; }
template <class K, class V>
static raw_key_type const& extract(std::pair<K, V> const& kv)
{
return kv.first;
}
static moved_type move(value_type& x)
static moved_type move(init_type& x)
{
return {std::move(x.first), std::move(x.second)};
}
static moved_type move(element_type& x)
{
// TODO: we probably need to launder here
return {std::move(const_cast<raw_key_type&>(x.first)),
std::move(const_cast<raw_mapped_type&>(x.second))};
}
template <class A, class... Args>
static void construct(A& al, init_type* p, Args&&... args)
{
boost::allocator_construct(al, p, std::forward<Args>(args)...);
}
template <class A, class... Args>
static void construct(A& al, value_type* p, Args&&... args)
{
boost::allocator_construct(al, p, std::forward<Args>(args)...);
}
template <class A> static void destroy(A& al, init_type* p) noexcept
{
boost::allocator_destroy(al, p);
}
template <class A> static void destroy(A& al, value_type* p) noexcept
{
boost::allocator_destroy(al, p);
}
};
} // namespace detail
template <class Key, class T, class Hash, class KeyEqual, class Allocator>
class unordered_flat_map
{
using map_types = detail::flat_map_types<Key, T>;
using table_type = detail::foa::table<map_types, Hash, KeyEqual,
typename boost::allocator_rebind<Allocator,
+27 -6
View File
@@ -10,7 +10,7 @@
#pragma once
#endif
#include <boost/unordered/detail/foa.hpp>
#include <boost/unordered/detail/foa/table.hpp>
#include <boost/unordered/detail/type_traits.hpp>
#include <boost/unordered/unordered_flat_set_fwd.hpp>
@@ -30,17 +30,38 @@ namespace boost {
#pragma warning(disable : 4714) /* marked as __forceinline not inlined */
#endif
template <class Key, class Hash, class KeyEqual, class Allocator>
class unordered_flat_set
{
struct set_types
namespace detail {
template <class Key> struct flat_set_types
{
using key_type = Key;
using init_type = Key;
using value_type = Key;
static Key const& extract(value_type const& key) { return key; }
static Key&& move(value_type& x) { return std::move(x); }
using element_type = value_type;
static Key& value_from(element_type& x) { return x; }
static element_type&& move(element_type& x) { return std::move(x); }
template <class A, class... Args>
static void construct(A& al, value_type* p, Args&&... args)
{
boost::allocator_construct(al, p, std::forward<Args>(args)...);
}
template <class A> static void destroy(A& al, value_type* p) noexcept
{
boost::allocator_destroy(al, p);
}
};
} // namespace detail
template <class Key, class Hash, class KeyEqual, class Allocator>
class unordered_flat_set
{
using set_types = detail::flat_set_types<Key>;
using table_type = detail::foa::table<set_types, Hash, KeyEqual,
typename boost::allocator_rebind<Allocator,
@@ -15,6 +15,7 @@
#pragma once
#endif
#include <boost/unordered/detail/requires_cxx11.hpp>
#include <boost/core/explicit_operator_bool.hpp>
#include <boost/functional/hash.hpp>
#include <boost/move/move.hpp>
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,49 @@
// 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_NODE_MAP_FWD_HPP_INCLUDED
#define BOOST_UNORDERED_NODE_MAP_FWD_HPP_INCLUDED
#include <boost/config.hpp>
#if defined(BOOST_HAS_PRAGMA_ONCE)
#pragma once
#endif
#include <boost/functional/hash_fwd.hpp>
#include <boost/unordered/detail/fwd.hpp>
#include <functional>
#include <memory>
namespace boost {
namespace unordered {
template <class Key, class T, class Hash = boost::hash<Key>,
class KeyEqual = std::equal_to<Key>,
class Allocator = std::allocator<std::pair<const Key, T> > >
class unordered_node_map;
template <class Key, class T, class Hash, class KeyEqual, class Allocator>
bool operator==(
unordered_node_map<Key, T, Hash, KeyEqual, Allocator> const& lhs,
unordered_node_map<Key, T, Hash, KeyEqual, Allocator> const& rhs);
template <class Key, class T, class Hash, class KeyEqual, class Allocator>
bool operator!=(
unordered_node_map<Key, T, Hash, KeyEqual, Allocator> const& lhs,
unordered_node_map<Key, T, Hash, KeyEqual, Allocator> const& rhs);
template <class Key, class T, class Hash, class KeyEqual, class Allocator>
void swap(unordered_node_map<Key, T, Hash, KeyEqual, Allocator>& lhs,
unordered_node_map<Key, T, Hash, KeyEqual, Allocator>& rhs)
noexcept(noexcept(lhs.swap(rhs)));
} // namespace unordered
using boost::unordered::unordered_node_map;
using boost::unordered::swap;
using boost::unordered::operator==;
using boost::unordered::operator!=;
} // namespace boost
#endif
@@ -0,0 +1,764 @@
// 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_UNORDERED_NODE_SET_HPP_INCLUDED
#define BOOST_UNORDERED_UNORDERED_NODE_SET_HPP_INCLUDED
#include <boost/config.hpp>
#if defined(BOOST_HAS_PRAGMA_ONCE)
#pragma once
#endif
#include <boost/unordered/detail/foa/element_type.hpp>
#include <boost/unordered/detail/foa/node_handle.hpp>
#include <boost/unordered/detail/foa/table.hpp>
#include <boost/unordered/detail/type_traits.hpp>
#include <boost/unordered/unordered_node_set_fwd.hpp>
#include <boost/core/allocator_access.hpp>
#include <boost/functional/hash.hpp>
#include <boost/throw_exception.hpp>
#include <initializer_list>
#include <iterator>
#include <type_traits>
#include <utility>
namespace boost {
namespace unordered {
#if defined(BOOST_MSVC)
#pragma warning(push)
#pragma warning(disable : 4714) /* marked as __forceinline not inlined */
#endif
namespace detail {
template <class Key> struct node_set_types
{
using key_type = Key;
using init_type = Key;
using value_type = Key;
static Key const& extract(value_type const& key) { return key; }
using element_type=foa::element_type<value_type>;
static value_type& value_from(element_type const& x) { return *x.p; }
static Key const& extract(element_type const& k) { return *k.p; }
static element_type&& move(element_type& x) { return std::move(x); }
static value_type&& move(value_type& x) { return std::move(x); }
template <class A>
static void construct(A& al, element_type* p, element_type const& copy)
{
construct(al, p, *copy.p);
}
template <typename Allocator>
static void construct(
Allocator&, element_type* p, element_type&& x) noexcept
{
p->p = x.p;
x.p = nullptr;
}
template <class A, class... Args>
static void construct(A& al, value_type* p, Args&&... args)
{
boost::allocator_construct(al, p, std::forward<Args>(args)...);
}
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));
BOOST_TRY
{
boost::allocator_construct(al, 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_RETHROW
}
BOOST_CATCH_END
}
template <class A> static void destroy(A& al, value_type* p) noexcept
{
boost::allocator_destroy(al, p);
}
template <class A> 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);
}
}
};
template <class TypePolicy, class Allocator>
struct node_set_handle
: public detail::foa::node_handle_base<TypePolicy, Allocator>
{
private:
using base_type = detail::foa::node_handle_base<TypePolicy, Allocator>;
using typename base_type::type_policy;
template <class Key, class Hash, class Pred, class Alloc>
friend class boost::unordered::unordered_node_set;
public:
using value_type = typename TypePolicy::value_type;
constexpr node_set_handle() noexcept = default;
node_set_handle(node_set_handle&& nh) noexcept = default;
node_set_handle& operator=(node_set_handle&&) noexcept = default;
value_type& value() const
{
BOOST_ASSERT(!this->empty());
return const_cast<value_type&>(this->data());
}
};
} // namespace detail
template <class Key, class Hash, class KeyEqual, class Allocator>
class unordered_node_set
{
using set_types = detail::node_set_types<Key>;
using table_type = detail::foa::table<set_types, Hash, KeyEqual,
typename boost::allocator_rebind<Allocator,
typename set_types::value_type>::type>;
table_type table_;
template <class K, class H, class KE, class A, class Pred>
typename unordered_node_set<K, H, KE, A>::size_type friend erase_if(
unordered_node_set<K, H, KE, A>& set, Pred pred);
public:
using key_type = Key;
using value_type = typename set_types::value_type;
using init_type = typename set_types::init_type;
using size_type = std::size_t;
using difference_type = std::ptrdiff_t;
using hasher = Hash;
using key_equal = KeyEqual;
using allocator_type = Allocator;
using reference = value_type&;
using const_reference = value_type const&;
using pointer = typename boost::allocator_pointer<allocator_type>::type;
using const_pointer =
typename boost::allocator_const_pointer<allocator_type>::type;
using iterator = typename table_type::iterator;
using const_iterator = typename table_type::const_iterator;
using node_type = detail::node_set_handle<set_types,
typename boost::allocator_rebind<Allocator,
typename set_types::value_type>::type>;
using insert_return_type =
detail::foa::insert_return_type<iterator, node_type>;
unordered_node_set() : unordered_node_set(0) {}
explicit unordered_node_set(size_type n, hasher const& h = hasher(),
key_equal const& pred = key_equal(),
allocator_type const& a = allocator_type())
: table_(n, h, pred, a)
{
}
unordered_node_set(size_type n, allocator_type const& a)
: unordered_node_set(n, hasher(), key_equal(), a)
{
}
unordered_node_set(size_type n, hasher const& h, allocator_type const& a)
: unordered_node_set(n, h, key_equal(), a)
{
}
template <class InputIterator>
unordered_node_set(
InputIterator f, InputIterator l, allocator_type const& a)
: unordered_node_set(f, l, size_type(0), hasher(), key_equal(), a)
{
}
explicit unordered_node_set(allocator_type const& a)
: unordered_node_set(0, a)
{
}
template <class Iterator>
unordered_node_set(Iterator first, Iterator last, size_type n = 0,
hasher const& h = hasher(), key_equal const& pred = key_equal(),
allocator_type const& a = allocator_type())
: unordered_node_set(n, h, pred, a)
{
this->insert(first, last);
}
template <class InputIt>
unordered_node_set(
InputIt first, InputIt last, size_type n, allocator_type const& a)
: unordered_node_set(first, last, n, hasher(), key_equal(), a)
{
}
template <class Iterator>
unordered_node_set(Iterator first, Iterator last, size_type n,
hasher const& h, allocator_type const& a)
: unordered_node_set(first, last, n, h, key_equal(), a)
{
}
unordered_node_set(unordered_node_set const& other) : table_(other.table_)
{
}
unordered_node_set(
unordered_node_set const& other, allocator_type const& a)
: table_(other.table_, a)
{
}
unordered_node_set(unordered_node_set&& other)
noexcept(std::is_nothrow_move_constructible<hasher>::value&&
std::is_nothrow_move_constructible<key_equal>::value&&
std::is_nothrow_move_constructible<allocator_type>::value)
: table_(std::move(other.table_))
{
}
unordered_node_set(unordered_node_set&& other, allocator_type const& al)
: table_(std::move(other.table_), al)
{
}
unordered_node_set(std::initializer_list<value_type> ilist,
size_type n = 0, hasher const& h = hasher(),
key_equal const& pred = key_equal(),
allocator_type const& a = allocator_type())
: unordered_node_set(ilist.begin(), ilist.end(), n, h, pred, a)
{
}
unordered_node_set(
std::initializer_list<value_type> il, allocator_type const& a)
: unordered_node_set(il, size_type(0), hasher(), key_equal(), a)
{
}
unordered_node_set(std::initializer_list<value_type> init, size_type n,
allocator_type const& a)
: unordered_node_set(init, n, hasher(), key_equal(), a)
{
}
unordered_node_set(std::initializer_list<value_type> init, size_type n,
hasher const& h, allocator_type const& a)
: unordered_node_set(init, n, h, key_equal(), a)
{
}
~unordered_node_set() = default;
unordered_node_set& operator=(unordered_node_set const& other)
{
table_ = other.table_;
return *this;
}
unordered_node_set& operator=(unordered_node_set&& other) noexcept(
noexcept(std::declval<table_type&>() = std::declval<table_type&&>()))
{
table_ = std::move(other.table_);
return *this;
}
allocator_type get_allocator() const noexcept
{
return table_.get_allocator();
}
/// Iterators
///
iterator begin() noexcept { return table_.begin(); }
const_iterator begin() const noexcept { return table_.begin(); }
const_iterator cbegin() const noexcept { return table_.cbegin(); }
iterator end() noexcept { return table_.end(); }
const_iterator end() const noexcept { return table_.end(); }
const_iterator cend() const noexcept { return table_.cend(); }
/// Capacity
///
BOOST_ATTRIBUTE_NODISCARD bool empty() const noexcept
{
return table_.empty();
}
size_type size() const noexcept { return table_.size(); }
size_type max_size() const noexcept { return table_.max_size(); }
/// Modifiers
///
void clear() noexcept { table_.clear(); }
BOOST_FORCEINLINE std::pair<iterator, bool> insert(
value_type const& value)
{
return table_.insert(value);
}
BOOST_FORCEINLINE std::pair<iterator, bool> insert(value_type&& value)
{
return table_.insert(std::move(value));
}
template <class K>
BOOST_FORCEINLINE typename std::enable_if<
detail::transparent_non_iterable<K, unordered_node_set>::value,
std::pair<iterator, bool> >::type
insert(K&& k)
{
return table_.try_emplace(std::forward<K>(k));
}
BOOST_FORCEINLINE iterator insert(const_iterator, value_type const& value)
{
return table_.insert(value).first;
}
BOOST_FORCEINLINE iterator insert(const_iterator, value_type&& value)
{
return table_.insert(std::move(value)).first;
}
template <class K>
BOOST_FORCEINLINE typename std::enable_if<
detail::transparent_non_iterable<K, unordered_node_set>::value,
iterator>::type
insert(const_iterator, K&& k)
{
return table_.try_emplace(std::forward<K>(k)).first;
}
template <class InputIterator>
void insert(InputIterator first, InputIterator last)
{
for (auto pos = first; pos != last; ++pos) {
table_.emplace(*pos);
}
}
void insert(std::initializer_list<value_type> ilist)
{
this->insert(ilist.begin(), ilist.end());
}
insert_return_type insert(node_type&& nh)
{
if (nh.empty()) {
return {end(), false, node_type{}};
}
BOOST_ASSERT(get_allocator() == nh.get_allocator());
auto itp = table_.insert(std::move(nh.element()));
if (itp.second) {
nh.reset();
return {itp.first, true, node_type{}};
} else {
return {itp.first, false, std::move(nh)};
}
}
iterator insert(const_iterator, node_type&& nh)
{
if (nh.empty()) {
return end();
}
BOOST_ASSERT(get_allocator() == nh.get_allocator());
auto itp = table_.insert(std::move(nh.element()));
if (itp.second) {
nh.reset();
return itp.first;
} else {
return itp.first;
}
}
template <class... Args>
BOOST_FORCEINLINE std::pair<iterator, bool> emplace(Args&&... args)
{
return table_.emplace(std::forward<Args>(args)...);
}
template <class... Args>
BOOST_FORCEINLINE iterator emplace_hint(const_iterator, Args&&... args)
{
return table_.emplace(std::forward<Args>(args)...).first;
}
BOOST_FORCEINLINE void erase(const_iterator pos)
{
return table_.erase(pos);
}
iterator erase(const_iterator first, const_iterator last)
{
while (first != last) {
this->erase(first++);
}
return iterator{detail::foa::const_iterator_cast_tag{}, last};
}
BOOST_FORCEINLINE size_type erase(key_type const& key)
{
return table_.erase(key);
}
template <class K>
BOOST_FORCEINLINE typename std::enable_if<
detail::transparent_non_iterable<K, unordered_node_set>::value,
size_type>::type
erase(K const& key)
{
return table_.erase(key);
}
void swap(unordered_node_set& rhs) noexcept(
noexcept(std::declval<table_type&>().swap(std::declval<table_type&>())))
{
table_.swap(rhs.table_);
}
node_type extract(const_iterator pos)
{
BOOST_ASSERT(pos != end());
node_type nh;
auto elem = table_.extract(pos);
nh.emplace(std::move(elem), get_allocator());
return nh;
}
node_type extract(key_type const& key)
{
auto pos = find(key);
return pos != end() ? extract(pos) : node_type();
}
template <class K>
typename std::enable_if<
boost::unordered::detail::transparent_non_iterable<K,
unordered_node_set>::value,
node_type>::type
extract(K const& key)
{
auto pos = find(key);
return pos != end() ? extract(pos) : node_type();
}
template <class H2, class P2>
void merge(unordered_node_set<key_type, H2, P2, allocator_type>& source)
{
table_.merge(source.table_);
}
template <class H2, class P2>
void merge(unordered_node_set<key_type, H2, P2, allocator_type>&& source)
{
table_.merge(std::move(source.table_));
}
/// Lookup
///
BOOST_FORCEINLINE size_type count(key_type const& key) const
{
auto pos = table_.find(key);
return pos != table_.end() ? 1 : 0;
}
template <class K>
BOOST_FORCEINLINE typename std::enable_if<
detail::are_transparent<K, hasher, key_equal>::value, size_type>::type
count(K const& key) const
{
auto pos = table_.find(key);
return pos != table_.end() ? 1 : 0;
}
BOOST_FORCEINLINE iterator find(key_type const& key)
{
return table_.find(key);
}
BOOST_FORCEINLINE const_iterator find(key_type const& key) const
{
return table_.find(key);
}
template <class K>
BOOST_FORCEINLINE typename std::enable_if<
boost::unordered::detail::are_transparent<K, hasher, key_equal>::value,
iterator>::type
find(K const& key)
{
return table_.find(key);
}
template <class K>
BOOST_FORCEINLINE typename std::enable_if<
boost::unordered::detail::are_transparent<K, hasher, key_equal>::value,
const_iterator>::type
find(K const& key) const
{
return table_.find(key);
}
BOOST_FORCEINLINE bool contains(key_type const& key) const
{
return this->find(key) != this->end();
}
template <class K>
BOOST_FORCEINLINE typename std::enable_if<
boost::unordered::detail::are_transparent<K, hasher, key_equal>::value,
bool>::type
contains(K const& key) const
{
return this->find(key) != this->end();
}
std::pair<iterator, iterator> equal_range(key_type const& key)
{
auto pos = table_.find(key);
if (pos == table_.end()) {
return {pos, pos};
}
auto next = pos;
++next;
return {pos, next};
}
std::pair<const_iterator, const_iterator> equal_range(
key_type const& key) const
{
auto pos = table_.find(key);
if (pos == table_.end()) {
return {pos, pos};
}
auto next = pos;
++next;
return {pos, next};
}
template <class K>
typename std::enable_if<
detail::are_transparent<K, hasher, key_equal>::value,
std::pair<iterator, iterator> >::type
equal_range(K const& key)
{
auto pos = table_.find(key);
if (pos == table_.end()) {
return {pos, pos};
}
auto next = pos;
++next;
return {pos, next};
}
template <class K>
typename std::enable_if<
detail::are_transparent<K, hasher, key_equal>::value,
std::pair<const_iterator, const_iterator> >::type
equal_range(K const& key) const
{
auto pos = table_.find(key);
if (pos == table_.end()) {
return {pos, pos};
}
auto next = pos;
++next;
return {pos, next};
}
/// 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
///
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==(
unordered_node_set<Key, Hash, KeyEqual, Allocator> const& lhs,
unordered_node_set<Key, Hash, KeyEqual, Allocator> const& rhs)
{
if (&lhs == &rhs) {
return true;
}
return (lhs.size() == rhs.size()) && ([&] {
for (auto const& key : lhs) {
auto pos = rhs.find(key);
if ((pos == rhs.end()) || (key != *pos)) {
return false;
}
}
return true;
})();
}
template <class Key, class Hash, class KeyEqual, class Allocator>
bool operator!=(
unordered_node_set<Key, Hash, KeyEqual, Allocator> const& lhs,
unordered_node_set<Key, Hash, KeyEqual, Allocator> const& rhs)
{
return !(lhs == rhs);
}
template <class Key, class Hash, class KeyEqual, class Allocator>
void swap(unordered_node_set<Key, Hash, KeyEqual, Allocator>& lhs,
unordered_node_set<Key, Hash, KeyEqual, Allocator>& rhs)
noexcept(noexcept(lhs.swap(rhs)))
{
lhs.swap(rhs);
}
template <class Key, class Hash, class KeyEqual, class Allocator,
class Pred>
typename unordered_node_set<Key, Hash, KeyEqual, Allocator>::size_type
erase_if(unordered_node_set<Key, Hash, KeyEqual, Allocator>& set, Pred pred)
{
return erase_if(set.table_, pred);
}
#if defined(BOOST_MSVC)
#pragma warning(pop) /* C4714 */
#endif
#if BOOST_UNORDERED_TEMPLATE_DEDUCTION_GUIDES
template <class InputIterator,
class Hash =
boost::hash<typename std::iterator_traits<InputIterator>::value_type>,
class Pred =
std::equal_to<typename std::iterator_traits<InputIterator>::value_type>,
class Allocator = std::allocator<
typename std::iterator_traits<InputIterator>::value_type>,
class = boost::enable_if_t<detail::is_input_iterator_v<InputIterator> >,
class = boost::enable_if_t<detail::is_hash_v<Hash> >,
class = boost::enable_if_t<detail::is_pred_v<Pred> >,
class = boost::enable_if_t<detail::is_allocator_v<Allocator> > >
unordered_node_set(InputIterator, InputIterator,
std::size_t = boost::unordered::detail::foa::default_bucket_count,
Hash = Hash(), Pred = Pred(), Allocator = Allocator())
-> unordered_node_set<
typename std::iterator_traits<InputIterator>::value_type, Hash, Pred,
Allocator>;
template <class T, class Hash = boost::hash<T>,
class Pred = std::equal_to<T>, class Allocator = std::allocator<T>,
class = 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> > >
unordered_node_set(std::initializer_list<T>,
std::size_t = boost::unordered::detail::foa::default_bucket_count,
Hash = Hash(), Pred = Pred(), Allocator = Allocator())
-> unordered_node_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> > >
unordered_node_set(InputIterator, InputIterator, std::size_t, Allocator)
-> unordered_node_set<
typename std::iterator_traits<InputIterator>::value_type,
boost::hash<typename std::iterator_traits<InputIterator>::value_type>,
std::equal_to<typename std::iterator_traits<InputIterator>::value_type>,
Allocator>;
template <class InputIterator, class Hash, class Allocator,
class = 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> > >
unordered_node_set(
InputIterator, InputIterator, std::size_t, Hash, Allocator)
-> unordered_node_set<
typename std::iterator_traits<InputIterator>::value_type, Hash,
std::equal_to<typename std::iterator_traits<InputIterator>::value_type>,
Allocator>;
template <class T, class Allocator,
class = boost::enable_if_t<detail::is_allocator_v<Allocator> > >
unordered_node_set(std::initializer_list<T>, std::size_t, Allocator)
-> unordered_node_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> > >
unordered_node_set(std::initializer_list<T>, std::size_t, Hash, Allocator)
-> unordered_node_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> > >
unordered_node_set(InputIterator, InputIterator, Allocator)
-> unordered_node_set<
typename std::iterator_traits<InputIterator>::value_type,
boost::hash<typename std::iterator_traits<InputIterator>::value_type>,
std::equal_to<typename std::iterator_traits<InputIterator>::value_type>,
Allocator>;
template <class T, class Allocator,
class = boost::enable_if_t<detail::is_allocator_v<Allocator> > >
unordered_node_set(std::initializer_list<T>, Allocator)
-> unordered_node_set<T, boost::hash<T>, std::equal_to<T>, Allocator>;
#endif
} // namespace unordered
} // namespace boost
#endif
@@ -0,0 +1,49 @@
// 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_NODE_SET_FWD_HPP_INCLUDED
#define BOOST_UNORDERED_NODE_SET_FWD_HPP_INCLUDED
#include <boost/config.hpp>
#if defined(BOOST_HAS_PRAGMA_ONCE)
#pragma once
#endif
#include <boost/functional/hash_fwd.hpp>
#include <boost/unordered/detail/fwd.hpp>
#include <functional>
#include <memory>
namespace boost {
namespace unordered {
template <class Key, class Hash = boost::hash<Key>,
class KeyEqual = std::equal_to<Key>,
class Allocator = std::allocator<Key> >
class unordered_node_set;
template <class Key, class Hash, class KeyEqual, class Allocator>
bool operator==(
unordered_node_set<Key, Hash, KeyEqual, Allocator> const& lhs,
unordered_node_set<Key, Hash, KeyEqual, Allocator> const& rhs);
template <class Key, class Hash, class KeyEqual, class Allocator>
bool operator!=(
unordered_node_set<Key, Hash, KeyEqual, Allocator> const& lhs,
unordered_node_set<Key, Hash, KeyEqual, Allocator> const& rhs);
template <class Key, class Hash, class KeyEqual, class Allocator>
void swap(unordered_node_set<Key, Hash, KeyEqual, Allocator>& lhs,
unordered_node_set<Key, Hash, KeyEqual, Allocator>& rhs)
noexcept(noexcept(lhs.swap(rhs)));
} // namespace unordered
using boost::unordered::unordered_node_set;
using boost::unordered::swap;
using boost::unordered::operator==;
using boost::unordered::operator!=;
} // namespace boost
#endif
@@ -15,6 +15,7 @@
#pragma once
#endif
#include <boost/unordered/detail/requires_cxx11.hpp>
#include <boost/core/explicit_operator_bool.hpp>
#include <boost/functional/hash.hpp>
#include <boost/move/move.hpp>
+78 -36
View File
@@ -1,6 +1,6 @@
# Copyright 2006-2008 Daniel James.
# Copyright 2022 Christian Mazakas
# Copyright 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)
@@ -26,6 +26,7 @@ project
<toolset>gcc-4.4:<cxxflags>-Wno-strict-aliasing
<toolset>gcc-4.4:<cxxflags>-fno-deduce-init-list
<toolset>clang-14:<cxxflags>-Wunused-template
<toolset>gcc:<warnings-as-errors>on
<toolset>clang:<warnings-as-errors>on
@@ -94,6 +95,7 @@ run exception/erase_exception_tests.cpp ;
run exception/rehash_exception_tests.cpp ;
run exception/swap_exception_tests.cpp : : : <define>BOOST_UNORDERED_SWAP_METHOD=2 ;
run exception/merge_exception_tests.cpp ;
run exception/less_tests.cpp ;
run unordered/narrow_cast_tests.cpp ;
run quick.cpp ;
@@ -102,47 +104,51 @@ import ../../config/checks/config : requires ;
CPP11 = [ requires cxx11_constexpr cxx11_noexcept cxx11_decltype cxx11_alignas ] ;
rule build_foa ( name )
local FOA_TESTS =
fwd_set_test
fwd_map_test
compile_set
compile_map
noexcept_tests
incomplete_test
simple_tests
equivalent_keys_tests
constructor_tests
copy_tests
move_tests
post_move_tests
assign_tests
insert_tests
insert_hint_tests
emplace_tests
erase_tests
merge_tests
find_tests
at_tests
load_factor_tests
rehash_tests
equality_tests
swap_tests
transparent_tests
reserve_tests
contains_tests
erase_if
scary_tests
init_type_insert_tests
max_load_tests
extract_tests
node_handle_tests
uses_allocator
;
for local test in $(FOA_TESTS)
{
run unordered/$(name).cpp : : : $(CPP11) <define>BOOST_UNORDERED_FOA_TESTS : foa_$(name) ;
run unordered/$(test).cpp : : : $(CPP11) <define>BOOST_UNORDERED_FOA_TESTS : foa_$(test) ;
}
build_foa fwd_set_test ;
build_foa fwd_map_test ;
build_foa compile_set ;
build_foa compile_map ;
build_foa noexcept_tests ;
run unordered/link_test_1.cpp unordered/link_test_2.cpp : : : $(CPP11) <define>BOOST_UNORDERED_FOA_TESTS : foa_link_test ;
build_foa incomplete_test ;
build_foa simple_tests ;
build_foa equivalent_keys_tests ;
build_foa constructor_tests ;
build_foa copy_tests ;
build_foa move_tests ;
build_foa post_move_tests ;
build_foa assign_tests ;
build_foa insert_tests ;
build_foa insert_hint_tests ;
build_foa emplace_tests ;
build_foa erase_tests ;
build_foa merge_tests ;
build_foa find_tests ;
build_foa at_tests ;
build_foa load_factor_tests ;
build_foa rehash_tests ;
build_foa equality_tests ;
build_foa swap_tests ;
run unordered/scoped_allocator.cpp : : : $(CPP11) <toolset>msvc-14.0:<build>no <define>BOOST_UNORDERED_FOA_TESTS : foa_scoped_allocator ;
build_foa transparent_tests ;
build_foa reserve_tests ;
build_foa contains_tests ;
build_foa erase_if ;
build_foa scary_tests ;
build_foa init_type_insert_tests ;
build_foa max_load_tests ;
run unordered/hash_is_avalanching_test.cpp ;
run exception/constructor_exception_tests.cpp : : : $(CPP11) <define>BOOST_UNORDERED_FOA_TESTS : foa_constructor_exception_tests ;
run exception/copy_exception_tests.cpp : : : $(CPP11) <define>BOOST_UNORDERED_FOA_TESTS : foa_copy_exception_tests ;
run exception/assign_exception_tests.cpp : : : $(CPP11) <define>BOOST_UNORDERED_FOA_TESTS : foa_assign_exception_tests ;
@@ -152,3 +158,39 @@ run exception/erase_exception_tests.cpp : : : $(CPP11) <define>BOOST_UNORD
run exception/rehash_exception_tests.cpp : : : $(CPP11) <define>BOOST_UNORDERED_FOA_TESTS : foa_rehash_exception_tests ;
run exception/swap_exception_tests.cpp : : : $(CPP11) <define>BOOST_UNORDERED_FOA_TESTS : foa_swap_exception_tests ;
run exception/merge_exception_tests.cpp : : : $(CPP11) <define>BOOST_UNORDERED_FOA_TESTS : foa_merge_exception_tests ;
alias foa_tests :
foa_$(FOA_TESTS)
foa_link_test
foa_scoped_allocator
hash_is_avalanching_test
foa_constructor_exception_tests
foa_copy_exception_tests
foa_assign_exception_tests
foa_move_assign_exception_tests
foa_insert_exception_tests
foa_erase_exception_tests
foa_rehash_exception_tests
foa_swap_exception_tests
foa_merge_exception_tests
;
local CFOA_TESTS =
insert_tests
erase_tests
try_emplace_tests
emplace_tests
visit_tests
constructor_tests
assign_tests
;
for local test in $(CFOA_TESTS)
{
run cfoa/$(test).cpp
: requirements $(CPP11)
: target-name cfoa_$(test)
;
}
alias cfoa_tests : cfoa_$(CFOA_TESTS) ;
+147
View File
@@ -0,0 +1,147 @@
// 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)
#include "helpers.hpp"
#include <boost/unordered/concurrent_flat_map.hpp>
test::seed_t initialize_seed{2762556623};
using test::default_generator;
using test::limited_range;
using test::sequential;
using hasher = stateful_hash;
using key_equal = stateful_key_equal;
using allocator_type = stateful_allocator<std::pair<raii const, raii> >;
using map_type = boost::unordered::concurrent_flat_map<raii, raii, hasher,
key_equal, allocator_type>;
using map_value_type = typename map_type::value_type;
namespace {
template <class G> void copy_assign(G gen, test::random_generator rg)
{
auto values = make_random_values(1024 * 16, [&] { return gen(rg); });
auto reference_map =
boost::unordered_flat_map<raii, raii>(values.begin(), values.end());
// to test:
// self-assign
// propagation
//
// lhs empty, rhs empty
{
raii::reset_counts();
map_type x(0, hasher(1), key_equal(2), allocator_type(3));
map_type y;
BOOST_TEST(x.empty());
BOOST_TEST(y.empty());
y = x;
BOOST_TEST_EQ(raii::destructor, 0u);
BOOST_TEST_EQ(raii::copy_assignment, 0u);
BOOST_TEST_EQ(raii::move_assignment, 0u);
BOOST_TEST_EQ(raii::copy_constructor, 0u);
BOOST_TEST_EQ(x.hash_function(), y.hash_function());
BOOST_TEST_EQ(x.key_eq(), y.key_eq());
BOOST_TEST(x.get_allocator() != y.get_allocator());
}
// lhs non-empty, rhs empty
{
raii::reset_counts();
map_type x(0, hasher(1), key_equal(2), allocator_type(3));
map_type y(values.begin(), values.end(), values.size());
auto const old_cc = +raii::copy_constructor;
auto const old_size = y.size();
BOOST_TEST(x.empty());
BOOST_TEST(!y.empty());
y = x;
BOOST_TEST_EQ(raii::destructor, 2 * old_size);
BOOST_TEST_EQ(raii::copy_assignment, 0u);
BOOST_TEST_EQ(raii::move_assignment, 0u);
BOOST_TEST_EQ(raii::copy_constructor, old_cc);
BOOST_TEST_EQ(x.hash_function(), y.hash_function());
BOOST_TEST_EQ(x.key_eq(), y.key_eq());
BOOST_TEST(x.get_allocator() != y.get_allocator());
}
check_raii_counts();
// lhs empty, rhs non-empty
{
raii::reset_counts();
map_type x(values.begin(), values.end(), values.size(), hasher(1),
key_equal(2), allocator_type(3));
map_type y;
auto const old_cc = +raii::copy_constructor;
BOOST_TEST(!x.empty());
BOOST_TEST(y.empty());
y = x;
BOOST_TEST_EQ(raii::destructor, 0u);
BOOST_TEST_EQ(raii::copy_assignment, 0u);
BOOST_TEST_EQ(raii::move_assignment, 0u);
BOOST_TEST_EQ(raii::copy_constructor, old_cc + (2 * x.size()));
BOOST_TEST_EQ(x.hash_function(), y.hash_function());
BOOST_TEST_EQ(x.key_eq(), y.key_eq());
BOOST_TEST(x.get_allocator() != y.get_allocator());
test_matches_reference(y, reference_map);
}
check_raii_counts();
// lhs non-empty, rhs non-empty
{
raii::reset_counts();
map_type x(values.begin(), values.end(), values.size(), hasher(1),
key_equal(2), allocator_type(3));
map_type y(values.begin(), values.end(), values.size());
auto const old_size = y.size();
auto const old_cc = +raii::copy_constructor;
BOOST_TEST(!x.empty());
BOOST_TEST(!y.empty());
y = x;
BOOST_TEST_EQ(raii::destructor, 2 * old_size);
BOOST_TEST_EQ(raii::copy_assignment, 0u);
BOOST_TEST_EQ(raii::move_assignment, 0u);
BOOST_TEST_EQ(raii::copy_constructor, old_cc + (2 * x.size()));
BOOST_TEST_EQ(x.hash_function(), y.hash_function());
BOOST_TEST_EQ(x.key_eq(), y.key_eq());
BOOST_TEST(x.get_allocator() != y.get_allocator());
}
check_raii_counts();
}
} // namespace
// clang-format off
UNORDERED_TEST(
copy_assign,
((value_type_generator))
((default_generator)(sequential)(limited_range)))
// clang-format on
RUN_TESTS()
+814
View File
@@ -0,0 +1,814 @@
// 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)
#include "helpers.hpp"
#include <boost/unordered/concurrent_flat_map.hpp>
test::seed_t initialize_seed(4122023);
using test::default_generator;
using test::limited_range;
using test::sequential;
template <class T> struct soccc_allocator
{
int x_ = -1;
using value_type = T;
soccc_allocator() = default;
soccc_allocator(soccc_allocator const&) = default;
soccc_allocator(soccc_allocator&&) = default;
soccc_allocator(int const x) : x_{x} {}
template <class U> soccc_allocator(soccc_allocator<U> const& rhs) : x_{rhs.x_}
{
}
T* allocate(std::size_t n)
{
return static_cast<T*>(::operator new(n * sizeof(T)));
}
void deallocate(T* p, std::size_t) { ::operator delete(p); }
soccc_allocator select_on_container_copy_construction() const
{
return {x_ + 1};
}
bool operator==(soccc_allocator const& rhs) const { return x_ == rhs.x_; }
bool operator!=(soccc_allocator const& rhs) const { return x_ != rhs.x_; }
};
using hasher = stateful_hash;
using key_equal = stateful_key_equal;
using allocator_type = stateful_allocator<std::pair<raii const, raii> >;
using map_type = boost::unordered::concurrent_flat_map<raii, raii, hasher,
key_equal, allocator_type>;
using map_value_type = typename map_type::value_type;
UNORDERED_AUTO_TEST (default_constructor) {
boost::unordered::concurrent_flat_map<raii, raii> x;
BOOST_TEST(x.empty());
BOOST_TEST_EQ(x.size(), 0u);
}
UNORDERED_AUTO_TEST (bucket_count_with_hasher_key_equal_and_allocator) {
raii::reset_counts();
{
map_type x(0);
BOOST_TEST(x.empty());
BOOST_TEST_EQ(x.size(), 0u);
BOOST_TEST_EQ(x.hash_function(), hasher());
BOOST_TEST_EQ(x.key_eq(), key_equal());
}
{
map_type x(0, hasher(1));
BOOST_TEST(x.empty());
BOOST_TEST_EQ(x.size(), 0u);
BOOST_TEST_EQ(x.hash_function(), hasher(1));
BOOST_TEST_EQ(x.key_eq(), key_equal());
}
{
map_type x(0, hasher(1), key_equal(2));
BOOST_TEST(x.empty());
BOOST_TEST_EQ(x.size(), 0u);
BOOST_TEST_EQ(x.hash_function(), hasher(1));
BOOST_TEST_EQ(x.key_eq(), key_equal(2));
}
{
map_type x(0, hasher(1), key_equal(2), allocator_type{});
BOOST_TEST(x.empty());
BOOST_TEST_EQ(x.size(), 0u);
BOOST_TEST_EQ(x.hash_function(), hasher(1));
BOOST_TEST_EQ(x.key_eq(), key_equal(2));
BOOST_TEST(x.get_allocator() == allocator_type{});
}
}
UNORDERED_AUTO_TEST (soccc) {
raii::reset_counts();
boost::unordered::concurrent_flat_map<raii, raii, hasher, key_equal,
soccc_allocator<std::pair<raii const, raii> > >
x;
boost::unordered::concurrent_flat_map<raii, raii, hasher, key_equal,
soccc_allocator<std::pair<raii const, raii> > >
y(x);
BOOST_TEST_EQ(y.hash_function(), x.hash_function());
BOOST_TEST_EQ(y.key_eq(), x.key_eq());
BOOST_TEST(y.get_allocator() != x.get_allocator());
}
namespace {
template <class G> void from_iterator_range(G gen, test::random_generator rg)
{
auto values = make_random_values(1024 * 16, [&] { return gen(rg); });
auto reference_map =
boost::unordered_flat_map<raii, raii>(values.begin(), values.end());
raii::reset_counts();
{
map_type x(values.begin(), values.end());
test_matches_reference(x, reference_map);
BOOST_TEST_GT(x.size(), 0u);
BOOST_TEST_LE(x.size(), values.size());
BOOST_TEST_EQ(x.hash_function(), hasher());
BOOST_TEST_EQ(x.key_eq(), key_equal());
BOOST_TEST(x.get_allocator() == allocator_type{});
if (rg == sequential) {
BOOST_TEST_EQ(x.size(), values.size());
}
}
{
map_type x(values.begin(), values.end(), 0);
test_matches_reference(x, reference_map);
BOOST_TEST_GT(x.size(), 0u);
BOOST_TEST_LE(x.size(), values.size());
BOOST_TEST_EQ(x.hash_function(), hasher());
BOOST_TEST_EQ(x.key_eq(), key_equal());
BOOST_TEST(x.get_allocator() == allocator_type{});
if (rg == sequential) {
BOOST_TEST_EQ(x.size(), values.size());
}
}
{
map_type x(values.begin(), values.end(), 0, hasher(1));
test_matches_reference(x, reference_map);
BOOST_TEST_GT(x.size(), 0u);
BOOST_TEST_LE(x.size(), values.size());
BOOST_TEST_EQ(x.hash_function(), hasher(1));
BOOST_TEST_EQ(x.key_eq(), key_equal());
BOOST_TEST(x.get_allocator() == allocator_type{});
if (rg == sequential) {
BOOST_TEST_EQ(x.size(), values.size());
}
}
{
map_type x(values.begin(), values.end(), 0, hasher(1), key_equal(2));
test_matches_reference(x, reference_map);
BOOST_TEST_GT(x.size(), 0u);
BOOST_TEST_LE(x.size(), values.size());
BOOST_TEST_EQ(x.hash_function(), hasher(1));
BOOST_TEST_EQ(x.key_eq(), key_equal(2));
BOOST_TEST(x.get_allocator() == allocator_type{});
if (rg == sequential) {
BOOST_TEST_EQ(x.size(), values.size());
}
}
{
map_type x(values.begin(), values.end(), 0, hasher(1), key_equal(2),
allocator_type{});
test_matches_reference(x, reference_map);
BOOST_TEST_GT(x.size(), 0u);
BOOST_TEST_LE(x.size(), values.size());
BOOST_TEST_EQ(x.hash_function(), hasher(1));
BOOST_TEST_EQ(x.key_eq(), key_equal(2));
BOOST_TEST(x.get_allocator() == allocator_type{});
if (rg == sequential) {
BOOST_TEST_EQ(x.size(), values.size());
}
}
check_raii_counts();
}
template <class G> void copy_constructor(G gen, test::random_generator rg)
{
{
map_type x(0, hasher(1), key_equal(2), allocator_type{});
map_type y(x);
BOOST_TEST_EQ(y.size(), x.size());
BOOST_TEST_EQ(y.hash_function(), x.hash_function());
BOOST_TEST_EQ(y.key_eq(), x.key_eq());
BOOST_TEST(y.get_allocator() == x.get_allocator());
}
auto values = make_random_values(1024 * 16, [&] { return gen(rg); });
auto reference_map =
boost::unordered_flat_map<raii, raii>(values.begin(), values.end());
raii::reset_counts();
{
map_type x(values.begin(), values.end(), 0, hasher(1), key_equal(2),
allocator_type{});
thread_runner(
values, [&x, &reference_map](
boost::span<span_value_type<decltype(values)> > s) {
(void)s;
map_type y(x);
test_matches_reference(x, reference_map);
test_matches_reference(y, reference_map);
BOOST_TEST_EQ(y.size(), x.size());
BOOST_TEST_EQ(y.hash_function(), x.hash_function());
BOOST_TEST_EQ(y.key_eq(), x.key_eq());
BOOST_TEST(y.get_allocator() == x.get_allocator());
});
}
check_raii_counts();
raii::reset_counts();
{
allocator_type a;
map_type x(values.begin(), values.end(), 0, hasher(1), key_equal(2), a);
thread_runner(
values, [&x, &reference_map, a](
boost::span<span_value_type<decltype(values)> > s) {
(void)s;
map_type y(x, a);
test_matches_reference(x, reference_map);
test_matches_reference(y, reference_map);
BOOST_TEST_EQ(y.size(), x.size());
BOOST_TEST_EQ(y.hash_function(), x.hash_function());
BOOST_TEST_EQ(y.key_eq(), x.key_eq());
BOOST_TEST(y.get_allocator() == x.get_allocator());
});
}
check_raii_counts();
}
template <class G>
void copy_constructor_with_insertion(G gen, test::random_generator rg)
{
auto values = make_random_values(1024 * 16, [&] { return gen(rg); });
auto reference_map =
boost::unordered_flat_map<raii, raii>(values.begin(), values.end());
raii::reset_counts();
std::mutex m;
std::condition_variable cv;
bool ready = false;
{
map_type x(0, hasher(1), key_equal(2), allocator_type{});
auto f = [&x, &values, &m, &cv, &ready] {
{
std::lock_guard<std::mutex> guard(m);
ready = true;
}
cv.notify_all();
for (auto const& val : values) {
x.insert(val);
}
};
std::thread t1(f);
std::thread t2(f);
thread_runner(
values, [&x, &reference_map, &values, rg, &m, &cv, &ready](
boost::span<span_value_type<decltype(values)> > s) {
(void)s;
{
std::unique_lock<std::mutex> lk(m);
cv.wait(lk, [&] { return ready; });
}
map_type y(x);
BOOST_TEST_LE(y.size(), values.size());
BOOST_TEST_EQ(y.hash_function(), x.hash_function());
BOOST_TEST_EQ(y.key_eq(), x.key_eq());
BOOST_TEST(y.get_allocator() == x.get_allocator());
x.visit_all([&reference_map, rg](
typename map_type::value_type const& val) {
BOOST_TEST(reference_map.contains(val.first));
if (rg == sequential) {
BOOST_TEST_EQ(val.second, reference_map.find(val.first)->second);
}
});
});
t1.join();
t2.join();
}
check_raii_counts();
}
template <class G> void move_constructor(G gen, test::random_generator rg)
{
{
map_type x(0, hasher(1), key_equal(2), allocator_type{});
auto const old_size = x.size();
map_type y(std::move(x));
BOOST_TEST_EQ(y.size(), old_size);
BOOST_TEST_EQ(y.hash_function(), hasher(1));
BOOST_TEST_EQ(y.key_eq(), key_equal(2));
BOOST_TEST_EQ(x.size(), 0u);
BOOST_TEST_EQ(x.hash_function(), hasher());
BOOST_TEST_EQ(x.key_eq(), key_equal());
BOOST_TEST(y.get_allocator() == x.get_allocator());
}
auto values = make_random_values(1024 * 16, [&] { return gen(rg); });
auto reference_map =
boost::unordered_flat_map<raii, raii>(values.begin(), values.end());
raii::reset_counts();
{
map_type x(values.begin(), values.end(), 0, hasher(1), key_equal(2),
allocator_type{});
std::atomic_uint num_transfers{0};
auto const old_mc = +raii::move_constructor;
thread_runner(
values, [&x, &reference_map, &num_transfers](
boost::span<span_value_type<decltype(values)> > s) {
(void)s;
auto const old_size = x.size();
map_type y(std::move(x));
if (!y.empty()) {
++num_transfers;
test_matches_reference(y, reference_map);
BOOST_TEST_EQ(y.size(), old_size);
BOOST_TEST_EQ(y.hash_function(), hasher(1));
BOOST_TEST_EQ(y.key_eq(), key_equal(2));
} else {
BOOST_TEST_EQ(y.size(), 0u);
BOOST_TEST_EQ(y.hash_function(), hasher());
BOOST_TEST_EQ(y.key_eq(), key_equal());
}
BOOST_TEST_EQ(x.size(), 0u);
BOOST_TEST_EQ(x.hash_function(), hasher());
BOOST_TEST_EQ(x.key_eq(), key_equal());
BOOST_TEST(y.get_allocator() == x.get_allocator());
});
BOOST_TEST_EQ(num_transfers, 1u);
BOOST_TEST_EQ(raii::move_constructor, old_mc);
}
check_raii_counts();
// allocator-aware move constructor, unequal allocators
raii::reset_counts();
{
map_type x(values.begin(), values.end(), 0, hasher(1), key_equal(2),
allocator_type{1});
std::atomic_uint num_transfers{0};
auto const old_mc = +raii::move_constructor;
auto const old_size = x.size();
thread_runner(
values, [&x, &reference_map, &num_transfers, old_size](
boost::span<span_value_type<decltype(values)> > s) {
(void)s;
auto a = allocator_type{2};
BOOST_TEST(a != x.get_allocator());
map_type y(std::move(x), a);
if (!y.empty()) {
++num_transfers;
test_matches_reference(y, reference_map);
BOOST_TEST_EQ(y.size(), old_size);
BOOST_TEST_EQ(y.hash_function(), hasher(1));
BOOST_TEST_EQ(y.key_eq(), key_equal(2));
} else {
BOOST_TEST_EQ(y.size(), 0u);
BOOST_TEST_EQ(y.hash_function(), hasher());
BOOST_TEST_EQ(y.key_eq(), key_equal());
}
BOOST_TEST_EQ(x.size(), 0u);
BOOST_TEST_EQ(x.hash_function(), hasher());
BOOST_TEST_EQ(x.key_eq(), key_equal());
BOOST_TEST(y.get_allocator() != x.get_allocator());
BOOST_TEST(y.get_allocator() == a);
});
BOOST_TEST_EQ(num_transfers, 1u);
BOOST_TEST_EQ(raii::move_constructor, old_mc + (2 * old_size));
}
check_raii_counts();
// allocator-aware move constructor, equal allocators
raii::reset_counts();
{
map_type x(values.begin(), values.end(), 0, hasher(1), key_equal(2),
allocator_type{1});
std::atomic_uint num_transfers{0};
auto const old_mc = +raii::move_constructor;
auto const old_size = x.size();
thread_runner(
values, [&x, &reference_map, &num_transfers, old_size](
boost::span<span_value_type<decltype(values)> > s) {
(void)s;
auto a = allocator_type{1};
BOOST_TEST(a == x.get_allocator());
map_type y(std::move(x), a);
if (!y.empty()) {
++num_transfers;
test_matches_reference(y, reference_map);
BOOST_TEST_EQ(y.size(), old_size);
BOOST_TEST_EQ(y.hash_function(), hasher(1));
BOOST_TEST_EQ(y.key_eq(), key_equal(2));
} else {
BOOST_TEST_EQ(y.size(), 0u);
BOOST_TEST_EQ(y.hash_function(), hasher());
BOOST_TEST_EQ(y.key_eq(), key_equal());
}
BOOST_TEST_EQ(x.size(), 0u);
BOOST_TEST_EQ(x.hash_function(), hasher());
BOOST_TEST_EQ(x.key_eq(), key_equal());
BOOST_TEST(y.get_allocator() == x.get_allocator());
BOOST_TEST(y.get_allocator() == a);
});
BOOST_TEST_EQ(num_transfers, 1u);
BOOST_TEST_EQ(raii::move_constructor, old_mc);
}
check_raii_counts();
}
template <class G>
void move_constructor_with_insertion(G gen, test::random_generator rg)
{
auto values = make_random_values(1024 * 16, [&] { return gen(rg); });
auto reference_map =
boost::unordered_flat_map<raii, raii>(values.begin(), values.end());
raii::reset_counts();
std::mutex m;
std::condition_variable cv;
bool ready = false;
{
map_type x(0, hasher(1), key_equal(2), allocator_type{});
std::atomic_uint num_transfers{0};
std::thread t1([&x, &values] {
for (auto const& val : values) {
x.insert(val);
}
});
std::thread t2([&x, &m, &cv, &ready] {
while (x.empty()) {
std::this_thread::yield();
}
{
std::lock_guard<std::mutex> guard(m);
ready = true;
}
cv.notify_all();
});
thread_runner(
values, [&x, &reference_map, &num_transfers, rg, &m, &ready, &cv](
boost::span<span_value_type<decltype(values)> > s) {
(void)s;
{
std::unique_lock<std::mutex> lk(m);
cv.wait(lk, [&] { return ready; });
}
map_type y(std::move(x));
if (!y.empty()) {
++num_transfers;
y.cvisit_all([&reference_map, rg](map_value_type const& val) {
BOOST_TEST(reference_map.contains(val.first));
if (rg == sequential) {
BOOST_TEST_EQ(
val.second, reference_map.find(val.first)->second);
}
});
}
});
t1.join();
t2.join();
BOOST_TEST_GE(num_transfers, 1u);
}
check_raii_counts();
}
template <class G>
void iterator_range_with_allocator(G gen, test::random_generator rg)
{
auto values = make_random_values(1024 * 16, [&] { return gen(rg); });
auto reference_map =
boost::unordered_flat_map<raii, raii>(values.begin(), values.end());
raii::reset_counts();
{
allocator_type a;
map_type x(values.begin(), values.end(), a);
BOOST_TEST_GT(x.size(), 0u);
BOOST_TEST_LE(x.size(), values.size());
if (rg == sequential) {
BOOST_TEST_EQ(x.size(), values.size());
}
BOOST_TEST_EQ(x.hash_function(), hasher());
BOOST_TEST_EQ(x.key_eq(), key_equal());
BOOST_TEST(x.get_allocator() == a);
test_fuzzy_matches_reference(x, reference_map, rg);
}
check_raii_counts();
}
UNORDERED_AUTO_TEST (explicit_allocator) {
raii::reset_counts();
{
allocator_type a;
map_type x(a);
BOOST_TEST_EQ(x.size(), 0u);
BOOST_TEST_EQ(x.hash_function(), hasher());
BOOST_TEST_EQ(x.key_eq(), key_equal());
BOOST_TEST(x.get_allocator() == a);
}
}
UNORDERED_AUTO_TEST (initializer_list_with_all_params) {
std::initializer_list<map_value_type> ilist{
map_value_type{raii{0}, raii{0}},
map_value_type{raii{1}, raii{1}},
map_value_type{raii{2}, raii{2}},
map_value_type{raii{3}, raii{3}},
map_value_type{raii{4}, raii{4}},
map_value_type{raii{5}, raii{5}},
map_value_type{raii{6}, raii{6}},
map_value_type{raii{6}, raii{6}},
map_value_type{raii{7}, raii{7}},
map_value_type{raii{8}, raii{8}},
map_value_type{raii{9}, raii{9}},
map_value_type{raii{10}, raii{10}},
map_value_type{raii{9}, raii{9}},
map_value_type{raii{8}, raii{8}},
map_value_type{raii{7}, raii{7}},
map_value_type{raii{6}, raii{6}},
map_value_type{raii{5}, raii{5}},
map_value_type{raii{4}, raii{4}},
map_value_type{raii{3}, raii{3}},
map_value_type{raii{2}, raii{2}},
map_value_type{raii{1}, raii{1}},
map_value_type{raii{0}, raii{0}},
};
{
raii::reset_counts();
map_type x(ilist, 0, hasher(1), key_equal(2), allocator_type(3));
BOOST_TEST_EQ(x.size(), 11u);
BOOST_TEST_EQ(x.hash_function(), hasher(1));
BOOST_TEST_EQ(x.key_eq(), key_equal(2));
BOOST_TEST(x.get_allocator() == allocator_type(3));
BOOST_TEST_EQ(raii::move_constructor, 0u);
}
check_raii_counts();
{
raii::reset_counts();
map_type x(ilist, allocator_type(3));
BOOST_TEST_EQ(x.size(), 11u);
BOOST_TEST_EQ(x.hash_function(), hasher());
BOOST_TEST_EQ(x.key_eq(), key_equal());
BOOST_TEST(x.get_allocator() == allocator_type(3));
BOOST_TEST_EQ(raii::move_constructor, 0u);
}
check_raii_counts();
{
raii::reset_counts();
map_type x(ilist, 0, allocator_type(3));
BOOST_TEST_EQ(x.size(), 11u);
BOOST_TEST_EQ(x.hash_function(), hasher());
BOOST_TEST_EQ(x.key_eq(), key_equal());
BOOST_TEST(x.get_allocator() == allocator_type(3));
BOOST_TEST_EQ(raii::move_constructor, 0u);
}
check_raii_counts();
{
raii::reset_counts();
map_type x(ilist, 0, hasher(1), allocator_type(3));
BOOST_TEST_EQ(x.size(), 11u);
BOOST_TEST_EQ(x.hash_function(), hasher(1));
BOOST_TEST_EQ(x.key_eq(), key_equal());
BOOST_TEST(x.get_allocator() == allocator_type(3));
BOOST_TEST_EQ(raii::move_constructor, 0u);
}
check_raii_counts();
}
UNORDERED_AUTO_TEST (bucket_count_and_allocator) {
raii::reset_counts();
{
map_type x(0, allocator_type(3));
BOOST_TEST_EQ(x.size(), 0u);
BOOST_TEST_EQ(x.hash_function(), hasher());
BOOST_TEST_EQ(x.key_eq(), key_equal());
BOOST_TEST(x.get_allocator() == allocator_type(3));
}
{
map_type x(4096, allocator_type(3));
BOOST_TEST_EQ(x.size(), 0u);
BOOST_TEST_EQ(x.hash_function(), hasher());
BOOST_TEST_EQ(x.key_eq(), key_equal());
BOOST_TEST(x.get_allocator() == allocator_type(3));
}
}
UNORDERED_AUTO_TEST (bucket_count_with_hasher_and_allocator) {
raii::reset_counts();
{
map_type x(0, hasher(1), allocator_type(3));
BOOST_TEST_EQ(x.size(), 0u);
BOOST_TEST_EQ(x.hash_function(), hasher(1));
BOOST_TEST_EQ(x.key_eq(), key_equal());
BOOST_TEST(x.get_allocator() == allocator_type(3));
}
}
template <class G>
void iterator_range_with_bucket_count_and_allocator(
G gen, test::random_generator rg)
{
auto values = make_random_values(1024 * 16, [&] { return gen(rg); });
auto reference_map =
boost::unordered_flat_map<raii, raii>(values.begin(), values.end());
raii::reset_counts();
{
allocator_type a(3);
map_type x(values.begin(), values.end(), 0, a);
test_fuzzy_matches_reference(x, reference_map, rg);
BOOST_TEST_EQ(x.hash_function(), hasher());
BOOST_TEST_EQ(x.key_eq(), key_equal());
BOOST_TEST(x.get_allocator() == a);
}
check_raii_counts();
}
template <class G>
void iterator_range_with_bucket_count_hasher_and_allocator(
G gen, test::random_generator rg)
{
auto values = make_random_values(1024 * 16, [&] { return gen(rg); });
auto reference_map =
boost::unordered_flat_map<raii, raii>(values.begin(), values.end());
raii::reset_counts();
{
allocator_type a(3);
hasher hf(1);
map_type x(values.begin(), values.end(), 0, hf, a);
test_fuzzy_matches_reference(x, reference_map, rg);
BOOST_TEST_EQ(x.hash_function(), hf);
BOOST_TEST_EQ(x.key_eq(), key_equal());
BOOST_TEST(x.get_allocator() == a);
}
check_raii_counts();
}
} // namespace
// clang-format off
UNORDERED_TEST(
from_iterator_range,
((value_type_generator))
((default_generator)(sequential)(limited_range)))
UNORDERED_TEST(
copy_constructor,
((value_type_generator))
((default_generator)(sequential)(limited_range)))
UNORDERED_TEST(
copy_constructor_with_insertion,
((value_type_generator))
((default_generator)(sequential)(limited_range)))
UNORDERED_TEST(
move_constructor,
((value_type_generator))
((default_generator)(sequential)(limited_range)))
UNORDERED_TEST(
move_constructor_with_insertion,
((value_type_generator))
((default_generator)(sequential)(limited_range)))
UNORDERED_TEST(
iterator_range_with_allocator,
((value_type_generator))
((default_generator)(sequential)(limited_range)))
UNORDERED_TEST(
iterator_range_with_bucket_count_and_allocator,
((value_type_generator))
((default_generator)(sequential)(limited_range)))
UNORDERED_TEST(
iterator_range_with_bucket_count_hasher_and_allocator,
((value_type_generator))
((default_generator)(sequential)(limited_range)))
// clang-format on
RUN_TESTS()
+167
View File
@@ -0,0 +1,167 @@
// 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)
#include "helpers.hpp"
#include <boost/unordered/concurrent_flat_map.hpp>
#include <boost/core/ignore_unused.hpp>
namespace {
test::seed_t initialize_seed(335740237);
struct lvalue_emplacer_type
{
template <class T, class X> void operator()(std::vector<T>& values, X& x)
{
std::atomic<std::uint64_t> num_inserts{0};
thread_runner(values, [&x, &num_inserts](boost::span<T> s) {
for (auto const& r : s) {
bool b = x.emplace(r.first.x_, r.second.x_);
if (b) {
++num_inserts;
}
}
});
BOOST_TEST_EQ(num_inserts, x.size());
BOOST_TEST_EQ(raii::default_constructor, 2 * values.size());
BOOST_TEST_EQ(raii::copy_constructor, 0u);
BOOST_TEST_GE(raii::move_constructor, 2 * x.size());
BOOST_TEST_EQ(raii::copy_constructor, 0u);
BOOST_TEST_EQ(raii::copy_assignment, 0u);
BOOST_TEST_EQ(raii::move_assignment, 0u);
}
} lvalue_emplacer;
struct norehash_lvalue_emplacer_type : public lvalue_emplacer_type
{
template <class T, class X> void operator()(std::vector<T>& values, X& x)
{
x.reserve(values.size());
lvalue_emplacer_type::operator()(values, x);
BOOST_TEST_EQ(raii::move_constructor, 2 * x.size());
}
} norehash_lvalue_emplacer;
struct lvalue_emplace_or_cvisit_type
{
template <class T, class X> void operator()(std::vector<T>& values, X& x)
{
std::atomic<std::uint64_t> num_inserts{0};
std::atomic<std::uint64_t> num_invokes{0};
thread_runner(values, [&x, &num_inserts, &num_invokes](boost::span<T> s) {
for (auto& r : s) {
bool b = x.emplace_or_cvisit(
r.first.x_, r.second.x_,
[&num_invokes](typename X::value_type const& v) {
(void)v;
++num_invokes;
});
if (b) {
++num_inserts;
}
}
});
BOOST_TEST_EQ(num_inserts, x.size());
BOOST_TEST_EQ(num_invokes, values.size() - x.size());
BOOST_TEST_EQ(raii::default_constructor, 2 * values.size());
BOOST_TEST_EQ(raii::copy_constructor, 0u);
BOOST_TEST_GE(raii::move_constructor, 2 * x.size());
BOOST_TEST_EQ(raii::move_assignment, 0u);
BOOST_TEST_EQ(raii::copy_assignment, 0u);
}
} lvalue_emplace_or_cvisit;
struct lvalue_emplace_or_visit_type
{
template <class T, class X> void operator()(std::vector<T>& values, X& x)
{
std::atomic<std::uint64_t> num_inserts{0};
std::atomic<std::uint64_t> num_invokes{0};
thread_runner(values, [&x, &num_inserts, &num_invokes](boost::span<T> s) {
for (auto& r : s) {
bool b = x.emplace_or_visit(
r.first.x_, r.second.x_,
[&num_invokes](typename X::value_type& v) {
(void)v;
++num_invokes;
});
if (b) {
++num_inserts;
}
}
});
BOOST_TEST_EQ(num_inserts, x.size());
BOOST_TEST_EQ(num_invokes, values.size() - x.size());
BOOST_TEST_EQ(raii::default_constructor, 2 * values.size());
BOOST_TEST_EQ(raii::copy_constructor, 0u);
BOOST_TEST_GE(raii::move_constructor, 2 * x.size());
BOOST_TEST_EQ(raii::move_assignment, 0u);
BOOST_TEST_EQ(raii::copy_assignment, 0u);
}
} lvalue_emplace_or_visit;
template <class X, class G, class F>
void emplace(X*, G gen, F emplacer, test::random_generator rg)
{
auto values = make_random_values(1024 * 16, [&] { return gen(rg); });
auto reference_map =
boost::unordered_flat_map<raii, raii>(values.begin(), values.end());
raii::reset_counts();
{
X x;
emplacer(values, x);
BOOST_TEST_EQ(x.size(), reference_map.size());
using value_type = typename X::value_type;
BOOST_TEST_EQ(x.size(), x.visit_all([&](value_type const& kv) {
BOOST_TEST(reference_map.contains(kv.first));
if (rg == test::sequential) {
BOOST_TEST_EQ(kv.second, reference_map[kv.first]);
}
}));
}
BOOST_TEST_GE(raii::default_constructor, 0u);
BOOST_TEST_GE(raii::copy_constructor, 0u);
BOOST_TEST_GE(raii::move_constructor, 0u);
BOOST_TEST_GT(raii::destructor, 0u);
BOOST_TEST_EQ(raii::default_constructor + raii::copy_constructor +
raii::move_constructor,
raii::destructor);
}
boost::unordered::concurrent_flat_map<raii, raii>* map;
} // namespace
using test::default_generator;
using test::limited_range;
using test::sequential;
// clang-format off
UNORDERED_TEST(
emplace,
((map))
((value_type_generator)(init_type_generator))
((lvalue_emplacer)(norehash_lvalue_emplacer)
(lvalue_emplace_or_cvisit)(lvalue_emplace_or_visit))
((default_generator)(sequential)(limited_range)))
// clang-format on
RUN_TESTS()
+368
View File
@@ -0,0 +1,368 @@
// 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)
#include "helpers.hpp"
#include <boost/unordered/concurrent_flat_map.hpp>
#include <boost/core/ignore_unused.hpp>
namespace {
test::seed_t initialize_seed(3292023);
struct lvalue_eraser_type
{
template <class T, class X> void operator()(std::vector<T>& values, X& x)
{
std::atomic<std::uint64_t> num_erased{0};
auto const old_size = x.size();
auto const old_dc = +raii::default_constructor;
auto const old_cc = +raii::copy_constructor;
auto const old_mc = +raii::move_constructor;
auto const old_d = +raii::destructor;
BOOST_TEST_EQ(raii::default_constructor + raii::copy_constructor +
raii::move_constructor,
raii::destructor + 2 * x.size());
thread_runner(values, [&values, &num_erased, &x](boost::span<T>) {
for (auto const& k : values) {
auto count = x.erase(k.first);
num_erased += count;
BOOST_TEST_LE(count, 1u);
BOOST_TEST_GE(count, 0u);
}
});
BOOST_TEST_EQ(raii::default_constructor, old_dc);
BOOST_TEST_EQ(raii::copy_constructor, old_cc);
BOOST_TEST_EQ(raii::move_constructor, old_mc);
BOOST_TEST_EQ(raii::destructor, old_d + 2 * old_size);
BOOST_TEST_EQ(x.size(), 0u);
BOOST_TEST(x.empty());
BOOST_TEST_EQ(num_erased, old_size);
}
} lvalue_eraser;
struct transp_lvalue_eraser_type
{
template <class T, class X> void operator()(std::vector<T>& values, X& x)
{
std::atomic<std::uint64_t> num_erased{0};
auto const old_size = x.size();
auto const old_dc = +raii::default_constructor;
auto const old_cc = +raii::copy_constructor;
auto const old_mc = +raii::move_constructor;
auto const old_d = +raii::destructor;
BOOST_TEST_EQ(raii::default_constructor + raii::copy_constructor +
raii::move_constructor,
raii::destructor + 2 * x.size());
thread_runner(values, [&num_erased, &x](boost::span<T> s) {
for (auto const& k : s) {
auto count = x.erase(k.first.x_);
num_erased += count;
BOOST_TEST_LE(count, 1u);
BOOST_TEST_GE(count, 0u);
}
});
BOOST_TEST_EQ(raii::default_constructor, old_dc);
BOOST_TEST_EQ(raii::copy_constructor, old_cc);
BOOST_TEST_EQ(raii::move_constructor, old_mc);
BOOST_TEST_EQ(raii::destructor, old_d + 2 * num_erased);
BOOST_TEST_EQ(x.size(), 0u);
BOOST_TEST(x.empty());
BOOST_TEST_EQ(num_erased, old_size);
}
} transp_lvalue_eraser;
struct lvalue_eraser_if_type
{
template <class T, class X> void operator()(std::vector<T>& values, X& x)
{
using value_type = typename X::value_type;
std::atomic<std::uint64_t> num_erased{0};
auto const old_size = x.size();
auto const old_dc = +raii::default_constructor;
auto const old_cc = +raii::copy_constructor;
auto const old_mc = +raii::move_constructor;
auto const old_d = +raii::destructor;
auto max = 0;
x.visit_all([&max](value_type const& v) {
if (v.second.x_ > max) {
max = v.second.x_;
}
});
auto threshold = max / 2;
auto expected_erasures = 0u;
x.visit_all([&expected_erasures, threshold](value_type const& v) {
if (v.second.x_ > threshold) {
++expected_erasures;
}
});
thread_runner(values, [&num_erased, &x, threshold](boost::span<T> s) {
for (auto const& k : s) {
auto count = x.erase_if(k.first,
[threshold](value_type& v) { return v.second.x_ > threshold; });
num_erased += count;
BOOST_TEST_LE(count, 1u);
BOOST_TEST_GE(count, 0u);
}
});
BOOST_TEST_EQ(num_erased, expected_erasures);
BOOST_TEST_EQ(x.size(), old_size - num_erased);
BOOST_TEST_EQ(raii::default_constructor, old_dc);
BOOST_TEST_EQ(raii::copy_constructor, old_cc);
BOOST_TEST_EQ(raii::move_constructor, old_mc);
BOOST_TEST_EQ(raii::destructor, old_d + 2 * num_erased);
}
} lvalue_eraser_if;
struct transp_lvalue_eraser_if_type
{
template <class T, class X> void operator()(std::vector<T>& values, X& x)
{
using value_type = typename X::value_type;
std::atomic<std::uint64_t> num_erased{0};
auto const old_size = x.size();
auto const old_dc = +raii::default_constructor;
auto const old_cc = +raii::copy_constructor;
auto const old_mc = +raii::move_constructor;
auto const old_d = +raii::destructor;
auto max = 0;
x.visit_all([&max](value_type const& v) {
if (v.second.x_ > max) {
max = v.second.x_;
}
});
auto threshold = max / 2;
auto expected_erasures = 0u;
x.visit_all([&expected_erasures, threshold](value_type const& v) {
if (v.second.x_ > threshold) {
++expected_erasures;
}
});
thread_runner(values, [&num_erased, &x, threshold](boost::span<T> s) {
for (auto const& k : s) {
auto count = x.erase_if(k.first.x_,
[threshold](value_type& v) { return v.second.x_ > threshold; });
num_erased += count;
BOOST_TEST_LE(count, 1u);
BOOST_TEST_GE(count, 0u);
}
});
BOOST_TEST_EQ(num_erased, expected_erasures);
BOOST_TEST_EQ(x.size(), old_size - num_erased);
BOOST_TEST_EQ(raii::default_constructor, old_dc);
BOOST_TEST_EQ(raii::copy_constructor, old_cc);
BOOST_TEST_EQ(raii::move_constructor, old_mc);
BOOST_TEST_EQ(raii::destructor, old_d + 2 * num_erased);
}
} transp_lvalue_eraser_if;
struct erase_if_type
{
template <class T, class X> void operator()(std::vector<T>& values, X& x)
{
using value_type = typename X::value_type;
std::atomic<std::uint64_t> num_erased{0};
auto const old_size = x.size();
auto const old_dc = +raii::default_constructor;
auto const old_cc = +raii::copy_constructor;
auto const old_mc = +raii::move_constructor;
auto const old_d = +raii::destructor;
auto max = 0;
x.visit_all([&max](value_type const& v) {
if (v.second.x_ > max) {
max = v.second.x_;
}
});
auto threshold = max / 2;
auto expected_erasures = 0u;
x.visit_all([&expected_erasures, threshold](value_type const& v) {
if (v.second.x_ > threshold) {
++expected_erasures;
}
});
thread_runner(values, [&num_erased, &x, threshold](boost::span<T> s) {
for (auto const& k : s) {
(void)k;
auto count = x.erase_if(
[threshold](value_type& v) { return v.second.x_ > threshold; });
num_erased += count;
}
});
BOOST_TEST_EQ(num_erased, expected_erasures);
BOOST_TEST_EQ(x.size(), old_size - num_erased);
BOOST_TEST_EQ(raii::default_constructor, old_dc);
BOOST_TEST_EQ(raii::copy_constructor, old_cc);
BOOST_TEST_EQ(raii::move_constructor, old_mc);
BOOST_TEST_EQ(raii::destructor, old_d + 2 * num_erased);
}
} erase_if;
struct erase_if_exec_policy_type
{
template <class T, class X> void operator()(std::vector<T>& values, X& x)
{
#if defined(BOOST_UNORDERED_PARALLEL_ALGORITHMS)
using value_type = typename X::value_type;
std::atomic<std::uint64_t> num_invokes{0};
auto const old_size = x.size();
auto const old_dc = +raii::default_constructor;
auto const old_cc = +raii::copy_constructor;
auto const old_mc = +raii::move_constructor;
auto const old_d = +raii::destructor;
auto max = 0;
x.visit_all([&max](value_type const& v) {
if (v.second.x_ > max) {
max = v.second.x_;
}
});
auto threshold = max / 2;
auto expected_erasures = 0u;
x.visit_all([&expected_erasures, threshold](value_type const& v) {
if (v.second.x_ > threshold) {
++expected_erasures;
}
});
thread_runner(values, [&num_invokes, &x, threshold](boost::span<T> s) {
(void)s;
x.erase_if(
std::execution::par_unseq, [&num_invokes, threshold](value_type& v) {
++num_invokes;
return v.second.x_ > threshold;
});
});
BOOST_TEST_GE(+num_invokes, old_size);
BOOST_TEST_LE(+num_invokes, old_size * num_threads);
BOOST_TEST_EQ(raii::default_constructor, old_dc);
BOOST_TEST_EQ(raii::copy_constructor, old_cc);
BOOST_TEST_EQ(raii::move_constructor, old_mc);
BOOST_TEST_EQ(raii::destructor, old_d + 2 * expected_erasures);
#else
(void)values;
(void)x;
#endif
}
} erase_if_exec_policy;
template <class X, class G, class F>
void erase(X*, G gen, F eraser, test::random_generator rg)
{
auto values = make_random_values(1024 * 16, [&] { return gen(rg); });
auto reference_map =
boost::unordered_flat_map<raii, raii>(values.begin(), values.end());
raii::reset_counts();
{
X x;
x.insert(values.begin(), values.end());
BOOST_TEST_EQ(x.size(), reference_map.size());
using value_type = typename X::value_type;
BOOST_TEST_EQ(x.size(), x.visit_all([&](value_type const& kv) {
BOOST_TEST(reference_map.contains(kv.first));
if (rg == test::sequential) {
BOOST_TEST_EQ(kv.second, reference_map[kv.first]);
}
}));
eraser(values, x);
}
BOOST_TEST_GE(raii::default_constructor, 0u);
BOOST_TEST_GE(raii::copy_constructor, 0u);
BOOST_TEST_GE(raii::move_constructor, 0u);
BOOST_TEST_GT(raii::destructor, 0u);
BOOST_TEST_EQ(raii::default_constructor + raii::copy_constructor +
raii::move_constructor,
raii::destructor);
}
boost::unordered::concurrent_flat_map<raii, raii>* map;
boost::unordered::concurrent_flat_map<raii, raii, transp_hash,
transp_key_equal>* transparent_map;
} // namespace
using test::default_generator;
using test::limited_range;
using test::sequential;
// clang-format off
UNORDERED_TEST(
erase,
((map))
((value_type_generator)(init_type_generator))
((lvalue_eraser)(lvalue_eraser_if)(erase_if)(erase_if_exec_policy))
((default_generator)(sequential)(limited_range)))
UNORDERED_TEST(
erase,
((transparent_map))
((value_type_generator)(init_type_generator))
((transp_lvalue_eraser)(transp_lvalue_eraser_if)(erase_if_exec_policy))
((default_generator)(sequential)(limited_range)))
// clang-format on
RUN_TESTS()
+390
View File
@@ -0,0 +1,390 @@
#ifndef BOOST_UNORDERED_TEST_CFOA_HELPERS_HPP
#define BOOST_UNORDERED_TEST_CFOA_HELPERS_HPP
#include "../helpers/generators.hpp"
#include "../helpers/test.hpp"
#include <boost/container_hash/hash.hpp>
#include <boost/core/span.hpp>
#include <boost/unordered/unordered_flat_map.hpp>
#include <atomic>
#include <condition_variable>
#include <cstddef>
#include <iostream>
#include <mutex>
#include <thread>
#include <vector>
constexpr std::size_t const num_threads = 16;
struct transp_hash
{
using is_transparent = void;
template <class T> std::size_t operator()(T const& t) const noexcept
{
std::this_thread::yield();
return boost::hash<T>()(t);
}
};
struct transp_key_equal
{
using is_transparent = void;
template <class T, class U> bool operator()(T const& lhs, U const& rhs) const
{
std::this_thread::yield();
return lhs == rhs;
}
};
struct stateful_hash
{
int x_ = -1;
stateful_hash() = default;
stateful_hash(stateful_hash const&) = default;
stateful_hash(stateful_hash&& rhs) noexcept
{
auto tmp = x_;
x_ = rhs.x_;
rhs.x_ = tmp;
}
stateful_hash(int const x) : x_{x} {}
template <class T> std::size_t operator()(T const& t) const noexcept
{
std::size_t h = static_cast<std::size_t>(x_);
boost::hash_combine(h, t);
std::this_thread::yield();
return h;
}
bool operator==(stateful_hash const& rhs) const { return x_ == rhs.x_; }
friend std::ostream& operator<<(std::ostream& os, stateful_hash const& rhs)
{
os << "{ x_: " << rhs.x_ << " }";
return os;
}
friend void swap(stateful_hash& lhs, stateful_hash& rhs) noexcept
{
if (&lhs != &rhs) {
std::swap(lhs.x_, rhs.x_);
}
}
};
struct stateful_key_equal
{
int x_ = -1;
stateful_key_equal() = default;
stateful_key_equal(stateful_key_equal const&) = default;
stateful_key_equal(stateful_key_equal&& rhs) noexcept
{
auto tmp = x_;
x_ = rhs.x_;
rhs.x_ = tmp;
}
stateful_key_equal(int const x) : x_{x} {}
template <class T, class U> bool operator()(T const& t, U const& u) const
{
std::this_thread::yield();
return t == u;
}
bool operator==(stateful_key_equal const& rhs) const { return x_ == rhs.x_; }
friend std::ostream& operator<<(
std::ostream& os, stateful_key_equal const& rhs)
{
os << "{ x_: " << rhs.x_ << " }";
return os;
}
friend void swap(stateful_key_equal& lhs, stateful_key_equal& rhs) noexcept
{
if (&lhs != &rhs) {
std::swap(lhs.x_, rhs.x_);
}
}
};
template <class T> struct stateful_allocator
{
int x_ = -1;
using value_type = T;
stateful_allocator() = default;
stateful_allocator(stateful_allocator const&) = default;
stateful_allocator(stateful_allocator&&) = default;
stateful_allocator(int const x) : x_{x} {}
template <class U>
stateful_allocator(stateful_allocator<U> const& rhs) : x_{rhs.x_}
{
}
T* allocate(std::size_t n)
{
return static_cast<T*>(::operator new(n * sizeof(T)));
}
void deallocate(T* p, std::size_t) { ::operator delete(p); }
bool operator==(stateful_allocator const& rhs) const { return x_ == rhs.x_; }
bool operator!=(stateful_allocator const& rhs) const { return x_ != rhs.x_; }
};
struct raii
{
static std::atomic<std::uint32_t> default_constructor;
static std::atomic<std::uint32_t> copy_constructor;
static std::atomic<std::uint32_t> move_constructor;
static std::atomic<std::uint32_t> destructor;
static std::atomic<std::uint32_t> copy_assignment;
static std::atomic<std::uint32_t> move_assignment;
int x_ = -1;
raii() { ++default_constructor; }
raii(int const x) : x_{x} { ++default_constructor; }
raii(raii const& rhs) : x_{rhs.x_} { ++copy_constructor; }
raii(raii&& rhs) noexcept : x_{rhs.x_}
{
rhs.x_ = -1;
++move_constructor;
}
~raii() { ++destructor; }
raii& operator=(raii const& rhs)
{
++copy_assignment;
if (this != &rhs) {
x_ = rhs.x_;
}
return *this;
}
raii& operator=(raii&& rhs) noexcept
{
++move_assignment;
if (this != &rhs) {
x_ = rhs.x_;
rhs.x_ = -1;
}
return *this;
}
friend bool operator==(raii const& lhs, raii const& rhs)
{
return lhs.x_ == rhs.x_;
}
friend bool operator!=(raii const& lhs, raii const& rhs)
{
return !(lhs == rhs);
}
friend bool operator==(raii const& lhs, int const x) { return lhs.x_ == x; }
friend bool operator!=(raii const& lhs, int const x)
{
return !(lhs.x_ == x);
}
friend bool operator==(int const x, raii const& rhs) { return rhs.x_ == x; }
friend bool operator!=(int const x, raii const& rhs)
{
return !(rhs.x_ == x);
}
friend std::ostream& operator<<(std::ostream& os, raii const& rhs)
{
os << "{ x_: " << rhs.x_ << " }";
return os;
}
friend std::ostream& operator<<(
std::ostream& os, std::pair<raii const, raii> const& rhs)
{
os << "pair<" << rhs.first << ", " << rhs.second << ">";
return os;
}
static void reset_counts()
{
default_constructor = 0;
copy_constructor = 0;
move_constructor = 0;
destructor = 0;
copy_assignment = 0;
move_assignment = 0;
}
};
std::atomic<std::uint32_t> raii::default_constructor{0};
std::atomic<std::uint32_t> raii::copy_constructor{0};
std::atomic<std::uint32_t> raii::move_constructor{0};
std::atomic<std::uint32_t> raii::destructor{0};
std::atomic<std::uint32_t> raii::copy_assignment{0};
std::atomic<std::uint32_t> raii::move_assignment{0};
std::size_t hash_value(raii const& r) noexcept
{
boost::hash<int> hasher;
return hasher(r.x_);
}
template <class F>
auto make_random_values(std::size_t count, F f) -> std::vector<decltype(f())>
{
using vector_type = std::vector<decltype(f())>;
vector_type v;
v.reserve(count);
for (std::size_t i = 0; i < count; ++i) {
v.emplace_back(f());
}
return v;
}
struct value_type_generator_type
{
std::pair<raii const, raii> operator()(test::random_generator rg)
{
int* p = nullptr;
int a = generate(p, rg);
int b = generate(p, rg);
return std::make_pair(raii{a}, raii{b});
}
} value_type_generator;
struct init_type_generator_type
{
std::pair<raii, raii> operator()(test::random_generator rg)
{
int* p = nullptr;
int a = generate(p, rg);
int b = generate(p, rg);
return std::make_pair(raii{a}, raii{b});
}
} init_type_generator;
template <class T>
std::vector<boost::span<T> > split(
boost::span<T> s, std::size_t const nt /* num threads*/)
{
std::vector<boost::span<T> > subslices;
subslices.reserve(nt);
auto a = s.size() / nt;
auto b = a;
if (s.size() % nt != 0) {
++b;
}
auto num_a = nt;
auto num_b = std::size_t{0};
if (nt * b > s.size()) {
num_a = nt * b - s.size();
num_b = nt - num_a;
}
auto sub_b = s.subspan(0, num_b * b);
auto sub_a = s.subspan(num_b * b);
for (std::size_t i = 0; i < num_b; ++i) {
subslices.push_back(sub_b.subspan(i * b, b));
}
for (std::size_t i = 0; i < num_a; ++i) {
auto const is_last = i == (num_a - 1);
subslices.push_back(
sub_a.subspan(i * a, is_last ? boost::dynamic_extent : a));
}
return subslices;
}
template <class T, class F> void thread_runner(std::vector<T>& values, F f)
{
std::mutex m;
std::condition_variable cv;
std::size_t c = 0;
std::vector<std::thread> threads;
auto subslices = split<T>(values, num_threads);
for (std::size_t i = 0; i < num_threads; ++i) {
threads.emplace_back([&f, &subslices, i, &m, &cv, &c] {
{
std::unique_lock<std::mutex> lk(m);
++c;
if (c == num_threads) {
lk.unlock();
cv.notify_all();
} else {
cv.wait(lk, [&] { return c == num_threads; });
}
}
auto s = subslices[i];
f(s);
});
}
for (auto& t : threads) {
t.join();
}
}
template <class X, class Y>
void test_matches_reference(X const& x, Y const& reference_map)
{
using value_type = typename X::value_type;
BOOST_TEST_EQ(x.size(), x.visit_all([&](value_type const& kv) {
BOOST_TEST(reference_map.contains(kv.first));
BOOST_TEST_EQ(kv.second, reference_map.find(kv.first)->second);
}));
}
template <class X, class Y>
void test_fuzzy_matches_reference(
X const& x, Y const& reference_map, test::random_generator rg)
{
using value_type = typename X::value_type;
BOOST_TEST_EQ(x.size(), x.visit_all([&](value_type const& kv) {
BOOST_TEST(reference_map.contains(kv.first));
if (rg == test::sequential) {
BOOST_TEST_EQ(kv.second, reference_map.find(kv.first)->second);
}
}));
}
template <class T> using span_value_type = typename T::value_type;
void check_raii_counts()
{
BOOST_TEST_GE(raii::default_constructor, 0u);
BOOST_TEST_GE(raii::copy_constructor, 0u);
BOOST_TEST_GE(raii::move_constructor, 0u);
BOOST_TEST_GT(raii::destructor, 0u);
BOOST_TEST_EQ(
raii::default_constructor + raii::copy_constructor + raii::move_constructor,
raii::destructor);
}
#endif // BOOST_UNORDERED_TEST_CFOA_HELPERS_HPP
+585
View File
@@ -0,0 +1,585 @@
// 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)
#include "helpers.hpp"
#include <boost/unordered/concurrent_flat_map.hpp>
#include <boost/core/ignore_unused.hpp>
namespace {
test::seed_t initialize_seed(78937);
struct lvalue_inserter_type
{
template <class T, class X> void operator()(std::vector<T>& values, X& x)
{
std::atomic<std::uint64_t> num_inserts{0};
thread_runner(values, [&x, &num_inserts](boost::span<T> s) {
for (auto const& r : s) {
bool b = x.insert(r);
if (b) {
++num_inserts;
}
}
});
BOOST_TEST_EQ(num_inserts, x.size());
BOOST_TEST_EQ(raii::copy_constructor, 2 * x.size());
BOOST_TEST_EQ(raii::copy_assignment, 0u);
BOOST_TEST_EQ(raii::move_assignment, 0u);
}
} lvalue_inserter;
struct norehash_lvalue_inserter_type : public lvalue_inserter_type
{
template <class T, class X> void operator()(std::vector<T>& values, X& x)
{
x.reserve(values.size());
lvalue_inserter_type::operator()(values, x);
BOOST_TEST_EQ(raii::copy_constructor, 2 * x.size());
BOOST_TEST_EQ(raii::move_constructor, 0u);
}
} norehash_lvalue_inserter;
struct rvalue_inserter_type
{
template <class T, class X> void operator()(std::vector<T>& values, X& x)
{
BOOST_TEST_EQ(raii::copy_constructor, 0u);
std::atomic<std::uint64_t> num_inserts{0};
thread_runner(values, [&x, &num_inserts](boost::span<T> s) {
for (auto& r : s) {
bool b = x.insert(std::move(r));
if (b) {
++num_inserts;
}
}
});
BOOST_TEST_EQ(num_inserts, x.size());
if (std::is_same<T, typename X::value_type>::value) {
BOOST_TEST_EQ(raii::copy_constructor, x.size());
} else {
BOOST_TEST_EQ(raii::copy_constructor, 0u);
}
BOOST_TEST_EQ(raii::copy_assignment, 0u);
BOOST_TEST_EQ(raii::move_assignment, 0u);
}
} rvalue_inserter;
struct norehash_rvalue_inserter_type : public rvalue_inserter_type
{
template <class T, class X> void operator()(std::vector<T>& values, X& x)
{
x.reserve(values.size());
BOOST_TEST_EQ(raii::copy_constructor, 0u);
BOOST_TEST_EQ(raii::move_constructor, 0u);
rvalue_inserter_type::operator()(values, x);
if (std::is_same<T, typename X::value_type>::value) {
BOOST_TEST_EQ(raii::copy_constructor, x.size());
BOOST_TEST_EQ(raii::move_constructor, x.size());
} else {
BOOST_TEST_EQ(raii::copy_constructor, 0u);
BOOST_TEST_EQ(raii::move_constructor, 2 * x.size());
}
}
} norehash_rvalue_inserter;
struct iterator_range_inserter_type
{
template <class T, class X> void operator()(std::vector<T>& values, X& x)
{
thread_runner(
values, [&x](boost::span<T> s) { x.insert(s.begin(), s.end()); });
BOOST_TEST_EQ(raii::copy_assignment, 0u);
BOOST_TEST_EQ(raii::move_assignment, 0u);
}
} iterator_range_inserter;
struct lvalue_insert_or_assign_copy_assign_type
{
template <class T, class X> void operator()(std::vector<T>& values, X& x)
{
thread_runner(values, [&x](boost::span<T> s) {
for (auto& r : s) {
x.insert_or_assign(r.first, r.second);
}
});
BOOST_TEST_EQ(raii::default_constructor, 0u);
BOOST_TEST_EQ(raii::copy_constructor, 2 * x.size());
// don't check move construction count here because of rehashing
BOOST_TEST_GT(raii::move_constructor, 0u);
BOOST_TEST_EQ(raii::copy_assignment, values.size() - x.size());
BOOST_TEST_EQ(raii::move_assignment, 0u);
}
} lvalue_insert_or_assign_copy_assign;
struct lvalue_insert_or_assign_move_assign_type
{
template <class T, class X> void operator()(std::vector<T>& values, X& x)
{
thread_runner(values, [&x](boost::span<T> s) {
for (auto& r : s) {
x.insert_or_assign(r.first, std::move(r.second));
}
});
BOOST_TEST_EQ(raii::default_constructor, 0u);
BOOST_TEST_EQ(raii::copy_constructor, x.size());
BOOST_TEST_GT(raii::move_constructor, x.size()); // rehashing
BOOST_TEST_EQ(raii::copy_assignment, 0u);
BOOST_TEST_EQ(raii::move_assignment, values.size() - x.size());
}
} lvalue_insert_or_assign_move_assign;
struct rvalue_insert_or_assign_copy_assign_type
{
template <class T, class X> void operator()(std::vector<T>& values, X& x)
{
thread_runner(values, [&x](boost::span<T> s) {
for (auto& r : s) {
x.insert_or_assign(std::move(r.first), r.second);
}
});
BOOST_TEST_EQ(raii::default_constructor, 0u);
BOOST_TEST_EQ(raii::copy_constructor, x.size());
BOOST_TEST_GT(raii::move_constructor, x.size()); // rehashing
BOOST_TEST_EQ(raii::copy_assignment, values.size() - x.size());
BOOST_TEST_EQ(raii::move_assignment, 0u);
}
} rvalue_insert_or_assign_copy_assign;
struct rvalue_insert_or_assign_move_assign_type
{
template <class T, class X> void operator()(std::vector<T>& values, X& x)
{
thread_runner(values, [&x](boost::span<T> s) {
for (auto& r : s) {
x.insert_or_assign(std::move(r.first), std::move(r.second));
}
});
BOOST_TEST_EQ(raii::default_constructor, 0u);
BOOST_TEST_EQ(raii::copy_constructor, 0u);
BOOST_TEST_GE(raii::move_constructor, 2 * x.size());
BOOST_TEST_EQ(raii::copy_assignment, 0u);
BOOST_TEST_EQ(raii::move_assignment, values.size() - x.size());
}
} rvalue_insert_or_assign_move_assign;
struct trans_insert_or_assign_copy_assign_type
{
template <class T, class X> void operator()(std::vector<T>& values, X& x)
{
using is_transparent =
typename boost::make_void<typename X::hasher::is_transparent,
typename X::key_equal::is_transparent>::type;
boost::ignore_unused<is_transparent>();
BOOST_TEST_EQ(raii::default_constructor, 0u);
thread_runner(values, [&x](boost::span<T> s) {
for (auto& r : s) {
x.insert_or_assign(r.first.x_, r.second);
}
});
BOOST_TEST_EQ(raii::default_constructor, x.size());
BOOST_TEST_EQ(raii::copy_constructor, x.size());
BOOST_TEST_GT(raii::move_constructor, x.size()); // rehashing
BOOST_TEST_EQ(raii::copy_assignment, values.size() - x.size());
BOOST_TEST_EQ(raii::move_assignment, 0u);
}
} trans_insert_or_assign_copy_assign;
struct trans_insert_or_assign_move_assign_type
{
template <class T, class X> void operator()(std::vector<T>& values, X& x)
{
using is_transparent =
typename boost::make_void<typename X::hasher::is_transparent,
typename X::key_equal::is_transparent>::type;
boost::ignore_unused<is_transparent>();
thread_runner(values, [&x](boost::span<T> s) {
for (auto& r : s) {
x.insert_or_assign(r.first.x_, std::move(r.second));
}
});
BOOST_TEST_EQ(raii::default_constructor, x.size());
BOOST_TEST_EQ(raii::copy_constructor, 0u);
BOOST_TEST_GT(raii::move_constructor, 2 * x.size()); // rehashing
BOOST_TEST_EQ(raii::copy_assignment, 0u);
BOOST_TEST_EQ(raii::move_assignment, values.size() - x.size());
}
} trans_insert_or_assign_move_assign;
struct lvalue_insert_or_cvisit_type
{
template <class T, class X> void operator()(std::vector<T>& values, X& x)
{
std::atomic<std::uint64_t> num_inserts{0};
std::atomic<std::uint64_t> num_invokes{0};
thread_runner(values, [&x, &num_inserts, &num_invokes](boost::span<T> s) {
for (auto& r : s) {
bool b = x.insert_or_cvisit(
r, [&num_invokes](typename X::value_type const& v) {
(void)v;
++num_invokes;
});
if (b) {
++num_inserts;
}
}
});
BOOST_TEST_EQ(num_inserts, x.size());
BOOST_TEST_EQ(num_invokes, values.size() - x.size());
BOOST_TEST_EQ(raii::default_constructor, 0u);
BOOST_TEST_EQ(raii::copy_constructor, 2 * x.size());
// don't check move construction count here because of rehashing
BOOST_TEST_GT(raii::move_constructor, 0u);
BOOST_TEST_EQ(raii::move_assignment, 0u);
}
} lvalue_insert_or_cvisit;
struct lvalue_insert_or_visit_type
{
template <class T, class X> void operator()(std::vector<T>& values, X& x)
{
std::atomic<std::uint64_t> num_inserts{0};
std::atomic<std::uint64_t> num_invokes{0};
thread_runner(values, [&x, &num_inserts, &num_invokes](boost::span<T> s) {
for (auto& r : s) {
bool b =
x.insert_or_visit(r, [&num_invokes](typename X::value_type& v) {
(void)v;
++num_invokes;
});
if (b) {
++num_inserts;
}
}
});
BOOST_TEST_EQ(num_inserts, x.size());
BOOST_TEST_EQ(num_invokes, values.size() - x.size());
BOOST_TEST_EQ(raii::default_constructor, 0u);
BOOST_TEST_EQ(raii::copy_constructor, 2 * x.size());
// don't check move construction count here because of rehashing
BOOST_TEST_GT(raii::move_constructor, 0u);
BOOST_TEST_EQ(raii::move_assignment, 0u);
}
} lvalue_insert_or_visit;
struct rvalue_insert_or_cvisit_type
{
template <class T, class X> void operator()(std::vector<T>& values, X& x)
{
std::atomic<std::uint64_t> num_inserts{0};
std::atomic<std::uint64_t> num_invokes{0};
thread_runner(values, [&x, &num_inserts, &num_invokes](boost::span<T> s) {
for (auto& r : s) {
bool b = x.insert_or_cvisit(
std::move(r), [&num_invokes](typename X::value_type const& v) {
(void)v;
++num_invokes;
});
if (b) {
++num_inserts;
}
}
});
BOOST_TEST_EQ(num_inserts, x.size());
BOOST_TEST_EQ(num_invokes, values.size() - x.size());
BOOST_TEST_EQ(raii::default_constructor, 0u);
if (std::is_same<T, typename X::value_type>::value) {
BOOST_TEST_EQ(raii::copy_constructor, x.size());
BOOST_TEST_GE(raii::move_constructor, x.size());
} else {
BOOST_TEST_EQ(raii::copy_constructor, 0u);
BOOST_TEST_GE(raii::move_constructor, 2 * x.size());
}
}
} rvalue_insert_or_cvisit;
struct rvalue_insert_or_visit_type
{
template <class T, class X> void operator()(std::vector<T>& values, X& x)
{
std::atomic<std::uint64_t> num_inserts{0};
std::atomic<std::uint64_t> num_invokes{0};
thread_runner(values, [&x, &num_inserts, &num_invokes](boost::span<T> s) {
for (auto& r : s) {
bool b = x.insert_or_visit(
std::move(r), [&num_invokes](typename X::value_type& v) {
(void)v;
++num_invokes;
});
if (b) {
++num_inserts;
}
}
});
BOOST_TEST_EQ(num_inserts, x.size());
BOOST_TEST_EQ(num_invokes, values.size() - x.size());
BOOST_TEST_EQ(raii::default_constructor, 0u);
if (std::is_same<T, typename X::value_type>::value) {
BOOST_TEST_EQ(raii::copy_constructor, x.size());
BOOST_TEST_GE(raii::move_constructor, x.size());
} else {
BOOST_TEST_EQ(raii::copy_constructor, 0u);
BOOST_TEST_GE(raii::move_constructor, 2 * x.size());
}
}
} rvalue_insert_or_visit;
struct iterator_range_insert_or_cvisit_type
{
template <class T, class X> void operator()(std::vector<T>& values, X& x)
{
std::atomic<std::uint64_t> num_invokes{0};
thread_runner(values, [&x, &num_invokes](boost::span<T> s) {
x.insert_or_cvisit(
s.begin(), s.end(), [&num_invokes](typename X::value_type const& v) {
(void)v;
++num_invokes;
});
});
BOOST_TEST_EQ(num_invokes, values.size() - x.size());
BOOST_TEST_EQ(raii::default_constructor, 0u);
BOOST_TEST_EQ(raii::copy_constructor, 2 * x.size());
BOOST_TEST_GT(raii::move_constructor, 0u);
}
} iterator_range_insert_or_cvisit;
struct iterator_range_insert_or_visit_type
{
template <class T, class X> void operator()(std::vector<T>& values, X& x)
{
std::atomic<std::uint64_t> num_invokes{0};
thread_runner(values, [&x, &num_invokes](boost::span<T> s) {
x.insert_or_visit(
s.begin(), s.end(), [&num_invokes](typename X::value_type const& v) {
(void)v;
++num_invokes;
});
});
BOOST_TEST_EQ(num_invokes, values.size() - x.size());
BOOST_TEST_EQ(raii::default_constructor, 0u);
BOOST_TEST_EQ(raii::copy_constructor, 2 * x.size());
BOOST_TEST_GT(raii::move_constructor, 0u);
}
} iterator_range_insert_or_visit;
template <class X, class G, class F>
void insert(X*, G gen, F inserter, test::random_generator rg)
{
auto values = make_random_values(1024 * 16, [&] { return gen(rg); });
auto reference_map =
boost::unordered_flat_map<raii, raii>(values.begin(), values.end());
raii::reset_counts();
{
X x;
inserter(values, x);
BOOST_TEST_EQ(x.size(), reference_map.size());
using value_type = typename X::value_type;
BOOST_TEST_EQ(x.size(), x.visit_all([&](value_type const& kv) {
BOOST_TEST(reference_map.contains(kv.first));
if (rg == test::sequential) {
BOOST_TEST_EQ(kv.second, reference_map[kv.first]);
}
}));
}
BOOST_TEST_GE(raii::default_constructor, 0u);
BOOST_TEST_GE(raii::copy_constructor, 0u);
BOOST_TEST_GE(raii::move_constructor, 0u);
BOOST_TEST_GT(raii::destructor, 0u);
BOOST_TEST_EQ(raii::default_constructor + raii::copy_constructor +
raii::move_constructor,
raii::destructor);
}
template <class X> void insert_initializer_list(X*)
{
using value_type = typename X::value_type;
std::initializer_list<value_type> values{
value_type{raii{0}, raii{0}},
value_type{raii{1}, raii{1}},
value_type{raii{2}, raii{2}},
value_type{raii{3}, raii{3}},
value_type{raii{4}, raii{4}},
value_type{raii{5}, raii{5}},
value_type{raii{6}, raii{6}},
value_type{raii{6}, raii{6}},
value_type{raii{7}, raii{7}},
value_type{raii{8}, raii{8}},
value_type{raii{9}, raii{9}},
value_type{raii{10}, raii{10}},
value_type{raii{9}, raii{9}},
value_type{raii{8}, raii{8}},
value_type{raii{7}, raii{7}},
value_type{raii{6}, raii{6}},
value_type{raii{5}, raii{5}},
value_type{raii{4}, raii{4}},
value_type{raii{3}, raii{3}},
value_type{raii{2}, raii{2}},
value_type{raii{1}, raii{1}},
value_type{raii{0}, raii{0}},
};
std::vector<raii> dummy;
auto reference_map =
boost::unordered_flat_map<raii, raii>(values.begin(), values.end());
raii::reset_counts();
{
{
X x;
thread_runner(
dummy, [&x, &values](boost::span<raii>) { x.insert(values); });
BOOST_TEST_EQ(x.size(), reference_map.size());
BOOST_TEST_EQ(x.size(), x.visit_all([&](value_type const& kv) {
BOOST_TEST(reference_map.contains(kv.first));
BOOST_TEST_EQ(kv.second, reference_map[kv.first]);
}));
}
BOOST_TEST_GE(raii::default_constructor, 0u);
BOOST_TEST_GE(raii::copy_constructor, 0u);
BOOST_TEST_GE(raii::move_constructor, 0u);
BOOST_TEST_GT(raii::destructor, 0u);
BOOST_TEST_EQ(raii::default_constructor + raii::copy_constructor +
raii::move_constructor,
raii::destructor);
BOOST_TEST_EQ(raii::copy_assignment, 0u);
BOOST_TEST_EQ(raii::move_assignment, 0u);
}
{
{
std::atomic<std::uint64_t> num_invokes{0};
X x;
thread_runner(dummy, [&x, &values, &num_invokes](boost::span<raii>) {
x.insert_or_visit(values, [&num_invokes](typename X::value_type& v) {
(void)v;
++num_invokes;
});
x.insert_or_cvisit(
values, [&num_invokes](typename X::value_type const& v) {
(void)v;
++num_invokes;
});
});
BOOST_TEST_EQ(num_invokes, (values.size() - x.size()) +
(num_threads - 1) * values.size() +
num_threads * values.size());
BOOST_TEST_EQ(x.size(), reference_map.size());
BOOST_TEST_EQ(x.size(), x.visit_all([&](value_type const& kv) {
BOOST_TEST(reference_map.contains(kv.first));
BOOST_TEST_EQ(kv.second, reference_map[kv.first]);
}));
}
BOOST_TEST_GE(raii::default_constructor, 0u);
BOOST_TEST_GE(raii::copy_constructor, 0u);
BOOST_TEST_GE(raii::move_constructor, 0u);
BOOST_TEST_GT(raii::destructor, 0u);
BOOST_TEST_EQ(raii::default_constructor + raii::copy_constructor +
raii::move_constructor,
raii::destructor);
BOOST_TEST_EQ(raii::copy_assignment, 0u);
BOOST_TEST_EQ(raii::move_assignment, 0u);
}
}
boost::unordered::concurrent_flat_map<raii, raii>* map;
boost::unordered::concurrent_flat_map<raii, raii, transp_hash,
transp_key_equal>* trans_map;
} // namespace
using test::default_generator;
using test::limited_range;
using test::sequential;
// clang-format off
UNORDERED_TEST(
insert_initializer_list,
((map)))
UNORDERED_TEST(
insert,
((map))
((value_type_generator)(init_type_generator))
((lvalue_inserter)(rvalue_inserter)(iterator_range_inserter)
(norehash_lvalue_inserter)(norehash_rvalue_inserter)
(lvalue_insert_or_cvisit)(lvalue_insert_or_visit)
(rvalue_insert_or_cvisit)(rvalue_insert_or_visit)
(iterator_range_insert_or_cvisit)(iterator_range_insert_or_visit))
((default_generator)(sequential)(limited_range)))
UNORDERED_TEST(
insert,
((map))
((init_type_generator))
((lvalue_insert_or_assign_copy_assign)(lvalue_insert_or_assign_move_assign)
(rvalue_insert_or_assign_copy_assign)(rvalue_insert_or_assign_move_assign))
((default_generator)(sequential)(limited_range)))
UNORDERED_TEST(
insert,
((trans_map))
((init_type_generator))
((trans_insert_or_assign_copy_assign)(trans_insert_or_assign_move_assign))
((default_generator)(sequential)(limited_range)))
// clang-format on
RUN_TESTS()
+396
View File
@@ -0,0 +1,396 @@
// 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)
#include "helpers.hpp"
#include <boost/unordered/concurrent_flat_map.hpp>
#include <boost/core/ignore_unused.hpp>
namespace {
test::seed_t initialize_seed(511933564);
struct lvalue_try_emplacer_type
{
template <class T, class X> void operator()(std::vector<T>& values, X& x)
{
std::atomic<std::uint64_t> num_inserts{0};
thread_runner(values, [&x, &num_inserts](boost::span<T> s) {
for (auto const& r : s) {
bool b = x.try_emplace(r.first, r.second.x_);
if (b) {
++num_inserts;
}
}
});
BOOST_TEST_EQ(num_inserts, x.size());
BOOST_TEST_EQ(raii::copy_constructor, x.size());
BOOST_TEST_EQ(raii::default_constructor, x.size());
BOOST_TEST_EQ(raii::copy_assignment, 0u);
BOOST_TEST_EQ(raii::move_assignment, 0u);
}
} lvalue_try_emplacer;
struct norehash_lvalue_try_emplacer_type : public lvalue_try_emplacer_type
{
template <class T, class X> void operator()(std::vector<T>& values, X& x)
{
x.reserve(values.size());
lvalue_try_emplacer_type::operator()(values, x);
BOOST_TEST_EQ(raii::move_constructor, 0u);
}
} norehash_lvalue_try_emplacer;
struct rvalue_try_emplacer_type
{
template <class T, class X> void operator()(std::vector<T>& values, X& x)
{
BOOST_TEST_EQ(raii::copy_constructor, 0u);
std::atomic<std::uint64_t> num_inserts{0};
thread_runner(values, [&x, &num_inserts](boost::span<T> s) {
for (auto& r : s) {
bool b = x.try_emplace(std::move(r.first), r.second.x_);
if (b) {
++num_inserts;
}
}
});
BOOST_TEST_EQ(num_inserts, x.size());
if (std::is_same<T, typename X::value_type>::value) {
BOOST_TEST_EQ(raii::copy_constructor, x.size());
} else {
BOOST_TEST_EQ(raii::copy_constructor, 0u);
BOOST_TEST_GE(raii::move_constructor, x.size());
}
BOOST_TEST_EQ(raii::default_constructor, x.size());
BOOST_TEST_EQ(raii::copy_assignment, 0u);
BOOST_TEST_EQ(raii::move_assignment, 0u);
}
} rvalue_try_emplacer;
struct norehash_rvalue_try_emplacer_type : public rvalue_try_emplacer_type
{
template <class T, class X> void operator()(std::vector<T>& values, X& x)
{
x.reserve(values.size());
BOOST_TEST_EQ(raii::copy_constructor, 0u);
BOOST_TEST_EQ(raii::move_constructor, 0u);
rvalue_try_emplacer_type::operator()(values, x);
if (std::is_same<T, typename X::value_type>::value) {
BOOST_TEST_EQ(raii::copy_constructor, x.size());
BOOST_TEST_EQ(raii::move_constructor, 0u);
} else {
BOOST_TEST_EQ(raii::copy_constructor, 0u);
BOOST_TEST_EQ(raii::move_constructor, x.size());
}
}
} norehash_rvalue_try_emplacer;
struct transp_try_emplace_type
{
template <class T, class X> void operator()(std::vector<T>& values, X& x)
{
using is_transparent =
typename boost::make_void<typename X::hasher::is_transparent,
typename X::key_equal::is_transparent>::type;
boost::ignore_unused<is_transparent>();
BOOST_TEST_EQ(raii::default_constructor, 0u);
std::atomic<std::uint64_t> num_inserts{0};
thread_runner(values, [&x, &num_inserts](boost::span<T> s) {
for (auto& r : s) {
bool b = x.try_emplace(r.first.x_, r.second.x_);
if (b) {
++num_inserts;
}
}
});
BOOST_TEST_EQ(num_inserts, x.size());
BOOST_TEST_EQ(raii::default_constructor, 2 * x.size());
BOOST_TEST_EQ(raii::copy_constructor, 0u);
BOOST_TEST_EQ(raii::copy_assignment, 0u);
BOOST_TEST_EQ(raii::move_assignment, 0u);
}
} transp_try_emplace;
struct norehash_transp_try_emplace_type : public transp_try_emplace_type
{
template <class T, class X> void operator()(std::vector<T>& values, X& x)
{
x.reserve(values.size());
transp_try_emplace_type::operator()(values, x);
BOOST_TEST_EQ(raii::move_constructor, 0u);
}
} norehash_transp_try_emplace;
struct lvalue_try_emplace_or_cvisit_type
{
template <class T, class X> void operator()(std::vector<T>& values, X& x)
{
std::atomic<std::uint64_t> num_inserts{0};
std::atomic<std::uint64_t> num_invokes{0};
thread_runner(values, [&x, &num_inserts, &num_invokes](boost::span<T> s) {
for (auto& r : s) {
bool b = x.try_emplace_or_cvisit(
r.first, r.second.x_,
[&num_invokes](typename X::value_type const& v) {
(void)v;
++num_invokes;
});
if (b) {
++num_inserts;
}
}
});
BOOST_TEST_EQ(num_inserts, x.size());
BOOST_TEST_EQ(num_invokes, values.size() - x.size());
BOOST_TEST_EQ(raii::default_constructor, x.size());
BOOST_TEST_EQ(raii::copy_constructor, x.size());
// don't check move construction count here because of rehashing
BOOST_TEST_GT(raii::move_constructor, 0u);
BOOST_TEST_EQ(raii::move_assignment, 0u);
BOOST_TEST_EQ(raii::copy_assignment, 0u);
}
} lvalue_try_emplace_or_cvisit;
struct lvalue_try_emplace_or_visit_type
{
template <class T, class X> void operator()(std::vector<T>& values, X& x)
{
std::atomic<std::uint64_t> num_inserts{0};
std::atomic<std::uint64_t> num_invokes{0};
thread_runner(values, [&x, &num_inserts, &num_invokes](boost::span<T> s) {
for (auto& r : s) {
bool b = x.try_emplace_or_visit(
r.first, r.second.x_,
[&num_invokes](typename X::value_type& v) {
(void)v;
++num_invokes;
});
if (b) {
++num_inserts;
}
}
});
BOOST_TEST_EQ(num_inserts, x.size());
BOOST_TEST_EQ(num_invokes, values.size() - x.size());
BOOST_TEST_EQ(raii::default_constructor, x.size());
BOOST_TEST_EQ(raii::copy_constructor, x.size());
// don't check move construction count here because of rehashing
BOOST_TEST_GT(raii::move_constructor, 0u);
BOOST_TEST_EQ(raii::move_assignment, 0u);
BOOST_TEST_EQ(raii::copy_assignment, 0u);
}
} lvalue_try_emplace_or_visit;
struct rvalue_try_emplace_or_cvisit_type
{
template <class T, class X> void operator()(std::vector<T>& values, X& x)
{
std::atomic<std::uint64_t> num_inserts{0};
std::atomic<std::uint64_t> num_invokes{0};
thread_runner(values, [&x, &num_inserts, &num_invokes](boost::span<T> s) {
for (auto& r : s) {
bool b = x.try_emplace_or_cvisit(
std::move(r.first), r.second.x_,
[&num_invokes](typename X::value_type const& v) {
(void)v;
++num_invokes;
});
if (b) {
++num_inserts;
}
}
});
BOOST_TEST_EQ(num_inserts, x.size());
BOOST_TEST_EQ(num_invokes, values.size() - x.size());
BOOST_TEST_EQ(raii::default_constructor, x.size());
if (std::is_same<T, typename X::value_type>::value) {
BOOST_TEST_EQ(raii::copy_constructor, x.size());
BOOST_TEST_GE(raii::move_constructor, x.size());
} else {
BOOST_TEST_EQ(raii::copy_constructor, 0u);
BOOST_TEST_GE(raii::move_constructor, x.size());
}
}
} rvalue_try_emplace_or_cvisit;
struct rvalue_try_emplace_or_visit_type
{
template <class T, class X> void operator()(std::vector<T>& values, X& x)
{
std::atomic<std::uint64_t> num_inserts{0};
std::atomic<std::uint64_t> num_invokes{0};
thread_runner(values, [&x, &num_inserts, &num_invokes](boost::span<T> s) {
for (auto& r : s) {
bool b = x.try_emplace_or_visit(
std::move(r.first), r.second.x_,
[&num_invokes](typename X::value_type& v) {
(void)v;
++num_invokes;
});
if (b) {
++num_inserts;
}
}
});
BOOST_TEST_EQ(num_inserts, x.size());
BOOST_TEST_EQ(num_invokes, values.size() - x.size());
BOOST_TEST_EQ(raii::default_constructor, x.size());
if (std::is_same<T, typename X::value_type>::value) {
BOOST_TEST_EQ(raii::copy_constructor, x.size());
BOOST_TEST_GE(raii::move_constructor, x.size());
} else {
BOOST_TEST_EQ(raii::copy_constructor, 0u);
BOOST_TEST_GE(raii::move_constructor, x.size());
}
}
} rvalue_try_emplace_or_visit;
struct transp_try_emplace_or_cvisit_type
{
template <class T, class X> void operator()(std::vector<T>& values, X& x)
{
std::atomic<std::uint64_t> num_inserts{0};
std::atomic<std::uint64_t> num_invokes{0};
thread_runner(values, [&x, &num_inserts, &num_invokes](boost::span<T> s) {
for (auto& r : s) {
bool b = x.try_emplace_or_cvisit(
r.first.x_, r.second.x_,
[&num_invokes](typename X::value_type const& v) {
(void)v;
++num_invokes;
});
if (b) {
++num_inserts;
}
}
});
BOOST_TEST_EQ(num_inserts, x.size());
BOOST_TEST_EQ(num_invokes, values.size() - x.size());
BOOST_TEST_EQ(raii::default_constructor, 2 * x.size());
BOOST_TEST_EQ(raii::copy_constructor, 0u);
}
} transp_try_emplace_or_cvisit;
struct transp_try_emplace_or_visit_type
{
template <class T, class X> void operator()(std::vector<T>& values, X& x)
{
std::atomic<std::uint64_t> num_inserts{0};
std::atomic<std::uint64_t> num_invokes{0};
thread_runner(values, [&x, &num_inserts, &num_invokes](boost::span<T> s) {
for (auto& r : s) {
bool b = x.try_emplace_or_visit(
r.first.x_, r.second.x_,
[&num_invokes](typename X::value_type& v) {
(void)v;
++num_invokes;
});
if (b) {
++num_inserts;
}
}
});
BOOST_TEST_EQ(num_inserts, x.size());
BOOST_TEST_EQ(num_invokes, values.size() - x.size());
BOOST_TEST_EQ(raii::default_constructor, 2 * x.size());
BOOST_TEST_EQ(raii::copy_constructor, 0u);
}
} transp_try_emplace_or_visit;
template <class X, class G, class F>
void try_emplace(X*, G gen, F try_emplacer, test::random_generator rg)
{
auto values = make_random_values(1024 * 16, [&] { return gen(rg); });
auto reference_map =
boost::unordered_flat_map<raii, raii>(values.begin(), values.end());
raii::reset_counts();
{
X x;
try_emplacer(values, x);
BOOST_TEST_EQ(x.size(), reference_map.size());
using value_type = typename X::value_type;
BOOST_TEST_EQ(x.size(), x.visit_all([&](value_type const& kv) {
BOOST_TEST(reference_map.contains(kv.first));
if (rg == test::sequential) {
BOOST_TEST_EQ(kv.second, reference_map[kv.first]);
}
}));
}
BOOST_TEST_GE(raii::default_constructor, 0u);
BOOST_TEST_GE(raii::copy_constructor, 0u);
BOOST_TEST_GE(raii::move_constructor, 0u);
BOOST_TEST_GT(raii::destructor, 0u);
BOOST_TEST_EQ(raii::default_constructor + raii::copy_constructor +
raii::move_constructor,
raii::destructor);
}
boost::unordered::concurrent_flat_map<raii, raii>* map;
boost::unordered::concurrent_flat_map<raii, raii, transp_hash,
transp_key_equal>* transp_map;
} // namespace
using test::default_generator;
using test::limited_range;
using test::sequential;
// clang-format off
UNORDERED_TEST(
try_emplace,
((map))
((value_type_generator)(init_type_generator))
((lvalue_try_emplacer)(norehash_lvalue_try_emplacer)
(rvalue_try_emplacer)(norehash_rvalue_try_emplacer)
(lvalue_try_emplace_or_cvisit)(lvalue_try_emplace_or_visit)
(rvalue_try_emplace_or_cvisit)(rvalue_try_emplace_or_visit))
((default_generator)(sequential)(limited_range)))
UNORDERED_TEST(
try_emplace,
((transp_map))
((init_type_generator))
((transp_try_emplace)(norehash_transp_try_emplace)
(transp_try_emplace_or_cvisit)(transp_try_emplace_or_visit))
((default_generator)(sequential)(limited_range)))
// clang-format on
RUN_TESTS()
+461
View File
@@ -0,0 +1,461 @@
// 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)
#include "helpers.hpp"
#include <boost/unordered/concurrent_flat_map.hpp>
#include <boost/core/ignore_unused.hpp>
#include <functional>
#include <vector>
namespace {
test::seed_t initialize_seed(335740237);
struct lvalue_visitor_type
{
template <class T, class X, class M>
void operator()(std::vector<T>& values, X& x, M const& reference_map)
{
using value_type = typename X::value_type;
std::atomic<std::uint64_t> num_visits{0};
std::atomic<std::uint64_t> total_count{0};
auto mut_visitor = [&num_visits, &reference_map](value_type& v) {
BOOST_TEST(reference_map.contains(v.first));
BOOST_TEST_EQ(v.second, reference_map.find(v.first)->second);
++num_visits;
};
auto const_visitor = [&num_visits, &reference_map](value_type const& v) {
BOOST_TEST(reference_map.contains(v.first));
BOOST_TEST_EQ(v.second, reference_map.find(v.first)->second);
++num_visits;
};
{
thread_runner(
values, [&x, &mut_visitor, &total_count](boost::span<T> s) {
for (auto const& val : s) {
auto r = val.first.x_;
BOOST_TEST(r >= 0);
auto count = x.visit(val.first, mut_visitor);
BOOST_TEST_EQ(count, 1u);
total_count += count;
count = x.visit(val.second, mut_visitor);
BOOST_TEST_EQ(count, 0u);
}
});
BOOST_TEST_EQ(num_visits, values.size());
BOOST_TEST_EQ(total_count, values.size());
num_visits = 0;
total_count = 0;
}
{
thread_runner(
values, [&x, &const_visitor, &total_count](boost::span<T> s) {
for (auto const& val : s) {
auto r = val.first.x_;
BOOST_TEST(r >= 0);
auto const& y = x;
auto count = y.visit(val.first, const_visitor);
BOOST_TEST_EQ(count, 1u);
total_count += count;
count = y.visit(val.second, const_visitor);
BOOST_TEST_EQ(count, 0u);
}
});
BOOST_TEST_EQ(num_visits, values.size());
BOOST_TEST_EQ(total_count, values.size());
num_visits = 0;
total_count = 0;
}
{
thread_runner(
values, [&x, &const_visitor, &total_count](boost::span<T> s) {
for (auto const& val : s) {
auto r = val.first.x_;
BOOST_TEST(r >= 0);
auto count = x.cvisit(val.first, const_visitor);
BOOST_TEST_EQ(count, 1u);
total_count += count;
count = x.cvisit(val.second, const_visitor);
BOOST_TEST_EQ(count, 0u);
}
});
BOOST_TEST_EQ(num_visits, values.size());
BOOST_TEST_EQ(total_count, values.size());
num_visits = 0;
total_count = 0;
}
}
} lvalue_visitor;
struct transp_visitor_type
{
template <class T, class X, class M>
void operator()(std::vector<T>& values, X& x, M const& reference_map)
{
using value_type = typename X::value_type;
std::atomic<std::uint64_t> num_visits{0};
std::atomic<std::uint64_t> total_count{0};
auto mut_visitor = [&num_visits, &reference_map](value_type& v) {
BOOST_TEST(reference_map.contains(v.first));
BOOST_TEST_EQ(v.second, reference_map.find(v.first)->second);
++num_visits;
};
auto const_visitor = [&num_visits, &reference_map](value_type const& v) {
BOOST_TEST(reference_map.contains(v.first));
BOOST_TEST_EQ(v.second, reference_map.find(v.first)->second);
++num_visits;
};
{
thread_runner(
values, [&x, &mut_visitor, &total_count](boost::span<T> s) {
for (auto const& val : s) {
auto r = val.first.x_;
BOOST_TEST(r >= 0);
auto count = x.visit(val.first.x_, mut_visitor);
BOOST_TEST_EQ(count, 1u);
total_count += count;
count = x.visit(val.second.x_, mut_visitor);
BOOST_TEST_EQ(count, 0u);
}
});
BOOST_TEST_EQ(num_visits, values.size());
BOOST_TEST_EQ(total_count, values.size());
num_visits = 0;
total_count = 0;
}
{
thread_runner(
values, [&x, &const_visitor, &total_count](boost::span<T> s) {
for (auto const& val : s) {
auto r = val.first.x_;
BOOST_TEST(r >= 0);
auto const& y = x;
auto count = y.visit(val.first.x_, const_visitor);
BOOST_TEST_EQ(count, 1u);
total_count += count;
count = y.visit(val.second.x_, const_visitor);
BOOST_TEST_EQ(count, 0u);
}
});
BOOST_TEST_EQ(num_visits, values.size());
BOOST_TEST_EQ(total_count, values.size());
num_visits = 0;
total_count = 0;
}
{
thread_runner(
values, [&x, &const_visitor, &total_count](boost::span<T> s) {
for (auto const& val : s) {
auto r = val.first.x_;
BOOST_TEST(r >= 0);
auto count = x.cvisit(val.first.x_, const_visitor);
BOOST_TEST_EQ(count, 1u);
total_count += count;
count = x.cvisit(val.second.x_, const_visitor);
BOOST_TEST_EQ(count, 0u);
}
});
BOOST_TEST_EQ(num_visits, values.size());
BOOST_TEST_EQ(total_count, values.size());
num_visits = 0;
total_count = 0;
}
}
} transp_visitor;
struct visit_all_type
{
template <class T, class X, class M>
void operator()(std::vector<T>& values, X& x, M const& reference_map)
{
using value_type = typename X::value_type;
std::atomic<std::uint64_t> total_count{0};
auto mut_visitor = [&reference_map](std::atomic<uint64_t>& num_visits) {
return [&reference_map, &num_visits](value_type& kv) {
BOOST_TEST(reference_map.contains(kv.first));
BOOST_TEST_EQ(kv.second, reference_map.find(kv.first)->second);
++num_visits;
};
};
auto const_visitor = [&reference_map](std::atomic<uint64_t>& num_visits) {
return [&reference_map, &num_visits](value_type const& kv) {
BOOST_TEST(reference_map.contains(kv.first));
BOOST_TEST_EQ(kv.second, reference_map.find(kv.first)->second);
++num_visits;
};
};
{
thread_runner(values, [&x, &total_count, &mut_visitor](boost::span<T>) {
std::atomic<std::uint64_t> num_visits{0};
total_count += x.visit_all(mut_visitor(num_visits));
BOOST_TEST_EQ(x.size(), num_visits);
});
BOOST_TEST_EQ(total_count, num_threads * x.size());
total_count = 0;
}
{
thread_runner(
values, [&x, &total_count, &const_visitor](boost::span<T>) {
std::atomic<std::uint64_t> num_visits{0};
auto const& y = x;
total_count += y.visit_all(const_visitor(num_visits));
BOOST_TEST_EQ(x.size(), num_visits);
});
BOOST_TEST_EQ(total_count, num_threads * x.size());
total_count = 0;
}
{
thread_runner(
values, [&x, &total_count, &const_visitor](boost::span<T>) {
std::atomic<std::uint64_t> num_visits{0};
total_count += x.cvisit_all(const_visitor(num_visits));
BOOST_TEST_EQ(x.size(), num_visits);
});
BOOST_TEST_EQ(total_count, num_threads * x.size());
total_count = 0;
}
}
} visit_all;
struct exec_policy_visit_all_type
{
template <class T, class X, class M>
void operator()(std::vector<T>& values, X& x, M const& reference_map)
{
#if defined(BOOST_UNORDERED_PARALLEL_ALGORITHMS)
using value_type = typename X::value_type;
auto mut_visitor = [&reference_map](std::atomic<uint64_t>& num_visits) {
return [&reference_map, &num_visits](value_type& kv) {
BOOST_TEST(reference_map.contains(kv.first));
BOOST_TEST_EQ(kv.second, reference_map.find(kv.first)->second);
++num_visits;
};
};
auto const_visitor = [&reference_map](std::atomic<uint64_t>& num_visits) {
return [&reference_map, &num_visits](value_type const& kv) {
BOOST_TEST(reference_map.contains(kv.first));
BOOST_TEST_EQ(kv.second, reference_map.find(kv.first)->second);
++num_visits;
};
};
{
thread_runner(values, [&x, &mut_visitor](boost::span<T>) {
std::atomic<std::uint64_t> num_visits{0};
x.visit_all(std::execution::par_unseq, mut_visitor(num_visits));
BOOST_TEST_EQ(x.size(), num_visits);
});
}
{
thread_runner(values, [&x, &const_visitor](boost::span<T>) {
std::atomic<std::uint64_t> num_visits{0};
auto const& y = x;
y.visit_all(std::execution::par_unseq, const_visitor(num_visits));
BOOST_TEST_EQ(x.size(), num_visits);
});
}
{
thread_runner(values, [&x, &const_visitor](boost::span<T>) {
std::atomic<std::uint64_t> num_visits{0};
x.cvisit_all(std::execution::par_unseq, const_visitor(num_visits));
BOOST_TEST_EQ(x.size(), num_visits);
});
}
#else
(void)values;
(void)x;
(void)reference_map;
#endif
}
} exec_policy_visit_all;
template <class X, class G, class F>
void visit(X*, G gen, F visitor, test::random_generator rg)
{
auto values = make_random_values(1024 * 16, [&] { return gen(rg); });
for (auto& val : values) {
if (val.second.x_ == 0) {
val.second.x_ = 1;
}
val.second.x_ *= -1;
}
auto reference_map =
boost::unordered_flat_map<raii, raii>(values.begin(), values.end());
raii::reset_counts();
{
X x;
for (auto const& v : values) {
x.insert(v);
}
BOOST_TEST_EQ(x.size(), reference_map.size());
std::uint64_t old_default_constructor = raii::default_constructor;
std::uint64_t old_copy_constructor = raii::copy_constructor;
std::uint64_t old_move_constructor = raii::move_constructor;
std::uint64_t old_copy_assignment = raii::copy_assignment;
std::uint64_t old_move_assignment = raii::move_assignment;
visitor(values, x, reference_map);
BOOST_TEST_EQ(old_default_constructor, raii::default_constructor);
BOOST_TEST_EQ(old_copy_constructor, raii::copy_constructor);
BOOST_TEST_EQ(old_move_constructor, raii::move_constructor);
BOOST_TEST_EQ(old_copy_assignment, raii::copy_assignment);
BOOST_TEST_EQ(old_move_assignment, raii::move_assignment);
}
BOOST_TEST_GE(raii::default_constructor, 0u);
BOOST_TEST_GE(raii::copy_constructor, 0u);
BOOST_TEST_GE(raii::move_constructor, 0u);
BOOST_TEST_GT(raii::destructor, 0u);
BOOST_TEST_EQ(raii::default_constructor + raii::copy_constructor +
raii::move_constructor,
raii::destructor);
}
template <class X, class G>
void empty_visit(X*, G gen, test::random_generator rg)
{
auto values = make_random_values(1024 * 16, [&] { return gen(rg); });
using values_type = decltype(values);
using span_value_type = typename values_type::value_type;
raii::reset_counts();
{
X x;
std::uint64_t old_default_constructor = raii::default_constructor;
std::uint64_t old_copy_constructor = raii::copy_constructor;
std::uint64_t old_move_constructor = raii::move_constructor;
std::uint64_t old_copy_assignment = raii::copy_assignment;
std::uint64_t old_move_assignment = raii::move_assignment;
{
thread_runner(values, [&x](boost::span<span_value_type> s) {
std::atomic<std::uint64_t> num_visits{0};
x.visit_all(
[&num_visits](typename X::value_type const&) { ++num_visits; });
BOOST_TEST_EQ(num_visits, 0u);
for (auto const& val : s) {
auto count = x.visit(val.first,
[&num_visits](typename X::value_type const&) { ++num_visits; });
BOOST_TEST_EQ(count, 0u);
}
});
}
BOOST_TEST_EQ(old_default_constructor, raii::default_constructor);
BOOST_TEST_EQ(old_copy_constructor, raii::copy_constructor);
BOOST_TEST_EQ(old_move_constructor, raii::move_constructor);
BOOST_TEST_EQ(old_copy_assignment, raii::copy_assignment);
BOOST_TEST_EQ(old_move_assignment, raii::move_assignment);
}
BOOST_TEST_EQ(raii::default_constructor, 0u);
BOOST_TEST_EQ(raii::copy_constructor, 0u);
BOOST_TEST_EQ(raii::move_constructor, 0u);
BOOST_TEST_EQ(raii::destructor, 0u);
}
boost::unordered::concurrent_flat_map<raii, raii>* map;
boost::unordered::concurrent_flat_map<raii, raii, transp_hash,
transp_key_equal>* transp_map;
} // namespace
using test::default_generator;
using test::limited_range;
using test::sequential;
// clang-format off
UNORDERED_TEST(
visit,
((map))
((value_type_generator)(init_type_generator))
((lvalue_visitor)(visit_all)(exec_policy_visit_all))
((default_generator)(sequential)(limited_range)))
UNORDERED_TEST(
visit,
((transp_map))
((value_type_generator)(init_type_generator))
((transp_visitor))
((default_generator)(sequential)(limited_range)))
UNORDERED_TEST(
empty_visit,
((map)(transp_map))
((value_type_generator)(init_type_generator))
((default_generator)(sequential)(limited_range))
)
// clang-format on
RUN_TESTS()
+18 -2
View File
@@ -25,8 +25,24 @@ typedef boost::unordered_flat_set<
test::exception::allocator<test::exception::object> >
test_pair_set;
#define CONTAINER_SEQ (test_set)(test_map)
#define CONTAINER_PAIR_SEQ (test_pair_set)(test_map)
typedef boost::unordered_node_set<test::exception::object,
test::exception::hash, test::exception::equal_to,
test::exception::allocator<test::exception::object> >
test_node_set;
typedef boost::unordered_node_map<test::exception::object,
test::exception::object, test::exception::hash, test::exception::equal_to,
test::exception::allocator2<test::exception::object> >
test_node_map;
typedef boost::unordered_node_set<
std::pair<test::exception::object, test::exception::object>,
test::exception::hash, test::exception::equal_to,
test::exception::allocator<test::exception::object> >
test_pair_node_set;
#define CONTAINER_SEQ (test_set)(test_map)(test_node_set)(test_node_map)
#define CONTAINER_PAIR_SEQ (test_pair_set)(test_map)(test_pair_node_set)(test_node_map)
#else
typedef boost::unordered_set<test::exception::object, test::exception::hash,
test::exception::equal_to,
+25 -7
View File
@@ -1,6 +1,6 @@
// Copyright 2006-2009 Daniel James.
// Copyright 2022 Christian Mazakas.
// Copyright 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)
#include "./containers.hpp"
@@ -229,10 +229,12 @@ using test::generate_collisions;
#ifdef BOOST_UNORDERED_FOA_TESTS
test_set* test_set_;
test_map* test_map_;
test_node_set* test_node_set_;
test_node_map* test_node_map_;
// clang-format off
UNORDERED_TEST(insert_exception_test,
((test_set_)(test_map_))
((test_set_)(test_map_)(test_node_set_)(test_node_map_))
((insert_lvalue)(insert_lvalue_begin)(insert_lvalue_end)
(insert_lvalue_pos)(insert_single_item_range)
(emplace_lvalue)(emplace_lvalue_begin)(emplace_lvalue_end)
@@ -242,7 +244,7 @@ UNORDERED_TEST(insert_exception_test,
)
UNORDERED_TEST(insert_rehash_exception_test,
((test_set_)(test_map_))
((test_set_)(test_map_)(test_node_set_)(test_node_map_))
((insert_lvalue)(insert_lvalue_begin)(insert_lvalue_end)
(insert_lvalue_pos)(insert_single_item_range)
(emplace_lvalue)(emplace_lvalue_begin)(emplace_lvalue_end)
@@ -314,15 +316,16 @@ struct pair_emplace2_type : inserter_base
#ifdef BOOST_UNORDERED_FOA_TESTS
test_pair_set* test_pair_set_;
test_pair_node_set* test_pair_node_set_;
// clang-format off
UNORDERED_TEST(insert_exception_test,
((test_pair_set_)(test_map_))
((test_pair_set_)(test_map_)(test_pair_node_set_)(test_node_map_))
((pair_emplace)(pair_emplace2))
((default_generator)(limited_range)(generate_collisions))
)
UNORDERED_TEST(insert_rehash_exception_test,
((test_pair_set_)(test_map_))
((test_pair_set_)(test_map_)(test_pair_node_set_)(test_node_map_))
((pair_emplace)(pair_emplace2))
((default_generator)(limited_range)(generate_collisions))
)
@@ -399,6 +402,20 @@ struct map_insert_or_assign_type : map_inserter_base
}
} map_insert_or_assign;
#ifdef BOOST_UNORDERED_FOA_TESTS
// clang-format off
UNORDERED_TEST(insert_exception_test,
((test_map_)(test_node_map_))
((try_emplace)(try_emplace2)(map_insert_operator)(map_insert_or_assign))
((default_generator)(limited_range)(generate_collisions))
)
UNORDERED_TEST(insert_rehash_exception_test,
((test_map_)(test_node_map_))
((try_emplace)(try_emplace2)(map_insert_operator)(map_insert_or_assign))
((default_generator)(limited_range)(generate_collisions))
)
// clang-format on
#else
// clang-format off
UNORDERED_TEST(insert_exception_test,
((test_map_))
@@ -411,6 +428,7 @@ UNORDERED_TEST(insert_rehash_exception_test,
((default_generator)(limited_range)(generate_collisions))
)
// clang-format on
#endif
// Range insert tests
@@ -459,12 +477,12 @@ void insert_range_rehash_exception_test(T*, test::random_generator gen)
#ifdef BOOST_UNORDERED_FOA_TESTS
// clang-format off
UNORDERED_TEST(insert_range_exception_test,
((test_set_)(test_map_))
((test_set_)(test_map_)(test_node_set_)(test_node_map_))
((default_generator)(limited_range)(generate_collisions))
)
UNORDERED_TEST(insert_range_rehash_exception_test,
((test_set_)(test_map_))
((test_set_)(test_map_)(test_node_set_)(test_node_map_))
((default_generator)(limited_range)(generate_collisions))
)
// clang-format on
+50
View File
@@ -0,0 +1,50 @@
// 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)
#include "./containers.hpp"
#include "../helpers/helpers.hpp"
#include "../helpers/invariants.hpp"
#include "../helpers/random_values.hpp"
#include "../helpers/strong.hpp"
#include "../helpers/tracker.hpp"
#include <vector>
UNORDERED_AUTO_TEST (less_osx_regression) {
DISABLE_EXCEPTIONS;
typedef test_pair_set::value_type value_type;
typedef test::exception::object object;
std::vector<value_type> v;
v.push_back(value_type(object(12, 98), object(88, 13)));
v.push_back(value_type(object(24, 71), object(62, 84)));
v.push_back(value_type(object(30, 0), object(5, 73)));
v.push_back(value_type(object(34, 64), object(79, 58)));
v.push_back(value_type(object(36, 95), object(64, 23)));
v.push_back(value_type(object(42, 89), object(68, 44)));
v.push_back(value_type(object(42, 26), object(93, 64)));
v.push_back(value_type(object(86, 86), object(16, 62)));
v.push_back(value_type(object(86, 86), object(75, 23)));
v.push_back(value_type(object(92, 37), object(41, 90)));
BOOST_TEST_EQ(v.size(), 10u);
std::set<value_type, test::exception::less> s;
s.insert(v.begin(), v.end());
BOOST_TEST_EQ(s.size(), v.size());
test::ordered<test_pair_set> tracker;
test_pair_set x;
for (std::vector<value_type>::iterator it = v.begin(); it != v.end();
++it) {
x.insert(*it);
}
tracker.insert(v.begin(), v.end());
tracker.compare(x);
}
RUN_TESTS()
+41 -1
View File
@@ -1,6 +1,6 @@
// Copyright 2017-2018 Daniel James.
// Copyright 2022 Christian Mazakas.
// Copyright 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)
@@ -64,6 +64,12 @@ boost::unordered_flat_set<test::exception::object, test::exception::hash,
boost::unordered_flat_map<test::exception::object, test::exception::object,
test::exception::hash, test::exception::equal_to,
test::exception::allocator2<test::exception::object> >* test_map_;
boost::unordered_node_set<test::exception::object, test::exception::hash,
test::exception::equal_to,
test::exception::allocator<test::exception::object> >* test_node_set_;
boost::unordered_node_map<test::exception::object, test::exception::object,
test::exception::hash, test::exception::equal_to,
test::exception::allocator2<test::exception::object> >* test_node_map_;
// clang-format off
UNORDERED_MULTI_TEST(set_merge, merge_exception_test,
@@ -99,6 +105,40 @@ UNORDERED_MULTI_TEST(map_merge_collisions, merge_exception_test,
((generate_collisions))
((generate_collisions))
)
UNORDERED_MULTI_TEST(node_set_merge, merge_exception_test,
((test_node_set_))
((test_node_set_))
(/* (0x0000)(0x6400) */(0x0064)/* (0x0a64)(0x3232) */)
((0x0000)(0x0001)(0x0102))
((default_generator)(limited_range))
((default_generator)(limited_range))
)
UNORDERED_MULTI_TEST(node_map_merge, merge_exception_test,
((test_node_map_))
((test_node_map_))
((0x0000)(0x6400)(0x0064)(0x0a64)(0x3232))
((0x0101)(0x0200)(0x0201))
((default_generator)(limited_range))
((default_generator)(limited_range))
)
// Run fewer generate_collisions tests, as they're slow.
UNORDERED_MULTI_TEST(node_set_merge_collisions, merge_exception_test,
((test_node_set_))
((test_node_set_))
((0x0a0a))
((0x0202)(0x0100)(0x0201))
((generate_collisions))
((generate_collisions))
)
UNORDERED_MULTI_TEST(node_map_merge_collisions, merge_exception_test,
((test_node_map_))
((test_node_map_))
((0x0a0a))
((0x0000)(0x0002)(0x0102))
((generate_collisions))
((generate_collisions))
)
// clang-format on
#else
boost::unordered_set<test::exception::object, test::exception::hash,
test::exception::equal_to,
+7 -1
View File
@@ -247,8 +247,14 @@ using unordered_flat_set = boost::unordered_flat_set<int, boost::hash<int>,
std::equal_to<int>, test::allocator1<int> >;
using unordered_flat_map = boost::unordered_flat_map<int, int, boost::hash<int>,
std::equal_to<int>, test::allocator1<std::pair<int const, int> > >;
using unordered_node_set = boost::unordered_node_set<int, boost::hash<int>,
std::equal_to<int>, test::allocator1<int> >;
using unordered_node_map = boost::unordered_node_map<int, int, boost::hash<int>,
std::equal_to<int>, test::allocator1<std::pair<int const, int> > >;
#define SWAP_CONTAINER_SEQ (unordered_flat_set)(unordered_flat_map)
#define SWAP_CONTAINER_SEQ \
(unordered_flat_set)(unordered_flat_map) \
(unordered_node_set)(unordered_node_map)
#else
+1
View File
@@ -6,6 +6,7 @@
#if !defined(BOOST_UNORDERED_TEST_TEST_HEADER)
#define BOOST_UNORDERED_TEST_TEST_HEADER
#include <boost/unordered/detail/fwd.hpp>
#include <boost/core/lightweight_test.hpp>
#include <boost/preprocessor/cat.hpp>
#include <boost/preprocessor/stringize.hpp>
+2 -3
View File
@@ -42,9 +42,8 @@ namespace test {
value_list values2(x2.begin(), x2.end());
values1.sort();
values2.sort();
BOOST_TEST(values1.size() == values2.size() &&
test::equal(values1.begin(), values1.end(), values2.begin(),
test::equivalent));
BOOST_TEST_ALL_WITH(values1.begin(), values1.end(), values2.begin(),
values2.end(), test::equivalent);
}
template <class X1, class X2, class T>
+2
View File
@@ -11,6 +11,8 @@
#ifdef BOOST_UNORDERED_FOA_TESTS
#include <boost/unordered/unordered_flat_set.hpp>
#include <boost/unordered/unordered_flat_map.hpp>
#include <boost/unordered/unordered_node_map.hpp>
#include <boost/unordered/unordered_node_set.hpp>
#include <boost/unordered/detail/implementation.hpp>
#else
#include <boost/unordered_set.hpp>
+1 -1
View File
@@ -262,7 +262,7 @@ namespace test {
if (less_impl(x1.first, x2.first)) {
return true;
}
if (!less_impl(x1.first, x2.first)) {
if (less_impl(x2.first, x1.first)) {
return false;
}
return less_impl(x1.second, x2.second);
+50 -3
View File
@@ -1,6 +1,6 @@
// Copyright 2006-2009 Daniel James.
// Copyright 2022 Christian Mazakas.
// Copyright 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)
@@ -209,40 +209,75 @@ namespace assign_tests {
#ifdef BOOST_UNORDERED_FOA_TESTS
boost::unordered_flat_map<test::object, test::object, test::hash,
test::equal_to, std::allocator<test::object> >* test_map_std_alloc;
boost::unordered_node_map<test::object, test::object, test::hash,
test::equal_to, std::allocator<test::object> >* test_node_map_std_alloc;
boost::unordered_flat_set<test::object, test::hash, test::equal_to,
test::allocator1<test::object> >* test_set;
boost::unordered_node_set<test::object, test::hash, test::equal_to,
test::allocator1<test::object> >* test_node_set;
boost::unordered_flat_map<test::object, test::object, test::hash,
test::equal_to, test::allocator2<test::object> >* test_map;
boost::unordered_node_map<test::object, test::object, test::hash,
test::equal_to, test::allocator2<test::object> >* test_node_map;
boost::unordered_flat_set<test::object, test::hash, test::equal_to,
test::cxx11_allocator<test::object, test::propagate_assign> >*
test_set_prop_assign;
boost::unordered_node_set<test::object, test::hash, test::equal_to,
test::cxx11_allocator<test::object, test::propagate_assign> >*
test_node_set_prop_assign;
boost::unordered_flat_map<test::object, test::object, test::hash,
test::equal_to,
test::cxx11_allocator<test::object, test::propagate_assign> >*
test_map_prop_assign;
boost::unordered_node_map<test::object, test::object, test::hash,
test::equal_to,
test::cxx11_allocator<test::object, test::propagate_assign> >*
test_node_map_prop_assign;
boost::unordered_flat_set<test::object, test::hash, test::equal_to,
test::cxx11_allocator<test::object, test::no_propagate_assign> >*
test_set_no_prop_assign;
boost::unordered_node_set<test::object, test::hash, test::equal_to,
test::cxx11_allocator<test::object, test::no_propagate_assign> >*
test_node_set_no_prop_assign;
boost::unordered_flat_map<test::object, test::object, test::hash,
test::equal_to,
test::cxx11_allocator<test::object, test::no_propagate_assign> >*
test_map_no_prop_assign;
boost::unordered_node_map<test::object, test::object, test::hash,
test::equal_to,
test::cxx11_allocator<test::object, test::no_propagate_assign> >*
test_node_map_no_prop_assign;
UNORDERED_AUTO_TEST (check_traits) {
BOOST_TEST(!is_propagate(test_set));
BOOST_TEST(is_propagate(test_set_prop_assign));
BOOST_TEST(!is_propagate(test_set_no_prop_assign));
BOOST_TEST(!is_propagate(test_node_set));
BOOST_TEST(is_propagate(test_node_set_prop_assign));
BOOST_TEST(!is_propagate(test_node_set_no_prop_assign));
}
UNORDERED_TEST(assign_tests1,
((test_map_std_alloc)(test_set)(test_map)(test_set_prop_assign)(test_map_prop_assign)(test_set_no_prop_assign)(test_map_no_prop_assign))(
((test_map_std_alloc)(test_node_map_std_alloc)
(test_set)(test_node_set)
(test_map)(test_node_map)
(test_set_prop_assign)(test_node_set_prop_assign)
(test_map_prop_assign)(test_node_map_prop_assign)
(test_set_no_prop_assign)(test_node_set_no_prop_assign)
(test_map_no_prop_assign)(test_node_map_no_prop_assign))(
(default_generator)(generate_collisions)(limited_range)))
UNORDERED_TEST(assign_tests2,
((test_set)(test_map)(test_set_prop_assign)(test_map_prop_assign)(test_set_no_prop_assign)(test_map_no_prop_assign))(
((test_set)(test_node_set)
(test_map)(test_node_map)
(test_set_prop_assign)(test_node_set_prop_assign)
(test_map_prop_assign)(test_node_map_prop_assign)
(test_set_no_prop_assign)(test_node_set_no_prop_assign)
(test_map_no_prop_assign)(test_node_map_no_prop_assign))(
(default_generator)(generate_collisions)(limited_range)))
#else
boost::unordered_map<test::object, test::object, test::hash, test::equal_to,
@@ -315,6 +350,12 @@ namespace assign_tests {
std::initializer_list<std::pair<int const, int> > init;
#ifdef BOOST_UNORDERED_FOA_TESTS
boost::unordered_flat_map<int, int> x1;
boost::unordered_node_map<int, int> x2;
x2[25] = 3;
x2[16] = 10;
BOOST_TEST(!x2.empty());
x2 = init;
BOOST_TEST(x2.empty());
#else
boost::unordered_map<int, int> x1;
#endif
@@ -333,6 +374,12 @@ namespace assign_tests {
#ifdef BOOST_UNORDERED_FOA_TESTS
boost::unordered_flat_set<int> x;
boost::unordered_node_set<int> y;
y.insert(10);
y.insert(20);
y = {1, 2, -10};
BOOST_TEST(y.find(10) == y.end());
BOOST_TEST(y.find(-10) != y.end());
#else
boost::unordered_set<int> x;
#endif
+21 -10
View File
@@ -1,6 +1,6 @@
// Copyright 2007-2009 Daniel James.
// Copyright 2022 Christian Mazakas.
// Copyright 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,16 +11,12 @@
namespace at_tests {
UNORDERED_AUTO_TEST (at_tests) {
template <class X> static void at_tests(X*)
{
BOOST_LIGHTWEIGHT_TEST_OSTREAM << "Create Map" << std::endl;
#ifdef BOOST_UNORDERED_FOA_TESTS
boost::unordered_flat_map<std::string, int> x;
boost::unordered_flat_map<std::string, int> const& x_const(x);
#else
boost::unordered_map<std::string, int> x;
boost::unordered_map<std::string, int> const& x_const(x);
#endif
X x;
X const& x_const(x);
BOOST_LIGHTWEIGHT_TEST_OSTREAM << "Check empty container" << std::endl;
@@ -64,6 +60,21 @@ namespace at_tests {
BOOST_LIGHTWEIGHT_TEST_OSTREAM << "Finished" << std::endl;
}
}
#ifdef BOOST_UNORDERED_FOA_TESTS
static boost::unordered_flat_map<std::string, int>* test_map;
static boost::unordered_node_map<std::string, int>* test_node_map;
// clang-format off
UNORDERED_TEST(at_tests, ((test_map)(test_node_map)))
// clang-format on
#else
static boost::unordered_map<std::string, int>* test_map;
// clang-format off
UNORDERED_TEST(at_tests, ((test_map)))
// clang-format on
#endif
} // namespace at_tests
RUN_TESTS()
+144 -136
View File
@@ -1,6 +1,6 @@
// Copyright 2006-2009 Daniel James.
// Copyright 2022 Christian Mazakas.
// Copyright 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)
@@ -35,6 +35,22 @@ template class instantiate_flat_map<test::minimal::assignable const,
test::minimal::equal_to<test::minimal::assignable>,
test::minimal::allocator<int> >;
template <typename K, typename T, typename H, typename P, typename A>
class instantiate_node_map
{
typedef boost::unordered_node_map<K, T, H, P, A> container;
container x;
};
template class instantiate_node_map<int, int, boost::hash<int>,
std::equal_to<int>, test::minimal::allocator<int> >;
template class instantiate_node_map<test::minimal::assignable const,
test::minimal::default_assignable const,
test::minimal::hash<test::minimal::assignable>,
test::minimal::equal_to<test::minimal::assignable>,
test::minimal::allocator<int> >;
#else
#define INSTANTIATE(type) \
template class boost::unordered::detail::instantiate_##type
@@ -55,149 +71,101 @@ INSTANTIATE(multimap)<test::minimal::assignable, test::minimal::assignable,
test::minimal::allocator<int> >;
#endif
UNORDERED_AUTO_TEST (test0) {
template <template <class Key, class T, class H = boost::hash<Key>,
class P = std::equal_to<Key>,
class Allocator = std::allocator<std::pair<Key const, T> > >
class Map>
static void test0_impl()
{
test::minimal::constructor_param x;
typedef std::pair<test::minimal::assignable const, test::minimal::assignable>
value_type;
value_type value(x, x);
BOOST_LIGHTWEIGHT_TEST_OSTREAM << "Test unordered_map.\n";
Map<int, int> int_map;
#ifdef BOOST_UNORDERED_FOA_TESTS
boost::unordered_flat_map<int, int> int_map;
boost::unordered_flat_map<int, int, boost::hash<int>, std::equal_to<int>,
Map<int, int, boost::hash<int>, std::equal_to<int>,
test::minimal::cxx11_allocator<std::pair<int const, int> > >
int_map2;
boost::unordered_flat_map<test::minimal::assignable,
test::minimal::assignable, test::minimal::hash<test::minimal::assignable>,
test::minimal::equal_to<test::minimal::assignable>,
test::minimal::allocator<value_type> >
map;
#else
boost::unordered_map<int, int> int_map;
boost::unordered_map<int, int, boost::hash<int>, std::equal_to<int>,
test::minimal::cxx11_allocator<std::pair<int const, int> > >
int_map2;
boost::unordered_map<test::minimal::assignable, test::minimal::assignable,
Map<test::minimal::assignable, test::minimal::assignable,
test::minimal::hash<test::minimal::assignable>,
test::minimal::equal_to<test::minimal::assignable>,
test::minimal::allocator<value_type> >
map;
#endif
container_test(int_map, std::pair<int const, int>(0, 0));
container_test(int_map2, std::pair<int const, int>(0, 0));
container_test(map, value);
}
#ifndef BOOST_UNORDERED_FOA_TESTS
BOOST_LIGHTWEIGHT_TEST_OSTREAM << "Test unordered_multimap.\n";
boost::unordered_multimap<int, int> int_multimap;
boost::unordered_multimap<int, int, boost::hash<int>, std::equal_to<int>,
test::minimal::cxx11_allocator<std::pair<int const, int> > >
int_multimap2;
boost::unordered_multimap<test::minimal::assignable,
test::minimal::assignable, test::minimal::hash<test::minimal::assignable>,
test::minimal::equal_to<test::minimal::assignable>,
test::minimal::allocator<value_type> >
multimap;
container_test(int_multimap, std::pair<int const, int>(0, 0));
container_test(int_multimap2, std::pair<int const, int>(0, 0));
container_test(multimap, value);
UNORDERED_AUTO_TEST (test0) {
#ifdef BOOST_UNORDERED_FOA_TESTS
test0_impl<boost::unordered_flat_map>();
test0_impl<boost::unordered_node_map>();
#else
test0_impl<boost::unordered_map>();
test0_impl<boost::unordered_multimap>();
#endif
}
UNORDERED_AUTO_TEST (equality_tests) {
template <template <class Key, class T, class H = boost::hash<Key>,
class P = std::equal_to<Key>,
class Allocator = std::allocator<std::pair<Key const, T> > >
class Map>
static void equality_tests_impl()
{
typedef std::pair<test::minimal::copy_constructible_equality_comparable const,
test::minimal::copy_constructible_equality_comparable>
value_type;
#ifdef BOOST_UNORDERED_FOA_TESTS
boost::unordered_flat_map<int, int> int_map;
Map<int, int> int_map;
boost::unordered_flat_map<int, int, boost::hash<int>, std::equal_to<int>,
Map<int, int, boost::hash<int>, std::equal_to<int>,
test::minimal::cxx11_allocator<std::pair<int const, int> > >
int_map2;
boost::unordered_flat_map<
test::minimal::copy_constructible_equality_comparable,
Map<test::minimal::copy_constructible_equality_comparable,
test::minimal::copy_constructible_equality_comparable,
test::minimal::hash<test::minimal::copy_constructible_equality_comparable>,
test::minimal::equal_to<
test::minimal::copy_constructible_equality_comparable>,
test::minimal::allocator<value_type> >
map;
#else
boost::unordered_map<int, int> int_map;
boost::unordered_map<int, int, boost::hash<int>, std::equal_to<int>,
test::minimal::cxx11_allocator<std::pair<int const, int> > >
int_map2;
boost::unordered_map<test::minimal::copy_constructible_equality_comparable,
test::minimal::copy_constructible_equality_comparable,
test::minimal::hash<test::minimal::copy_constructible_equality_comparable>,
test::minimal::equal_to<
test::minimal::copy_constructible_equality_comparable>,
test::minimal::allocator<value_type> >
map;
#endif
equality_test(int_map);
equality_test(int_map2);
equality_test(map);
}
#ifndef BOOST_UNORDERED_FOA_TESTS
boost::unordered_multimap<int, int> int_multimap;
boost::unordered_multimap<int, int, boost::hash<int>, std::equal_to<int>,
test::minimal::cxx11_allocator<std::pair<int const, int> > >
int_multimap2;
boost::unordered_multimap<
test::minimal::copy_constructible_equality_comparable,
test::minimal::copy_constructible_equality_comparable,
test::minimal::hash<test::minimal::copy_constructible_equality_comparable>,
test::minimal::equal_to<
test::minimal::copy_constructible_equality_comparable>,
test::minimal::allocator<value_type> >
multimap;
equality_test(int_multimap);
equality_test(int_multimap2);
equality_test(multimap);
UNORDERED_AUTO_TEST (equality_tests) {
#ifdef BOOST_UNORDERED_FOA_TESTS
equality_tests_impl<boost::unordered_flat_map>();
equality_tests_impl<boost::unordered_node_map>();
#else
equality_tests_impl<boost::unordered_map>();
equality_tests_impl<boost::unordered_multimap>();
#endif
}
UNORDERED_AUTO_TEST (test1) {
template <template <class Key, class T, class H = boost::hash<Key>,
class P = std::equal_to<Key>,
class Allocator = std::allocator<std::pair<Key const, T> > >
class Map>
static void test1_unique_impl()
{
boost::hash<int> hash;
std::equal_to<int> equal_to;
int value = 0;
std::pair<int const, int> map_value(0, 0);
BOOST_LIGHTWEIGHT_TEST_OSTREAM << "Test unordered_map.\n";
Map<int, int> map;
#ifdef BOOST_UNORDERED_FOA_TESTS
boost::unordered_flat_map<int, int> map;
boost::unordered_flat_map<int, int, boost::hash<int>, std::equal_to<int>,
Map<int, int, boost::hash<int>, std::equal_to<int>,
test::minimal::cxx11_allocator<std::pair<int const, int> > >
map2;
#else
boost::unordered_map<int, int> map;
boost::unordered_map<int, int, boost::hash<int>, std::equal_to<int>,
test::minimal::cxx11_allocator<std::pair<int const, int> > >
map2;
#endif
unordered_unique_test(map, map_value);
unordered_map_test(map, value, value);
@@ -208,27 +176,53 @@ UNORDERED_AUTO_TEST (test1) {
unordered_map_test(map2, value, value);
unordered_copyable_test(map2, value, map_value, hash, equal_to);
unordered_map_functions(map2, value, value);
}
#ifndef BOOST_UNORDERED_FOA_TESTS
BOOST_LIGHTWEIGHT_TEST_OSTREAM << "Test unordered_multimap.\n";
template <template <class Key, class T, class H = boost::hash<Key>,
class P = std::equal_to<Key>,
class Allocator = std::allocator<std::pair<Key const, T> > >
class Map>
static void test1_equivalent_impl()
{
boost::unordered_multimap<int, int> multimap;
boost::hash<int> hash;
std::equal_to<int> equal_to;
int value = 0;
std::pair<int const, int> map_value(0, 0);
boost::unordered_multimap<int, int, boost::hash<int>, std::equal_to<int>,
Map<int, int> map;
Map<int, int, boost::hash<int>, std::equal_to<int>,
test::minimal::cxx11_allocator<std::pair<int const, int> > >
multimap2;
map2;
unordered_equivalent_test(multimap, map_value);
unordered_map_test(multimap, value, value);
unordered_copyable_test(multimap, value, map_value, hash, equal_to);
unordered_equivalent_test(map, map_value);
unordered_map_test(map, value, value);
unordered_copyable_test(map, value, map_value, hash, equal_to);
unordered_equivalent_test(multimap2, map_value);
unordered_map_test(multimap2, value, value);
unordered_copyable_test(multimap2, value, map_value, hash, equal_to);
unordered_equivalent_test(map2, map_value);
unordered_map_test(map2, value, value);
unordered_copyable_test(map2, value, map_value, hash, equal_to);
}
#endif
UNORDERED_AUTO_TEST (test1) {
#ifdef BOOST_UNORDERED_FOA_TESTS
test1_unique_impl<boost::unordered_flat_map>();
test1_unique_impl<boost::unordered_node_map>();
#else
test1_unique_impl<boost::unordered_map>();
test1_equivalent_impl<boost::unordered_multimap>();
#endif
}
UNORDERED_AUTO_TEST (test2) {
template <template <class Key, class T, class H = boost::hash<Key>,
class P = std::equal_to<Key>,
class Allocator = std::allocator<std::pair<Key const, T> > >
class Map>
static void test2_unique_impl()
{
test::minimal::constructor_param x;
test::minimal::assignable assignable(x);
@@ -240,60 +234,66 @@ UNORDERED_AUTO_TEST (test2) {
map_value_type;
map_value_type map_value(assignable, assignable);
BOOST_LIGHTWEIGHT_TEST_OSTREAM << "Test unordered_map.\n";
#ifdef BOOST_UNORDERED_FOA_TESTS
boost::unordered_flat_map<test::minimal::assignable,
test::minimal::assignable, test::minimal::hash<test::minimal::assignable>,
test::minimal::equal_to<test::minimal::assignable>,
test::minimal::allocator<map_value_type> >
map;
#else
boost::unordered_map<test::minimal::assignable, test::minimal::assignable,
Map<test::minimal::assignable, test::minimal::assignable,
test::minimal::hash<test::minimal::assignable>,
test::minimal::equal_to<test::minimal::assignable>,
test::minimal::allocator<map_value_type> >
map;
#endif
unordered_unique_test(map, map_value);
unordered_map_test(map, assignable, assignable);
unordered_copyable_test(map, assignable, map_value, hash, equal_to);
unordered_map_member_test(map, map_value);
#ifdef BOOST_UNORDERED_FOA_TESTS
boost::unordered_flat_map<test::minimal::assignable,
test::minimal::default_assignable,
Map<test::minimal::assignable, test::minimal::default_assignable,
test::minimal::hash<test::minimal::assignable>,
test::minimal::equal_to<test::minimal::assignable>,
test::minimal::allocator<map_value_type> >
map2;
#else
boost::unordered_map<test::minimal::assignable,
test::minimal::default_assignable,
test::minimal::hash<test::minimal::assignable>,
test::minimal::equal_to<test::minimal::assignable>,
test::minimal::allocator<map_value_type> >
map2;
#endif
test::minimal::default_assignable default_assignable;
unordered_map_functions(map2, assignable, default_assignable);
}
#ifndef BOOST_UNORDERED_FOA_TESTS
BOOST_LIGHTWEIGHT_TEST_OSTREAM << "Test unordered_multimap.\n";
template <template <class Key, class T, class H = boost::hash<Key>,
class P = std::equal_to<Key>,
class Allocator = std::allocator<std::pair<Key const, T> > >
class Map>
static void test2_equivalent_impl()
{
test::minimal::constructor_param x;
boost::unordered_multimap<test::minimal::assignable,
test::minimal::assignable, test::minimal::hash<test::minimal::assignable>,
test::minimal::assignable assignable(x);
test::minimal::copy_constructible copy_constructible(x);
test::minimal::hash<test::minimal::assignable> hash(x);
test::minimal::equal_to<test::minimal::assignable> equal_to(x);
typedef std::pair<test::minimal::assignable const, test::minimal::assignable>
map_value_type;
map_value_type map_value(assignable, assignable);
Map<test::minimal::assignable, test::minimal::assignable,
test::minimal::hash<test::minimal::assignable>,
test::minimal::equal_to<test::minimal::assignable>,
test::minimal::allocator<map_value_type> >
multimap;
map;
unordered_equivalent_test(multimap, map_value);
unordered_map_test(multimap, assignable, assignable);
unordered_copyable_test(multimap, assignable, map_value, hash, equal_to);
unordered_map_member_test(multimap, map_value);
unordered_equivalent_test(map, map_value);
unordered_map_test(map, assignable, assignable);
unordered_copyable_test(map, assignable, map_value, hash, equal_to);
unordered_map_member_test(map, map_value);
}
#endif
UNORDERED_AUTO_TEST (test2) {
#ifdef BOOST_UNORDERED_FOA_TESTS
test2_unique_impl<boost::unordered_flat_map>();
test2_unique_impl<boost::unordered_node_map>();
#else
test2_unique_impl<boost::unordered_map>();
test2_equivalent_impl<boost::unordered_multimap>();
#endif
}
@@ -316,9 +316,17 @@ bool operator==(lwg2059_key x, lwg2059_key y) { return x.value == y.value; }
UNORDERED_AUTO_TEST (lwg2059) {
#ifdef BOOST_UNORDERED_FOA_TESTS
boost::unordered_flat_map<lwg2059_key, int> x;
x.emplace(lwg2059_key(10), 5);
x.erase(x.begin());
{
boost::unordered_flat_map<lwg2059_key, int> x;
x.emplace(lwg2059_key(10), 5);
x.erase(x.begin());
}
{
boost::unordered_node_map<lwg2059_key, int> x;
x.emplace(lwg2059_key(10), 5);
x.erase(x.begin());
}
#else
{
boost::unordered_map<lwg2059_key, int> x;
+187 -200
View File
@@ -1,6 +1,6 @@
// Copyright 2006-2009 Daniel James.
// Copyright 2022 Christian Mazakas.
// Copyright 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)
@@ -35,6 +35,21 @@ template class instantiate_flat_set<test::minimal::assignable const,
test::minimal::equal_to<test::minimal::assignable>,
test::minimal::allocator<int> >;
template <typename T, typename H, typename P, typename A>
class instantiate_node_set
{
typedef boost::unordered_node_set<T, H, P, A> container;
container x;
};
template class instantiate_node_set<int, boost::hash<int>, std::equal_to<int>,
test::minimal::allocator<int> >;
template class instantiate_node_set<test::minimal::assignable const,
test::minimal::hash<test::minimal::assignable>,
test::minimal::equal_to<test::minimal::assignable>,
test::minimal::allocator<int> >;
#else
#define INSTANTIATE(type) \
@@ -56,153 +71,106 @@ INSTANTIATE(multiset)<test::minimal::assignable,
#endif
UNORDERED_AUTO_TEST (type_traits) {
#ifdef BOOST_UNORDERED_FOA_TESTS
typedef boost::unordered_flat_set<int> set_type;
#else
typedef boost::unordered_set<int> set_type;
#endif
typedef set_type::iterator iterator;
template <class X> static void type_traits_impl()
{
BOOST_STATIC_ASSERT(boost::is_same<int const&,
std::iterator_traits<iterator>::reference>::value);
typename std::iterator_traits<typename X::iterator>::reference>::value);
}
UNORDERED_AUTO_TEST (test0) {
UNORDERED_AUTO_TEST (type_traits) {
#ifdef BOOST_UNORDERED_FOA_TESTS
type_traits_impl<boost::unordered_flat_set<int> >();
type_traits_impl<boost::unordered_node_set<int> >();
#else
type_traits_impl<boost::unordered_set<int> >();
type_traits_impl<boost::unordered_multiset<int> >();
#endif
}
template <template <class T, class H = boost::hash<T>,
class P = std::equal_to<T>, class A = std::allocator<T> >
class Set>
static void test0_impl()
{
test::minimal::constructor_param x;
test::minimal::assignable assignable(x);
BOOST_LIGHTWEIGHT_TEST_OSTREAM << "Test unordered_set.\n";
Set<int> int_set;
#ifdef BOOST_UNORDERED_FOA_TESTS
boost::unordered_flat_set<int> int_set;
boost::unordered_flat_set<int, boost::hash<int>, std::equal_to<int>,
Set<int, boost::hash<int>, std::equal_to<int>,
test::minimal::cxx11_allocator<int> >
int_set2;
boost::unordered_flat_set<test::minimal::assignable,
test::minimal::hash<test::minimal::assignable>,
Set<test::minimal::assignable, test::minimal::hash<test::minimal::assignable>,
test::minimal::equal_to<test::minimal::assignable>,
test::minimal::allocator<test::minimal::assignable> >
set;
#else
boost::unordered_set<int> int_set;
boost::unordered_set<int, boost::hash<int>, std::equal_to<int>,
test::minimal::cxx11_allocator<int> >
int_set2;
boost::unordered_set<test::minimal::assignable,
test::minimal::hash<test::minimal::assignable>,
test::minimal::equal_to<test::minimal::assignable>,
test::minimal::allocator<test::minimal::assignable> >
set;
#endif
container_test(int_set, 0);
container_test(int_set2, 0);
container_test(set, assignable);
}
#ifndef BOOST_UNORDERED_FOA_TESTS
BOOST_LIGHTWEIGHT_TEST_OSTREAM << "Test unordered_multiset.\n";
boost::unordered_multiset<int> int_multiset;
boost::unordered_multiset<int, boost::hash<int>, std::equal_to<int>,
test::minimal::cxx11_allocator<int> >
int_multiset2;
boost::unordered_multiset<test::minimal::assignable,
test::minimal::hash<test::minimal::assignable>,
test::minimal::equal_to<test::minimal::assignable>,
test::minimal::allocator<test::minimal::assignable> >
multiset;
container_test(int_multiset, 0);
container_test(int_multiset2, 0);
container_test(multiset, assignable);
UNORDERED_AUTO_TEST (test0) {
#ifdef BOOST_UNORDERED_FOA_TESTS
test0_impl<boost::unordered_flat_set>();
test0_impl<boost::unordered_node_set>();
#else
test0_impl<boost::unordered_set>();
test0_impl<boost::unordered_multiset>();
#endif
}
UNORDERED_AUTO_TEST (equality_tests) {
template <template <class T, class H = boost::hash<T>,
class P = std::equal_to<T>, class A = std::allocator<T> >
class Set>
static void equality_tests_impl()
{
typedef test::minimal::copy_constructible_equality_comparable value_type;
#ifdef BOOST_UNORDERED_FOA_TESTS
boost::unordered_flat_set<int> int_set;
Set<int> int_set;
boost::unordered_flat_set<int, boost::hash<int>, std::equal_to<int>,
Set<int, boost::hash<int>, std::equal_to<int>,
test::minimal::cxx11_allocator<int> >
int_set2;
boost::unordered_flat_set<
test::minimal::copy_constructible_equality_comparable,
Set<test::minimal::copy_constructible_equality_comparable,
test::minimal::hash<test::minimal::copy_constructible_equality_comparable>,
test::minimal::equal_to<
test::minimal::copy_constructible_equality_comparable>,
test::minimal::allocator<value_type> >
set;
#else
boost::unordered_set<int> int_set;
boost::unordered_set<int, boost::hash<int>, std::equal_to<int>,
test::minimal::cxx11_allocator<int> >
int_set2;
boost::unordered_set<test::minimal::copy_constructible_equality_comparable,
test::minimal::hash<test::minimal::copy_constructible_equality_comparable>,
test::minimal::equal_to<
test::minimal::copy_constructible_equality_comparable>,
test::minimal::allocator<value_type> >
set;
#endif
equality_test(int_set);
equality_test(int_set2);
equality_test(set);
}
#ifndef BOOST_UNORDERED_FOA_TESTS
boost::unordered_multiset<int> int_multiset;
boost::unordered_multiset<int, boost::hash<int>, std::equal_to<int>,
test::minimal::cxx11_allocator<int> >
int_multiset2;
boost::unordered_multiset<
test::minimal::copy_constructible_equality_comparable,
test::minimal::hash<test::minimal::copy_constructible_equality_comparable>,
test::minimal::equal_to<
test::minimal::copy_constructible_equality_comparable>,
test::minimal::allocator<value_type> >
multiset;
equality_test(int_multiset);
equality_test(int_multiset2);
equality_test(multiset);
UNORDERED_AUTO_TEST (equality_tests) {
#ifdef BOOST_UNORDERED_FOA_TESTS
equality_tests_impl<boost::unordered_flat_set>();
equality_tests_impl<boost::unordered_node_set>();
#else
equality_tests_impl<boost::unordered_set>();
equality_tests_impl<boost::unordered_multiset>();
#endif
}
UNORDERED_AUTO_TEST (test1) {
template <template <class T, class H = boost::hash<T>,
class P = std::equal_to<T>, class A = std::allocator<T> >
class Set>
static void test1_unique_impl()
{
boost::hash<int> hash;
std::equal_to<int> equal_to;
int value = 0;
Set<int> set;
BOOST_LIGHTWEIGHT_TEST_OSTREAM << "Test unordered_set." << std::endl;
#ifdef BOOST_UNORDERED_FOA_TESTS
boost::unordered_flat_set<int> set;
boost::unordered_flat_set<int, boost::hash<int>, std::equal_to<int>,
Set<int, boost::hash<int>, std::equal_to<int>,
test::minimal::cxx11_allocator<int> >
set2;
#else
boost::unordered_set<int> set;
boost::unordered_set<int, boost::hash<int>, std::equal_to<int>,
test::minimal::cxx11_allocator<int> >
set2;
#endif
unordered_unique_test(set, value);
unordered_set_test(set, value);
@@ -211,27 +179,49 @@ UNORDERED_AUTO_TEST (test1) {
unordered_unique_test(set2, value);
unordered_set_test(set2, value);
unordered_copyable_test(set2, value, value, hash, equal_to);
}
#ifndef BOOST_UNORDERED_FOA_TESTS
BOOST_LIGHTWEIGHT_TEST_OSTREAM << "Test unordered_multiset." << std::endl;
template <template <class T, class H = boost::hash<T>,
class P = std::equal_to<T>, class A = std::allocator<T> >
class Set>
static void test1_equivalent_impl()
{
boost::unordered_multiset<int> multiset;
boost::hash<int> hash;
std::equal_to<int> equal_to;
int value = 0;
Set<int> set;
boost::unordered_multiset<int, boost::hash<int>, std::equal_to<int>,
Set<int, boost::hash<int>, std::equal_to<int>,
test::minimal::cxx11_allocator<int> >
multiset2;
set2;
unordered_equivalent_test(multiset, value);
unordered_set_test(multiset, value);
unordered_copyable_test(multiset, value, value, hash, equal_to);
unordered_equivalent_test(set, value);
unordered_set_test(set, value);
unordered_copyable_test(set, value, value, hash, equal_to);
unordered_equivalent_test(multiset2, value);
unordered_set_test(multiset2, value);
unordered_copyable_test(multiset2, value, value, hash, equal_to);
unordered_equivalent_test(set2, value);
unordered_set_test(set2, value);
unordered_copyable_test(set2, value, value, hash, equal_to);
}
#endif
UNORDERED_AUTO_TEST (test1) {
#ifdef BOOST_UNORDERED_FOA_TESTS
test1_unique_impl<boost::unordered_flat_set>();
test1_unique_impl<boost::unordered_node_set>();
#else
test1_unique_impl<boost::unordered_set>();
test1_equivalent_impl<boost::unordered_multiset>();
#endif
}
UNORDERED_AUTO_TEST (test2) {
template <template <class T, class H = boost::hash<T>,
class P = std::equal_to<T>, class A = std::allocator<T> >
class Set>
static void test2_unique_impl()
{
test::minimal::constructor_param x;
test::minimal::assignable assignable(x);
@@ -239,155 +229,144 @@ UNORDERED_AUTO_TEST (test2) {
test::minimal::hash<test::minimal::assignable> hash(x);
test::minimal::equal_to<test::minimal::assignable> equal_to(x);
BOOST_LIGHTWEIGHT_TEST_OSTREAM << "Test unordered_set.\n";
#ifdef BOOST_UNORDERED_FOA_TESTS
boost::unordered_flat_set<test::minimal::assignable,
test::minimal::hash<test::minimal::assignable>,
Set<test::minimal::assignable, test::minimal::hash<test::minimal::assignable>,
test::minimal::equal_to<test::minimal::assignable>,
test::minimal::allocator<test::minimal::assignable> >
set;
#else
boost::unordered_set<test::minimal::assignable,
test::minimal::hash<test::minimal::assignable>,
test::minimal::equal_to<test::minimal::assignable>,
test::minimal::allocator<test::minimal::assignable> >
set;
#endif
unordered_unique_test(set, assignable);
unordered_set_test(set, assignable);
unordered_copyable_test(set, assignable, assignable, hash, equal_to);
unordered_set_member_test(set, assignable);
}
#ifndef BOOST_UNORDERED_FOA_TESTS
BOOST_LIGHTWEIGHT_TEST_OSTREAM << "Test unordered_multiset.\n";
template <template <class T, class H = boost::hash<T>,
class P = std::equal_to<T>, class A = std::allocator<T> >
class Set>
static void test2_equivalent_impl()
{
test::minimal::constructor_param x;
boost::unordered_multiset<test::minimal::assignable,
test::minimal::hash<test::minimal::assignable>,
test::minimal::assignable assignable(x);
test::minimal::copy_constructible copy_constructible(x);
test::minimal::hash<test::minimal::assignable> hash(x);
test::minimal::equal_to<test::minimal::assignable> equal_to(x);
Set<test::minimal::assignable, test::minimal::hash<test::minimal::assignable>,
test::minimal::equal_to<test::minimal::assignable>,
test::minimal::allocator<test::minimal::assignable> >
multiset;
set;
unordered_equivalent_test(multiset, assignable);
unordered_set_test(multiset, assignable);
unordered_copyable_test(multiset, assignable, assignable, hash, equal_to);
unordered_set_member_test(multiset, assignable);
unordered_equivalent_test(set, assignable);
unordered_set_test(set, assignable);
unordered_copyable_test(set, assignable, assignable, hash, equal_to);
unordered_set_member_test(set, assignable);
}
#endif
UNORDERED_AUTO_TEST (test2) {
#ifdef BOOST_UNORDERED_FOA_TESTS
test2_unique_impl<boost::unordered_flat_set>();
test2_unique_impl<boost::unordered_node_set>();
#else
test2_unique_impl<boost::unordered_set>();
test2_equivalent_impl<boost::unordered_multiset>();
#endif
}
UNORDERED_AUTO_TEST (movable1_tests) {
template <template <class T, class H = boost::hash<T>,
class P = std::equal_to<T>, class A = std::allocator<T> >
class Set>
static void movable1_tests_impl()
{
test::minimal::constructor_param x;
test::minimal::movable1 movable1(x);
test::minimal::hash<test::minimal::movable1> hash(x);
test::minimal::equal_to<test::minimal::movable1> equal_to(x);
BOOST_LIGHTWEIGHT_TEST_OSTREAM << "Test unordered_set.\n";
#ifdef BOOST_UNORDERED_FOA_TESTS
boost::unordered_flat_set<test::minimal::movable1,
test::minimal::hash<test::minimal::movable1>,
Set<test::minimal::movable1, test::minimal::hash<test::minimal::movable1>,
test::minimal::equal_to<test::minimal::movable1>,
test::minimal::allocator<test::minimal::movable1> >
set;
#else
boost::unordered_set<test::minimal::movable1,
test::minimal::hash<test::minimal::movable1>,
test::minimal::equal_to<test::minimal::movable1>,
test::minimal::allocator<test::minimal::movable1> >
set;
#endif
// TODO: find out why Daniel had this commented out and if we need it and the
// corresponding equivalent impl
//
// unordered_unique_test(set, movable1);
unordered_set_test(set, movable1);
unordered_movable_test(set, movable1, movable1, hash, equal_to);
}
#ifndef BOOST_UNORDERED_FOA_TESTS
BOOST_LIGHTWEIGHT_TEST_OSTREAM << "Test unordered_multiset.\n";
boost::unordered_multiset<test::minimal::movable1,
test::minimal::hash<test::minimal::movable1>,
test::minimal::equal_to<test::minimal::movable1>,
test::minimal::allocator<test::minimal::movable1> >
multiset;
// unordered_equivalent_test(multiset, movable1);
unordered_set_test(multiset, movable1);
unordered_movable_test(multiset, movable1, movable1, hash, equal_to);
UNORDERED_AUTO_TEST (movable1_tests) {
#ifdef BOOST_UNORDERED_FOA_TESTS
movable1_tests_impl<boost::unordered_flat_set>();
movable1_tests_impl<boost::unordered_node_set>();
#else
movable1_tests_impl<boost::unordered_set>();
movable1_tests_impl<boost::unordered_multiset>();
#endif
}
UNORDERED_AUTO_TEST (movable2_tests) {
template <template <class T, class H = boost::hash<T>,
class P = std::equal_to<T>, class A = std::allocator<T> >
class Set>
static void movable2_tests_impl()
{
test::minimal::constructor_param x;
test::minimal::movable2 movable2(x);
test::minimal::hash<test::minimal::movable2> hash(x);
test::minimal::equal_to<test::minimal::movable2> equal_to(x);
BOOST_LIGHTWEIGHT_TEST_OSTREAM << "Test unordered_set.\n";
#ifdef BOOST_UNORDERED_FOA_TESTS
boost::unordered_flat_set<test::minimal::movable2,
test::minimal::hash<test::minimal::movable2>,
Set<test::minimal::movable2, test::minimal::hash<test::minimal::movable2>,
test::minimal::equal_to<test::minimal::movable2>,
test::minimal::allocator<test::minimal::movable2> >
set;
#else
boost::unordered_set<test::minimal::movable2,
test::minimal::hash<test::minimal::movable2>,
test::minimal::equal_to<test::minimal::movable2>,
test::minimal::allocator<test::minimal::movable2> >
set;
#endif
// unordered_unique_test(set, movable2);
unordered_set_test(set, movable2);
unordered_movable_test(set, movable2, movable2, hash, equal_to);
}
#ifndef BOOST_UNORDERED_FOA_TESTS
BOOST_LIGHTWEIGHT_TEST_OSTREAM << "Test unordered_multiset.\n";
UNORDERED_AUTO_TEST (movable2_tests) {
boost::unordered_multiset<test::minimal::movable2,
test::minimal::hash<test::minimal::movable2>,
test::minimal::equal_to<test::minimal::movable2>,
test::minimal::allocator<test::minimal::movable2> >
multiset;
// unordered_equivalent_test(multiset, movable2);
unordered_set_test(multiset, movable2);
unordered_movable_test(multiset, movable2, movable2, hash, equal_to);
#ifdef BOOST_UNORDERED_FOA_TESTS
movable2_tests_impl<boost::unordered_flat_set>();
movable2_tests_impl<boost::unordered_node_set>();
#else
movable2_tests_impl<boost::unordered_set>();
movable2_tests_impl<boost::unordered_multiset>();
#endif
}
UNORDERED_AUTO_TEST (destructible_tests) {
template <template <class T, class H = boost::hash<T>,
class P = std::equal_to<T>, class A = std::allocator<T> >
class Set>
static void destructible_tests_impl()
{
test::minimal::constructor_param x;
test::minimal::destructible destructible(x);
test::minimal::hash<test::minimal::destructible> hash(x);
test::minimal::equal_to<test::minimal::destructible> equal_to(x);
BOOST_LIGHTWEIGHT_TEST_OSTREAM << "Test unordered_set.\n";
#ifdef BOOST_UNORDERED_FOA_TESTS
boost::unordered_flat_set<test::minimal::destructible,
Set<test::minimal::destructible,
test::minimal::hash<test::minimal::destructible>,
test::minimal::equal_to<test::minimal::destructible> >
set;
#else
boost::unordered_set<test::minimal::destructible,
test::minimal::hash<test::minimal::destructible>,
test::minimal::equal_to<test::minimal::destructible> >
set;
#endif
unordered_destructible_test(set);
}
#ifndef BOOST_UNORDERED_FOA_TESTS
BOOST_LIGHTWEIGHT_TEST_OSTREAM << "Test unordered_multiset.\n";
boost::unordered_multiset<test::minimal::destructible,
test::minimal::hash<test::minimal::destructible>,
test::minimal::equal_to<test::minimal::destructible> >
multiset;
unordered_destructible_test(multiset);
UNORDERED_AUTO_TEST (destructible_tests) {
#ifdef BOOST_UNORDERED_FOA_TESTS
destructible_tests_impl<boost::unordered_flat_set>();
destructible_tests_impl<boost::unordered_node_set>();
#else
destructible_tests_impl<boost::unordered_set>();
destructible_tests_impl<boost::unordered_multiset>();
#endif
}
@@ -410,9 +389,17 @@ bool operator==(lwg2059_key x, lwg2059_key y) { return x.value == y.value; }
UNORDERED_AUTO_TEST (lwg2059) {
#ifdef BOOST_UNORDERED_FOA_TESTS
boost::unordered_flat_set<lwg2059_key> x;
x.emplace(lwg2059_key(10));
x.erase(x.begin());
{
boost::unordered_flat_set<lwg2059_key> x;
x.emplace(lwg2059_key(10));
x.erase(x.begin());
}
{
boost::unordered_node_set<lwg2059_key> x;
x.emplace(lwg2059_key(10));
x.erase(x.begin());
}
#else
{
boost::unordered_set<lwg2059_key> x;
+14 -5
View File
@@ -1,6 +1,6 @@
// Copyright 2006-2010 Daniel James.
// Copyright (C) 2022 Christian Mazakas
// Copyright (C) 2022-2023 Christian Mazakas
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -655,23 +655,27 @@ namespace constructor_tests {
boost::unordered_flat_set<test::object, test::hash, test::equal_to,
test::allocator1<test::object> >* test_set;
boost::unordered_node_set<test::object, test::hash, test::equal_to,
test::allocator1<test::object> >* test_node_set;
boost::unordered_flat_map<test::object, test::object, test::hash,
test::equal_to, test::allocator2<test::object> >* test_map;
boost::unordered_node_map<test::object, test::object, test::hash,
test::equal_to, test::allocator2<test::object> >* test_node_map;
UNORDERED_TEST(constructor_tests1,
((test_map_std_alloc)(test_set)(test_map))(
((test_map_std_alloc)(test_set)(test_node_set)(test_map)(test_node_map))(
(default_generator)(generate_collisions)(limited_range)))
UNORDERED_TEST(constructor_tests2,
((test_set)(test_map))(
((test_set)(test_node_set)(test_map)(test_node_map))(
(default_generator)(generate_collisions)(limited_range)))
UNORDERED_TEST(map_constructor_test,
((test_map_std_alloc)(test_map))(
((test_map_std_alloc)(test_map)(test_node_map))(
(default_generator)(generate_collisions)(limited_range)))
UNORDERED_TEST(no_alloc_default_construct_test,
((test_set)(test_map))(
((test_set)(test_node_set)(test_map)(test_node_map))(
(default_generator)(generate_collisions)(limited_range)))
#else
boost::unordered_map<test::object, test::object, test::hash, test::equal_to,
@@ -709,6 +713,8 @@ namespace constructor_tests {
std::initializer_list<int> init;
#ifdef BOOST_UNORDERED_FOA_TESTS
boost::unordered_flat_set<int> x1 = init;
boost::unordered_node_set<int> x2 = init;
BOOST_TEST(x2.empty());
#else
boost::unordered_set<int> x1 = init;
#endif
@@ -722,6 +728,9 @@ namespace constructor_tests {
UNORDERED_AUTO_TEST (test_initializer_list) {
#ifdef BOOST_UNORDERED_FOA_TESTS
boost::unordered_flat_set<int> x1 = {2, 10, 45, -5};
boost::unordered_node_set<int> x2 = {2, 10, 45, -5};
BOOST_TEST(x2.find(10) != x2.end());
BOOST_TEST(x2.find(46) == x2.end());
#else
boost::unordered_set<int> x1 = {2, 10, 45, -5};
#endif
+35 -1
View File
@@ -1,4 +1,4 @@
// Copyright 2021-2022 Christian Mazakas.
// Copyright 2021-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)
@@ -137,6 +137,24 @@ void test_map()
typedef boost::unordered_flat_map<key, int, hasher, key_equal>
non_transparent_map3;
typedef boost::unordered_node_map<key, int, transparent_hasher,
transparent_key_equal>
transparent_node_map;
typedef boost::unordered_node_map<key, int, transparent_hasher, key_equal>
non_transparent_node_map1;
typedef boost::unordered_node_map<key, int, hasher, transparent_key_equal>
non_transparent_node_map2;
typedef boost::unordered_node_map<key, int, hasher, key_equal>
non_transparent_node_map3;
test_map_transparent_contains<transparent_node_map>();
test_map_non_transparent_contains<non_transparent_node_map1>();
test_map_non_transparent_contains<non_transparent_node_map2>();
test_map_non_transparent_contains<non_transparent_node_map3>();
#else
typedef boost::unordered_map<key, int, transparent_hasher,
transparent_key_equal>
@@ -254,6 +272,22 @@ void test_set()
non_transparent_set2;
typedef boost::unordered_flat_set<key, hasher, key_equal>
non_transparent_set3;
typedef boost::unordered_node_set<key, transparent_hasher,
transparent_key_equal>
transparent_node_set;
typedef boost::unordered_node_set<key, transparent_hasher, key_equal>
non_transparent_node_set1;
typedef boost::unordered_node_set<key, hasher, transparent_key_equal>
non_transparent_node_set2;
typedef boost::unordered_node_set<key, hasher, key_equal>
non_transparent_node_set3;
test_set_transparent_contains<transparent_node_set>();
test_set_non_transparent_contains<non_transparent_node_set1>();
test_set_non_transparent_contains<non_transparent_node_set2>();
test_set_non_transparent_contains<non_transparent_node_set3>();
#else
typedef boost::unordered_set<key, transparent_hasher, transparent_key_equal>
transparent_set;
+73 -12
View File
@@ -1,18 +1,18 @@
// Copyright 2006-2009 Daniel James.
// Copyright (C) 2022 Christian Mazakas
// Copyright (C) 2022-2023 Christian Mazakas
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
#include "../helpers/unordered.hpp"
#include "../helpers/test.hpp"
#include "../objects/test.hpp"
#include "../objects/cxx11_allocator.hpp"
#include "../helpers/random_values.hpp"
#include "../helpers/tracker.hpp"
#include "../helpers/equivalent.hpp"
#include "../helpers/invariants.hpp"
#include "../helpers/random_values.hpp"
#include "../helpers/test.hpp"
#include "../helpers/tracker.hpp"
#include "../objects/cxx11_allocator.hpp"
#include "../objects/test.hpp"
test::seed_t initialize_seed(9063);
@@ -482,31 +482,82 @@ namespace copy_tests {
allocator<std::pair<int const, int> > >*
test_map_trivially_copyable_no_construct;
boost::unordered_node_set<test::object, test::hash, test::equal_to,
test::allocator1<test::object> >* test_node_set;
boost::unordered_node_map<test::object, test::object, test::hash,
test::equal_to, test::allocator1<test::object> >* test_node_map;
boost::unordered_node_set<test::object, test::hash, test::equal_to,
test::cxx11_allocator<test::object, test::select_copy> >*
test_node_set_select_copy;
boost::unordered_node_map<test::object, test::object, test::hash,
test::equal_to, test::cxx11_allocator<test::object, test::select_copy> >*
test_node_map_select_copy;
boost::unordered_node_set<test::object, test::hash, test::equal_to,
test::cxx11_allocator<test::object, test::no_select_copy> >*
test_node_set_no_select_copy;
boost::unordered_node_map<test::object, test::object, test::hash,
test::equal_to, test::cxx11_allocator<test::object, test::no_select_copy> >*
test_node_map_no_select_copy;
boost::unordered_node_set<int, test::hash, test::equal_to,
test::allocator1<int> >* test_node_set_trivially_copyable;
boost::unordered_node_map<int, int, test::hash, test::equal_to,
test::allocator1<std::pair<int const, int> > >*
test_node_map_trivially_copyable;
boost::unordered_node_set<int, test::hash, test::equal_to,
std::allocator<int> >* test_node_set_trivially_copyable_std_allocator;
boost::unordered_node_map<int, int, test::hash, test::equal_to,
std::allocator<std::pair<int const, int> > >*
test_node_map_trivially_copyable_std_allocator;
boost::unordered_node_set<int, test::hash, test::equal_to, allocator<int> >*
test_node_set_trivially_copyable_no_construct;
boost::unordered_node_map<int, int, test::hash, test::equal_to,
allocator<std::pair<int const, int> > >*
test_node_map_trivially_copyable_no_construct;
// clang-format off
UNORDERED_TEST(copy_construct_tests1,
((test_set)(test_map)(test_set_select_copy)(test_map_select_copy)
(test_set_no_select_copy)(test_map_no_select_copy)
(test_set_trivially_copyable)(test_map_trivially_copyable))
(test_set_trivially_copyable)(test_map_trivially_copyable)
(test_node_set)(test_node_map)(test_node_set_select_copy)(test_node_map_select_copy)
(test_node_set_no_select_copy)(test_node_map_no_select_copy)
(test_node_set_trivially_copyable)(test_node_map_trivially_copyable))
((default_generator)(generate_collisions)(limited_range)))
UNORDERED_TEST(copy_construct_tests2,
((test_set)(test_map)(test_set_select_copy)(test_map_select_copy)
(test_set_no_select_copy)(test_map_no_select_copy)
(test_set_trivially_copyable)(test_map_trivially_copyable))
(test_set_trivially_copyable)(test_map_trivially_copyable)
(test_node_set)(test_node_map)(test_node_set_select_copy)(test_node_map_select_copy)
(test_node_set_no_select_copy)(test_node_map_no_select_copy)
(test_node_set_trivially_copyable)(test_node_map_trivially_copyable))
((default_generator)(generate_collisions)(limited_range)))
UNORDERED_TEST(copy_construct_tests_std_allocator1,
((test_set_trivially_copyable_std_allocator)
(test_map_trivially_copyable_std_allocator)
(test_set_trivially_copyable_no_construct)
(test_map_trivially_copyable_no_construct))
(test_map_trivially_copyable_no_construct)
(test_node_set_trivially_copyable_std_allocator)
(test_node_map_trivially_copyable_std_allocator)
(test_node_set_trivially_copyable_no_construct)
(test_node_map_trivially_copyable_no_construct))
((default_generator)(generate_collisions)(limited_range)))
UNORDERED_TEST(copy_construct_tests_std_allocator2,
((test_set_trivially_copyable_std_allocator)
(test_map_trivially_copyable_std_allocator)
(test_set_trivially_copyable_no_construct)
(test_map_trivially_copyable_no_construct))
(test_map_trivially_copyable_no_construct)
(test_node_set_trivially_copyable_std_allocator)
(test_node_map_trivially_copyable_std_allocator)
(test_node_set_trivially_copyable_no_construct)
(test_node_map_trivially_copyable_no_construct))
((default_generator)(generate_collisions)(limited_range)))
// clang-format on
#else
@@ -545,13 +596,23 @@ namespace copy_tests {
test::equal_to, test::cxx11_allocator<test::object, test::no_select_copy> >*
test_multimap_no_select_copy;
// clang-format off
UNORDERED_TEST(copy_construct_tests1,
((test_set)(test_multiset)(test_map)(test_multimap)(test_set_select_copy)(test_multiset_select_copy)(test_map_select_copy)(test_multimap_select_copy)(test_set_no_select_copy)(test_multiset_no_select_copy)(test_map_no_select_copy)(test_multimap_no_select_copy))(
((test_set)(test_multiset)(test_map)(test_multimap)
(test_set_select_copy)(test_multiset_select_copy)
(test_map_select_copy)(test_multimap_select_copy)
(test_set_no_select_copy)(test_multiset_no_select_copy)
(test_map_no_select_copy)(test_multimap_no_select_copy))(
(default_generator)(generate_collisions)(limited_range)))
UNORDERED_TEST(copy_construct_tests2,
((test_set)(test_multiset)(test_map)(test_multimap)(test_set_select_copy)(test_multiset_select_copy)(test_map_select_copy)(test_multimap_select_copy)(test_set_no_select_copy)(test_multiset_no_select_copy)(test_map_no_select_copy)(test_multimap_no_select_copy))(
((test_set)(test_multiset)(test_map)(test_multimap)
(test_set_select_copy)(test_multiset_select_copy)
(test_map_select_copy)(test_multimap_select_copy)
(test_set_no_select_copy)(test_multiset_no_select_copy)
(test_map_no_select_copy)(test_multimap_no_select_copy))(
(default_generator)(generate_collisions)(limited_range)))
// clang-format on
#endif
} // namespace copy_tests
+74 -44
View File
@@ -179,18 +179,13 @@ namespace emplace_tests {
};
UNORDERED_AUTO_TEST (emplace_set) {
template <class X> static void emplace_set(X*)
{
test::check_instances check_;
#ifdef BOOST_UNORDERED_FOA_TESTS
typedef boost::unordered_flat_set<emplace_value,
boost::hash<emplace_value> >
container;
#else
typedef boost::unordered_set<emplace_value, boost::hash<emplace_value> >
container;
#endif
typedef container::iterator iterator;
typedef X container;
typedef typename container::iterator iterator;
typedef std::pair<iterator, bool> return_type;
container x(10);
iterator i1;
@@ -225,7 +220,7 @@ namespace emplace_tests {
BOOST_TEST(r1.first == x.find(v2));
BOOST_TEST_EQ(check_.instances(), 4);
#ifdef BOOST_UNORDERED_FOA_TESTS
BOOST_TEST_EQ(check_.constructions(), 6);
BOOST_TEST_EQ(check_.constructions(), 6);
#else
BOOST_TEST_EQ(check_.constructions(), 4);
#endif
@@ -294,6 +289,21 @@ namespace emplace_tests {
BOOST_TEST(x.count(v4) == 1);
}
#ifdef BOOST_UNORDERED_FOA_TESTS
static boost::unordered_flat_set<emplace_value, boost::hash<emplace_value> >*
test_set;
static boost::unordered_node_set<emplace_value, boost::hash<emplace_value> >*
test_node_set;
UNORDERED_TEST(emplace_set, ((test_set)(test_node_set)))
#else
static boost::unordered_set<emplace_value, boost::hash<emplace_value> >*
test_set;
UNORDERED_TEST(emplace_set, ((test_set)))
#endif
#ifndef BOOST_UNORDERED_FOA_TESTS
UNORDERED_AUTO_TEST (emplace_multiset) {
test::check_instances check_;
@@ -374,18 +384,12 @@ namespace emplace_tests {
}
#endif
UNORDERED_AUTO_TEST (emplace_map) {
template <class X> static void emplace_map(X*)
{
test::check_instances check_;
#ifdef BOOST_UNORDERED_FOA_TESTS
typedef boost::unordered_flat_map<emplace_value, emplace_value,
boost::hash<emplace_value> >
container;
#else
typedef boost::unordered_map<emplace_value, emplace_value,
boost::hash<emplace_value> >
container;
#endif
typedef container::iterator iterator;
typedef X container;
typedef typename container::iterator iterator;
typedef std::pair<iterator, bool> return_type;
container x(10);
return_type r1, r2;
@@ -393,7 +397,9 @@ namespace emplace_tests {
#ifdef BOOST_UNORDERED_FOA_TESTS
// 5/8 args + duplicate
emplace_value k1(5, "", 'b', 4, 5);
BOOST_TEST_EQ(check_.constructions(), 1);
emplace_value m1(8, "xxx", 'z', 4, 5, 6, 7, 8);
BOOST_TEST_EQ(check_.constructions(), 2);
r1 = x.emplace(std::piecewise_construct, std::make_tuple(5, "", 'b', 4, 5),
std::make_tuple(8, "xxx", 'z', 4, 5, 6, 7, 8));
BOOST_TEST_EQ(x.size(), 1u);
@@ -485,18 +491,18 @@ namespace emplace_tests {
BOOST_TEST_EQ(check_.instances(), 8);
BOOST_TEST_EQ(check_.constructions(), 10);
BOOST_TEST(r1.first ==
x.emplace_hint(r1.first, boost::unordered::piecewise_construct,
boost::make_tuple(9, "", 'b', 4, 5, 6, 7, 8, 9),
boost::make_tuple(15, "jkjk")));
BOOST_TEST(r1.first ==
x.emplace_hint(r2.first, boost::unordered::piecewise_construct,
boost::make_tuple(9, "", 'b', 4, 5, 6, 7, 8, 9),
boost::make_tuple(275, "xxx", 'm', 6)));
BOOST_TEST(r1.first ==
x.emplace_hint(x.end(), boost::unordered::piecewise_construct,
boost::make_tuple(9, "", 'b', 4, 5, 6, 7, 8, 9),
boost::make_tuple(-10, "blah blah", '\0')));
BOOST_TEST(r1.first == x.emplace_hint(r1.first,
boost::unordered::piecewise_construct,
boost::make_tuple(9, "", 'b', 4, 5, 6, 7, 8, 9),
boost::make_tuple(15, "jkjk")));
BOOST_TEST(r1.first == x.emplace_hint(r2.first,
boost::unordered::piecewise_construct,
boost::make_tuple(9, "", 'b', 4, 5, 6, 7, 8, 9),
boost::make_tuple(275, "xxx", 'm', 6)));
BOOST_TEST(
r1.first == x.emplace_hint(x.end(), boost::unordered::piecewise_construct,
boost::make_tuple(9, "", 'b', 4, 5, 6, 7, 8, 9),
boost::make_tuple(-10, "blah blah", '\0')));
BOOST_TEST_EQ(x.size(), 2u);
BOOST_TEST(x.find(k2)->second == m2);
BOOST_TEST_EQ(check_.instances(), 8);
@@ -504,6 +510,21 @@ namespace emplace_tests {
#endif
}
#ifdef BOOST_UNORDERED_FOA_TESTS
static boost::unordered_flat_map<emplace_value, emplace_value,
boost::hash<emplace_value> >* test_map;
static boost::unordered_node_map<emplace_value, emplace_value,
boost::hash<emplace_value> >* test_node_map;
UNORDERED_TEST(emplace_map, ((test_map)(test_node_map)))
#else
static boost::unordered_map<emplace_value, emplace_value,
boost::hash<emplace_value> >* test_map;
UNORDERED_TEST(emplace_map, ((test_map)))
#endif
#ifndef BOOST_UNORDERED_FOA_TESTS
UNORDERED_AUTO_TEST (emplace_multimap) {
test::check_instances check_;
@@ -571,14 +592,13 @@ namespace emplace_tests {
}
#endif
UNORDERED_AUTO_TEST (try_emplace) {
template <class X> static void try_emplace(X*)
{
test::check_instances check_;
#ifdef BOOST_UNORDERED_FOA_TESTS
typedef boost::unordered_flat_map<int, emplace_value> container;
#else
typedef boost::unordered_map<int, emplace_value> container;
#endif
typedef container::iterator iterator;
typedef X container;
typedef typename container::iterator iterator;
typedef std::pair<iterator, bool> return_type;
container x(10);
return_type r1, r2, r3;
@@ -615,12 +635,22 @@ namespace emplace_tests {
BOOST_TEST_EQ(check_.constructions(), 4);
BOOST_TEST(r2.first == x.try_emplace(r2.first, k2, 808709, "what"));
BOOST_TEST(
r2.first ==
x.try_emplace(r2.first, k2, 10, "xxx", 'a', 4, 5, 6, 7, 8, 9, 10));
BOOST_TEST(r2.first == x.try_emplace(r2.first, k2, 10, "xxx", 'a', 4, 5, 6,
7, 8, 9, 10));
BOOST_TEST(r2.first->second == m2);
BOOST_TEST_EQ(x.size(), 2u);
}
#ifdef BOOST_UNORDERED_FOA_TESTS
static boost::unordered_flat_map<int, emplace_value>* test_int_map;
static boost::unordered_node_map<int, emplace_value>* test_int_node_map;
UNORDERED_TEST(try_emplace, ((test_int_map)(test_int_node_map)))
#else
static boost::unordered_map<int, emplace_value>* test_int_map;
UNORDERED_TEST(try_emplace, ((test_int_map)))
#endif
}
RUN_TESTS()
+131 -6
View File
@@ -1,14 +1,18 @@
// Copyright 2008-2009 Daniel James.
// Copyright 2022 Christian Mazakas.
// Copyright 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)
#include "../helpers/unordered.hpp"
#include "../helpers/test.hpp"
#include <boost/preprocessor/seq.hpp>
#include <list>
#include "../helpers/test.hpp"
// TODO: this test needs to be someday cleaned up to not be so heavily
// macro-generated
//
namespace equality_tests {
struct mod_compare
@@ -33,6 +37,12 @@ namespace equality_tests {
using boost_unordered_map =
boost::unordered_flat_map<int, int, mod_compare, mod_compare>;
using boost_unordered_node_set =
boost::unordered_node_set<int, mod_compare, mod_compare>;
using boost_unordered_node_map =
boost::unordered_node_map<int, int, mod_compare, mod_compare>;
#define UNORDERED_EQUALITY_MULTISET_TEST(seq1, op, seq2) \
{ \
}
@@ -64,14 +74,46 @@ namespace equality_tests {
}
#endif
#ifdef BOOST_UNORDERED_FOA_TESTS
#define UNORDERED_EQUALITY_SET_TEST(seq1, op, seq2) \
{ \
boost_unordered_set set1, set2; \
boost_unordered_set set1, set2; \
BOOST_PP_SEQ_FOR_EACH(UNORDERED_SET_INSERT, set1, seq1) \
BOOST_PP_SEQ_FOR_EACH(UNORDERED_SET_INSERT, set2, seq2) \
BOOST_TEST(set1 op set2); \
} \
{ \
boost_unordered_node_set set1, set2; \
BOOST_PP_SEQ_FOR_EACH(UNORDERED_SET_INSERT, set1, seq1) \
BOOST_PP_SEQ_FOR_EACH(UNORDERED_SET_INSERT, set2, seq2) \
BOOST_TEST(set1 op set2); \
}
#else
#define UNORDERED_EQUALITY_SET_TEST(seq1, op, seq2) \
{ \
boost_unordered_set set1, set2; \
BOOST_PP_SEQ_FOR_EACH(UNORDERED_SET_INSERT, set1, seq1) \
BOOST_PP_SEQ_FOR_EACH(UNORDERED_SET_INSERT, set2, seq2) \
BOOST_TEST(set1 op set2); \
}
#endif
#ifdef BOOST_UNORDERED_FOA_TESTS
#define UNORDERED_EQUALITY_MAP_TEST(seq1, op, seq2) \
{ \
boost_unordered_map map1, map2; \
BOOST_PP_SEQ_FOR_EACH(UNORDERED_MAP_INSERT, map1, seq1) \
BOOST_PP_SEQ_FOR_EACH(UNORDERED_MAP_INSERT, map2, seq2) \
BOOST_TEST(map1 op map2); \
} \
\
{ \
boost_unordered_node_map map1, map2; \
BOOST_PP_SEQ_FOR_EACH(UNORDERED_MAP_INSERT, map1, seq1) \
BOOST_PP_SEQ_FOR_EACH(UNORDERED_MAP_INSERT, map2, seq2) \
BOOST_TEST(map1 op map2); \
}
#else
#define UNORDERED_EQUALITY_MAP_TEST(seq1, op, seq2) \
{ \
boost_unordered_map map1, map2; \
@@ -79,6 +121,7 @@ namespace equality_tests {
BOOST_PP_SEQ_FOR_EACH(UNORDERED_MAP_INSERT, map2, seq2) \
BOOST_TEST(map1 op map2); \
}
#endif
#define UNORDERED_SET_INSERT(r, set, item) set.insert(item);
#define UNORDERED_MAP_INSERT(r, map, item) \
@@ -86,10 +129,51 @@ namespace equality_tests {
UNORDERED_AUTO_TEST (equality_size_tests) {
#ifdef BOOST_UNORDERED_FOA_TESTS
boost::unordered_flat_set<int> x1, x2;
{
boost::unordered_flat_set<int> x1, x2;
BOOST_TEST(x1 == x2);
BOOST_TEST(!(x1 != x2));
x1.insert(1);
BOOST_TEST(x1 != x2);
BOOST_TEST(!(x1 == x2));
BOOST_TEST(x2 != x1);
BOOST_TEST(!(x2 == x1));
x2.insert(1);
BOOST_TEST(x1 == x2);
BOOST_TEST(!(x1 != x2));
x2.insert(2);
BOOST_TEST(x1 != x2);
BOOST_TEST(!(x1 == x2));
BOOST_TEST(x2 != x1);
BOOST_TEST(!(x2 == x1));
}
{
boost::unordered_node_set<int> x1, x2;
BOOST_TEST(x1 == x2);
BOOST_TEST(!(x1 != x2));
x1.insert(1);
BOOST_TEST(x1 != x2);
BOOST_TEST(!(x1 == x2));
BOOST_TEST(x2 != x1);
BOOST_TEST(!(x2 == x1));
x2.insert(1);
BOOST_TEST(x1 == x2);
BOOST_TEST(!(x1 != x2));
x2.insert(2);
BOOST_TEST(x1 != x2);
BOOST_TEST(!(x1 == x2));
BOOST_TEST(x2 != x1);
BOOST_TEST(!(x2 == x1));
}
#else
boost::unordered_set<int> x1, x2;
#endif
BOOST_TEST(x1 == x2);
BOOST_TEST(!(x1 != x2));
@@ -108,6 +192,7 @@ namespace equality_tests {
BOOST_TEST(!(x1 == x2));
BOOST_TEST(x2 != x1);
BOOST_TEST(!(x2 == x1));
#endif
}
UNORDERED_AUTO_TEST (equality_key_value_tests) {
@@ -156,6 +241,45 @@ namespace equality_tests {
// different hash functions but the same equality predicate.
UNORDERED_AUTO_TEST (equality_different_hash_test) {
#ifdef BOOST_UNORDERED_FOA_TESTS
{
typedef boost_unordered_set set;
set set1(0, mod_compare(false), mod_compare(false));
set set2(0, mod_compare(true), mod_compare(true));
BOOST_TEST(set1 == set2);
set1.insert(1);
set2.insert(2);
BOOST_TEST(set1 != set2);
set1.insert(2);
set2.insert(1);
BOOST_TEST(set1 == set2);
set1.insert(10);
set2.insert(20);
BOOST_TEST(set1 != set2);
set1.insert(20);
set2.insert(10);
BOOST_TEST(set1 == set2);
}
{
typedef boost_unordered_node_set set;
set set1(0, mod_compare(false), mod_compare(false));
set set2(0, mod_compare(true), mod_compare(true));
BOOST_TEST(set1 == set2);
set1.insert(1);
set2.insert(2);
BOOST_TEST(set1 != set2);
set1.insert(2);
set2.insert(1);
BOOST_TEST(set1 == set2);
set1.insert(10);
set2.insert(20);
BOOST_TEST(set1 != set2);
set1.insert(20);
set2.insert(10);
BOOST_TEST(set1 == set2);
}
#else
typedef boost_unordered_set set;
set set1(0, mod_compare(false), mod_compare(false));
set set2(0, mod_compare(true), mod_compare(true));
@@ -172,7 +296,8 @@ namespace equality_tests {
set1.insert(20);
set2.insert(10);
BOOST_TEST(set1 == set2);
#endif
}
}
} // namespace equality_tests
RUN_TESTS()
+22 -3
View File
@@ -1,6 +1,6 @@
// Copyright 2006-2009 Daniel James.
// Copyright 2022 Christian Mazakas.
// Copyright 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)
@@ -36,9 +36,21 @@ UNORDERED_AUTO_TEST (set_tests) {
#ifdef BOOST_UNORDERED_FOA_TESTS
typedef boost::unordered_flat_set<int> set;
test_equal_insertion<set>(values[0], values[0] + 1);
test_equal_insertion<set>(values[1], values[1] + 2);
test_equal_insertion<set>(values[2], values[2] + 2);
test_equal_insertion<set>(values[3], values[3] + 2);
test_equal_insertion<set>(values[4], values[4] + 3);
typedef boost::unordered_node_set<int> node_set;
test_equal_insertion<node_set>(values[0], values[0] + 1);
test_equal_insertion<node_set>(values[1], values[1] + 2);
test_equal_insertion<node_set>(values[2], values[2] + 2);
test_equal_insertion<node_set>(values[3], values[3] + 2);
test_equal_insertion<node_set>(values[4], values[4] + 3);
#else
typedef boost::unordered_set<int> set;
#endif
test_equal_insertion<set>(values[0], values[0] + 1);
test_equal_insertion<set>(values[1], values[1] + 2);
@@ -52,6 +64,8 @@ UNORDERED_AUTO_TEST (set_tests) {
test_equal_insertion<multiset>(values[2], values[2] + 2);
test_equal_insertion<multiset>(values[3], values[3] + 2);
test_equal_insertion<multiset>(values[4], values[4] + 3);
#endif
}
UNORDERED_AUTO_TEST (map_tests) {
@@ -65,11 +79,16 @@ UNORDERED_AUTO_TEST (map_tests) {
v[2].push_back(std::pair<int const, int>(9, 13));
v[2].push_back(std::pair<int const, int>(432, 24));
for (int i = 0; i < 5; ++i)
#ifdef BOOST_UNORDERED_FOA_TESTS
for (int i = 0; i < 5; ++i)
test_equal_insertion<boost::unordered_flat_map<int, int> >(
v[i].begin(), v[i].end());
for (int i2 = 0; i2 < 5; ++i2)
test_equal_insertion<boost::unordered_node_map<int, int> >(
v[i2].begin(), v[i2].end());
#else
for (int i = 0; i < 5; ++i)
test_equal_insertion<boost::unordered_map<int, int> >(
v[i].begin(), v[i].end());
+2
View File
@@ -110,6 +110,8 @@ UNORDERED_AUTO_TEST (unordered_erase_if) {
#ifdef BOOST_UNORDERED_FOA_TESTS
test_map_erase_if<boost::unordered_flat_map<std::string, int> >();
test_set_erase_if<boost::unordered_flat_set<int> >();
test_map_erase_if<boost::unordered_node_map<std::string, int> >();
test_set_erase_if<boost::unordered_node_set<int> >();
#else
test_map_erase_if<boost::unordered_map<std::string, int> >();
test_map_erase_if<boost::unordered_multimap<std::string, int> >();
+9 -2
View File
@@ -270,10 +270,16 @@ namespace erase_tests {
test::allocator1<test::object> >* test_set;
boost::unordered_flat_map<test::object, test::object, test::hash,
test::equal_to, test::allocator1<test::object> >* test_map;
boost::unordered_node_set<test::object, test::hash, test::equal_to,
test::allocator1<test::object> >* test_node_set;
boost::unordered_node_map<test::object, test::object, test::hash,
test::equal_to, test::allocator1<test::object> >* test_node_map;
// clang-format off
UNORDERED_TEST(
erase_tests1, ((test_set)(test_map))(
erase_tests1, ((test_set)(test_map)(test_node_set)(test_node_map))(
(default_generator)(generate_collisions)(limited_range)))
// clang-format on
#else
boost::unordered_set<test::object, test::hash, test::equal_to,
test::allocator1<test::object> >* test_set;
@@ -284,10 +290,11 @@ namespace erase_tests {
boost::unordered_multimap<test::object, test::object, test::hash,
test::equal_to, test::allocator2<test::object> >* test_multimap;
// clang-format off
UNORDERED_TEST(
erase_tests1, ((test_set)(test_multiset)(test_map)(test_multimap))(
(default_generator)(generate_collisions)(limited_range)))
// clang-format on
#endif
}

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