Compare commits
107 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 0c93fbc330 | |||
| b8ff76f4ab | |||
| 5df80086ed | |||
| f9c32e7f8c | |||
| 21e673c697 | |||
| b96dd2184f | |||
| 9636875596 | |||
| bd6829220c | |||
| 71bf336c9d | |||
| b7c1e6a184 | |||
| 77edd24b4e | |||
| b7032baaf4 | |||
| 6580dc8d80 | |||
| 5384edd568 | |||
| 2a7a8f55e1 | |||
| 18a664c4a6 | |||
| e1b394d5b2 | |||
| dc548a1a9e | |||
| 2787717d78 | |||
| d6ba04ca4b | |||
| 43add11970 | |||
| 8e45197d4d | |||
| a46220986f | |||
| 16099478db | |||
| e5320df017 | |||
| b9fcfeb24e | |||
| f1aeb3b099 | |||
| 5b706bb7b5 | |||
| fd7d888832 | |||
| 827c69eb1a | |||
| 9973b3f8a8 | |||
| e6f911e815 | |||
| 53e4531295 | |||
| 8da44f4f46 | |||
| dddfbf8aa8 | |||
| a66eedef58 | |||
| 202049ddc0 | |||
| 000833d647 | |||
| d84e6f7be2 | |||
| 8acfccdf6e | |||
| c85dd289f9 | |||
| 8ac6019d30 | |||
| 9602799710 | |||
| 217a171b33 | |||
| b372ab6641 | |||
| 140cb56e2b | |||
| 8b842c4640 | |||
| 9b2ecd6ec5 | |||
| 351e5ae41a | |||
| 1c5733ed15 | |||
| cd44e0ba08 | |||
| 3363261ad8 | |||
| 58b8b00233 | |||
| 6474f3c8d3 | |||
| 63269d2115 | |||
| 0925a99d98 | |||
| 1b23d08941 | |||
| 28142b6251 | |||
| b60f2ce22c | |||
| 473ed9b88b | |||
| c554c5b1c9 | |||
| e244b35d23 | |||
| f7bfb85d84 | |||
| 5e0257cb08 | |||
| 64721fa1ef | |||
| 5bfc77c3e4 | |||
| aaf0e40247 | |||
| 8e86a263fc | |||
| a5a5b75dcc | |||
| b57b51b036 | |||
| 91b3863c77 | |||
| b4a23a875c | |||
| f0afa5aaef | |||
| 7ce7ef5050 | |||
| a3d4a078de | |||
| 1e61423eac | |||
| 904e806b1e | |||
| 3201a014c4 | |||
| 1a87580c58 | |||
| ab58b613c9 | |||
| bb57d91675 | |||
| 7d1e64a3c6 | |||
| ff956ad080 | |||
| d9bf1e435c | |||
| eb6d0d1f66 | |||
| bce898165c | |||
| e3c66a5e82 | |||
| b167ab7d84 | |||
| 71fd0c07ed | |||
| 130b483123 | |||
| 03ee5d135b | |||
| 72841102f6 | |||
| cc6748e2b1 | |||
| f99decc0ca | |||
| ae10a1e0df | |||
| b1e497cd1d | |||
| cbe19722a3 | |||
| dcff2ac5b5 | |||
| cb4e636d78 | |||
| 6e41418744 | |||
| 1d8d065113 | |||
| a858517c49 | |||
| 2cdcbd24f5 | |||
| 721d8713cd | |||
| 62e1d3eafb | |||
| 3744bafa55 | |||
| f034e43b74 |
@@ -6,7 +6,7 @@ local library = "unordered";
|
||||
|
||||
local triggers =
|
||||
{
|
||||
branch: [ "master", "develop", "feature/*", "bugfix/*", "fix/*", "pr/*" ]
|
||||
branch: [ "master", "develop", "feature/*", "bugfix/*" ]
|
||||
};
|
||||
|
||||
local ubsan = { UBSAN: '1', UBSAN_OPTIONS: 'print_stacktrace=1' };
|
||||
|
||||
@@ -75,7 +75,7 @@ jobs:
|
||||
- { compiler: clang, cxxstd: '03,11,14,17,2a', os: macos-11, }
|
||||
- { compiler: clang, cxxstd: '03,11,14,17,2a', os: macos-12, sanitize: yes }
|
||||
|
||||
timeout-minutes: 180
|
||||
timeout-minutes: 120
|
||||
runs-on: ${{matrix.os}}
|
||||
container: ${{matrix.container}}
|
||||
env: {B2_USE_CCACHE: 1}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
// 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
|
||||
|
||||
@@ -7,7 +6,6 @@
|
||||
#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>
|
||||
@@ -274,9 +272,6 @@ 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>>;
|
||||
|
||||
@@ -350,9 +345,6 @@ 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>>;
|
||||
|
||||
@@ -381,7 +373,6 @@ 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
|
||||
@@ -399,7 +390,6 @@ 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
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
// 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
|
||||
|
||||
@@ -7,7 +6,6 @@
|
||||
#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>
|
||||
@@ -275,9 +273,6 @@ 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>>;
|
||||
|
||||
@@ -351,9 +346,6 @@ 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>>;
|
||||
|
||||
@@ -382,7 +374,6 @@ 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
|
||||
@@ -400,7 +391,6 @@ 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
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
// 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
|
||||
|
||||
@@ -7,7 +6,6 @@
|
||||
#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>
|
||||
@@ -291,9 +289,6 @@ 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>>;
|
||||
|
||||
@@ -320,7 +315,6 @@ 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
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
// 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
|
||||
|
||||
@@ -7,7 +6,6 @@
|
||||
#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>
|
||||
@@ -291,9 +289,6 @@ 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>>;
|
||||
|
||||
@@ -330,7 +325,6 @@ 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
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
// 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
|
||||
|
||||
@@ -7,7 +6,6 @@
|
||||
#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>
|
||||
@@ -342,9 +340,6 @@ 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>>;
|
||||
|
||||
@@ -371,7 +366,6 @@ 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
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
// 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
|
||||
|
||||
@@ -7,7 +6,6 @@
|
||||
#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
|
||||
@@ -229,9 +227,6 @@ 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>>;
|
||||
|
||||
@@ -305,9 +300,6 @@ 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>>;
|
||||
|
||||
@@ -336,7 +328,6 @@ 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
|
||||
@@ -354,7 +345,6 @@ 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
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
// 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
|
||||
|
||||
@@ -7,7 +6,6 @@
|
||||
#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
|
||||
@@ -183,9 +181,6 @@ 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>>;
|
||||
|
||||
@@ -212,7 +207,6 @@ 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
|
||||
|
||||
|
Before Width: | Height: | Size: 46 KiB After Width: | Height: | Size: 36 KiB |
|
Before Width: | Height: | Size: 45 KiB After Width: | Height: | Size: 37 KiB |
|
Before Width: | Height: | Size: 46 KiB After Width: | Height: | Size: 40 KiB |
|
Before Width: | Height: | Size: 62 KiB After Width: | Height: | Size: 49 KiB |
|
Before Width: | Height: | Size: 51 KiB After Width: | Height: | Size: 37 KiB |
|
Before Width: | Height: | Size: 46 KiB After Width: | Height: | Size: 35 KiB |
|
Before Width: | Height: | Size: 44 KiB After Width: | Height: | Size: 40 KiB |
|
Before Width: | Height: | Size: 56 KiB After Width: | Height: | Size: 45 KiB |
|
Before Width: | Height: | Size: 51 KiB After Width: | Height: | Size: 38 KiB |
|
Before Width: | Height: | Size: 54 KiB After Width: | Height: | Size: 41 KiB |
|
Before Width: | Height: | Size: 45 KiB After Width: | Height: | Size: 40 KiB |
|
Before Width: | Height: | Size: 53 KiB After Width: | Height: | Size: 43 KiB |
|
Before Width: | Height: | Size: 52 KiB After Width: | Height: | Size: 37 KiB |
|
Before Width: | Height: | Size: 46 KiB After Width: | Height: | Size: 36 KiB |
|
Before Width: | Height: | Size: 44 KiB After Width: | Height: | Size: 39 KiB |
|
Before Width: | Height: | Size: 56 KiB After Width: | Height: | Size: 44 KiB |
|
Before Width: | Height: | Size: 52 KiB After Width: | Height: | Size: 37 KiB |
|
Before Width: | Height: | Size: 52 KiB After Width: | Height: | Size: 40 KiB |
|
Before Width: | Height: | Size: 48 KiB After Width: | Height: | Size: 41 KiB |
|
Before Width: | Height: | Size: 55 KiB After Width: | Height: | Size: 44 KiB |
|
Before Width: | Height: | Size: 46 KiB After Width: | Height: | Size: 37 KiB |
|
Before Width: | Height: | Size: 52 KiB After Width: | Height: | Size: 38 KiB |
|
Before Width: | Height: | Size: 51 KiB After Width: | Height: | Size: 44 KiB |
|
Before Width: | Height: | Size: 52 KiB After Width: | Height: | Size: 40 KiB |
|
Before Width: | Height: | Size: 47 KiB After Width: | Height: | Size: 37 KiB |
|
Before Width: | Height: | Size: 51 KiB After Width: | Height: | Size: 38 KiB |
|
Before Width: | Height: | Size: 45 KiB After Width: | Height: | Size: 41 KiB |
|
Before Width: | Height: | Size: 51 KiB After Width: | Height: | Size: 43 KiB |
@@ -278,14 +278,13 @@ max load factor 5
|
||||
|
||||
|===
|
||||
|
||||
== boost::unordered_(flat|node)_map
|
||||
== boost::unordered_flat_map
|
||||
|
||||
All benchmarks were created using:
|
||||
|
||||
* `https://abseil.io/docs/cpp/guides/container[absl::flat_hash_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>`
|
||||
* `boost::unordered_map<uint64_t, uint64_t>`
|
||||
|
||||
The source code can be https://github.com/boostorg/boost_unordered_benchmarks/tree/boost_unordered_flat_map[found here^].
|
||||
|
||||
|
||||
@@ -134,8 +134,7 @@ 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_node_set`, `boost::unordered_node_map` +
|
||||
`boost::unordered_flat_set`, `boost::unordered_flat_map`
|
||||
h|*Method* h|*Description*
|
||||
|
||||
|`size_type max_load() const`
|
||||
@@ -161,9 +160,8 @@ 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 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
|
||||
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
|
||||
`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.
|
||||
@@ -254,16 +252,15 @@ xref:#rationale_boostunordered_multiset_and_boostunordered_multimap[correspondin
|
||||
|
||||
== Open Addressing Implementation
|
||||
|
||||
The diagram shows the basic internal layout of `boost::unordered_flat_map`/`unordered_node_map` and
|
||||
`boost:unordered_flat_set`/`unordered_node_set`.
|
||||
The diagram shows the basic internal layout of `boost::unordered_flat_map` and
|
||||
`boost:unordered_flat_set`.
|
||||
|
||||
|
||||
[#img-foa-layout]
|
||||
.Open-addressing layout used by Boost.Unordered.
|
||||
image::foa.png[align=center]
|
||||
|
||||
As with all open-addressing containers, elements (or pointers to the element nodes in the case of
|
||||
`boost::unordered_node_map` and `boost::unordered_node_set`) are stored directly in the bucket array.
|
||||
As with all open-addressing containers, elements 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.
|
||||
|
||||
@@ -6,23 +6,10 @@
|
||||
:github-pr-url: https://github.com/boostorg/unordered/pull
|
||||
:cpp: C++
|
||||
|
||||
== Release 1.83.0
|
||||
== Release 1.82.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/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.
|
||||
https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2022/p2363r3.html[P2363].
|
||||
|
||||
== Release 1.81.0 - Major update
|
||||
|
||||
|
||||
@@ -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>>. +
|
||||
**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.
|
||||
**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.
|
||||
|
||||
|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.
|
||||
|
||||
@@ -5,9 +5,9 @@
|
||||
|
||||
:cpp: C++
|
||||
|
||||
== Closed-addressing containers
|
||||
== Closed-addressing containers: unordered_[multi]set, unordered_[multi]map
|
||||
|
||||
`unordered_[multi]set` and `unordered_[multi]map` are intended to provide a conformant
|
||||
The intent of Boost.Unordered is 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,31 +117,27 @@ 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
|
||||
== Open-addressing containers: unordered_flat_set, unordered_flat_map
|
||||
|
||||
The C++ standard does not currently provide any open-addressing container
|
||||
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
|
||||
specification to adhere to, so `boost::unordered_flat_set` and
|
||||
`boost::unordered_flat_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).
|
||||
radically different from that imposed by the standard (closed addressing, node based).
|
||||
|
||||
Open-addressing containers provided by Boost.Unordered only work with reasonably
|
||||
`unordered_flat_set` and `unordered_flat_map` only work with reasonably
|
||||
compliant C++11 (or later) compilers. Language-level features such as move semantics
|
||||
and variadic template parameters are then not emulated.
|
||||
The containers are fully https://en.cppreference.com/w/cpp/named_req/AllocatorAwareContainer[AllocatorAware^].
|
||||
`unordered_flat_set` and `unordered_flat_map` are fully https://en.cppreference.com/w/cpp/named_req/AllocatorAwareContainer[AllocatorAware^].
|
||||
|
||||
The main differences with C++ unordered associative containers are:
|
||||
|
||||
* 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.
|
||||
* `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.
|
||||
|
||||
//-
|
||||
|
||||
@@ -9,10 +9,10 @@ Copyright (C) 2003, 2004 Jeremy B. Maitin-Shepard
|
||||
|
||||
Copyright (C) 2005-2008 Daniel James
|
||||
|
||||
Copyright (C) 2022-2023 Christian Mazakas
|
||||
Copyright (C) 2022 Christian Mazakas
|
||||
|
||||
Copyright (C) 2022-2023 Joaquín M López Muñoz
|
||||
Copyright (C) 2022 Joaquín M López Muñoz
|
||||
|
||||
Copyright (C) 2022-2023 Peter Dimov
|
||||
Copyright (C) 2022 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)
|
||||
|
||||
@@ -106,69 +106,8 @@ namespace boost {
|
||||
}
|
||||
----
|
||||
|
||||
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_flat_set` and `boost::unordered_flat_map` require a
|
||||
reasonably compliant C++11 compiler.
|
||||
|
||||
Boost.Unordered containers are used in a similar manner to the normal associative
|
||||
containers:
|
||||
|
||||
@@ -4,10 +4,9 @@
|
||||
|
||||
= Implementation Rationale
|
||||
|
||||
== Closed-addressing containers
|
||||
== boost::unordered_[multi]set and boost::unordered_[multi]map
|
||||
|
||||
`boost::unordered_[multi]set` and `boost::unordered_[multi]map`
|
||||
adhere to the standard requirements for unordered associative
|
||||
These containers 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.
|
||||
@@ -65,8 +64,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 `2^w` divided
|
||||
by the golden ratio, `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 the golden
|
||||
ratio multiplied by `2^w`, `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.
|
||||
|
||||
@@ -74,7 +73,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.
|
||||
|
||||
== Open-addresing containers
|
||||
== boost::unordered_flat_set and boost::unordered_flat_map
|
||||
|
||||
The C++ standard specification of unordered associative containers impose
|
||||
severe limitations on permissible implementations, the most important being
|
||||
@@ -82,32 +81,29 @@ 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`/`unordered_node_set` and `boost::unordered_flat_map`/`unordered_node_map` has been
|
||||
The design of `boost::unordered_flat_set` and `boost::unordered_flat_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 open-addressing containers.
|
||||
`boost::hash` remains the default hash function of `boost::unordered_flat_set` and `boost::unordered_flat_map`.
|
||||
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:
|
||||
|
||||
{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^∕https://en.wikipedia.org/wiki/Golden_ratio[_φ_],
|
||||
whereas in 32 bits _C_ = 0xE817FB2Du has been obtained from https://arxiv.org/abs/2001.05304[Steele and Vigna (2021)^].
|
||||
* 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.
|
||||
|
||||
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`/`unordered_node_set` and `boost::unordered_flat_map`/`unordered_node_map` is deterministically
|
||||
The observable behavior of `boost::unordered_flat_set` and `boost::unordered_flat_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
|
||||
—this includes elements being ordered in exactly the same way for the same sequence of
|
||||
|
||||
@@ -8,5 +8,3 @@ 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[]
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
/* 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)
|
||||
@@ -12,6 +11,8 @@
|
||||
#ifndef BOOST_UNORDERED_DETAIL_FOA_HPP
|
||||
#define BOOST_UNORDERED_DETAIL_FOA_HPP
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include <boost/assert.hpp>
|
||||
#include <boost/config.hpp>
|
||||
#include <boost/config/workaround.hpp>
|
||||
@@ -39,24 +40,12 @@
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
#if !defined(BOOST_UNORDERED_DISABLE_SSE2)
|
||||
#if defined(BOOST_UNORDERED_ENABLE_SSE2)|| \
|
||||
defined(__SSE2__)|| \
|
||||
#if defined(__SSE2__)||\
|
||||
defined(_M_X64)||(defined(_M_IX86_FP)&&_M_IX86_FP>=2)
|
||||
#define BOOST_UNORDERED_SSE2
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if !defined(BOOST_UNORDERED_DISABLE_NEON)
|
||||
#if defined(BOOST_UNORDERED_ENABLE_NEON)||\
|
||||
(defined(__ARM_NEON)&&!defined(__ARM_BIG_ENDIAN))
|
||||
#define BOOST_UNORDERED_LITTLE_ENDIAN_NEON
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if defined(BOOST_UNORDERED_SSE2)
|
||||
#include <emmintrin.h>
|
||||
#elif defined(BOOST_UNORDERED_LITTLE_ENDIAN_NEON)
|
||||
#elif defined(__ARM_NEON)&&!defined(__ARM_BIG_ENDIAN)
|
||||
#define BOOST_UNORDERED_LITTLE_ENDIAN_NEON
|
||||
#include <arm_neon.h>
|
||||
#endif
|
||||
|
||||
@@ -161,7 +150,6 @@ static const std::size_t default_bucket_count = 0;
|
||||
struct group15
|
||||
{
|
||||
static constexpr int N=15;
|
||||
static constexpr bool regular_layout=true;
|
||||
|
||||
struct dummy_group_type
|
||||
{
|
||||
@@ -187,11 +175,6 @@ struct group15
|
||||
return at(pos)==sentinel_;
|
||||
}
|
||||
|
||||
static inline bool is_sentinel(unsigned char* pc)noexcept
|
||||
{
|
||||
return *pc==sentinel_;
|
||||
}
|
||||
|
||||
inline void reset(std::size_t pos)
|
||||
{
|
||||
BOOST_ASSERT(pos<N);
|
||||
@@ -238,11 +221,6 @@ struct group15
|
||||
_mm_cmpeq_epi8(m,_mm_setzero_si128()))&0x7FFF;
|
||||
}
|
||||
|
||||
static inline bool is_occupied(unsigned char* pc)noexcept
|
||||
{
|
||||
return *pc!=available_;
|
||||
}
|
||||
|
||||
inline int match_occupied()const
|
||||
{
|
||||
return (~match_available())&0x7FFF;
|
||||
@@ -326,179 +304,11 @@ private:
|
||||
alignas(16) __m128i m;
|
||||
};
|
||||
|
||||
struct group14
|
||||
{
|
||||
static constexpr int N=14;
|
||||
static constexpr bool regular_layout=true;
|
||||
|
||||
struct dummy_group_type
|
||||
{
|
||||
alignas(16) unsigned char storage[N+2]={0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0};
|
||||
};
|
||||
|
||||
inline void initialize(){m=_mm_setzero_si128();}
|
||||
|
||||
inline void set(std::size_t pos,std::size_t hash)
|
||||
{
|
||||
BOOST_ASSERT(pos<N);
|
||||
at(pos)=reduced_hash(hash);
|
||||
}
|
||||
|
||||
inline void set_sentinel()
|
||||
{
|
||||
at(N-1)=sentinel_;
|
||||
}
|
||||
|
||||
inline bool is_sentinel(std::size_t pos)const
|
||||
{
|
||||
BOOST_ASSERT(pos<N);
|
||||
return at(pos)==sentinel_;
|
||||
}
|
||||
|
||||
static inline bool is_sentinel(unsigned char* pc)noexcept
|
||||
{
|
||||
return *pc==sentinel_;
|
||||
}
|
||||
|
||||
inline void reset(std::size_t pos)
|
||||
{
|
||||
BOOST_ASSERT(pos<N);
|
||||
at(pos)=available_;
|
||||
}
|
||||
|
||||
static inline void reset(unsigned char* pc)
|
||||
{
|
||||
*pc=available_;
|
||||
}
|
||||
|
||||
inline int match(std::size_t hash)const
|
||||
{
|
||||
return _mm_movemask_epi8(
|
||||
_mm_cmpeq_epi8(m,_mm_set1_epi32(match_word(hash))))&0x3FFF;
|
||||
}
|
||||
|
||||
inline bool is_not_overflowed(std::size_t hash)const
|
||||
{
|
||||
static constexpr unsigned shift[]={
|
||||
0x0001,0x0002,0x0004,0x0008,0x0010,0x0020,0x0040,0x0080,
|
||||
0x0100,0x0200,0x0400,0x0800,0x1000,0x2000,0x4000,0x8000
|
||||
};
|
||||
|
||||
return !(overflow()&shift[hash%16]);
|
||||
}
|
||||
|
||||
inline void mark_overflow(std::size_t hash)
|
||||
{
|
||||
overflow()|=static_cast<boost::uint16_t>(1<<(hash%16));
|
||||
}
|
||||
|
||||
static inline bool maybe_caused_overflow(unsigned char* pc)
|
||||
{
|
||||
std::size_t pos=reinterpret_cast<uintptr_t>(pc)%sizeof(group14);
|
||||
auto *pg=reinterpret_cast<group14*>(pc-pos);
|
||||
return !pg->is_not_overflowed(*pc);
|
||||
};
|
||||
|
||||
inline int match_available()const
|
||||
{
|
||||
return _mm_movemask_epi8(
|
||||
_mm_cmpeq_epi8(m,_mm_setzero_si128()))&0x3FFF;
|
||||
}
|
||||
|
||||
static inline bool is_occupied(unsigned char* pc)noexcept
|
||||
{
|
||||
return *pc!=available_;
|
||||
}
|
||||
|
||||
inline int match_occupied()const
|
||||
{
|
||||
return (~match_available())&0x3FFF;
|
||||
}
|
||||
|
||||
inline int match_really_occupied()const /* excluding sentinel */
|
||||
{
|
||||
return at(N-1)==sentinel_?match_occupied()&0x1FFF:match_occupied();
|
||||
}
|
||||
|
||||
private:
|
||||
static constexpr unsigned char available_=0,
|
||||
sentinel_=1;
|
||||
|
||||
inline static int match_word(std::size_t hash)
|
||||
{
|
||||
static constexpr boost::uint32_t word[]=
|
||||
{
|
||||
0x10101010u,0x11111111u,0x02020202u,0x03030303u,0x04040404u,0x05050505u,0x06060606u,0x07070707u,
|
||||
0x08080808u,0x09090909u,0x0A0A0A0Au,0x0B0B0B0Bu,0x0C0C0C0Cu,0x0D0D0D0Du,0x0E0E0E0Eu,0x0F0F0F0Fu,
|
||||
0x10101010u,0x11111111u,0x12121212u,0x13131313u,0x14141414u,0x15151515u,0x16161616u,0x17171717u,
|
||||
0x18181818u,0x19191919u,0x1A1A1A1Au,0x1B1B1B1Bu,0x1C1C1C1Cu,0x1D1D1D1Du,0x1E1E1E1Eu,0x1F1F1F1Fu,
|
||||
0x20202020u,0x21212121u,0x22222222u,0x23232323u,0x24242424u,0x25252525u,0x26262626u,0x27272727u,
|
||||
0x28282828u,0x29292929u,0x2A2A2A2Au,0x2B2B2B2Bu,0x2C2C2C2Cu,0x2D2D2D2Du,0x2E2E2E2Eu,0x2F2F2F2Fu,
|
||||
0x30303030u,0x31313131u,0x32323232u,0x33333333u,0x34343434u,0x35353535u,0x36363636u,0x37373737u,
|
||||
0x38383838u,0x39393939u,0x3A3A3A3Au,0x3B3B3B3Bu,0x3C3C3C3Cu,0x3D3D3D3Du,0x3E3E3E3Eu,0x3F3F3F3Fu,
|
||||
0x40404040u,0x41414141u,0x42424242u,0x43434343u,0x44444444u,0x45454545u,0x46464646u,0x47474747u,
|
||||
0x48484848u,0x49494949u,0x4A4A4A4Au,0x4B4B4B4Bu,0x4C4C4C4Cu,0x4D4D4D4Du,0x4E4E4E4Eu,0x4F4F4F4Fu,
|
||||
0x50505050u,0x51515151u,0x52525252u,0x53535353u,0x54545454u,0x55555555u,0x56565656u,0x57575757u,
|
||||
0x58585858u,0x59595959u,0x5A5A5A5Au,0x5B5B5B5Bu,0x5C5C5C5Cu,0x5D5D5D5Du,0x5E5E5E5Eu,0x5F5F5F5Fu,
|
||||
0x60606060u,0x61616161u,0x62626262u,0x63636363u,0x64646464u,0x65656565u,0x66666666u,0x67676767u,
|
||||
0x68686868u,0x69696969u,0x6A6A6A6Au,0x6B6B6B6Bu,0x6C6C6C6Cu,0x6D6D6D6Du,0x6E6E6E6Eu,0x6F6F6F6Fu,
|
||||
0x70707070u,0x71717171u,0x72727272u,0x73737373u,0x74747474u,0x75757575u,0x76767676u,0x77777777u,
|
||||
0x78787878u,0x79797979u,0x7A7A7A7Au,0x7B7B7B7Bu,0x7C7C7C7Cu,0x7D7D7D7Du,0x7E7E7E7Eu,0x7F7F7F7Fu,
|
||||
0x80808080u,0x81818181u,0x82828282u,0x83838383u,0x84848484u,0x85858585u,0x86868686u,0x87878787u,
|
||||
0x88888888u,0x89898989u,0x8A8A8A8Au,0x8B8B8B8Bu,0x8C8C8C8Cu,0x8D8D8D8Du,0x8E8E8E8Eu,0x8F8F8F8Fu,
|
||||
0x90909090u,0x91919191u,0x92929292u,0x93939393u,0x94949494u,0x95959595u,0x96969696u,0x97979797u,
|
||||
0x98989898u,0x99999999u,0x9A9A9A9Au,0x9B9B9B9Bu,0x9C9C9C9Cu,0x9D9D9D9Du,0x9E9E9E9Eu,0x9F9F9F9Fu,
|
||||
0xA0A0A0A0u,0xA1A1A1A1u,0xA2A2A2A2u,0xA3A3A3A3u,0xA4A4A4A4u,0xA5A5A5A5u,0xA6A6A6A6u,0xA7A7A7A7u,
|
||||
0xA8A8A8A8u,0xA9A9A9A9u,0xAAAAAAAAu,0xABABABABu,0xACACACACu,0xADADADADu,0xAEAEAEAEu,0xAFAFAFAFu,
|
||||
0xB0B0B0B0u,0xB1B1B1B1u,0xB2B2B2B2u,0xB3B3B3B3u,0xB4B4B4B4u,0xB5B5B5B5u,0xB6B6B6B6u,0xB7B7B7B7u,
|
||||
0xB8B8B8B8u,0xB9B9B9B9u,0xBABABABAu,0xBBBBBBBBu,0xBCBCBCBCu,0xBDBDBDBDu,0xBEBEBEBEu,0xBFBFBFBFu,
|
||||
0xC0C0C0C0u,0xC1C1C1C1u,0xC2C2C2C2u,0xC3C3C3C3u,0xC4C4C4C4u,0xC5C5C5C5u,0xC6C6C6C6u,0xC7C7C7C7u,
|
||||
0xC8C8C8C8u,0xC9C9C9C9u,0xCACACACAu,0xCBCBCBCBu,0xCCCCCCCCu,0xCDCDCDCDu,0xCECECECEu,0xCFCFCFCFu,
|
||||
0xD0D0D0D0u,0xD1D1D1D1u,0xD2D2D2D2u,0xD3D3D3D3u,0xD4D4D4D4u,0xD5D5D5D5u,0xD6D6D6D6u,0xD7D7D7D7u,
|
||||
0xD8D8D8D8u,0xD9D9D9D9u,0xDADADADAu,0xDBDBDBDBu,0xDCDCDCDCu,0xDDDDDDDDu,0xDEDEDEDEu,0xDFDFDFDFu,
|
||||
0xE0E0E0E0u,0xE1E1E1E1u,0xE2E2E2E2u,0xE3E3E3E3u,0xE4E4E4E4u,0xE5E5E5E5u,0xE6E6E6E6u,0xE7E7E7E7u,
|
||||
0xE8E8E8E8u,0xE9E9E9E9u,0xEAEAEAEAu,0xEBEBEBEBu,0xECECECECu,0xEDEDEDEDu,0xEEEEEEEEu,0xEFEFEFEFu,
|
||||
0xF0F0F0F0u,0xF1F1F1F1u,0xF2F2F2F2u,0xF3F3F3F3u,0xF4F4F4F4u,0xF5F5F5F5u,0xF6F6F6F6u,0xF7F7F7F7u,
|
||||
0xF8F8F8F8u,0xF9F9F9F9u,0xFAFAFAFAu,0xFBFBFBFBu,0xFCFCFCFCu,0xFDFDFDFDu,0xFEFEFEFEu,0xFFFFFFFFu,
|
||||
};
|
||||
|
||||
return (int)word[narrow_cast<unsigned char>(hash)];
|
||||
}
|
||||
|
||||
inline static unsigned char reduced_hash(std::size_t hash)
|
||||
{
|
||||
return narrow_cast<unsigned char>(match_word(hash));
|
||||
}
|
||||
|
||||
inline unsigned char& at(std::size_t pos)
|
||||
{
|
||||
return reinterpret_cast<unsigned char*>(&m)[pos];
|
||||
}
|
||||
|
||||
inline unsigned char at(std::size_t pos)const
|
||||
{
|
||||
return reinterpret_cast<const unsigned char*>(&m)[pos];
|
||||
}
|
||||
|
||||
inline boost::uint16_t& overflow()
|
||||
{
|
||||
return reinterpret_cast<boost::uint16_t*>(&m)[7];
|
||||
}
|
||||
|
||||
inline boost::uint16_t overflow()const
|
||||
{
|
||||
return reinterpret_cast<boost::uint16_t const*>(&m)[7];
|
||||
}
|
||||
|
||||
alignas(16) __m128i m;
|
||||
};
|
||||
|
||||
#elif defined(BOOST_UNORDERED_LITTLE_ENDIAN_NEON)
|
||||
|
||||
struct group15
|
||||
{
|
||||
static constexpr int N=15;
|
||||
static constexpr bool regular_layout=true;
|
||||
|
||||
struct dummy_group_type
|
||||
{
|
||||
@@ -524,11 +334,6 @@ struct group15
|
||||
return pos==N-1&&at(N-1)==sentinel_;
|
||||
}
|
||||
|
||||
static inline bool is_sentinel(unsigned char* pc)noexcept
|
||||
{
|
||||
return *pc==sentinel_;
|
||||
}
|
||||
|
||||
inline void reset(std::size_t pos)
|
||||
{
|
||||
BOOST_ASSERT(pos<N);
|
||||
@@ -570,11 +375,6 @@ struct group15
|
||||
return simde_mm_movemask_epi8(vceqq_s8(m,vdupq_n_s8(0)))&0x7FFF;
|
||||
}
|
||||
|
||||
static inline bool is_occupied(unsigned char* pc)noexcept
|
||||
{
|
||||
return *pc!=available_;
|
||||
}
|
||||
|
||||
inline int match_occupied()const
|
||||
{
|
||||
return simde_mm_movemask_epi8(
|
||||
@@ -667,7 +467,6 @@ private:
|
||||
struct group15
|
||||
{
|
||||
static constexpr int N=15;
|
||||
static constexpr bool regular_layout=false;
|
||||
|
||||
struct dummy_group_type
|
||||
{
|
||||
@@ -999,7 +798,8 @@ class table;
|
||||
* - pg = pc-n
|
||||
*
|
||||
* (for explanatory purposes pg and pc are treated above as if they were memory
|
||||
* addresses rather than pointers).
|
||||
* addresses rather than pointers).The main drawback of this two-pointer
|
||||
* representation is that iterator increment is relatively slow.
|
||||
*
|
||||
* p = nullptr is conventionally used to mark end() iterators.
|
||||
*/
|
||||
@@ -1012,9 +812,6 @@ 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;
|
||||
@@ -1053,64 +850,32 @@ private:
|
||||
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},
|
||||
pc{reinterpret_cast<unsigned char*>(const_cast<Group*>(pg))+n},
|
||||
p{const_cast<table_element_type*>(p_)}
|
||||
{}
|
||||
|
||||
inline std::size_t rebase() noexcept
|
||||
{
|
||||
std::size_t off=reinterpret_cast<uintptr_t>(pc)%sizeof(Group);
|
||||
pc-=off;
|
||||
return off;
|
||||
}
|
||||
|
||||
inline void increment()noexcept
|
||||
{
|
||||
increment(std::integral_constant<bool,regular_layout>{});
|
||||
}
|
||||
std::size_t n0=rebase();
|
||||
|
||||
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);
|
||||
int mask=(reinterpret_cast<Group*>(pc)->match_occupied()>>(n0+1))<<(n0+1);
|
||||
if(!mask){
|
||||
do{
|
||||
pc+=sizeof(group_type);
|
||||
p+=N;
|
||||
pc+=sizeof(Group);
|
||||
p+=Group::N;
|
||||
}
|
||||
while((mask=reinterpret_cast<group_type*>(pc)->match_occupied())==0);
|
||||
while((mask=reinterpret_cast<Group*>(pc)->match_occupied())==0);
|
||||
}
|
||||
|
||||
auto n=unchecked_countr_zero(mask);
|
||||
if(BOOST_UNLIKELY(reinterpret_cast<group_type*>(pc)->is_sentinel(n))){
|
||||
if(BOOST_UNLIKELY(reinterpret_cast<Group*>(pc)->is_sentinel(n))){
|
||||
p=nullptr;
|
||||
}
|
||||
else{
|
||||
@@ -1162,6 +927,7 @@ struct table_arrays
|
||||
template<typename Allocator>
|
||||
static table_arrays new_(Allocator& al,std::size_t n)
|
||||
{
|
||||
// using alloc_traits=boost::allocator_traits<Allocator>;
|
||||
using storage_allocator=
|
||||
typename boost::allocator_rebind<Allocator, Value>::type;
|
||||
using storage_traits=boost::allocator_traits<storage_allocator>;
|
||||
@@ -1201,13 +967,13 @@ struct table_arrays
|
||||
static void delete_(Allocator& al,table_arrays& arrays)noexcept
|
||||
{
|
||||
using storage_alloc=typename boost::allocator_rebind<Allocator,Value>::type;
|
||||
using storage_traits=boost::allocator_traits<storage_alloc>;
|
||||
using pointer=typename storage_traits::pointer;
|
||||
using alloc_traits=boost::allocator_traits<storage_alloc>;
|
||||
using pointer=typename alloc_traits::pointer;
|
||||
using pointer_traits=boost::pointer_traits<pointer>;
|
||||
|
||||
auto sal=storage_alloc(al);
|
||||
if(arrays.elements){
|
||||
storage_traits::deallocate(
|
||||
alloc_traits::deallocate(
|
||||
sal,pointer_traits::pointer_to(*arrays.elements),
|
||||
buffer_size(arrays.groups_size_mask+1));
|
||||
}
|
||||
@@ -1347,85 +1113,51 @@ _STL_RESTORE_DEPRECATED_WARNING
|
||||
#pragma warning(disable:4702)
|
||||
#endif
|
||||
|
||||
/* We expose the hard-coded max load factor so that tests can use it without
|
||||
* needing to pull it from an instantiated class template such as the table
|
||||
* class
|
||||
*/
|
||||
constexpr static float const mlf = 0.875f;
|
||||
|
||||
template <class T>
|
||||
union uninitialized_storage
|
||||
{
|
||||
T t_;
|
||||
uninitialized_storage(){}
|
||||
~uninitialized_storage(){}
|
||||
};
|
||||
|
||||
/* 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: node-based and flat. In the
|
||||
* node-based case, buckets store pointers to individually heap-allocated
|
||||
* elements. For flat, buckets directly store elements.
|
||||
*
|
||||
* For both tables:
|
||||
*
|
||||
* (boost::unordered_flat_[map|set] wrappers complete it as appropriate) and,
|
||||
* more importantly, because of fundamental restrictions imposed by open
|
||||
* addressing:
|
||||
*
|
||||
* - value_type must be moveable.
|
||||
* - Pointer stability is not kept under rehashing.
|
||||
* - begin() is not O(1).
|
||||
* - No bucket API.
|
||||
* - Load factor is fixed and can't be set by the user.
|
||||
*
|
||||
* For the inline table:
|
||||
*
|
||||
* - value_type must be moveable.
|
||||
* - Pointer stability is not kept under rehashing.
|
||||
* - No extract API.
|
||||
*
|
||||
*
|
||||
* The TypePolicy template parameter is used to generate instantiations
|
||||
* suitable for either maps or sets, and introduces non-standard init_type:
|
||||
*
|
||||
*
|
||||
* - TypePolicy::key_type and TypePolicy::value_type have the obvious
|
||||
* meaning.
|
||||
*
|
||||
* - TypePolicy::init_type is the type implicitly converted to when
|
||||
* writing x.insert({...}). For maps, this is std::pair<Key,T> rather
|
||||
* than std::pair<const Key,T> so that, for instance, x.insert({"hello",0})
|
||||
* produces a cheaply moveable std::string&& ("hello") rather than
|
||||
* a copyable const std::string&&. foa::table::insert is extended to accept
|
||||
* both init_type and value_type references.
|
||||
*
|
||||
* - TypePolicy::construct and TypePolicy::destroy are used for the
|
||||
* construction and destruction of the internal types: value_type, init_type
|
||||
* and element_type.
|
||||
*
|
||||
* - TypePolicy::move is used to provide move semantics for the internal
|
||||
* types used by the container during rehashing and emplace. These types
|
||||
* are init_type, value_type and emplace_type. During insertion, a
|
||||
* stack-local type will be created based on the constructibility of the
|
||||
* value_type and the supplied arguments. TypePolicy::move is used here
|
||||
* for transfer of ownership. Similarly, TypePolicy::move is also used
|
||||
* during rehashing when elements are moved to the new table.
|
||||
*
|
||||
* - TypePolicy::move(value_type&) returns a temporary object for value
|
||||
* transfer on rehashing, move copy/assignment, and merge. For maps, this
|
||||
* object is a std::pair<Key&&,T&&>, which is generally cheaper to move
|
||||
* than std::pair<const Key,T>&& because of the constness in Key.
|
||||
* - TypePolicy::extract returns a const reference to the key part of
|
||||
* a value of type value_type, init_type, element_type or
|
||||
* a value of type value_type, init_type or
|
||||
* decltype(TypePolicy::move(...)).
|
||||
*
|
||||
* - TypePolicy::element_type is the type that table_arrays uses when
|
||||
* allocating buckets. For flat containers, this is value_type. For node
|
||||
* containers, this is a strong typedef to value_type*.
|
||||
*
|
||||
* - TypePolicy::value_from returns a mutable reference to value_type from
|
||||
* a given element_type. This is used when elements of the table themselves
|
||||
* need to be moved, such as during move construction/assignment when
|
||||
* allocators are unequal and there is no propagation. For all other cases,
|
||||
* the element_type itself is moved.
|
||||
*
|
||||
* try_emplace, erase and find support heterogenous lookup by default, that is,
|
||||
* without checking for any ::is_transparent typedefs --the checking is done by
|
||||
* boost::unordered_[flat|node]_[map|set].
|
||||
*
|
||||
* try_emplace, erase and find support heterogenous lookup by default, that is,
|
||||
* without checking for any ::is_transparent typedefs --the checking is done by
|
||||
* boost::unordered_flat_[map|set].
|
||||
*
|
||||
* At the moment, we're not supporting allocators with fancy pointers.
|
||||
* Allocator::pointer must be convertible to/from regular pointers.
|
||||
*/
|
||||
|
||||
/* We pull this out so the tests don't have to rely on a magic constant or
|
||||
* instantiate the table class template as it can be quite gory.
|
||||
*/
|
||||
constexpr static float const mlf = 0.875f;
|
||||
|
||||
template<typename TypePolicy,typename Hash,typename Pred,typename Allocator>
|
||||
class
|
||||
|
||||
@@ -1439,11 +1171,7 @@ table:empty_value<Hash,0>,empty_value<Pred,1>,empty_value<Allocator,2>
|
||||
using pred_base=empty_value<Pred,1>;
|
||||
using allocator_base=empty_value<Allocator,2>;
|
||||
using type_policy=TypePolicy;
|
||||
#if defined(BOOST_UNORDERED_SSE2)
|
||||
using group_type=group14;
|
||||
#else
|
||||
using group_type=group15;
|
||||
#endif
|
||||
static constexpr auto N=group_type::N;
|
||||
using size_policy=pow2_size_policy;
|
||||
using prober=pow2_quadratic_prober;
|
||||
@@ -1642,7 +1370,7 @@ public:
|
||||
iterator begin()noexcept
|
||||
{
|
||||
iterator it{arrays.groups,0,arrays.elements};
|
||||
if(arrays.elements&&!(arrays.groups[0].match_occupied()&0x1))++it;
|
||||
if(!(arrays.groups[0].match_occupied()&0x1))++it;
|
||||
return it;
|
||||
}
|
||||
|
||||
@@ -1660,25 +1388,18 @@ public:
|
||||
template<typename... Args>
|
||||
BOOST_FORCEINLINE std::pair<iterator,bool> emplace(Args&&... args)
|
||||
{
|
||||
using emplace_type=typename std::conditional<
|
||||
std::is_constructible<init_type,Args...>::value,
|
||||
init_type,
|
||||
value_type
|
||||
>::type;
|
||||
|
||||
using insert_type=typename std::conditional<
|
||||
/* We dispatch based on whether or not the value_type is constructible from
|
||||
* an rvalue refernce to the deduced emplace_type. We do this specifically
|
||||
* for the csae of the node-based containers. To this end, we're able to
|
||||
* avoid allocating a node when a duplicate element is attempted to be
|
||||
* inserted. For immovable types, we instead dispatch to the routine that
|
||||
* unconditionally allocates via `type_policy::construct()`.
|
||||
*/
|
||||
return emplace_dispatch(
|
||||
std::is_constructible<
|
||||
value_type,emplace_type>::value,
|
||||
emplace_type,element_type
|
||||
>::type;
|
||||
|
||||
uninitialized_storage<insert_type> s;
|
||||
auto *p=std::addressof(s.t_);
|
||||
|
||||
type_policy::construct(al(),p,std::forward<Args>(args)...);
|
||||
|
||||
destroy_on_exit<insert_type> guard{al(),p};
|
||||
return emplace_impl(type_policy::move(*p));
|
||||
value_type,
|
||||
emplace_type<Args...>&&>{},
|
||||
std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template<typename Key,typename... Args>
|
||||
@@ -1790,7 +1511,7 @@ public:
|
||||
element_type extract(const_iterator pos)
|
||||
{
|
||||
BOOST_ASSERT(pos!=end());
|
||||
erase_on_exit e{*this,pos};
|
||||
erase_on_exit e{*this,iterator{const_iterator_cast_tag{},pos}};
|
||||
(void)e;
|
||||
return std::move(*pos.p);
|
||||
}
|
||||
@@ -1863,6 +1584,15 @@ private:
|
||||
template<typename,typename,typename,typename> friend class table;
|
||||
using arrays_type=table_arrays<element_type,group_type,size_policy>;
|
||||
|
||||
template<typename... Args>
|
||||
using emplace_type = typename std::conditional<
|
||||
std::is_constructible<
|
||||
init_type,Args...
|
||||
>::value,
|
||||
init_type,
|
||||
value_type
|
||||
>::type;
|
||||
|
||||
struct clear_on_exit
|
||||
{
|
||||
~clear_on_exit(){x.clear();}
|
||||
@@ -1871,22 +1601,14 @@ private:
|
||||
|
||||
struct erase_on_exit
|
||||
{
|
||||
erase_on_exit(table& x_,const_iterator it_):x{x_},it{it_}{}
|
||||
erase_on_exit(table& x_,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;
|
||||
};
|
||||
|
||||
template <class T>
|
||||
struct destroy_on_exit
|
||||
{
|
||||
Allocator &a;
|
||||
T *p;
|
||||
~destroy_on_exit(){type_policy::destroy(a,p);};
|
||||
table& x;
|
||||
iterator it;
|
||||
bool rollback_=false;
|
||||
};
|
||||
|
||||
Hash& h(){return hash_base::get();}
|
||||
@@ -2152,6 +1874,29 @@ private:
|
||||
#pragma warning(pop) /* C4800 */
|
||||
#endif
|
||||
|
||||
template<typename... Args>
|
||||
BOOST_FORCEINLINE std::pair<iterator,bool> emplace_dispatch(
|
||||
std::true_type,Args&&... args
|
||||
) {
|
||||
using emplace_type_t = emplace_type<Args...>;
|
||||
return emplace_impl(emplace_type_t(std::forward<Args>(args)...));
|
||||
}
|
||||
|
||||
template<typename... Args>
|
||||
BOOST_FORCEINLINE std::pair<iterator,bool> emplace_dispatch(
|
||||
std::false_type,Args&&... args
|
||||
) {
|
||||
alignas(element_type)
|
||||
unsigned char buf[sizeof(element_type)];
|
||||
element_type* p = reinterpret_cast<element_type*>(buf);
|
||||
|
||||
type_policy::construct(al(),p,std::forward<Args>(args)...);
|
||||
destroy_element_on_exit d{this,p};
|
||||
(void)d;
|
||||
|
||||
return emplace_impl(type_policy::move(*p));
|
||||
}
|
||||
|
||||
template<typename... Args>
|
||||
BOOST_FORCEINLINE std::pair<iterator,bool> emplace_impl(Args&&... args)
|
||||
{
|
||||
@@ -2435,7 +2180,13 @@ private:
|
||||
} /* namespace unordered */
|
||||
} /* namespace boost */
|
||||
|
||||
#undef BOOST_UNORDERED_STATIC_ASSERT_HASH_PRED
|
||||
#undef BOOST_UNORDERED_ASSUME
|
||||
#undef BOOST_UNORDERED_HAS_BUILTIN
|
||||
#undef BOOST_UNORDERED_STATIC_ASSERT_HASH_PRED
|
||||
#ifdef BOOST_UNORDERED_LITTLE_ENDIAN_NEON
|
||||
#undef BOOST_UNORDERED_LITTLE_ENDIAN_NEON
|
||||
#endif
|
||||
#ifdef BOOST_UNORDERED_SSE2
|
||||
#undef BOOST_UNORDERED_SSE2
|
||||
#endif
|
||||
#endif
|
||||
|
||||
@@ -1,60 +0,0 @@
|
||||
/* 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
|
||||
@@ -45,30 +45,20 @@ struct node_handle_base
|
||||
|
||||
private:
|
||||
using node_value_type=typename type_policy::value_type;
|
||||
element_type p_;
|
||||
node_value_type* p_=nullptr;
|
||||
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
|
||||
node_value_type& element()noexcept
|
||||
{
|
||||
BOOST_ASSERT(!empty());
|
||||
return p_;
|
||||
return *p_;
|
||||
}
|
||||
|
||||
element_type const& element()const noexcept
|
||||
node_value_type const& element()const noexcept
|
||||
{
|
||||
BOOST_ASSERT(!empty());
|
||||
return p_;
|
||||
return *p_;
|
||||
}
|
||||
|
||||
Allocator& al()noexcept
|
||||
@@ -83,114 +73,110 @@ struct node_handle_base
|
||||
return a_.t_;
|
||||
}
|
||||
|
||||
void emplace(element_type&& x,Allocator a)
|
||||
void emplace(node_value_type* p,Allocator a)
|
||||
{
|
||||
BOOST_ASSERT(empty());
|
||||
auto* p=x.p;
|
||||
p_.p=p;
|
||||
p_=p;
|
||||
new(&a_.t_)Allocator(a);
|
||||
}
|
||||
|
||||
void emplace(element_type&& x,Allocator a)
|
||||
{
|
||||
emplace(x.p,a);
|
||||
x.p=nullptr;
|
||||
}
|
||||
|
||||
void reset()
|
||||
void clear()
|
||||
{
|
||||
a_.t_.~Allocator();
|
||||
p_.p=nullptr;
|
||||
al().~Allocator();
|
||||
p_=nullptr;
|
||||
}
|
||||
|
||||
public:
|
||||
constexpr node_handle_base()noexcept:p_{nullptr}{}
|
||||
constexpr node_handle_base()noexcept{}
|
||||
|
||||
node_handle_base(node_handle_base&& nh) noexcept
|
||||
{
|
||||
p_.p = nullptr;
|
||||
if (!nh.empty()){
|
||||
emplace(std::move(nh.p_),nh.al());
|
||||
nh.reset();
|
||||
emplace(nh.p_,nh.al());
|
||||
nh.clear();
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
bool const pocma=
|
||||
boost::allocator_propagate_on_container_move_assignment<
|
||||
Allocator>::type::value;
|
||||
|
||||
BOOST_ASSERT(pocma||al()==nh.al());
|
||||
BOOST_ASSERT(
|
||||
pocma
|
||||
||empty()
|
||||
||nh.empty()
|
||||
||(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();
|
||||
}
|
||||
if(!empty()){
|
||||
type_policy::destroy(al(),p_);
|
||||
if (pocma&&!nh.empty()){al()=std::move(nh.al());}
|
||||
}
|
||||
|
||||
if(!nh.empty()){
|
||||
if(empty()){new(&a_.t_)Allocator(std::move(nh.al()));}
|
||||
p_=nh.p_;
|
||||
|
||||
nh.p_=nullptr;
|
||||
nh.a_.t_.~Allocator();
|
||||
}else if (!empty()){
|
||||
a_.t_.~Allocator();
|
||||
p_=nullptr;
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
~node_handle_base()
|
||||
{
|
||||
if(!empty()){
|
||||
type_policy::destroy(al(),&p_);
|
||||
reset();
|
||||
type_policy::destroy(al(),p_);
|
||||
a_.t_.~Allocator();
|
||||
}
|
||||
}
|
||||
|
||||
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;}
|
||||
BOOST_ATTRIBUTE_NODISCARD bool empty()const noexcept{return 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;
|
||||
using std::swap;
|
||||
|
||||
BOOST_ASSERT(pocs || al()==nh.al());
|
||||
bool const pocs=
|
||||
boost::allocator_propagate_on_container_swap<Allocator>::type::value;
|
||||
|
||||
using std::swap;
|
||||
p_.swap(nh.p_);
|
||||
if(pocs)swap(al(),nh.al());
|
||||
}
|
||||
if (!empty()&&!nh.empty()){
|
||||
BOOST_ASSERT(pocs || al()==nh.al());
|
||||
|
||||
node_value_type *p=p_;
|
||||
p_=nh.p_;
|
||||
nh.p_=p;
|
||||
|
||||
if(pocs){
|
||||
swap(al(),nh.al());
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (empty()&&nh.empty()){return;}
|
||||
|
||||
if (empty()){
|
||||
emplace(nh.p_,nh.al());
|
||||
nh.clear();
|
||||
}else{
|
||||
nh.emplace(p_,al());
|
||||
clear();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
#ifndef BOOST_UNORDERED_DETAIL_REQUIRES_CXX11_HPP_INCLUDED
|
||||
#define BOOST_UNORDERED_DETAIL_REQUIRES_CXX11_HPP_INCLUDED
|
||||
|
||||
// Copyright 2023 Peter Dimov
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// https://www.boost.org/LICENSE_1_0.txt
|
||||
|
||||
#include <boost/config.hpp>
|
||||
#include <boost/config/pragma_message.hpp>
|
||||
|
||||
#if defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES) || \
|
||||
defined(BOOST_NO_CXX11_RVALUE_REFERENCES) || \
|
||||
defined(BOOST_NO_CXX11_DECLTYPE) || \
|
||||
defined(BOOST_NO_CXX11_CONSTEXPR) || \
|
||||
defined(BOOST_NO_CXX11_NOEXCEPT)
|
||||
|
||||
BOOST_PRAGMA_MESSAGE("C++03 support is deprecated in Boost.Unordered 1.82 and will be removed in Boost.Unordered 1.84.")
|
||||
|
||||
#endif
|
||||
|
||||
#endif // #ifndef BOOST_UNORDERED_DETAIL_REQUIRES_CXX11_HPP_INCLUDED
|
||||
@@ -53,11 +53,6 @@ namespace boost {
|
||||
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
|
||||
@@ -65,24 +60,19 @@ namespace boost {
|
||||
std::move(const_cast<raw_mapped_type&>(x.second))};
|
||||
}
|
||||
|
||||
template <class A>
|
||||
static void construct(A& al, element_type* p, moved_type&& x)
|
||||
{
|
||||
boost::allocator_construct(al, p, std::move(x));
|
||||
}
|
||||
|
||||
template <class A, class... Args>
|
||||
static void construct(A& al, init_type* p, Args&&... args)
|
||||
static void construct(A& al, element_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
|
||||
template <class A> static void destroy(A& al, element_type* p) noexcept
|
||||
{
|
||||
boost::allocator_destroy(al, p);
|
||||
}
|
||||
|
||||
@@ -45,13 +45,25 @@ namespace boost {
|
||||
|
||||
static element_type&& move(element_type& x) { return std::move(x); }
|
||||
|
||||
template <class A>
|
||||
static void construct(A& al, element_type* p, element_type const& copy)
|
||||
{
|
||||
boost::allocator_construct(al, p, copy);
|
||||
}
|
||||
|
||||
template <class A>
|
||||
static void construct(A& al, element_type* p, Key&& x)
|
||||
{
|
||||
boost::allocator_construct(al, p, std::move(x));
|
||||
}
|
||||
|
||||
template <class A, class... Args>
|
||||
static void construct(A& al, value_type* p, Args&&... args)
|
||||
static void construct(A& al, element_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
|
||||
template <class A> static void destroy(A& al, element_type* p) noexcept
|
||||
{
|
||||
boost::allocator_destroy(al, p);
|
||||
}
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
#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>
|
||||
|
||||
@@ -11,7 +11,6 @@
|
||||
#endif
|
||||
|
||||
#include <boost/unordered/detail/foa.hpp>
|
||||
#include <boost/unordered/detail/foa/element_type.hpp>
|
||||
#include <boost/unordered/detail/foa/node_handle.hpp>
|
||||
#include <boost/unordered/detail/type_traits.hpp>
|
||||
#include <boost/unordered/unordered_node_map_fwd.hpp>
|
||||
@@ -46,7 +45,23 @@ namespace boost {
|
||||
using value_type = std::pair<Key const, T>;
|
||||
using moved_type = std::pair<raw_key_type&&, raw_mapped_type&&>;
|
||||
|
||||
using element_type=foa::element_type<value_type>;
|
||||
struct element_type
|
||||
{
|
||||
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(element_type const&) = delete;
|
||||
element_type(element_type&& rhs) noexcept
|
||||
{
|
||||
p = rhs.p;
|
||||
rhs.p = nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
static value_type& value_from(element_type const& x) { return *(x.p); }
|
||||
|
||||
@@ -62,11 +77,6 @@ namespace boost {
|
||||
}
|
||||
|
||||
static element_type&& move(element_type& x) { return std::move(x); }
|
||||
static moved_type move(init_type& x)
|
||||
{
|
||||
return {std::move(x.first), std::move(x.second)};
|
||||
}
|
||||
|
||||
static moved_type move(value_type& x)
|
||||
{
|
||||
return {std::move(const_cast<raw_key_type&>(x.first)),
|
||||
@@ -86,18 +96,6 @@ namespace boost {
|
||||
construct(al, p, *copy.p);
|
||||
}
|
||||
|
||||
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, class... Args>
|
||||
static void construct(A& al, element_type* p, Args&&... args)
|
||||
{
|
||||
@@ -108,11 +106,10 @@ namespace boost {
|
||||
}
|
||||
BOOST_CATCH(...)
|
||||
{
|
||||
using pointer_type = typename boost::allocator_pointer<A>::type;
|
||||
using pointer_traits = boost::pointer_traits<pointer_type>;
|
||||
|
||||
boost::allocator_deallocate(
|
||||
al, pointer_traits::pointer_to(*(p->p)), 1);
|
||||
boost::allocator_deallocate(al,
|
||||
boost::pointer_traits<
|
||||
typename boost::allocator_pointer<A>::type>::pointer_to(*p->p),
|
||||
1);
|
||||
BOOST_RETHROW
|
||||
}
|
||||
BOOST_CATCH_END
|
||||
@@ -121,32 +118,27 @@ namespace boost {
|
||||
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, init_type* p) noexcept
|
||||
{
|
||||
boost::allocator_destroy(al, p);
|
||||
boost::allocator_deallocate(al,
|
||||
boost::pointer_traits<
|
||||
typename boost::allocator_pointer<A>::type>::pointer_to(*p),
|
||||
1);
|
||||
}
|
||||
|
||||
template <class A> static void destroy(A& al, element_type* p) noexcept
|
||||
{
|
||||
if (p->p) {
|
||||
using pointer_type = typename boost::allocator_pointer<A>::type;
|
||||
using pointer_traits = boost::pointer_traits<pointer_type>;
|
||||
|
||||
destroy(al, p->p);
|
||||
boost::allocator_deallocate(
|
||||
al, pointer_traits::pointer_to(*(p->p)), 1);
|
||||
destroy(al,p->p);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template <class TypePolicy, class Allocator>
|
||||
template <class NodeMapTypes, class Allocator>
|
||||
struct node_map_handle
|
||||
: public detail::foa::node_handle_base<TypePolicy, Allocator>
|
||||
: public detail::foa::node_handle_base<NodeMapTypes, Allocator>
|
||||
{
|
||||
private:
|
||||
using base_type = detail::foa::node_handle_base<TypePolicy, Allocator>;
|
||||
using base_type =
|
||||
detail::foa::node_handle_base<NodeMapTypes, Allocator>;
|
||||
|
||||
using typename base_type::type_policy;
|
||||
|
||||
@@ -154,8 +146,8 @@ namespace boost {
|
||||
friend class boost::unordered::unordered_node_map;
|
||||
|
||||
public:
|
||||
using key_type = typename TypePolicy::key_type;
|
||||
using mapped_type = typename TypePolicy::mapped_type;
|
||||
using key_type = typename NodeMapTypes::key_type;
|
||||
using mapped_type = typename NodeMapTypes::mapped_type;
|
||||
|
||||
constexpr node_map_handle() noexcept = default;
|
||||
node_map_handle(node_map_handle&& nh) noexcept = default;
|
||||
@@ -165,13 +157,13 @@ namespace boost {
|
||||
key_type& key() const
|
||||
{
|
||||
BOOST_ASSERT(!this->empty());
|
||||
return const_cast<key_type&>(this->data().first);
|
||||
return const_cast<key_type&>(this->element().first);
|
||||
}
|
||||
|
||||
mapped_type& mapped() const
|
||||
{
|
||||
BOOST_ASSERT(!this->empty());
|
||||
return const_cast<mapped_type&>(this->data().second);
|
||||
return const_cast<mapped_type&>(this->element().second);
|
||||
}
|
||||
};
|
||||
} // namespace detail
|
||||
@@ -410,9 +402,12 @@ namespace boost {
|
||||
|
||||
BOOST_ASSERT(get_allocator() == nh.get_allocator());
|
||||
|
||||
auto itp = table_.insert(std::move(nh.element()));
|
||||
typename map_types::element_type x;
|
||||
x.p=std::addressof(nh.element());
|
||||
|
||||
auto itp = table_.insert(std::move(x));
|
||||
if (itp.second) {
|
||||
nh.reset();
|
||||
nh.clear();
|
||||
return {itp.first, true, node_type{}};
|
||||
} else {
|
||||
return {itp.first, false, std::move(nh)};
|
||||
@@ -427,13 +422,8 @@ namespace boost {
|
||||
|
||||
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;
|
||||
}
|
||||
auto itp = table_.insert(map_types::move(nh.element()));
|
||||
return itp.first;
|
||||
}
|
||||
|
||||
template <class M>
|
||||
@@ -604,7 +594,7 @@ namespace boost {
|
||||
node_type extract(key_type const& key)
|
||||
{
|
||||
auto pos = find(key);
|
||||
return pos != end() ? extract(pos) : node_type();
|
||||
return pos!=end()?extract(pos):node_type();
|
||||
}
|
||||
|
||||
template <class K>
|
||||
@@ -615,7 +605,7 @@ namespace boost {
|
||||
extract(K const& key)
|
||||
{
|
||||
auto pos = find(key);
|
||||
return pos != end() ? extract(pos) : node_type();
|
||||
return pos!=end()?extract(pos):node_type();
|
||||
}
|
||||
|
||||
template <class H2, class P2>
|
||||
|
||||
@@ -11,7 +11,6 @@
|
||||
#endif
|
||||
|
||||
#include <boost/unordered/detail/foa.hpp>
|
||||
#include <boost/unordered/detail/foa/element_type.hpp>
|
||||
#include <boost/unordered/detail/foa/node_handle.hpp>
|
||||
#include <boost/unordered/detail/type_traits.hpp>
|
||||
#include <boost/unordered/unordered_node_set_fwd.hpp>
|
||||
@@ -42,7 +41,23 @@ namespace boost {
|
||||
|
||||
static Key const& extract(value_type const& key) { return key; }
|
||||
|
||||
using element_type=foa::element_type<value_type>;
|
||||
struct element_type
|
||||
{
|
||||
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(element_type const&) = delete;
|
||||
element_type(element_type&& rhs) noexcept
|
||||
{
|
||||
p = rhs.p;
|
||||
rhs.p = nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
static value_type& value_from(element_type const& x) { return *x.p; }
|
||||
static Key const& extract(element_type const& k) { return *k.p; }
|
||||
@@ -63,12 +78,6 @@ namespace boost {
|
||||
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)
|
||||
{
|
||||
@@ -91,26 +100,27 @@ namespace boost {
|
||||
template <class A> static void destroy(A& al, value_type* p) noexcept
|
||||
{
|
||||
boost::allocator_destroy(al, p);
|
||||
boost::allocator_deallocate(al,
|
||||
boost::pointer_traits<
|
||||
typename boost::allocator_pointer<A>::type>::pointer_to(*p),
|
||||
1);
|
||||
}
|
||||
|
||||
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>
|
||||
template <class NodeSetTypes, class Allocator>
|
||||
struct node_set_handle
|
||||
: public detail::foa::node_handle_base<TypePolicy, Allocator>
|
||||
: public detail::foa::node_handle_base<NodeSetTypes, Allocator>
|
||||
{
|
||||
private:
|
||||
using base_type = detail::foa::node_handle_base<TypePolicy, Allocator>;
|
||||
using base_type =
|
||||
detail::foa::node_handle_base<NodeSetTypes, Allocator>;
|
||||
|
||||
using typename base_type::type_policy;
|
||||
|
||||
@@ -118,7 +128,7 @@ namespace boost {
|
||||
friend class boost::unordered::unordered_node_set;
|
||||
|
||||
public:
|
||||
using value_type = typename TypePolicy::value_type;
|
||||
using value_type = typename NodeSetTypes::value_type;
|
||||
|
||||
constexpr node_set_handle() noexcept = default;
|
||||
node_set_handle(node_set_handle&& nh) noexcept = default;
|
||||
@@ -127,7 +137,7 @@ namespace boost {
|
||||
value_type& value() const
|
||||
{
|
||||
BOOST_ASSERT(!this->empty());
|
||||
return const_cast<value_type&>(this->data());
|
||||
return const_cast<value_type&>(this->element());
|
||||
}
|
||||
};
|
||||
} // namespace detail
|
||||
@@ -380,9 +390,12 @@ namespace boost {
|
||||
|
||||
BOOST_ASSERT(get_allocator() == nh.get_allocator());
|
||||
|
||||
auto itp = table_.insert(std::move(nh.element()));
|
||||
typename set_types::element_type x;
|
||||
x.p=std::addressof(nh.element());
|
||||
|
||||
auto itp = table_.insert(std::move(x));
|
||||
if (itp.second) {
|
||||
nh.reset();
|
||||
nh.clear();
|
||||
return {itp.first, true, node_type{}};
|
||||
} else {
|
||||
return {itp.first, false, std::move(nh)};
|
||||
@@ -397,13 +410,8 @@ namespace boost {
|
||||
|
||||
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;
|
||||
}
|
||||
auto itp = table_.insert(set_types::move(nh.element()));
|
||||
return itp.first;
|
||||
}
|
||||
|
||||
template <class... Args>
|
||||
@@ -462,7 +470,7 @@ namespace boost {
|
||||
node_type extract(key_type const& key)
|
||||
{
|
||||
auto pos = find(key);
|
||||
return pos != end() ? extract(pos) : node_type();
|
||||
return pos!=end()?extract(pos):node_type();
|
||||
}
|
||||
|
||||
template <class K>
|
||||
@@ -473,7 +481,7 @@ namespace boost {
|
||||
extract(K const& key)
|
||||
{
|
||||
auto pos = find(key);
|
||||
return pos != end() ? extract(pos) : node_type();
|
||||
return pos!=end()?extract(pos):node_type();
|
||||
}
|
||||
|
||||
template <class H2, class P2>
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
#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>
|
||||
|
||||
@@ -144,7 +144,6 @@ build_foa init_type_insert_tests ;
|
||||
build_foa max_load_tests ;
|
||||
build_foa extract_tests ;
|
||||
build_foa node_handle_tests ;
|
||||
build_foa uses_allocator ;
|
||||
|
||||
run unordered/hash_is_avalanching_test.cpp ;
|
||||
|
||||
|
||||
@@ -220,7 +220,7 @@ namespace insert_tests {
|
||||
test::check_instances check_;
|
||||
|
||||
X x;
|
||||
const_iterator pos = x.begin();
|
||||
iterator pos = x.begin();
|
||||
tracker_type tracker = test::create_ordered(x);
|
||||
|
||||
test::random_values<X> v(1000, generator);
|
||||
@@ -248,7 +248,7 @@ namespace insert_tests {
|
||||
test::check_instances check_;
|
||||
|
||||
X x;
|
||||
const_iterator pos = x.begin();
|
||||
iterator pos = x.begin();
|
||||
tracker_type tracker = test::create_ordered(x);
|
||||
|
||||
test::random_values<X> v(1000, generator);
|
||||
|
||||
@@ -1,8 +1,3 @@
|
||||
|
||||
// Copyright 2022-2023 Christian Mazakas.
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or move at http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#if !defined(BOOST_UNORDERED_FOA_TESTS)
|
||||
#error "max_load_tests is currently only supported by open-addressed containers"
|
||||
#else
|
||||
|
||||
@@ -337,12 +337,8 @@ static void insert_node_handle_unique(Container1& c1, Container2& c2)
|
||||
(boost::is_same<node_type, typename Container2::node_type>::value));
|
||||
|
||||
typedef typename Container1::iterator iterator1;
|
||||
typedef typename Container2::iterator iterator2;
|
||||
typedef typename Container2::insert_return_type insert_return_type2;
|
||||
|
||||
Container1 c1_copy(c1);
|
||||
Container2 c2_copy;
|
||||
|
||||
iterator1 r1 = insert_empty_node(c1);
|
||||
insert_return_type2 r2 = c2.insert(node_type());
|
||||
BOOST_TEST(r1 == c1.end());
|
||||
@@ -371,24 +367,6 @@ static void insert_node_handle_unique(Container1& c1, Container2& c2)
|
||||
node_handle_compare(r.node, v);
|
||||
}
|
||||
}
|
||||
|
||||
while (!c1_copy.empty()) {
|
||||
value_type v = *c1_copy.begin();
|
||||
value_type const* v_ptr = boost::to_address(c1_copy.begin());
|
||||
std::size_t count = c2_copy.count(test::get_key<Container1>(v));
|
||||
iterator2 pos =
|
||||
c2_copy.insert(c2_copy.begin(), c1_copy.extract(c1_copy.begin()));
|
||||
if (!count) {
|
||||
BOOST_TEST_EQ(c2_copy.count(test::get_key<Container1>(v)), count + 1);
|
||||
BOOST_TEST(pos != c2.end());
|
||||
BOOST_TEST(boost::to_address(pos) == v_ptr);
|
||||
} else {
|
||||
BOOST_TEST_EQ(c2_copy.count(test::get_key<Container1>(v)), count);
|
||||
BOOST_TEST(pos != c2_copy.end());
|
||||
BOOST_TEST(
|
||||
test::get_key<Container2>(*pos) == test::get_key<Container2>(v));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Container1, typename Container2>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
|
||||
// Copyright 2013 Daniel James.
|
||||
// Copyright 2022-2023 Christian Mazakas.
|
||||
// Copyright 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)
|
||||
|
||||
|
||||
@@ -174,7 +174,7 @@ template <class UnorderedContainer> void rehash_tests(UnorderedContainer*)
|
||||
BOOST_TEST_LT(s.bucket_count(), prev_count + count);
|
||||
BOOST_TEST_LE(total_allocation,
|
||||
(prev_count + count) * sizeof(typename UnorderedContainer::value_type) +
|
||||
((prev_count + count) / 14 + 2) * 16);
|
||||
((prev_count + count) / 15 + 1) * 16);
|
||||
#else
|
||||
std::size_t const estimated_bucket_group_size =
|
||||
3 * sizeof(void*) + sizeof(std::size_t);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2022-2023 Christian Mazakas.
|
||||
// Copyright 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)
|
||||
|
||||
|
||||
@@ -1833,6 +1833,7 @@ multiset_extract_const_overload_compile_test()
|
||||
|
||||
template <class UnorderedSet> void test_set_transparent_extract(UnorderedSet*)
|
||||
{
|
||||
#ifndef BOOST_UNORDERED_FOA_TESTS
|
||||
typedef typename UnorderedSet::node_type node_type;
|
||||
|
||||
count_reset();
|
||||
@@ -1876,6 +1877,7 @@ template <class UnorderedSet> void test_set_transparent_extract(UnorderedSet*)
|
||||
BOOST_TEST_EQ(set.size(), set_size);
|
||||
|
||||
BOOST_TEST_EQ(key::count_, expected_key_count);
|
||||
#endif
|
||||
}
|
||||
|
||||
template <class UnorderedSet>
|
||||
|
||||
@@ -1,189 +0,0 @@
|
||||
|
||||
// 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 "../helpers/test.hpp"
|
||||
|
||||
#include <boost/unordered/detail/implementation.hpp>
|
||||
#include <boost/unordered/unordered_flat_map.hpp>
|
||||
#include <boost/unordered/unordered_flat_set.hpp>
|
||||
#include <boost/unordered/unordered_node_map.hpp>
|
||||
#include <boost/unordered/unordered_node_set.hpp>
|
||||
|
||||
#include <boost/config.hpp>
|
||||
#include <boost/config/pragma_message.hpp>
|
||||
#include <boost/config/workaround.hpp>
|
||||
|
||||
#if BOOST_CXX_VERSION <= 199711L || \
|
||||
BOOST_WORKAROUND(BOOST_GCC_VERSION, < 40800) || \
|
||||
(defined(BOOST_LIBSTDCXX_VERSION) && BOOST_CXX_VERSION > 201703L) || \
|
||||
(defined(BOOST_MSVC_FULL_VER) && BOOST_MSVC_FULL_VER >= 192000000 && \
|
||||
BOOST_MSVC_FULL_VER < 193000000)
|
||||
|
||||
// automatically disable this test for C++03 builds so we can use the STL's
|
||||
// scoped_allocator_adaptor
|
||||
// we remove C++20 support for libstdc++ builds because of:
|
||||
// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=108952
|
||||
//
|
||||
// msvc-14.2 w/ C++20 is similarly affected
|
||||
//
|
||||
|
||||
|
||||
BOOST_PRAGMA_MESSAGE("uses_allocator tests require C++11, scoped_allocator")
|
||||
|
||||
int main() {}
|
||||
|
||||
#else
|
||||
|
||||
#include <memory>
|
||||
#include <scoped_allocator>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
template <class T> struct allocator
|
||||
{
|
||||
typedef T value_type;
|
||||
|
||||
int tag_ = -1;
|
||||
|
||||
allocator() = default;
|
||||
allocator(int tag) : tag_{tag} {}
|
||||
allocator(allocator const&) = default;
|
||||
allocator(allocator&&) = default;
|
||||
|
||||
template <class U> allocator(allocator<U> const& rhs) : tag_{rhs.tag_} {}
|
||||
|
||||
BOOST_ATTRIBUTE_NODISCARD T* allocate(std::size_t n)
|
||||
{
|
||||
return static_cast<T*>(::operator new(n * sizeof(T)));
|
||||
}
|
||||
|
||||
void deallocate(T* p, std::size_t) noexcept { ::operator delete(p); }
|
||||
|
||||
allocator& operator=(allocator const& rhs)
|
||||
{
|
||||
tag_ = rhs.tag_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
allocator& operator=(allocator&& rhs) noexcept
|
||||
{
|
||||
tag_ = rhs.tag_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool operator==(allocator const&) const { return true; }
|
||||
bool operator!=(allocator const&) const { return false; }
|
||||
};
|
||||
|
||||
struct raii_tracker
|
||||
{
|
||||
static int count;
|
||||
static int copy_count;
|
||||
static int move_count;
|
||||
static int alloc_move_count;
|
||||
|
||||
using allocator_type = allocator<int>;
|
||||
|
||||
allocator_type a_;
|
||||
|
||||
raii_tracker(allocator_type a) : a_(a) { ++count; }
|
||||
raii_tracker(int, allocator_type const& a) : a_(a) { ++count; }
|
||||
|
||||
raii_tracker(raii_tracker const&) { ++copy_count; }
|
||||
raii_tracker(raii_tracker&&) noexcept { ++move_count; }
|
||||
raii_tracker(raii_tracker&&, allocator_type const& a) noexcept : a_(a)
|
||||
{
|
||||
++alloc_move_count;
|
||||
}
|
||||
|
||||
allocator_type get_allocator() const noexcept { return a_; }
|
||||
|
||||
friend bool operator==(raii_tracker const&, raii_tracker const&)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
int raii_tracker::count = 0;
|
||||
int raii_tracker::copy_count = 0;
|
||||
int raii_tracker::move_count = 0;
|
||||
int raii_tracker::alloc_move_count = 0;
|
||||
|
||||
static void reset_counts()
|
||||
{
|
||||
raii_tracker::count = 0;
|
||||
raii_tracker::copy_count = 0;
|
||||
raii_tracker::move_count = 0;
|
||||
raii_tracker::alloc_move_count = 0;
|
||||
}
|
||||
|
||||
std::size_t hash_value(raii_tracker const&) { return 0; }
|
||||
|
||||
using map_allocator_type = std::scoped_allocator_adaptor<
|
||||
allocator<std::pair<raii_tracker const, raii_tracker> >, allocator<int> >;
|
||||
|
||||
using set_allocator_type =
|
||||
std::scoped_allocator_adaptor<allocator<raii_tracker>, allocator<int> >;
|
||||
|
||||
using map_type = boost::unordered_flat_map<raii_tracker, raii_tracker,
|
||||
boost::hash<raii_tracker>, std::equal_to<raii_tracker>, map_allocator_type>;
|
||||
|
||||
using node_map_type = boost::unordered_node_map<raii_tracker, raii_tracker,
|
||||
boost::hash<raii_tracker>, std::equal_to<raii_tracker>, map_allocator_type>;
|
||||
|
||||
using set_type = boost::unordered_flat_set<raii_tracker,
|
||||
boost::hash<raii_tracker>, std::equal_to<raii_tracker>, set_allocator_type>;
|
||||
|
||||
using node_set_type = boost::unordered_node_set<raii_tracker,
|
||||
boost::hash<raii_tracker>, std::equal_to<raii_tracker>, set_allocator_type>;
|
||||
|
||||
map_type* flat_map;
|
||||
node_map_type* node_map;
|
||||
|
||||
set_type* flat_set;
|
||||
node_set_type* node_set;
|
||||
|
||||
template <class X> static void map_uses_allocator_construction(X*)
|
||||
{
|
||||
reset_counts();
|
||||
|
||||
map_allocator_type alloc(
|
||||
allocator<std::pair<raii_tracker const, raii_tracker> >{12},
|
||||
allocator<int>{34});
|
||||
|
||||
X map(1, alloc);
|
||||
map.emplace(
|
||||
std::piecewise_construct, std::make_tuple(1337), std::make_tuple(7331));
|
||||
|
||||
BOOST_TEST_EQ(raii_tracker::count, 2);
|
||||
BOOST_TEST_EQ(raii_tracker::move_count, 0);
|
||||
BOOST_TEST_EQ(raii_tracker::alloc_move_count, 2);
|
||||
|
||||
BOOST_TEST_EQ(map.begin()->first.get_allocator().tag_, 34);
|
||||
BOOST_TEST_EQ(map.begin()->second.get_allocator().tag_, 34);
|
||||
}
|
||||
|
||||
template <class X> static void set_uses_allocator_construction(X*)
|
||||
{
|
||||
reset_counts();
|
||||
|
||||
set_allocator_type alloc(allocator<raii_tracker>{12}, allocator<int>{34});
|
||||
|
||||
X set(1, alloc);
|
||||
set.emplace();
|
||||
|
||||
BOOST_TEST_EQ(raii_tracker::count, 1);
|
||||
BOOST_TEST_EQ(raii_tracker::move_count, 0);
|
||||
BOOST_TEST_EQ(raii_tracker::alloc_move_count, 1);
|
||||
|
||||
BOOST_TEST_EQ(set.begin()->get_allocator().tag_, 34);
|
||||
}
|
||||
|
||||
UNORDERED_TEST(map_uses_allocator_construction, ((flat_map)(node_map)))
|
||||
UNORDERED_TEST(set_uses_allocator_construction, ((flat_set)(node_set)))
|
||||
|
||||
RUN_TESTS()
|
||||
|
||||
#endif
|
||||