forked from MassiveAtoms/hashmap-bench
we can test 19 libs now!!!
This commit is contained in:
parent
f12d25a595
commit
24ff483dd6
3
.vscode/settings.json
vendored
3
.vscode/settings.json
vendored
@ -58,6 +58,7 @@
|
||||
"thread": "cpp",
|
||||
"cinttypes": "cpp",
|
||||
"typeinfo": "cpp",
|
||||
"variant": "cpp"
|
||||
"variant": "cpp",
|
||||
"sparse_hash_map": "cpp"
|
||||
}
|
||||
}
|
@ -3,12 +3,29 @@ project(another_studproject)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
set(CMAKE_CXX_FLAGS "-O3 -flto=thin -march=native")
|
||||
add_executable(another_studproject
|
||||
|
||||
|
||||
# Process Abseil's CMake build system
|
||||
add_subdirectory(./src/includes/3thparty/abseil-cpp
|
||||
./src/includes/3thparty/tsl
|
||||
)
|
||||
|
||||
|
||||
add_executable(studproject
|
||||
./src/includes/generator.h
|
||||
./src/generator.cpp
|
||||
./src/includes/tests.h
|
||||
./src/includes/aggregate_tests.h
|
||||
./src/includes/3thparty/emilib/loguru.cpp
|
||||
main.cpp
|
||||
|
||||
)
|
||||
|
||||
target_link_libraries(studproject
|
||||
absl::hash
|
||||
absl::node_hash_map
|
||||
absl::flat_hash_map
|
||||
pthread
|
||||
dl
|
||||
)
|
||||
|
||||
)
|
131
main.cpp
131
main.cpp
@ -1,53 +1,116 @@
|
||||
#include <iostream>
|
||||
#include <unordered_map>
|
||||
#include <functional>
|
||||
#include <fstream>
|
||||
// #include <sparsehash/sparse_hash_map>
|
||||
|
||||
#include "./src/includes/aggregate_tests.h"
|
||||
|
||||
// // we can use to switch the map implementations to that
|
||||
// // we can add some cli handling so we can specify which maps to tests (or all)
|
||||
|
||||
|
||||
|
||||
// typedef std::unordered_map<int, int> intmap;
|
||||
// typedef std::unordered_map<string, string> stringmap;
|
||||
//
|
||||
// // google sparse
|
||||
// typedef google::sparse_hash_map<int, int> intmap;
|
||||
// typedef google::sparse_hash_map<std::string,std::string> stringmap
|
||||
typedef std::unordered_map<int, int> intmap;
|
||||
typedef std::unordered_map<string, string> stringmap;
|
||||
// we can use ^ to switch the map implementations to that
|
||||
// we can add some cli handling so we can specify which maps to tests (or all)
|
||||
// typedef google::sparse_hash_map<string,string> stringmap;
|
||||
//
|
||||
// // google dense
|
||||
// typedef google::dense_hash_map<int, int> intmap;
|
||||
// typedef google::dense_hash_map<string,string> stringmap;
|
||||
//
|
||||
// // abseil nodehashmap
|
||||
typedef absl::node_hash_map<int, int> intmap;
|
||||
typedef absl::node_hash_map<string,string> stringmap;
|
||||
//
|
||||
// // flat hashmap
|
||||
// typedef absl::flat_hash_map<int, int> intmap;
|
||||
// typedef absl::flat_hash_map<string,string> stringmap;
|
||||
//
|
||||
// // tessil flat hashmap
|
||||
// typedef tsl::sparse_map<int, int> intmap;
|
||||
// typedef tsl::sparse_map<string,string> stringmap;
|
||||
//
|
||||
// // Tessil tsl::array_map
|
||||
// typedef tsl::array_map<int, int> intmap;
|
||||
// typedef tsl::array_map<string,string> stringmap;
|
||||
//
|
||||
// // Tessil tsl::ordered_map
|
||||
// typedef tsl::ordered_map<int, int> intmap;
|
||||
// typedef tsl::ordered_map<string,string> stringmap;
|
||||
//
|
||||
// // Tessil tsl::robin_map
|
||||
// typedef tsl::robin_map<int, int> intmap;
|
||||
// typedef tsl::robin_map<string,string> stringmap;
|
||||
//
|
||||
// // Tessil hopscotch_map
|
||||
// typedef tsl::hopscotch_map<int, int> intmap;
|
||||
// typedef tsl::hopscotch_map<string,string> stringmap;
|
||||
//
|
||||
// // Boost::unordered_map
|
||||
// typedef boost::unordered_map<int, int> intmap;
|
||||
// typedef boost::unordered_map<string,string> stringmap;
|
||||
//
|
||||
// // skarupke's unordered map
|
||||
// typedef ska::unordered_map<int, int> intmap;
|
||||
// typedef ska::unordered_map<string,string> stringmap;
|
||||
//
|
||||
// // skarupke's bytell hash map
|
||||
// typedef ska::bytell_hash_map<int, int> intmap;
|
||||
// typedef ska::bytell_hash_map<string,string> stringmap;
|
||||
//
|
||||
// // skarupke's flat hash map
|
||||
// typedef ska::flat_hash_map<int, int> intmap;
|
||||
// typedef ska::flat_hash_map<string,string> stringmap;
|
||||
//
|
||||
// // greg7mdp's flat hash map
|
||||
// typedef phmap::parallel_flat_hash_map<int, int> intmap;
|
||||
// typedef phmap::parallel_flat_hash_map<string,string> stringmap;
|
||||
//
|
||||
// // greg7mdp's hash map
|
||||
// typedef phmap::parallel_node_hash_map<int, int> intmap;
|
||||
// typedef phmap::parallel_node_hash_map<string,string> stringmap;
|
||||
// // emilib's hash map
|
||||
// typedef emilib::HashMap<int, int> intmap;
|
||||
// typedef emilib::HashMap<string,string> stringmap;
|
||||
// // martin flat map
|
||||
// typedef robin_hood::unordered_flat_map<int, int> intmap;
|
||||
// typedef robin_hood::unordered_flat_map<string,string> stringmap;
|
||||
// // martin flat map
|
||||
// typedef robin_hood::unordered_node_map<int, int> intmap;
|
||||
// typedef robin_hood::unordered_node_map<string,string> stringmap;
|
||||
|
||||
|
||||
int main() {
|
||||
time_point<steady_clock> start_test = steady_clock::now();
|
||||
// string_test(stringmap{}, 1); // process gets killed for sizes >35000
|
||||
int_test_aggregate(intmap{}, 30);
|
||||
string_test_aggregate(stringmap{}, 30);
|
||||
int_test_aggregate(intmap{}, 2);
|
||||
string_test_aggregate(stringmap{}, 2);
|
||||
time_point<steady_clock> end_test = steady_clock::now();
|
||||
std::cout << "\n\n 30 runs for all tests for 1 map: " << duration_cast<seconds>(end_test-start_test).count() << " seconds\n\n";
|
||||
|
||||
// test takes 52 mins for 10 runs for one hashmap
|
||||
// so it'll take ~3 hours per map if we want 30 runs per test
|
||||
// test takes 2hrs for 30 runs for one hashmap
|
||||
/* if the other maps have about the same operation times ************
|
||||
|
||||
// maps to benchmark:
|
||||
1. Google dense_hash_map
|
||||
2. Google sparse_hash_map
|
||||
// possible maps to bench. priorities are that the interface must be the same/similar to unordered_map
|
||||
// and that we don't have to jump through hoops to get it to work
|
||||
1. Google dense_hash_map [y] https://github.com/sparsehash/sparsehash
|
||||
2. Google sparse_hash_map [y]
|
||||
3. abseil node_hash_map [y] https://abseil.io/docs/cpp/tools/cmake-installs
|
||||
4. abseil flat_hash_map [y]
|
||||
5. Tessil/sparse-map/ [y] header only implementation for all tessil
|
||||
6. Tessil/hopscotch-map[y]
|
||||
7. tessil/robin-map[y] [y]
|
||||
8. Boost unordered_map [y] just install boost with your package manager
|
||||
9. skarupke/flat_hash_map [y] header only implementation
|
||||
10. skarupke /bytell_hash_map [y]
|
||||
11. skarupke/unordered_map [y]
|
||||
12. greg7mdp/parallel-hashmap/paralel_flat [y] header only
|
||||
13. greg7mdp/parallel-hashmap/paralel_node [y]
|
||||
17. emilk/emilib emilib::hashmap [y] header only
|
||||
18. martinus robin_hood::unordered_node_map [y]
|
||||
19. martinus/robin_hood/ flatmap [y]
|
||||
3. folly F14ValueMap
|
||||
4. folly F14NodeMap
|
||||
5. Tessil/ordered-map
|
||||
6. Tessil/array-hash
|
||||
7. Tessil/hopscotch-map
|
||||
8. Tessil/sparse-map/
|
||||
9. abseil node_hash_map
|
||||
10. abseil flat_hash_map
|
||||
11. Glib GHashTable
|
||||
12. Boost unordered_map
|
||||
13. Qt QHash
|
||||
14. skarupke/flat_hash_map
|
||||
15. greg7mdp/sparsepp
|
||||
16. greg7mdp /parallel-hashmap (phmap::flat_hash_map and phmap::node_hash_map)
|
||||
17. emilk/emilib emilib::hashmap
|
||||
18. martinus robin_hood::unordered_node_map
|
||||
19. martinus/robin-hood-hashing/
|
||||
20. skarupke /flat_hash_map
|
||||
5. Tessil/ordered-map [n] something is wrong with this one, verrrrry slow
|
||||
6. Tessil/array-hash[n] (not with a small modification of the insert function)
|
||||
*/
|
||||
|
||||
}
|
||||
|
305
results.csv
305
results.csv
@ -0,0 +1,305 @@
|
||||
|
||||
int_insert, 'google::dense_hash_map<int, int>', 26, 34, 64, 40, 40, 50, 40, 44, 51, 45, 77, 47, 51, 57
|
||||
int_succ_lookup, 'google::dense_hash_map<int, int>', 10, 18, 30, 20, 18, 22, 21, 23, 24, 23, 27, 24, 25, 26
|
||||
int_nosucc_lookup, 'google::dense_hash_map<int, int>', 31, 33, 62, 54, 43, 40, 43, 49, 56, 42, 53, 52, 55, 59
|
||||
int_delete, 'google::dense_hash_map<int, int>', 13, 11, 26, 38, 16, 13, 15, 17, 22, 15, 32, 19, 23, 24
|
||||
int_insert, 'google::dense_hash_map<int, int>', 19, 53, 38, 36, 40, 36, 40, 43, 51, 41, 44, 48, 52, 59
|
||||
int_succ_lookup, 'google::dense_hash_map<int, int>', 7, 19, 20, 17, 18, 20, 21, 22, 24, 23, 24, 24, 27, 26
|
||||
int_nosucc_lookup, 'google::dense_hash_map<int, int>', 23, 72, 39, 36, 43, 40, 43, 50, 56, 42, 50, 49, 57, 59
|
||||
int_delete, 'google::dense_hash_map<int, int>', 9, 35, 17, 13, 17, 16, 15, 21, 22, 17, 22, 18, 24, 24
|
||||
int_insert, 'google::dense_hash_map<std::__cxx11::basic_string<char>, std::__cxx11::basic_string<char> >', 146, 155, 159, 175, 178, 161, 167, 179, 195, 185, 191, 202, 205, 231
|
||||
int_succ_lookup, 'google::dense_hash_map<std::__cxx11::basic_string<char>, std::__cxx11::basic_string<char> >', 87, 98, 110, 106, 110, 117, 113, 112, 121, 117, 119, 119, 129, 128
|
||||
int_nosucc_lookup, 'google::dense_hash_map<std::__cxx11::basic_string<char>, std::__cxx11::basic_string<char> >', 189, 160, 184, 170, 182, 200, 189, 184, 192, 170, 188, 185, 199, 193
|
||||
int_delete, 'google::dense_hash_map<std::__cxx11::basic_string<char>, std::__cxx11::basic_string<char> >', 91, 91, 113, 119, 108, 134, 103, 104, 110, 105, 108, 109, 118, 131
|
||||
int_insert, 'google::dense_hash_map<std::__cxx11::basic_string<char>, std::__cxx11::basic_string<char> >', 213, 154, 148, 169, 185, 162, 170, 175, 192, 180, 186, 197, 206, 214
|
||||
int_succ_lookup, 'google::dense_hash_map<std::__cxx11::basic_string<char>, std::__cxx11::basic_string<char> >', 122, 91, 98, 103, 109, 101, 107, 107, 114, 114, 120, 123, 127, 141
|
||||
int_nosucc_lookup, 'google::dense_hash_map<std::__cxx11::basic_string<char>, std::__cxx11::basic_string<char> >', 234, 166, 159, 174, 183, 160, 173, 181, 184, 169, 183, 194, 200, 194
|
||||
int_delete, 'google::dense_hash_map<std::__cxx11::basic_string<char>, std::__cxx11::basic_string<char> >', 126, 90, 89, 111, 105, 94, 96, 102, 112, 111, 110, 111, 123, 124
|
||||
int_insert, 'absl::node_hash_map<int, int>', 101, 116, 91, 90, 101, 99, 107, 106, 114, 114, 123, 161, 138, 130
|
||||
int_succ_lookup, 'absl::node_hash_map<int, int>', 24, 87, 40, 40, 52, 48, 49, 49, 58, 59, 61, 68, 61, 73
|
||||
int_nosucc_lookup, 'absl::node_hash_map<int, int>', 464, 141, 90, 95, 99, 98, 100, 105, 112, 114, 118, 134, 144, 132
|
||||
int_delete, 'absl::node_hash_map<int, int>', 140, 257, 139, 126, 138, 145, 146, 145, 189, 182, 179, 222, 201, 270
|
||||
int_insert, 'absl::node_hash_map<int, int>', 73, 110, 83, 90, 103, 99, 101, 105, 116, 118, 120, 130, 142, 134
|
||||
int_succ_lookup, 'absl::node_hash_map<int, int>', 19, 64, 38, 40, 52, 47, 50, 51, 62, 61, 61, 61, 61, 70
|
||||
int_nosucc_lookup, 'absl::node_hash_map<int, int>', 502, 192, 90, 90, 101, 99, 111, 105, 125, 117, 122, 127, 141, 126
|
||||
int_delete, 'absl::node_hash_map<int, int>', 95, 269, 110, 121, 144, 139, 175, 146, 190, 187, 187, 187, 194, 222
|
||||
int_insert, 'absl::node_hash_map<std::__cxx11::basic_string<char>, std::__cxx11::basic_string<char> >', 127, 163, 171, 180, 190, 187, 193, 226, 208, 235, 243, 286, 287, 228
|
||||
int_succ_lookup, 'absl::node_hash_map<std::__cxx11::basic_string<char>, std::__cxx11::basic_string<char> >', 185, 164, 173, 178, 204, 197, 219, 206, 231, 236, 240, 290, 240, 278
|
||||
int_nosucc_lookup, 'absl::node_hash_map<std::__cxx11::basic_string<char>, std::__cxx11::basic_string<char> >', 938, 139, 133, 145, 145, 152, 333, 205, 167, 189, 187, 268, 221, 183
|
||||
int_delete, 'absl::node_hash_map<std::__cxx11::basic_string<char>, std::__cxx11::basic_string<char> >', 244, 223, 191, 190, 208, 211, 326, 303, 250, 253, 252, 292, 259, 289
|
||||
int_insert, 'absl::node_hash_map<std::__cxx11::basic_string<char>, std::__cxx11::basic_string<char> >', 130, 148, 169, 174, 187, 197, 191, 203, 220, 211, 218, 237, 259, 232
|
||||
int_succ_lookup, 'absl::node_hash_map<std::__cxx11::basic_string<char>, std::__cxx11::basic_string<char> >', 106, 142, 169, 178, 194, 199, 199, 199, 248, 236, 239, 247, 243, 286
|
||||
int_nosucc_lookup, 'absl::node_hash_map<std::__cxx11::basic_string<char>, std::__cxx11::basic_string<char> >', 844, 133, 129, 140, 144, 146, 147, 158, 164, 164, 168, 178, 200, 188
|
||||
int_delete, 'absl::node_hash_map<std::__cxx11::basic_string<char>, std::__cxx11::basic_string<char> >', 223, 163, 179, 187, 213, 210, 233, 225, 244, 249, 275, 260, 335, 296
|
||||
int_insert, 'absl::flat_hash_map<int, int>', 42, 53, 45, 44, 55, 45, 41, 41, 55, 55, 52, 58, 69, 71
|
||||
int_succ_lookup, 'absl::flat_hash_map<int, int>', 25, 58, 53, 31, 68, 29, 25, 24, 35, 34, 35, 35, 34, 52
|
||||
int_nosucc_lookup, 'absl::flat_hash_map<int, int>', 334, 76, 54, 59, 73, 58, 50, 56, 67, 64, 63, 69, 79, 83
|
||||
int_delete, 'absl::flat_hash_map<int, int>', 101, 61, 58, 51, 60, 48, 33, 35, 51, 46, 47, 48, 47, 69
|
||||
int_insert, 'absl::flat_hash_map<int, int>', 43, 29, 76, 40, 51, 38, 38, 45, 86, 52, 52, 57, 69, 70
|
||||
int_succ_lookup, 'absl::flat_hash_map<int, int>', 16, 10, 52, 26, 40, 25, 24, 26, 56, 33, 38, 34, 35, 44
|
||||
int_nosucc_lookup, 'absl::flat_hash_map<int, int>', 188, 36, 74, 63, 63, 50, 49, 55, 65, 61, 65, 68, 79, 80
|
||||
int_delete, 'absl::flat_hash_map<int, int>', 29, 26, 157, 59, 57, 32, 32, 36, 48, 45, 49, 49, 46, 66
|
||||
int_insert, 'absl::flat_hash_map<std::__cxx11::basic_string<char>, std::__cxx11::basic_string<char> >', 129, 155, 198, 174, 188, 187, 188, 194, 212, 213, 216, 224, 239, 240
|
||||
int_succ_lookup, 'absl::flat_hash_map<std::__cxx11::basic_string<char>, std::__cxx11::basic_string<char> >', 88, 148, 140, 115, 139, 133, 134, 135, 164, 165, 164, 164, 164, 193
|
||||
int_nosucc_lookup, 'absl::flat_hash_map<std::__cxx11::basic_string<char>, std::__cxx11::basic_string<char> >', 978, 157, 178, 135, 146, 146, 149, 155, 171, 170, 168, 177, 195, 199
|
||||
int_delete, 'absl::flat_hash_map<std::__cxx11::basic_string<char>, std::__cxx11::basic_string<char> >', 193, 141, 139, 100, 130, 132, 130, 134, 162, 158, 159, 161, 162, 196
|
||||
int_insert, 'absl::flat_hash_map<std::__cxx11::basic_string<char>, std::__cxx11::basic_string<char> >', 263, 181, 166, 169, 184, 187, 191, 194, 210, 209, 213, 220, 236, 243
|
||||
int_succ_lookup, 'absl::flat_hash_map<std::__cxx11::basic_string<char>, std::__cxx11::basic_string<char> >', 173, 136, 115, 115, 135, 135, 138, 136, 164, 164, 165, 169, 165, 193
|
||||
int_nosucc_lookup, 'absl::flat_hash_map<std::__cxx11::basic_string<char>, std::__cxx11::basic_string<char> >', 576, 223, 129, 142, 145, 143, 211, 297, 168, 163, 174, 175, 193, 193
|
||||
int_delete, 'absl::flat_hash_map<std::__cxx11::basic_string<char>, std::__cxx11::basic_string<char> >', 123, 132, 101, 104, 134, 129, 180, 137, 177, 160, 159, 159, 157, 195
|
||||
int_insert, 'absl::flat_hash_map<int, int>', 42, 32, 92, 48, 35, 36, 40, 41, 50, 51, 54, 56, 71, 71
|
||||
int_succ_lookup, 'absl::flat_hash_map<int, int>', 19, 20, 68, 26, 24, 24, 26, 25, 34, 35, 38, 35, 36, 46
|
||||
int_nosucc_lookup, 'absl::flat_hash_map<int, int>', 312, 40, 58, 57, 52, 48, 52, 50, 63, 62, 65, 71, 77, 78
|
||||
int_delete, 'absl::flat_hash_map<int, int>', 55, 28, 177, 37, 30, 29, 35, 30, 46, 52, 45, 54, 45, 71
|
||||
int_insert, 'absl::flat_hash_map<int, int>', 27, 31, 30, 37, 36, 39, 37, 41, 50, 52, 54, 58, 71, 71
|
||||
int_succ_lookup, 'absl::flat_hash_map<int, int>', 8, 12, 16, 23, 25, 26, 29, 25, 34, 35, 41, 36, 36, 52
|
||||
int_nosucc_lookup, 'absl::flat_hash_map<int, int>', 173, 39, 38, 51, 49, 51, 51, 51, 62, 65, 66, 67, 82, 80
|
||||
int_delete, 'absl::flat_hash_map<int, int>', 19, 25, 22, 62, 32, 29, 30, 31, 44, 45, 57, 45, 52, 73
|
||||
int_insert, 'absl::flat_hash_map<std::__cxx11::basic_string<char>, std::__cxx11::basic_string<char> >', 183, 178, 155, 154, 193, 187, 189, 193, 208, 223, 217, 222, 238, 243
|
||||
int_succ_lookup, 'absl::flat_hash_map<std::__cxx11::basic_string<char>, std::__cxx11::basic_string<char> >', 104, 171, 111, 114, 138, 136, 153, 141, 166, 164, 165, 165, 168, 192
|
||||
int_nosucc_lookup, 'absl::flat_hash_map<std::__cxx11::basic_string<char>, std::__cxx11::basic_string<char> >', 347, 127, 126, 132, 144, 144, 145, 168, 164, 163, 169, 174, 188, 195
|
||||
int_delete, 'absl::flat_hash_map<std::__cxx11::basic_string<char>, std::__cxx11::basic_string<char> >', 102, 101, 94, 96, 133, 134, 131, 137, 159, 155, 159, 161, 158, 188
|
||||
int_insert, 'absl::flat_hash_map<std::__cxx11::basic_string<char>, std::__cxx11::basic_string<char> >', 186, 140, 154, 159, 185, 182, 187, 214, 207, 257, 214, 220, 292, 240
|
||||
int_succ_lookup, 'absl::flat_hash_map<std::__cxx11::basic_string<char>, std::__cxx11::basic_string<char> >', 150, 101, 112, 114, 136, 137, 138, 138, 156, 219, 164, 165, 200, 195
|
||||
int_nosucc_lookup, 'absl::flat_hash_map<std::__cxx11::basic_string<char>, std::__cxx11::basic_string<char> >', 370, 122, 127, 131, 138, 141, 150, 149, 163, 274, 169, 173, 269, 206
|
||||
int_delete, 'absl::flat_hash_map<std::__cxx11::basic_string<char>, std::__cxx11::basic_string<char> >', 127, 83, 93, 100, 132, 133, 128, 125, 154, 221, 159, 158, 206, 212
|
||||
int_insert, 'tsl::sparse_map<int, int>', 67, 88, 108, 81, 94, 97, 118, 118, 142, 142, 143, 151, 159, 171
|
||||
int_succ_lookup, 'tsl::sparse_map<int, int>', 20, 35, 36, 32, 36, 42, 50, 50, 59, 68, 68, 72, 76, 80
|
||||
int_nosucc_lookup, 'tsl::sparse_map<int, int>', 79, 130, 112, 90, 103, 111, 127, 133, 151, 161, 166, 178, 176, 202
|
||||
int_delete, 'tsl::sparse_map<int, int>', 32, 42, 54, 41, 56, 51, 54, 65, 69, 72, 80, 82, 85, 101
|
||||
int_insert, 'tsl::sparse_map<int, int>', 65, 103, 107, 98, 90, 155, 128, 137, 127, 155, 140, 175, 153, 159
|
||||
int_succ_lookup, 'tsl::sparse_map<int, int>', 20, 29, 71, 33, 36, 120, 49, 53, 56, 70, 66, 73, 72, 76
|
||||
int_nosucc_lookup, 'tsl::sparse_map<int, int>', 74, 85, 121, 88, 101, 147, 120, 130, 140, 151, 158, 173, 177, 184
|
||||
int_delete, 'tsl::sparse_map<int, int>', 31, 36, 68, 41, 48, 53, 54, 59, 67, 72, 76, 82, 89, 90
|
||||
int_insert, 'tsl::sparse_map<std::__cxx11::basic_string<char>, std::__cxx11::basic_string<char> >', 344, 626, 395, 487, 589, 462, 509, 545, 654, 467, 504, 556, 648, 679
|
||||
int_succ_lookup, 'tsl::sparse_map<std::__cxx11::basic_string<char>, std::__cxx11::basic_string<char> >', 94, 151, 119, 125, 133, 135, 139, 145, 159, 154, 164, 167, 175, 178
|
||||
int_nosucc_lookup, 'tsl::sparse_map<std::__cxx11::basic_string<char>, std::__cxx11::basic_string<char> >', 650, 576, 600, 494, 580, 461, 516, 552, 653, 472, 519, 549, 621, 676
|
||||
int_delete, 'tsl::sparse_map<std::__cxx11::basic_string<char>, std::__cxx11::basic_string<char> >', 325, 380, 376, 313, 360, 285, 322, 344, 400, 310, 336, 365, 395, 426
|
||||
int_insert, 'tsl::sparse_map<std::__cxx11::basic_string<char>, std::__cxx11::basic_string<char> >', 547, 407, 391, 469, 549, 432, 477, 569, 603, 474, 514, 559, 602, 649
|
||||
int_succ_lookup, 'tsl::sparse_map<std::__cxx11::basic_string<char>, std::__cxx11::basic_string<char> >', 71, 103, 115, 122, 130, 136, 137, 162, 150, 158, 161, 166, 169, 178
|
||||
int_nosucc_lookup, 'tsl::sparse_map<std::__cxx11::basic_string<char>, std::__cxx11::basic_string<char> >', 493, 443, 382, 478, 565, 428, 470, 709, 603, 468, 509, 544, 596, 641
|
||||
int_delete, 'tsl::sparse_map<std::__cxx11::basic_string<char>, std::__cxx11::basic_string<char> >', 253, 265, 256, 301, 356, 287, 312, 387, 396, 315, 349, 366, 392, 427
|
||||
int_insert, 'tsl::sparse_map<int, int>', 69, 234, 77, 145, 102, 108, 114, 117, 139, 143, 148, 157, 276, 192
|
||||
int_succ_lookup, 'tsl::sparse_map<int, int>', 21, 52, 36, 79, 38, 47, 47, 50, 56, 67, 72, 77, 75, 87
|
||||
int_nosucc_lookup, 'tsl::sparse_map<int, int>', 90, 135, 85, 127, 103, 113, 125, 135, 152, 154, 164, 178, 187, 183
|
||||
int_delete, 'tsl::sparse_map<int, int>', 56, 49, 38, 44, 48, 50, 55, 60, 71, 71, 77, 96, 90, 95
|
||||
int_insert, 'tsl::sparse_map<int, int>', 67, 87, 102, 153, 89, 119, 129, 135, 133, 137, 158, 152, 181, 159
|
||||
int_succ_lookup, 'tsl::sparse_map<int, int>', 19, 28, 53, 23, 35, 47, 49, 89, 59, 66, 73, 102, 77, 80
|
||||
int_nosucc_lookup, 'tsl::sparse_map<int, int>', 72, 89, 92, 85, 97, 115, 119, 184, 151, 148, 172, 180, 290, 178
|
||||
int_delete, 'tsl::sparse_map<int, int>', 31, 66, 49, 40, 52, 53, 56, 63, 72, 72, 79, 85, 87, 93
|
||||
int_insert, 'tsl::sparse_map<std::__cxx11::basic_string<char>, std::__cxx11::basic_string<char> >', 323, 451, 559, 481, 558, 464, 513, 561, 640, 486, 527, 576, 675, 708
|
||||
int_succ_lookup, 'tsl::sparse_map<std::__cxx11::basic_string<char>, std::__cxx11::basic_string<char> >', 67, 159, 154, 120, 141, 136, 141, 153, 181, 167, 173, 177, 187, 191
|
||||
int_nosucc_lookup, 'tsl::sparse_map<std::__cxx11::basic_string<char>, std::__cxx11::basic_string<char> >', 614, 628, 449, 501, 560, 479, 518, 568, 962, 478, 537, 577, 661, 714
|
||||
int_delete, 'tsl::sparse_map<std::__cxx11::basic_string<char>, std::__cxx11::basic_string<char> >', 245, 309, 251, 312, 377, 296, 311, 391, 431, 326, 352, 383, 415, 449
|
||||
int_insert, 'tsl::sparse_map<std::__cxx11::basic_string<char>, std::__cxx11::basic_string<char> >', 547, 418, 406, 495, 576, 454, 494, 554, 641, 485, 544, 588, 633, 668
|
||||
int_succ_lookup, 'tsl::sparse_map<std::__cxx11::basic_string<char>, std::__cxx11::basic_string<char> >', 69, 101, 116, 128, 136, 139, 151, 150, 165, 167, 169, 176, 184, 190
|
||||
int_nosucc_lookup, 'tsl::sparse_map<std::__cxx11::basic_string<char>, std::__cxx11::basic_string<char> >', 546, 446, 408, 511, 591, 447, 504, 550, 634, 488, 536, 583, 643, 662
|
||||
int_delete, 'tsl::sparse_map<std::__cxx11::basic_string<char>, std::__cxx11::basic_string<char> >', 340, 279, 262, 321, 372, 301, 329, 361, 423, 328, 367, 386, 417, 441
|
||||
int_insert, 'tsl::robin_map<int, int>', 18, 25, 37, 36, 35, 28, 31, 31, 41, 26, 30, 33, 39, 53
|
||||
int_succ_lookup, 'tsl::robin_map<int, int>', 9, 25, 21, 20, 20, 23, 24, 24, 24, 20, 29, 24, 24, 26
|
||||
int_nosucc_lookup, 'tsl::robin_map<int, int>', 23, 44, 28, 33, 39, 32, 30, 34, 42, 29, 32, 38, 38, 49
|
||||
int_delete, 'tsl::robin_map<int, int>', 21, 51, 24, 24, 31, 22, 30, 27, 35, 23, 26, 29, 34, 68
|
||||
int_insert, 'tsl::robin_map<int, int>', 22, 34, 31, 36, 61, 26, 32, 30, 39, 27, 32, 33, 37, 42
|
||||
int_succ_lookup, 'tsl::robin_map<int, int>', 11, 24, 27, 22, 24, 24, 37, 24, 24, 24, 25, 25, 25, 28
|
||||
int_nosucc_lookup, 'tsl::robin_map<int, int>', 26, 38, 30, 33, 36, 33, 31, 36, 45, 34, 33, 35, 40, 46
|
||||
int_delete, 'tsl::robin_map<int, int>', 23, 41, 25, 33, 30, 21, 25, 28, 36, 25, 27, 31, 33, 38
|
||||
int_insert, 'tsl::robin_map<std::__cxx11::basic_string<char>, std::__cxx11::basic_string<char> >', 199, 145, 149, 172, 182, 166, 174, 176, 238, 173, 218, 193, 206, 209
|
||||
int_succ_lookup, 'tsl::robin_map<std::__cxx11::basic_string<char>, std::__cxx11::basic_string<char> >', 95, 97, 100, 108, 114, 110, 112, 109, 180, 113, 161, 127, 130, 133
|
||||
int_nosucc_lookup, 'tsl::robin_map<std::__cxx11::basic_string<char>, std::__cxx11::basic_string<char> >', 174, 128, 119, 133, 149, 126, 136, 140, 250, 132, 211, 159, 165, 174
|
||||
int_delete, 'tsl::robin_map<std::__cxx11::basic_string<char>, std::__cxx11::basic_string<char> >', 126, 112, 100, 119, 150, 104, 114, 167, 141, 108, 183, 135, 169, 157
|
||||
int_insert, 'tsl::robin_map<std::__cxx11::basic_string<char>, std::__cxx11::basic_string<char> >', 140, 166, 149, 165, 177, 157, 180, 180, 195, 179, 188, 208, 213, 211
|
||||
int_succ_lookup, 'tsl::robin_map<std::__cxx11::basic_string<char>, std::__cxx11::basic_string<char> >', 90, 133, 105, 104, 114, 109, 113, 115, 122, 119, 122, 150, 134, 139
|
||||
int_nosucc_lookup, 'tsl::robin_map<std::__cxx11::basic_string<char>, std::__cxx11::basic_string<char> >', 125, 153, 116, 134, 149, 129, 137, 143, 165, 141, 145, 149, 161, 171
|
||||
int_delete, 'tsl::robin_map<std::__cxx11::basic_string<char>, std::__cxx11::basic_string<char> >', 105, 116, 95, 117, 157, 109, 118, 127, 149, 117, 127, 165, 148, 159
|
||||
int_insert, 'tsl::hopscotch_map<int, int>', 43, 47, 135, 85, 62, 72, 89, 103, 66, 78, 94, 115, 163, 114
|
||||
int_succ_lookup, 'tsl::hopscotch_map<int, int>', 11, 12, 69, 24, 20, 23, 29, 30, 22, 25, 28, 33, 42, 40
|
||||
int_nosucc_lookup, 'tsl::hopscotch_map<int, int>', 437, 56, 114, 87, 64, 76, 87, 111, 67, 80, 96, 123, 182, 110
|
||||
int_delete, 'tsl::hopscotch_map<int, int>', 26, 14, 24, 24, 20, 24, 27, 34, 24, 26, 32, 37, 47, 32
|
||||
int_insert, 'tsl::hopscotch_map<int, int>', 56, 76, 89, 77, 62, 168, 97, 178, 67, 80, 93, 114, 164, 70
|
||||
int_succ_lookup, 'tsl::hopscotch_map<int, int>', 14, 23, 20, 23, 21, 62, 26, 30, 22, 25, 28, 35, 40, 24
|
||||
int_nosucc_lookup, 'tsl::hopscotch_map<int, int>', 232, 103, 76, 84, 64, 122, 91, 122, 65, 81, 95, 121, 172, 73
|
||||
int_delete, 'tsl::hopscotch_map<int, int>', 19, 41, 20, 25, 20, 41, 31, 36, 21, 26, 29, 36, 54, 25
|
||||
int_insert, 'tsl::hopscotch_map<std::__cxx11::basic_string<char>, std::__cxx11::basic_string<char> >', 230, 167, 163, 190, 161, 167, 181, 207, 172, 188, 204, 341, 314, 260
|
||||
int_succ_lookup, 'tsl::hopscotch_map<std::__cxx11::basic_string<char>, std::__cxx11::basic_string<char> >', 156, 98, 99, 113, 100, 108, 109, 118, 114, 115, 123, 208, 142, 148
|
||||
int_nosucc_lookup, 'tsl::hopscotch_map<std::__cxx11::basic_string<char>, std::__cxx11::basic_string<char> >', 887, 194, 151, 194, 141, 162, 174, 206, 162, 172, 199, 302, 336, 192
|
||||
int_delete, 'tsl::hopscotch_map<std::__cxx11::basic_string<char>, std::__cxx11::basic_string<char> >', 143, 90, 90, 104, 100, 100, 107, 113, 101, 113, 117, 217, 136, 112
|
||||
int_insert, 'tsl::hopscotch_map<std::__cxx11::basic_string<char>, std::__cxx11::basic_string<char> >', 140, 170, 162, 186, 157, 216, 185, 206, 173, 226, 205, 274, 322, 185
|
||||
int_succ_lookup, 'tsl::hopscotch_map<std::__cxx11::basic_string<char>, std::__cxx11::basic_string<char> >', 74, 98, 98, 109, 103, 116, 112, 114, 112, 136, 123, 181, 145, 119
|
||||
int_nosucc_lookup, 'tsl::hopscotch_map<std::__cxx11::basic_string<char>, std::__cxx11::basic_string<char> >', 623, 190, 152, 194, 149, 174, 175, 207, 147, 185, 195, 261, 347, 161
|
||||
int_delete, 'tsl::hopscotch_map<std::__cxx11::basic_string<char>, std::__cxx11::basic_string<char> >', 83, 92, 94, 139, 101, 102, 109, 112, 107, 109, 116, 126, 152, 252
|
||||
int_insert, 'boost::unordered::unordered_map<int, int>', 175, 206, 424, 392, 373, 430, 290, 255, 369, 404, 268, 214, 222, 316
|
||||
int_succ_lookup, 'boost::unordered::unordered_map<int, int>', 66, 56, 153, 223, 89, 143, 84, 74, 208, 154, 85, 124, 79, 79
|
||||
int_nosucc_lookup, 'boost::unordered::unordered_map<int, int>', 263, 164, 248, 286, 201, 348, 309, 204, 384, 257, 259, 275, 223, 233
|
||||
int_delete, 'boost::unordered::unordered_map<int, int>', 393, 144, 217, 320, 237, 342, 414, 265, 336, 408, 433, 539, 409, 338
|
||||
int_insert, 'boost::unordered::unordered_map<int, int>', 159, 323, 316, 207, 294, 469, 369, 347, 300, 326, 311, 243, 318, 300
|
||||
int_succ_lookup, 'boost::unordered::unordered_map<int, int>', 67, 200, 84, 154, 153, 201, 136, 128, 87, 160, 169, 174, 176, 241
|
||||
int_nosucc_lookup, 'boost::unordered::unordered_map<int, int>', 155, 367, 212, 211, 287, 304, 398, 219, 274, 343, 338, 447, 255, 363
|
||||
int_delete, 'boost::unordered::unordered_map<int, int>', 293, 486, 282, 224, 332, 472, 311, 326, 447, 377, 388, 374, 295, 345
|
||||
int_insert, 'boost::unordered::unordered_map<std::__cxx11::basic_string<char>, std::__cxx11::basic_string<char> >', 446, 614, 504, 317, 415, 347, 787, 761, 534, 390, 502, 428, 509, 712
|
||||
int_succ_lookup, 'boost::unordered::unordered_map<std::__cxx11::basic_string<char>, std::__cxx11::basic_string<char> >', 308, 506, 427, 284, 268, 373, 514, 491, 417, 367, 488, 388, 317, 326
|
||||
int_nosucc_lookup, 'boost::unordered::unordered_map<std::__cxx11::basic_string<char>, std::__cxx11::basic_string<char> >', 445, 405, 398, 370, 294, 464, 562, 326, 516, 502, 324, 528, 624, 564
|
||||
int_delete, 'boost::unordered::unordered_map<std::__cxx11::basic_string<char>, std::__cxx11::basic_string<char> >', 363, 482, 436, 472, 384, 359, 683, 498, 516, 393, 549, 442, 672, 706
|
||||
int_insert, 'boost::unordered::unordered_map<std::__cxx11::basic_string<char>, std::__cxx11::basic_string<char> >', 315, 538, 328, 377, 462, 270, 385, 429, 560, 416, 497, 570, 474, 581
|
||||
int_succ_lookup, 'boost::unordered::unordered_map<std::__cxx11::basic_string<char>, std::__cxx11::basic_string<char> >', 306, 249, 338, 348, 433, 326, 318, 381, 313, 410, 292, 306, 529, 330
|
||||
int_nosucc_lookup, 'boost::unordered::unordered_map<std::__cxx11::basic_string<char>, std::__cxx11::basic_string<char> >', 504, 285, 511, 417, 624, 384, 417, 482, 306, 450, 262, 511, 336, 616
|
||||
int_delete, 'boost::unordered::unordered_map<std::__cxx11::basic_string<char>, std::__cxx11::basic_string<char> >', 459, 495, 417, 348, 393, 748, 346, 409, 478, 434, 439, 590, 443, 659
|
||||
int_insert, 'ska::unordered_map<int, int>', 135, 168, 222, 129, 158, 137, 138, 165, 178, 144, 153, 163, 180, 297
|
||||
int_succ_lookup, 'ska::unordered_map<int, int>', 41, 79, 60, 33, 38, 35, 36, 39, 43, 40, 43, 44, 45, 98
|
||||
int_nosucc_lookup, 'ska::unordered_map<int, int>', 212, 141, 199, 127, 195, 134, 139, 157, 170, 144, 209, 163, 175, 273
|
||||
int_delete, 'ska::unordered_map<int, int>', 206, 157, 120, 107, 152, 137, 179, 152, 171, 151, 180, 193, 185, 253
|
||||
int_insert, 'ska::unordered_map<int, int>', 94, 203, 115, 130, 286, 169, 145, 156, 186, 150, 214, 167, 179, 202
|
||||
int_succ_lookup, 'ska::unordered_map<int, int>', 23, 76, 32, 47, 82, 46, 52, 39, 61, 38, 44, 46, 43, 47
|
||||
int_nosucc_lookup, 'ska::unordered_map<int, int>', 78, 185, 109, 169, 231, 131, 173, 155, 219, 147, 262, 181, 173, 184
|
||||
int_delete, 'ska::unordered_map<int, int>', 57, 219, 100, 134, 283, 129, 152, 141, 181, 153, 154, 169, 160, 170
|
||||
int_insert, 'ska::unordered_map<std::__cxx11::basic_string<char>, std::__cxx11::basic_string<char> >', 184, 182, 221, 241, 240, 230, 293, 253, 260, 233, 236, 249, 270, 451
|
||||
int_succ_lookup, 'ska::unordered_map<std::__cxx11::basic_string<char>, std::__cxx11::basic_string<char> >', 115, 147, 195, 191, 185, 180, 246, 193, 196, 198, 191, 195, 240, 355
|
||||
int_nosucc_lookup, 'ska::unordered_map<std::__cxx11::basic_string<char>, std::__cxx11::basic_string<char> >', 146, 242, 234, 248, 257, 229, 312, 264, 269, 241, 270, 278, 287, 427
|
||||
int_delete, 'ska::unordered_map<std::__cxx11::basic_string<char>, std::__cxx11::basic_string<char> >', 138, 217, 210, 206, 227, 194, 228, 228, 221, 199, 214, 217, 224, 352
|
||||
int_insert, 'ska::unordered_map<std::__cxx11::basic_string<char>, std::__cxx11::basic_string<char> >', 154, 212, 345, 218, 268, 350, 239, 337, 247, 221, 252, 237, 249, 269
|
||||
int_succ_lookup, 'ska::unordered_map<std::__cxx11::basic_string<char>, std::__cxx11::basic_string<char> >', 132, 165, 294, 192, 187, 241, 185, 310, 196, 216, 201, 196, 206, 236
|
||||
int_nosucc_lookup, 'ska::unordered_map<std::__cxx11::basic_string<char>, std::__cxx11::basic_string<char> >', 258, 199, 335, 218, 224, 309, 252, 356, 239, 217, 225, 231, 248, 329
|
||||
int_delete, 'ska::unordered_map<std::__cxx11::basic_string<char>, std::__cxx11::basic_string<char> >', 212, 190, 186, 214, 214, 248, 205, 284, 210, 209, 242, 213, 222, 290
|
||||
int_insert, 'ska::bytell_hash_map<int, int>', 60, 50, 71, 62, 54, 71, 76, 90, 68, 78, 120, 117, 150, 100
|
||||
int_succ_lookup, 'ska::bytell_hash_map<int, int>', 14, 9, 20, 14, 20, 27, 23, 23, 25, 26, 29, 29, 30, 38
|
||||
int_nosucc_lookup, 'ska::bytell_hash_map<int, int>', 118, 56, 91, 68, 54, 70, 74, 103, 67, 75, 93, 115, 184, 104
|
||||
int_delete, 'ska::bytell_hash_map<int, int>', 27, 19, 36, 27, 26, 35, 29, 41, 33, 37, 40, 51, 67, 39
|
||||
int_insert, 'ska::bytell_hash_map<int, int>', 44, 113, 53, 106, 75, 61, 89, 155, 78, 78, 99, 118, 177, 80
|
||||
int_succ_lookup, 'ska::bytell_hash_map<int, int>', 8, 40, 21, 20, 29, 21, 24, 45, 26, 28, 28, 30, 44, 28
|
||||
int_nosucc_lookup, 'ska::bytell_hash_map<int, int>', 66, 111, 66, 79, 78, 64, 81, 160, 67, 77, 93, 113, 218, 70
|
||||
int_delete, 'ska::bytell_hash_map<int, int>', 20, 24, 26, 26, 32, 27, 33, 75, 29, 34, 40, 48, 66, 37
|
||||
int_insert, 'ska::bytell_hash_map<std::__cxx11::basic_string<char>, std::__cxx11::basic_string<char> >', 170, 191, 176, 198, 185, 190, 213, 222, 197, 205, 216, 240, 367, 210
|
||||
int_succ_lookup, 'ska::bytell_hash_map<std::__cxx11::basic_string<char>, std::__cxx11::basic_string<char> >', 230, 116, 107, 118, 122, 106, 124, 120, 123, 135, 121, 127, 176, 124
|
||||
int_nosucc_lookup, 'ska::bytell_hash_map<std::__cxx11::basic_string<char>, std::__cxx11::basic_string<char> >', 267, 192, 212, 195, 137, 161, 201, 207, 142, 159, 196, 368, 372, 162
|
||||
int_delete, 'ska::bytell_hash_map<std::__cxx11::basic_string<char>, std::__cxx11::basic_string<char> >', 97, 116, 114, 132, 112, 122, 121, 131, 121, 122, 135, 234, 201, 132
|
||||
int_insert, 'ska::bytell_hash_map<std::__cxx11::basic_string<char>, std::__cxx11::basic_string<char> >', 214, 186, 175, 197, 175, 189, 255, 247, 188, 201, 255, 241, 589, 209
|
||||
int_succ_lookup, 'ska::bytell_hash_map<std::__cxx11::basic_string<char>, std::__cxx11::basic_string<char> >', 83, 97, 103, 95, 120, 115, 155, 123, 120, 121, 130, 135, 153, 131
|
||||
int_nosucc_lookup, 'ska::bytell_hash_map<std::__cxx11::basic_string<char>, std::__cxx11::basic_string<char> >', 191, 177, 172, 171, 195, 160, 210, 218, 169, 171, 190, 219, 299, 235
|
||||
int_delete, 'ska::bytell_hash_map<std::__cxx11::basic_string<char>, std::__cxx11::basic_string<char> >', 218, 104, 109, 102, 115, 121, 131, 134, 126, 128, 135, 142, 152, 157
|
||||
int_insert, 'ska::flat_hash_map<int, int>', 60, 30, 84, 41, 51, 41, 46, 48, 54, 57, 49, 52, 66, 70
|
||||
int_succ_lookup, 'ska::flat_hash_map<int, int>', 28, 12, 60, 19, 21, 22, 30, 24, 24, 27, 26, 27, 28, 27
|
||||
int_nosucc_lookup, 'ska::flat_hash_map<int, int>', 49, 33, 58, 45, 49, 48, 77, 51, 60, 50, 52, 59, 59, 62
|
||||
int_delete, 'ska::flat_hash_map<int, int>', 23, 18, 29, 23, 28, 21, 36, 27, 35, 24, 30, 30, 38, 36
|
||||
int_insert, 'ska::flat_hash_map<int, int>', 25, 79, 43, 43, 49, 44, 49, 48, 52, 45, 49, 50, 52, 70
|
||||
int_succ_lookup, 'ska::flat_hash_map<int, int>', 8, 19, 66, 19, 21, 23, 29, 24, 24, 25, 26, 28, 26, 26
|
||||
int_nosucc_lookup, 'ska::flat_hash_map<int, int>', 27, 63, 85, 50, 61, 45, 101, 71, 59, 52, 47, 57, 55, 60
|
||||
int_delete, 'ska::flat_hash_map<int, int>', 15, 46, 30, 25, 45, 20, 47, 51, 35, 25, 25, 30, 38, 35
|
||||
int_insert, 'ska::flat_hash_map<std::__cxx11::basic_string<char>, std::__cxx11::basic_string<char> >', 170, 180, 169, 162, 180, 158, 174, 173, 261, 176, 181, 190, 277, 203
|
||||
int_succ_lookup, 'ska::flat_hash_map<std::__cxx11::basic_string<char>, std::__cxx11::basic_string<char> >', 187, 109, 114, 101, 109, 102, 106, 109, 121, 113, 115, 122, 170, 129
|
||||
int_nosucc_lookup, 'ska::flat_hash_map<std::__cxx11::basic_string<char>, std::__cxx11::basic_string<char> >', 211, 135, 161, 138, 152, 147, 123, 146, 158, 136, 139, 145, 171, 170
|
||||
int_delete, 'ska::flat_hash_map<std::__cxx11::basic_string<char>, std::__cxx11::basic_string<char> >', 135, 129, 127, 121, 144, 128, 109, 119, 143, 118, 148, 138, 145, 147
|
||||
int_insert, 'ska::flat_hash_map<std::__cxx11::basic_string<char>, std::__cxx11::basic_string<char> >', 137, 149, 144, 163, 176, 158, 251, 171, 188, 179, 184, 186, 203, 203
|
||||
int_succ_lookup, 'ska::flat_hash_map<std::__cxx11::basic_string<char>, std::__cxx11::basic_string<char> >', 112, 98, 78, 109, 145, 105, 174, 109, 117, 114, 120, 116, 125, 137
|
||||
int_nosucc_lookup, 'ska::flat_hash_map<std::__cxx11::basic_string<char>, std::__cxx11::basic_string<char> >', 222, 135, 99, 130, 198, 130, 137, 140, 153, 130, 170, 148, 154, 172
|
||||
int_delete, 'ska::flat_hash_map<std::__cxx11::basic_string<char>, std::__cxx11::basic_string<char> >', 178, 104, 74, 115, 159, 101, 132, 119, 138, 108, 125, 127, 139, 157
|
||||
int_insert, 'phmap::parallel_flat_hash_map<int, int>', 47, 45, 60, 58, 55, 56, 69, 75, 72, 71, 129, 93, 112, 98
|
||||
int_succ_lookup, 'phmap::parallel_flat_hash_map<int, int>', 12, 12, 48, 21, 32, 29, 32, 30, 40, 43, 52, 58, 48, 55
|
||||
int_nosucc_lookup, 'phmap::parallel_flat_hash_map<int, int>', 158, 54, 66, 64, 59, 60, 67, 74, 78, 76, 86, 123, 128, 93
|
||||
int_delete, 'phmap::parallel_flat_hash_map<int, int>', 48, 25, 39, 32, 38, 32, 41, 37, 53, 51, 62, 70, 63, 78
|
||||
int_insert, 'phmap::parallel_flat_hash_map<int, int>', 59, 152, 61, 49, 81, 55, 57, 69, 67, 78, 82, 86, 128, 89
|
||||
int_succ_lookup, 'phmap::parallel_flat_hash_map<int, int>', 22, 99, 41, 17, 36, 28, 27, 30, 38, 48, 52, 40, 67, 53
|
||||
int_nosucc_lookup, 'phmap::parallel_flat_hash_map<int, int>', 197, 98, 123, 60, 61, 60, 69, 79, 72, 104, 88, 95, 207, 117
|
||||
int_delete, 'phmap::parallel_flat_hash_map<int, int>', 48, 44, 70, 35, 37, 36, 38, 42, 58, 73, 60, 91, 85, 95
|
||||
int_insert, 'phmap::parallel_flat_hash_map<std::__cxx11::basic_string<char>, std::__cxx11::basic_string<char> >', 309, 245, 199, 231, 222, 230, 237, 261, 252, 427, 268, 287, 335, 325
|
||||
int_succ_lookup, 'phmap::parallel_flat_hash_map<std::__cxx11::basic_string<char>, std::__cxx11::basic_string<char> >', 139, 147, 120, 131, 156, 160, 147, 164, 174, 271, 170, 179, 178, 205
|
||||
int_nosucc_lookup, 'phmap::parallel_flat_hash_map<std::__cxx11::basic_string<char>, std::__cxx11::basic_string<char> >', 957, 200, 166, 172, 183, 179, 171, 194, 200, 364, 200, 261, 284, 236
|
||||
int_delete, 'phmap::parallel_flat_hash_map<std::__cxx11::basic_string<char>, std::__cxx11::basic_string<char> >', 259, 117, 116, 117, 135, 133, 135, 183, 165, 265, 169, 183, 208, 197
|
||||
int_insert, 'phmap::parallel_flat_hash_map<std::__cxx11::basic_string<char>, std::__cxx11::basic_string<char> >', 281, 247, 200, 236, 225, 224, 246, 247, 247, 266, 267, 392, 330, 277
|
||||
int_succ_lookup, 'phmap::parallel_flat_hash_map<std::__cxx11::basic_string<char>, std::__cxx11::basic_string<char> >', 175, 152, 125, 130, 111, 140, 140, 142, 196, 168, 172, 249, 180, 201
|
||||
int_nosucc_lookup, 'phmap::parallel_flat_hash_map<std::__cxx11::basic_string<char>, std::__cxx11::basic_string<char> >', 617, 188, 151, 204, 149, 166, 172, 173, 200, 204, 218, 251, 240, 224
|
||||
int_delete, 'phmap::parallel_flat_hash_map<std::__cxx11::basic_string<char>, std::__cxx11::basic_string<char> >', 123, 131, 113, 123, 104, 128, 179, 122, 162, 174, 212, 178, 230, 201
|
||||
int_insert, 'phmap::parallel_node_hash_map<int, int>', 259, 122, 136, 137, 168, 162, 179, 174, 165, 169, 192, 197, 260, 214
|
||||
int_succ_lookup, 'phmap::parallel_node_hash_map<int, int>', 32, 40, 51, 56, 82, 70, 69, 70, 71, 79, 75, 98, 84, 90
|
||||
int_nosucc_lookup, 'phmap::parallel_node_hash_map<int, int>', 533, 181, 131, 158, 165, 164, 175, 180, 182, 176, 193, 218, 262, 248
|
||||
int_delete, 'phmap::parallel_node_hash_map<int, int>', 112, 122, 123, 183, 168, 173, 191, 192, 236, 210, 207, 230, 249, 249
|
||||
int_insert, 'phmap::parallel_node_hash_map<int, int>', 91, 140, 114, 141, 144, 141, 306, 183, 156, 284, 182, 379, 243, 205
|
||||
int_succ_lookup, 'phmap::parallel_node_hash_map<int, int>', 24, 55, 48, 52, 79, 66, 63, 66, 71, 102, 83, 79, 102, 100
|
||||
int_nosucc_lookup, 'phmap::parallel_node_hash_map<int, int>', 389, 392, 123, 147, 143, 150, 178, 182, 177, 201, 183, 210, 796, 211
|
||||
int_delete, 'phmap::parallel_node_hash_map<int, int>', 105, 171, 126, 138, 173, 151, 206, 175, 229, 229, 208, 224, 261, 252
|
||||
int_insert, 'phmap::parallel_node_hash_map<std::__cxx11::basic_string<char>, std::__cxx11::basic_string<char> >', 177, 238, 276, 277, 245, 284, 303, 313, 283, 296, 320, 366, 426, 358
|
||||
int_succ_lookup, 'phmap::parallel_node_hash_map<std::__cxx11::basic_string<char>, std::__cxx11::basic_string<char> >', 101, 143, 174, 171, 197, 215, 212, 219, 237, 230, 245, 257, 270, 276
|
||||
int_nosucc_lookup, 'phmap::parallel_node_hash_map<std::__cxx11::basic_string<char>, std::__cxx11::basic_string<char> >', 971, 221, 224, 228, 222, 219, 243, 259, 246, 257, 272, 315, 364, 313
|
||||
int_delete, 'phmap::parallel_node_hash_map<std::__cxx11::basic_string<char>, std::__cxx11::basic_string<char> >', 147, 157, 198, 201, 247, 223, 251, 255, 279, 262, 259, 297, 298, 300
|
||||
int_insert, 'phmap::parallel_node_hash_map<std::__cxx11::basic_string<char>, std::__cxx11::basic_string<char> >', 210, 241, 242, 282, 264, 282, 292, 343, 288, 443, 324, 395, 423, 314
|
||||
int_succ_lookup, 'phmap::parallel_node_hash_map<std::__cxx11::basic_string<char>, std::__cxx11::basic_string<char> >', 148, 142, 173, 208, 202, 240, 207, 217, 244, 388, 241, 267, 267, 282
|
||||
int_nosucc_lookup, 'phmap::parallel_node_hash_map<std::__cxx11::basic_string<char>, std::__cxx11::basic_string<char> >', 1011, 225, 221, 248, 219, 250, 246, 284, 291, 569, 276, 384, 376, 281
|
||||
int_delete, 'phmap::parallel_node_hash_map<std::__cxx11::basic_string<char>, std::__cxx11::basic_string<char> >', 138, 168, 220, 212, 224, 230, 237, 248, 412, 463, 264, 295, 299, 297
|
||||
int_insert, 'emilib::HashMap<int, int>', 23, 20, 22, 24, 26, 35, 29, 31, 40, 44, 37, 40, 45, 49
|
||||
int_succ_lookup, 'emilib::HashMap<int, int>', 11, 14, 14, 20, 21, 25, 26, 27, 33, 33, 31, 36, 34, 34
|
||||
int_nosucc_lookup, 'emilib::HashMap<int, int>', 26, 21, 28, 28, 28, 38, 31, 36, 40, 53, 36, 41, 43, 47
|
||||
int_delete, 'emilib::HashMap<int, int>', 9, 8, 11, 15, 14, 18, 16, 19, 21, 26, 21, 23, 29, 26
|
||||
int_insert, 'emilib::HashMap<int, int>', 15, 16, 22, 22, 29, 80, 121, 33, 37, 50, 39, 39, 45, 51
|
||||
int_succ_lookup, 'emilib::HashMap<int, int>', 9, 12, 13, 19, 24, 28, 46, 28, 33, 34, 32, 31, 43, 36
|
||||
int_nosucc_lookup, 'emilib::HashMap<int, int>', 18, 18, 24, 23, 31, 37, 37, 32, 39, 50, 37, 38, 54, 50
|
||||
int_delete, 'emilib::HashMap<int, int>', 6, 7, 10, 11, 15, 17, 61, 17, 21, 25, 21, 22, 28, 26
|
||||
int_insert, 'emilib::HashMap<std::__cxx11::basic_string<char>, std::__cxx11::basic_string<char> >', 134, 138, 161, 155, 156, 159, 163, 165, 182, 178, 174, 177, 488, 182
|
||||
int_succ_lookup, 'emilib::HashMap<std::__cxx11::basic_string<char>, std::__cxx11::basic_string<char> >', 80, 93, 106, 118, 108, 114, 110, 111, 116, 119, 117, 120, 341, 133
|
||||
int_nosucc_lookup, 'emilib::HashMap<std::__cxx11::basic_string<char>, std::__cxx11::basic_string<char> >', 169, 120, 121, 142, 124, 125, 129, 129, 130, 141, 138, 151, 179, 147
|
||||
int_delete, 'emilib::HashMap<std::__cxx11::basic_string<char>, std::__cxx11::basic_string<char> >', 127, 84, 91, 115, 106, 102, 103, 101, 105, 124, 114, 112, 153, 123
|
||||
int_insert, 'emilib::HashMap<std::__cxx11::basic_string<char>, std::__cxx11::basic_string<char> >', 183, 137, 146, 154, 147, 161, 154, 159, 168, 173, 170, 174, 179, 181
|
||||
int_succ_lookup, 'emilib::HashMap<std::__cxx11::basic_string<char>, std::__cxx11::basic_string<char> >', 138, 90, 101, 101, 105, 125, 104, 106, 120, 118, 120, 127, 122, 129
|
||||
int_nosucc_lookup, 'emilib::HashMap<std::__cxx11::basic_string<char>, std::__cxx11::basic_string<char> >', 208, 111, 121, 111, 123, 137, 121, 122, 135, 134, 138, 139, 144, 142
|
||||
int_delete, 'emilib::HashMap<std::__cxx11::basic_string<char>, std::__cxx11::basic_string<char> >', 94, 79, 94, 87, 95, 105, 101, 100, 153, 115, 111, 114, 115, 124
|
||||
int_insert, 'robin_hood::detail::Table<true, 80, int, int, robin_hood::hash<int>, std::equal_to<int> >', 102, 126, 38, 112, 81, 144, 120, 168, 69, 163, 130, 135, 112, 91
|
||||
int_succ_lookup, 'robin_hood::detail::Table<true, 80, int, int, robin_hood::hash<int>, std::equal_to<int> >', 35, 53, 22, 65, 33, 61, 30, 127, 73, 51, 52, 119, 179, 43
|
||||
int_nosucc_lookup, 'robin_hood::detail::Table<true, 80, int, int, robin_hood::hash<int>, std::equal_to<int> >', 266, 366, 38, 911, 40, 84, 93, 167, 67, 103, 82, 113, 114, 83
|
||||
int_delete, 'robin_hood::detail::Table<true, 80, int, int, robin_hood::hash<int>, std::equal_to<int> >', 22, 25, 64, 40, 35, 99, 118, 276, 44, 98, 104, 207, 185, 103
|
||||
int_insert, 'robin_hood::detail::Table<true, 80, int, int, robin_hood::hash<int>, std::equal_to<int> >', 89, 87, 50, 97, 66, 42, 84, 151, 180, 78, 218, 101, 58, 135
|
||||
int_succ_lookup, 'robin_hood::detail::Table<true, 80, int, int, robin_hood::hash<int>, std::equal_to<int> >', 40, 63, 49, 30, 29, 34, 35, 104, 79, 38, 88, 74, 37, 85
|
||||
int_nosucc_lookup, 'robin_hood::detail::Table<true, 80, int, int, robin_hood::hash<int>, std::equal_to<int> >', 245, 298, 94, 552, 40, 87, 90, 110, 92, 77, 176, 124, 70, 89
|
||||
int_delete, 'robin_hood::detail::Table<true, 80, int, int, robin_hood::hash<int>, std::equal_to<int> >', 105, 77, 62, 126, 65, 79, 111, 77, 132, 176, 136, 118, 64, 131
|
||||
int_insert, 'robin_hood::detail::Table<true, 80, std::__cxx11::basic_string<char>, std::__cxx11::basic_string<char>, robin_hood::hash<std::__cxx11::basic_string<char> >, std::equal_to<std::__cxx11::basic_string<char> > >', 495, 480, 352, 848, 246, 185, 347, 315, 176, 174, 240, 353, 150, 180
|
||||
int_succ_lookup, 'robin_hood::detail::Table<true, 80, std::__cxx11::basic_string<char>, std::__cxx11::basic_string<char>, robin_hood::hash<std::__cxx11::basic_string<char> >, std::equal_to<std::__cxx11::basic_string<char> > >', 247, 224, 202, 241, 200, 190, 80, 121, 121, 112, 125, 127, 119, 89
|
||||
int_nosucc_lookup, 'robin_hood::detail::Table<true, 80, std::__cxx11::basic_string<char>, std::__cxx11::basic_string<char>, robin_hood::hash<std::__cxx11::basic_string<char> >, std::equal_to<std::__cxx11::basic_string<char> > >', 981, 2163, 456, 5086, 252, 204, 179, 314, 184, 185, 216, 367, 131, 115
|
||||
int_delete, 'robin_hood::detail::Table<true, 80, std::__cxx11::basic_string<char>, std::__cxx11::basic_string<char>, robin_hood::hash<std::__cxx11::basic_string<char> >, std::equal_to<std::__cxx11::basic_string<char> > >', 135, 149, 263, 287, 253, 238, 153, 268, 133, 173, 194, 475, 133, 111
|
||||
int_insert, 'robin_hood::detail::Table<true, 80, std::__cxx11::basic_string<char>, std::__cxx11::basic_string<char>, robin_hood::hash<std::__cxx11::basic_string<char> >, std::equal_to<std::__cxx11::basic_string<char> > >', 261, 278, 141, 408, 141, 158, 203, 300, 262, 275, 392, 503, 315, 543
|
||||
int_succ_lookup, 'robin_hood::detail::Table<true, 80, std::__cxx11::basic_string<char>, std::__cxx11::basic_string<char>, robin_hood::hash<std::__cxx11::basic_string<char> >, std::equal_to<std::__cxx11::basic_string<char> > >', 124, 84, 93, 73, 98, 108, 109, 119, 114, 216, 136, 195, 265, 305
|
||||
int_nosucc_lookup, 'robin_hood::detail::Table<true, 80, std::__cxx11::basic_string<char>, std::__cxx11::basic_string<char>, robin_hood::hash<std::__cxx11::basic_string<char> >, std::equal_to<std::__cxx11::basic_string<char> > >', 530, 763, 277, 2340, 128, 158, 202, 341, 256, 544, 355, 541, 377, 315
|
||||
int_delete, 'robin_hood::detail::Table<true, 80, std::__cxx11::basic_string<char>, std::__cxx11::basic_string<char>, robin_hood::hash<std::__cxx11::basic_string<char> >, std::equal_to<std::__cxx11::basic_string<char> > >', 82, 114, 196, 83, 127, 176, 239, 285, 259, 355, 405, 643, 296, 298
|
||||
int_insert, 'robin_hood::detail::Table<false, 80, int, int, robin_hood::hash<int>, std::equal_to<int> >', 58, 147, 92, 239, 73, 172, 123, 237, 151, 151, 192, 187, 72, 230
|
||||
int_succ_lookup, 'robin_hood::detail::Table<false, 80, int, int, robin_hood::hash<int>, std::equal_to<int> >', 24, 69, 47, 105, 123, 235, 77, 67, 212, 65, 69, 74, 103, 146
|
||||
int_nosucc_lookup, 'robin_hood::detail::Table<false, 80, int, int, robin_hood::hash<int>, std::equal_to<int> >', 343, 1310, 71, 1802, 89, 104, 107, 145, 118, 136, 131, 194, 83, 134
|
||||
int_delete, 'robin_hood::detail::Table<false, 80, int, int, robin_hood::hash<int>, std::equal_to<int> >', 44, 60, 106, 155, 255, 262, 163, 219, 171, 222, 226, 244, 276, 297
|
||||
int_insert, 'robin_hood::detail::Table<false, 80, int, int, robin_hood::hash<int>, std::equal_to<int> >', 146, 76, 42, 197, 122, 96, 163, 172, 85, 227, 150, 180, 108, 173
|
||||
int_succ_lookup, 'robin_hood::detail::Table<false, 80, int, int, robin_hood::hash<int>, std::equal_to<int> >', 68, 80, 51, 80, 166, 67, 60, 133, 108, 161, 159, 131, 89, 164
|
||||
int_nosucc_lookup, 'robin_hood::detail::Table<false, 80, int, int, robin_hood::hash<int>, std::equal_to<int> >', 451, 1078, 99, 1906, 89, 168, 105, 151, 125, 202, 95, 315, 58, 160
|
||||
int_delete, 'robin_hood::detail::Table<false, 80, int, int, robin_hood::hash<int>, std::equal_to<int> >', 83, 220, 123, 221, 206, 151, 151, 288, 319, 260, 238, 456, 268, 230
|
||||
int_insert, 'robin_hood::detail::Table<false, 80, std::__cxx11::basic_string<char>, std::__cxx11::basic_string<char>, robin_hood::hash<std::__cxx11::basic_string<char> >, std::equal_to<std::__cxx11::basic_string<char> > >', 188, 229, 240, 326, 312, 271, 325, 465, 264, 203, 312, 422, 258, 395
|
||||
int_succ_lookup, 'robin_hood::detail::Table<false, 80, std::__cxx11::basic_string<char>, std::__cxx11::basic_string<char>, robin_hood::hash<std::__cxx11::basic_string<char> >, std::equal_to<std::__cxx11::basic_string<char> > >', 105, 250, 349, 256, 320, 244, 311, 333, 254, 331, 320, 355, 271, 217
|
||||
int_nosucc_lookup, 'robin_hood::detail::Table<false, 80, std::__cxx11::basic_string<char>, std::__cxx11::basic_string<char>, robin_hood::hash<std::__cxx11::basic_string<char> >, std::equal_to<std::__cxx11::basic_string<char> > >', 939, 1950, 232, 4612, 253, 133, 326, 327, 273, 290, 368, 293, 224, 150
|
||||
int_delete, 'robin_hood::detail::Table<false, 80, std::__cxx11::basic_string<char>, std::__cxx11::basic_string<char>, robin_hood::hash<std::__cxx11::basic_string<char> >, std::equal_to<std::__cxx11::basic_string<char> > >', 174, 260, 254, 305, 310, 297, 289, 546, 324, 345, 328, 383, 282, 204
|
||||
int_insert, 'robin_hood::detail::Table<false, 80, std::__cxx11::basic_string<char>, std::__cxx11::basic_string<char>, robin_hood::hash<std::__cxx11::basic_string<char> >, std::equal_to<std::__cxx11::basic_string<char> > >', 317, 336, 242, 295, 196, 216, 298, 412, 401, 256, 455, 296, 202, 284
|
||||
int_succ_lookup, 'robin_hood::detail::Table<false, 80, std::__cxx11::basic_string<char>, std::__cxx11::basic_string<char>, robin_hood::hash<std::__cxx11::basic_string<char> >, std::equal_to<std::__cxx11::basic_string<char> > >', 236, 194, 293, 240, 321, 283, 252, 328, 422, 280, 265, 321, 239, 274
|
||||
int_nosucc_lookup, 'robin_hood::detail::Table<false, 80, std::__cxx11::basic_string<char>, std::__cxx11::basic_string<char>, robin_hood::hash<std::__cxx11::basic_string<char> >, std::equal_to<std::__cxx11::basic_string<char> > >', 1039, 1621, 155, 3866, 176, 170, 308, 420, 231, 321, 283, 322, 209, 182
|
||||
int_delete, 'robin_hood::detail::Table<false, 80, std::__cxx11::basic_string<char>, std::__cxx11::basic_string<char>, robin_hood::hash<std::__cxx11::basic_string<char> >, std::equal_to<std::__cxx11::basic_string<char> > >', 294, 265, 183, 409, 445, 274, 433, 364, 316, 397, 410, 328, 333, 322
|
||||
int_insert, 'robin_hood::detail::Table<false, 80, int, int, robin_hood::hash<int>, std::equal_to<int> >', 74, 109, 108, 124, 60, 69, 65, 86, 47, 74, 77, 108, 52, 134
|
||||
int_succ_lookup, 'robin_hood::detail::Table<false, 80, int, int, robin_hood::hash<int>, std::equal_to<int> >', 37, 137, 155, 90, 73, 80, 52, 54, 48, 57, 55, 63, 57, 61
|
||||
int_nosucc_lookup, 'robin_hood::detail::Table<false, 80, int, int, robin_hood::hash<int>, std::equal_to<int> >', 324, 936, 89, 2001, 67, 88, 69, 95, 48, 79, 81, 112, 54, 79
|
||||
int_delete, 'robin_hood::detail::Table<false, 80, int, int, robin_hood::hash<int>, std::equal_to<int> >', 57, 144, 125, 160, 121, 152, 109, 133, 124, 133, 142, 162, 140, 180
|
||||
int_insert, 'robin_hood::detail::Table<false, 80, int, int, robin_hood::hash<int>, std::equal_to<int> >', 45, 89, 74, 74, 46, 166, 88, 128, 102, 147, 224, 229, 243, 72
|
||||
int_succ_lookup, 'robin_hood::detail::Table<false, 80, int, int, robin_hood::hash<int>, std::equal_to<int> >', 39, 66, 86, 128, 73, 130, 56, 65, 70, 155, 116, 65, 146, 59
|
||||
int_nosucc_lookup, 'robin_hood::detail::Table<false, 80, int, int, robin_hood::hash<int>, std::equal_to<int> >', 415, 930, 64, 1715, 68, 94, 100, 183, 117, 79, 156, 160, 160, 59
|
||||
int_delete, 'robin_hood::detail::Table<false, 80, int, int, robin_hood::hash<int>, std::equal_to<int> >', 29, 55, 67, 107, 102, 236, 134, 167, 199, 191, 269, 309, 183, 199
|
||||
int_insert, 'robin_hood::detail::Table<false, 80, std::__cxx11::basic_string<char>, std::__cxx11::basic_string<char>, robin_hood::hash<std::__cxx11::basic_string<char> >, std::equal_to<std::__cxx11::basic_string<char> > >', 185, 258, 320, 245, 211, 218, 251, 273, 260, 273, 271, 473, 143, 354
|
||||
int_succ_lookup, 'robin_hood::detail::Table<false, 80, std::__cxx11::basic_string<char>, std::__cxx11::basic_string<char>, robin_hood::hash<std::__cxx11::basic_string<char> >, std::equal_to<std::__cxx11::basic_string<char> > >', 120, 284, 314, 179, 290, 253, 253, 223, 226, 299, 317, 366, 189, 360
|
||||
int_nosucc_lookup, 'robin_hood::detail::Table<false, 80, std::__cxx11::basic_string<char>, std::__cxx11::basic_string<char>, robin_hood::hash<std::__cxx11::basic_string<char> >, std::equal_to<std::__cxx11::basic_string<char> > >', 743, 1682, 221, 3335, 251, 144, 132, 287, 201, 254, 559, 333, 173, 206
|
||||
int_delete, 'robin_hood::detail::Table<false, 80, std::__cxx11::basic_string<char>, std::__cxx11::basic_string<char>, robin_hood::hash<std::__cxx11::basic_string<char> >, std::equal_to<std::__cxx11::basic_string<char> > >', 90, 156, 239, 228, 253, 235, 288, 307, 229, 282, 289, 277, 186, 276
|
||||
int_insert, 'robin_hood::detail::Table<false, 80, std::__cxx11::basic_string<char>, std::__cxx11::basic_string<char>, robin_hood::hash<std::__cxx11::basic_string<char> >, std::equal_to<std::__cxx11::basic_string<char> > >', 188, 176, 128, 171, 134, 142, 160, 183, 141, 151, 235, 198, 137, 160
|
||||
int_succ_lookup, 'robin_hood::detail::Table<false, 80, std::__cxx11::basic_string<char>, std::__cxx11::basic_string<char>, robin_hood::hash<std::__cxx11::basic_string<char> >, std::equal_to<std::__cxx11::basic_string<char> > >', 118, 158, 142, 148, 156, 160, 166, 171, 172, 177, 237, 194, 193, 240
|
||||
int_nosucc_lookup, 'robin_hood::detail::Table<false, 80, std::__cxx11::basic_string<char>, std::__cxx11::basic_string<char>, robin_hood::hash<std::__cxx11::basic_string<char> >, std::equal_to<std::__cxx11::basic_string<char> > >', 669, 1179, 96, 2382, 107, 112, 131, 161, 132, 142, 178, 207, 141, 188
|
||||
int_delete, 'robin_hood::detail::Table<false, 80, std::__cxx11::basic_string<char>, std::__cxx11::basic_string<char>, robin_hood::hash<std::__cxx11::basic_string<char> >, std::equal_to<std::__cxx11::basic_string<char> > >', 108, 126, 154, 161, 147, 152, 196, 197, 176, 184, 199, 216, 194, 234
|
||||
int_insert, 'absl::node_hash_map<int, int>', 136, 129, 113, 106, 117, 211, 122, 131, 140, 121, 142, 215, 171, 149
|
||||
int_succ_lookup, 'absl::node_hash_map<int, int>', 61, 81, 127, 53, 59, 145, 64, 64, 71, 66, 76, 147, 74, 80
|
||||
int_nosucc_lookup, 'absl::node_hash_map<int, int>', 801, 170, 269, 109, 115, 205, 118, 122, 131, 120, 133, 225, 156, 140
|
||||
int_delete, 'absl::node_hash_map<int, int>', 164, 189, 308, 164, 188, 256, 203, 204, 228, 199, 231, 325, 290, 256
|
||||
int_insert, 'absl::node_hash_map<int, int>', 96, 128, 137, 283, 114, 115, 118, 312, 129, 137, 137, 141, 249, 229
|
||||
int_succ_lookup, 'absl::node_hash_map<int, int>', 29, 79, 127, 310, 58, 58, 64, 146, 74, 73, 75, 75, 133, 165
|
||||
int_nosucc_lookup, 'absl::node_hash_map<int, int>', 411, 137, 160, 263, 115, 117, 118, 292, 130, 131, 134, 142, 257, 231
|
||||
int_delete, 'absl::node_hash_map<int, int>', 92, 168, 166, 270, 236, 208, 214, 385, 416, 236, 238, 250, 279, 288
|
||||
int_insert, 'absl::node_hash_map<std::__cxx11::basic_string<char>, std::__cxx11::basic_string<char> >', 140, 157, 297, 183, 189, 202, 205, 211, 386, 333, 258, 281, 315, 378
|
||||
int_succ_lookup, 'absl::node_hash_map<std::__cxx11::basic_string<char>, std::__cxx11::basic_string<char> >', 114, 154, 361, 182, 205, 233, 272, 228, 275, 387, 261, 259, 272, 529
|
||||
int_nosucc_lookup, 'absl::node_hash_map<std::__cxx11::basic_string<char>, std::__cxx11::basic_string<char> >', 846, 367, 210, 186, 180, 204, 216, 282, 463, 496, 252, 274, 296, 399
|
||||
int_delete, 'absl::node_hash_map<std::__cxx11::basic_string<char>, std::__cxx11::basic_string<char> >', 181, 232, 224, 192, 218, 251, 261, 340, 425, 460, 290, 297, 299, 466
|
||||
int_insert, 'absl::node_hash_map<std::__cxx11::basic_string<char>, std::__cxx11::basic_string<char> >', 332, 351, 180, 308, 210, 445, 214, 245, 336, 490, 226, 245, 263, 578
|
||||
int_succ_lookup, 'absl::node_hash_map<std::__cxx11::basic_string<char>, std::__cxx11::basic_string<char> >', 161, 212, 192, 248, 238, 430, 252, 209, 304, 331, 264, 263, 268, 402
|
||||
int_nosucc_lookup, 'absl::node_hash_map<std::__cxx11::basic_string<char>, std::__cxx11::basic_string<char> >', 1630, 385, 182, 393, 208, 425, 209, 210, 369, 464, 234, 251, 284, 606
|
||||
int_delete, 'absl::node_hash_map<std::__cxx11::basic_string<char>, std::__cxx11::basic_string<char> >', 362, 347, 213, 410, 267, 336, 253, 243, 464, 491, 287, 299, 299, 642
|
Can't render this file because it has a wrong number of fields in line 242.
|
1
src/includes/3thparty/abseil-cpp
Submodule
1
src/includes/3thparty/abseil-cpp
Submodule
@ -0,0 +1 @@
|
||||
Subproject commit 08a7e7bf972c8451855a5022f2faf3d3655db015
|
666
src/includes/3thparty/emilib/hash_map.hpp
Normal file
666
src/includes/3thparty/emilib/hash_map.hpp
Normal file
@ -0,0 +1,666 @@
|
||||
// By Emil Ernerfeldt 2014-2017
|
||||
// LICENSE:
|
||||
// This software is dual-licensed to the public domain and under the following
|
||||
// license: you are granted a perpetual, irrevocable license to copy, modify,
|
||||
// publish, and distribute this file as you see fit.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstdlib>
|
||||
#include <iterator>
|
||||
#include <utility>
|
||||
|
||||
#include "loguru.hpp"
|
||||
|
||||
namespace emilib {
|
||||
|
||||
/// like std::equal_to but no need to #include <functional>
|
||||
template<typename T>
|
||||
struct HashMapEqualTo
|
||||
{
|
||||
constexpr bool operator()(const T& lhs, const T& rhs) const
|
||||
{
|
||||
return lhs == rhs;
|
||||
}
|
||||
};
|
||||
|
||||
/// A cache-friendly hash table with open addressing, linear probing and power-of-two capacity
|
||||
template <typename KeyT, typename ValueT, typename HashT = std::hash<KeyT>, typename EqT = HashMapEqualTo<KeyT>>
|
||||
class HashMap
|
||||
{
|
||||
private:
|
||||
using MyType = HashMap<KeyT, ValueT, HashT, EqT>;
|
||||
|
||||
using PairT = std::pair<KeyT, ValueT>;
|
||||
public:
|
||||
using size_type = size_t;
|
||||
using value_type = PairT;
|
||||
using reference = PairT&;
|
||||
using const_reference = const PairT&;
|
||||
|
||||
class iterator
|
||||
{
|
||||
public:
|
||||
using iterator_category = std::forward_iterator_tag;
|
||||
using difference_type = size_t;
|
||||
using distance_type = size_t;
|
||||
using value_type = std::pair<KeyT, ValueT>;
|
||||
using pointer = value_type*;
|
||||
using reference = value_type&;
|
||||
|
||||
iterator() { }
|
||||
|
||||
iterator(MyType* hash_map, size_t bucket) : _map(hash_map), _bucket(bucket)
|
||||
{
|
||||
}
|
||||
|
||||
iterator& operator++()
|
||||
{
|
||||
this->goto_next_element();
|
||||
return *this;
|
||||
}
|
||||
|
||||
iterator operator++(int)
|
||||
{
|
||||
size_t old_index = _bucket;
|
||||
this->goto_next_element();
|
||||
return iterator(_map, old_index);
|
||||
}
|
||||
|
||||
reference operator*() const
|
||||
{
|
||||
return _map->_pairs[_bucket];
|
||||
}
|
||||
|
||||
pointer operator->() const
|
||||
{
|
||||
return _map->_pairs + _bucket;
|
||||
}
|
||||
|
||||
bool operator==(const iterator& rhs) const
|
||||
{
|
||||
DCHECK_EQ_F(_map, rhs._map);
|
||||
return this->_bucket == rhs._bucket;
|
||||
}
|
||||
|
||||
bool operator!=(const iterator& rhs) const
|
||||
{
|
||||
DCHECK_EQ_F(_map, rhs._map);
|
||||
return this->_bucket != rhs._bucket;
|
||||
}
|
||||
|
||||
private:
|
||||
void goto_next_element()
|
||||
{
|
||||
DCHECK_LT_F(_bucket, _map->_num_buckets);
|
||||
do {
|
||||
_bucket++;
|
||||
} while (_bucket < _map->_num_buckets && _map->_states[_bucket] != State::FILLED);
|
||||
}
|
||||
|
||||
//private:
|
||||
// friend class MyType;
|
||||
public:
|
||||
MyType* _map;
|
||||
size_t _bucket;
|
||||
};
|
||||
|
||||
class const_iterator
|
||||
{
|
||||
public:
|
||||
using iterator_category = std::forward_iterator_tag;
|
||||
using difference_type = size_t;
|
||||
using distance_type = size_t;
|
||||
using value_type = const std::pair<KeyT, ValueT>;
|
||||
using pointer = value_type*;
|
||||
using reference = value_type&;
|
||||
|
||||
const_iterator() { }
|
||||
|
||||
const_iterator(iterator proto) : _map(proto._map), _bucket(proto._bucket)
|
||||
{
|
||||
}
|
||||
|
||||
const_iterator(const MyType* hash_map, size_t bucket) : _map(hash_map), _bucket(bucket)
|
||||
{
|
||||
}
|
||||
|
||||
const_iterator& operator++()
|
||||
{
|
||||
this->goto_next_element();
|
||||
return *this;
|
||||
}
|
||||
|
||||
const_iterator operator++(int)
|
||||
{
|
||||
size_t old_index = _bucket;
|
||||
this->goto_next_element();
|
||||
return const_iterator(_map, old_index);
|
||||
}
|
||||
|
||||
reference operator*() const
|
||||
{
|
||||
return _map->_pairs[_bucket];
|
||||
}
|
||||
|
||||
pointer operator->() const
|
||||
{
|
||||
return _map->_pairs + _bucket;
|
||||
}
|
||||
|
||||
bool operator==(const const_iterator& rhs) const
|
||||
{
|
||||
DCHECK_EQ_F(_map, rhs._map);
|
||||
return this->_bucket == rhs._bucket;
|
||||
}
|
||||
|
||||
bool operator!=(const const_iterator& rhs) const
|
||||
{
|
||||
DCHECK_EQ_F(_map, rhs._map);
|
||||
return this->_bucket != rhs._bucket;
|
||||
}
|
||||
|
||||
private:
|
||||
void goto_next_element()
|
||||
{
|
||||
DCHECK_LT_F(_bucket, _map->_num_buckets);
|
||||
do {
|
||||
_bucket++;
|
||||
} while (_bucket < _map->_num_buckets && _map->_states[_bucket] != State::FILLED);
|
||||
}
|
||||
|
||||
//private:
|
||||
// friend class MyType;
|
||||
public:
|
||||
const MyType* _map;
|
||||
size_t _bucket;
|
||||
};
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
HashMap() = default;
|
||||
|
||||
HashMap(const HashMap& other)
|
||||
{
|
||||
reserve(other.size());
|
||||
insert(other.cbegin(), other.cend());
|
||||
}
|
||||
|
||||
HashMap(HashMap&& other)
|
||||
{
|
||||
*this = std::move(other);
|
||||
}
|
||||
|
||||
HashMap& operator=(const HashMap& other)
|
||||
{
|
||||
clear();
|
||||
reserve(other.size());
|
||||
insert(other.cbegin(), other.cend());
|
||||
return *this;
|
||||
}
|
||||
|
||||
void operator=(HashMap&& other)
|
||||
{
|
||||
this->swap(other);
|
||||
}
|
||||
|
||||
~HashMap()
|
||||
{
|
||||
for (size_t bucket=0; bucket<_num_buckets; ++bucket) {
|
||||
if (_states[bucket] == State::FILLED) {
|
||||
_pairs[bucket].~PairT();
|
||||
}
|
||||
}
|
||||
free(_states);
|
||||
free(_pairs);
|
||||
}
|
||||
|
||||
void swap(HashMap& other)
|
||||
{
|
||||
std::swap(_hasher, other._hasher);
|
||||
std::swap(_eq, other._eq);
|
||||
std::swap(_states, other._states);
|
||||
std::swap(_pairs, other._pairs);
|
||||
std::swap(_num_buckets, other._num_buckets);
|
||||
std::swap(_num_filled, other._num_filled);
|
||||
std::swap(_max_probe_length, other._max_probe_length);
|
||||
std::swap(_mask, other._mask);
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------
|
||||
|
||||
iterator begin()
|
||||
{
|
||||
size_t bucket = 0;
|
||||
while (bucket<_num_buckets && _states[bucket] != State::FILLED) {
|
||||
++bucket;
|
||||
}
|
||||
return iterator(this, bucket);
|
||||
}
|
||||
|
||||
const_iterator cbegin() const
|
||||
{
|
||||
size_t bucket = 0;
|
||||
while (bucket<_num_buckets && _states[bucket] != State::FILLED) {
|
||||
++bucket;
|
||||
}
|
||||
return const_iterator(this, bucket);
|
||||
}
|
||||
|
||||
const_iterator begin() const
|
||||
{
|
||||
return cbegin();
|
||||
}
|
||||
|
||||
iterator end()
|
||||
{
|
||||
return iterator(this, _num_buckets);
|
||||
}
|
||||
|
||||
const_iterator cend() const
|
||||
{
|
||||
return const_iterator(this, _num_buckets);
|
||||
}
|
||||
|
||||
const_iterator end() const
|
||||
{
|
||||
return cend();
|
||||
}
|
||||
|
||||
size_t size() const
|
||||
{
|
||||
return _num_filled;
|
||||
}
|
||||
|
||||
bool empty() const
|
||||
{
|
||||
return _num_filled==0;
|
||||
}
|
||||
|
||||
// Returns the number of buckets.
|
||||
size_t bucket_count() const
|
||||
{
|
||||
return _num_buckets;
|
||||
}
|
||||
|
||||
/// Returns average number of elements per bucket.
|
||||
float load_factor() const
|
||||
{
|
||||
return static_cast<float>(_num_filled) / static_cast<float>(_num_buckets);
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------
|
||||
|
||||
template<typename KeyLike>
|
||||
iterator find(const KeyLike& key)
|
||||
{
|
||||
auto bucket = this->find_filled_bucket(key);
|
||||
if (bucket == (size_t)-1) {
|
||||
return this->end();
|
||||
}
|
||||
return iterator(this, bucket);
|
||||
}
|
||||
|
||||
template<typename KeyLike>
|
||||
const_iterator find(const KeyLike& key) const
|
||||
{
|
||||
auto bucket = this->find_filled_bucket(key);
|
||||
if (bucket == (size_t)-1)
|
||||
{
|
||||
return this->end();
|
||||
}
|
||||
return const_iterator(this, bucket);
|
||||
}
|
||||
|
||||
template<typename KeyLike>
|
||||
bool contains(const KeyLike& k) const
|
||||
{
|
||||
return find_filled_bucket(k) != (size_t)-1;
|
||||
}
|
||||
|
||||
template<typename KeyLike>
|
||||
size_t count(const KeyLike& k) const
|
||||
{
|
||||
return find_filled_bucket(k) != (size_t)-1 ? 1 : 0;
|
||||
}
|
||||
|
||||
/// Returns the matching ValueT or nullptr if k isn't found.
|
||||
template<typename KeyLike>
|
||||
ValueT* try_get(const KeyLike& k)
|
||||
{
|
||||
auto bucket = find_filled_bucket(k);
|
||||
if (bucket != (size_t)-1) {
|
||||
return &_pairs[bucket].second;
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
/// Const version of the above
|
||||
template<typename KeyLike>
|
||||
const ValueT* try_get(const KeyLike& k) const
|
||||
{
|
||||
auto bucket = find_filled_bucket(k);
|
||||
if (bucket != (size_t)-1) {
|
||||
return &_pairs[bucket].second;
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
/// Convenience function.
|
||||
template<typename KeyLike>
|
||||
const ValueT get_or_return_default(const KeyLike& k) const
|
||||
{
|
||||
const ValueT* ret = try_get(k);
|
||||
if (ret) {
|
||||
return *ret;
|
||||
} else {
|
||||
return ValueT();
|
||||
}
|
||||
}
|
||||
|
||||
// -----------------------------------------------------
|
||||
|
||||
/// Returns a pair consisting of an iterator to the inserted element
|
||||
/// (or to the element that prevented the insertion)
|
||||
/// and a bool denoting whether the insertion took place.
|
||||
std::pair<iterator, bool> insert(const KeyT& key, const ValueT& value)
|
||||
{
|
||||
check_expand_need();
|
||||
|
||||
auto bucket = find_or_allocate(key);
|
||||
|
||||
if (_states[bucket] == State::FILLED) {
|
||||
return { iterator(this, bucket), false };
|
||||
} else {
|
||||
_states[bucket] = State::FILLED;
|
||||
new(_pairs + bucket) PairT(key, value);
|
||||
_num_filled++;
|
||||
return { iterator(this, bucket), true };
|
||||
}
|
||||
}
|
||||
|
||||
std::pair<iterator, bool> insert(const std::pair<KeyT, ValueT>& p)
|
||||
{
|
||||
return insert(p.first, p.second);
|
||||
}
|
||||
|
||||
void insert(const_iterator begin, const_iterator end)
|
||||
{
|
||||
// TODO: reserve space exactly once.
|
||||
for (; begin != end; ++begin) {
|
||||
insert(begin->first, begin->second);
|
||||
}
|
||||
}
|
||||
|
||||
/// Same as above, but contains(key) MUST be false
|
||||
void insert_unique(KeyT&& key, ValueT&& value)
|
||||
{
|
||||
DCHECK_F(!contains(key));
|
||||
check_expand_need();
|
||||
auto bucket = find_empty_bucket(key);
|
||||
_states[bucket] = State::FILLED;
|
||||
new(_pairs + bucket) PairT(std::move(key), std::move(value));
|
||||
_num_filled++;
|
||||
}
|
||||
|
||||
void insert_unique(std::pair<KeyT, ValueT>&& p)
|
||||
{
|
||||
insert_unique(std::move(p.first), std::move(p.second));
|
||||
}
|
||||
|
||||
void insert_or_assign(const KeyT& key, ValueT&& value)
|
||||
{
|
||||
check_expand_need();
|
||||
|
||||
auto bucket = find_or_allocate(key);
|
||||
|
||||
// Check if inserting a new value rather than overwriting an old entry
|
||||
if (_states[bucket] == State::FILLED) {
|
||||
_pairs[bucket].second = value;
|
||||
} else {
|
||||
_states[bucket] = State::FILLED;
|
||||
new(_pairs + bucket) PairT(key, value);
|
||||
_num_filled++;
|
||||
}
|
||||
}
|
||||
|
||||
/// Return the old value or ValueT() if it didn't exist.
|
||||
ValueT set_get(const KeyT& key, const ValueT& new_value)
|
||||
{
|
||||
check_expand_need();
|
||||
|
||||
auto bucket = find_or_allocate(key);
|
||||
|
||||
// Check if inserting a new value rather than overwriting an old entry
|
||||
if (_states[bucket] == State::FILLED) {
|
||||
ValueT old_value = _pairs[bucket].second;
|
||||
_pairs[bucket] = new_value.second;
|
||||
return old_value;
|
||||
} else {
|
||||
_states[bucket] = State::FILLED;
|
||||
new(_pairs + bucket) PairT(key, new_value);
|
||||
_num_filled++;
|
||||
return ValueT();
|
||||
}
|
||||
}
|
||||
|
||||
/// Like std::map<KeyT,ValueT>::operator[].
|
||||
ValueT& operator[](const KeyT& key)
|
||||
{
|
||||
check_expand_need();
|
||||
|
||||
auto bucket = find_or_allocate(key);
|
||||
|
||||
/* Check if inserting a new value rather than overwriting an old entry */
|
||||
if (_states[bucket] != State::FILLED) {
|
||||
_states[bucket] = State::FILLED;
|
||||
new(_pairs + bucket) PairT(key, ValueT());
|
||||
_num_filled++;
|
||||
}
|
||||
|
||||
return _pairs[bucket].second;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------
|
||||
|
||||
/// Erase an element from the hash table.
|
||||
/// return false if element was not found
|
||||
bool erase(const KeyT& key)
|
||||
{
|
||||
auto bucket = find_filled_bucket(key);
|
||||
if (bucket != (size_t)-1) {
|
||||
_states[bucket] = State::ACTIVE;
|
||||
_pairs[bucket].~PairT();
|
||||
_num_filled -= 1;
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// Erase an element using an iterator.
|
||||
/// Returns an iterator to the next element (or end()).
|
||||
iterator erase(iterator it)
|
||||
{
|
||||
DCHECK_EQ_F(it._map, this);
|
||||
DCHECK_LT_F(it._bucket, _num_buckets);
|
||||
_states[it._bucket] = State::ACTIVE;
|
||||
_pairs[it._bucket].~PairT();
|
||||
_num_filled -= 1;
|
||||
return ++it;
|
||||
}
|
||||
|
||||
/// Remove all elements, keeping full capacity.
|
||||
void clear()
|
||||
{
|
||||
for (size_t bucket=0; bucket<_num_buckets; ++bucket) {
|
||||
if (_states[bucket] == State::FILLED) {
|
||||
_states[bucket] = State::INACTIVE;
|
||||
_pairs[bucket].~PairT();
|
||||
}
|
||||
}
|
||||
_num_filled = 0;
|
||||
_max_probe_length = -1;
|
||||
}
|
||||
|
||||
/// Make room for this many elements
|
||||
void reserve(size_t num_elems)
|
||||
{
|
||||
size_t required_buckets = num_elems + num_elems/2 + 1;
|
||||
if (required_buckets <= _num_buckets) {
|
||||
return;
|
||||
}
|
||||
size_t num_buckets = 4;
|
||||
while (num_buckets < required_buckets) { num_buckets *= 2; }
|
||||
|
||||
auto new_states = (State*)malloc(num_buckets * sizeof(State));
|
||||
auto new_pairs = (PairT*)malloc(num_buckets * sizeof(PairT));
|
||||
|
||||
if (!new_states || !new_pairs) {
|
||||
free(new_states);
|
||||
free(new_pairs);
|
||||
throw std::bad_alloc();
|
||||
}
|
||||
|
||||
//auto old_num_filled = _num_filled;
|
||||
auto old_num_buckets = _num_buckets;
|
||||
auto old_states = _states;
|
||||
auto old_pairs = _pairs;
|
||||
|
||||
_num_filled = 0;
|
||||
_num_buckets = num_buckets;
|
||||
_mask = _num_buckets - 1;
|
||||
_states = new_states;
|
||||
_pairs = new_pairs;
|
||||
|
||||
std::fill_n(_states, num_buckets, State::INACTIVE);
|
||||
|
||||
_max_probe_length = -1;
|
||||
|
||||
for (size_t src_bucket=0; src_bucket<old_num_buckets; src_bucket++) {
|
||||
if (old_states[src_bucket] == State::FILLED) {
|
||||
auto& src_pair = old_pairs[src_bucket];
|
||||
|
||||
auto dst_bucket = find_empty_bucket(src_pair.first);
|
||||
DCHECK_NE_F(dst_bucket, (size_t)-1);
|
||||
DCHECK_NE_F(_states[dst_bucket], State::FILLED);
|
||||
_states[dst_bucket] = State::FILLED;
|
||||
new(_pairs + dst_bucket) PairT(std::move(src_pair));
|
||||
_num_filled += 1;
|
||||
|
||||
src_pair.~PairT();
|
||||
}
|
||||
}
|
||||
|
||||
//DCHECK_EQ_F(old_num_filled, _num_filled);
|
||||
|
||||
free(old_states);
|
||||
free(old_pairs);
|
||||
}
|
||||
|
||||
private:
|
||||
// Can we fit another element?
|
||||
void check_expand_need()
|
||||
{
|
||||
reserve(_num_filled + 1);
|
||||
}
|
||||
|
||||
// Find the bucket with this key, or return (size_t)-1
|
||||
template<typename KeyLike>
|
||||
size_t find_filled_bucket(const KeyLike& key) const
|
||||
{
|
||||
if (empty()) { return (size_t)-1; } // Optimization
|
||||
|
||||
auto hash_value = _hasher(key);
|
||||
for (int offset=0; offset<=_max_probe_length; ++offset) {
|
||||
auto bucket = (hash_value + offset) & _mask;
|
||||
if (_states[bucket] == State::FILLED) {
|
||||
if (_eq(_pairs[bucket].first, key)) {
|
||||
return bucket;
|
||||
}
|
||||
} else if (_states[bucket] == State::INACTIVE) {
|
||||
return (size_t)-1; // End of the chain!
|
||||
}
|
||||
}
|
||||
return (size_t)-1;
|
||||
}
|
||||
|
||||
// Find the bucket with this key, or return a good empty bucket to place the key in.
|
||||
// In the latter case, the bucket is expected to be filled.
|
||||
size_t find_or_allocate(const KeyT& key)
|
||||
{
|
||||
auto hash_value = _hasher(key);
|
||||
size_t hole = (size_t)-1;
|
||||
int offset=0;
|
||||
for (; offset<=_max_probe_length; ++offset) {
|
||||
auto bucket = (hash_value + offset) & _mask;
|
||||
|
||||
if (_states[bucket] == State::FILLED) {
|
||||
if (_eq(_pairs[bucket].first, key)) {
|
||||
return bucket;
|
||||
}
|
||||
} else if (_states[bucket] == State::INACTIVE) {
|
||||
return bucket;
|
||||
} else {
|
||||
// ACTIVE: keep searching
|
||||
if (hole == (size_t)-1) {
|
||||
hole = bucket;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// No key found - but maybe a hole for it
|
||||
|
||||
DCHECK_EQ_F(offset, _max_probe_length+1);
|
||||
|
||||
if (hole != (size_t)-1) {
|
||||
return hole;
|
||||
}
|
||||
|
||||
// No hole found within _max_probe_length
|
||||
for (; ; ++offset) {
|
||||
auto bucket = (hash_value + offset) & _mask;
|
||||
|
||||
if (_states[bucket] != State::FILLED) {
|
||||
_max_probe_length = offset;
|
||||
return bucket;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// key is not in this map. Find a place to put it.
|
||||
size_t find_empty_bucket(const KeyT& key)
|
||||
{
|
||||
auto hash_value = _hasher(key);
|
||||
for (int offset=0; ; ++offset) {
|
||||
auto bucket = (hash_value + offset) & _mask;
|
||||
if (_states[bucket] != State::FILLED) {
|
||||
if (offset > _max_probe_length) {
|
||||
_max_probe_length = offset;
|
||||
}
|
||||
return bucket;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
enum class State : uint8_t
|
||||
{
|
||||
INACTIVE, // Never been touched
|
||||
ACTIVE, // Is inside a search-chain, but is empty
|
||||
FILLED // Is set with key/value
|
||||
};
|
||||
|
||||
HashT _hasher;
|
||||
EqT _eq;
|
||||
State* _states = nullptr;
|
||||
PairT* _pairs = nullptr;
|
||||
size_t _num_buckets = 0;
|
||||
size_t _num_filled = 0;
|
||||
int _max_probe_length = -1; // Our longest bucket-brigade is this long. ONLY when we have zero elements is this ever negative (-1).
|
||||
size_t _mask = 0; // _num_buckets minus one
|
||||
};
|
||||
|
||||
} // namespace emilib
|
1787
src/includes/3thparty/emilib/loguru.cpp
Normal file
1787
src/includes/3thparty/emilib/loguru.cpp
Normal file
File diff suppressed because it is too large
Load Diff
1324
src/includes/3thparty/emilib/loguru.hpp
Normal file
1324
src/includes/3thparty/emilib/loguru.hpp
Normal file
File diff suppressed because it is too large
Load Diff
4063
src/includes/3thparty/parallel_hashmap/btree.h
Normal file
4063
src/includes/3thparty/parallel_hashmap/btree.h
Normal file
File diff suppressed because it is too large
Load Diff
36
src/includes/3thparty/parallel_hashmap/conanfile.py
Normal file
36
src/includes/3thparty/parallel_hashmap/conanfile.py
Normal file
@ -0,0 +1,36 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from conans import ConanFile, tools
|
||||
import os
|
||||
|
||||
class SparseppConan(ConanFile):
|
||||
name = "parallel_hashmap"
|
||||
version = "1.27"
|
||||
description = "A header-only, very fast and memory-friendly hash map"
|
||||
|
||||
# Indicates License type of the packaged library
|
||||
license = "https://github.com/greg7mdp/parallel-hashmap/blob/master/LICENSE"
|
||||
|
||||
# Packages the license for the conanfile.py
|
||||
exports = ["LICENSE"]
|
||||
|
||||
# Custom attributes for Bincrafters recipe conventions
|
||||
source_subfolder = "source_subfolder"
|
||||
|
||||
def source(self):
|
||||
source_url = "https://github.com/greg7mdp/parallel-hashmap"
|
||||
tools.get("{0}/archive/{1}.tar.gz".format(source_url, self.version))
|
||||
extracted_dir = self.name + "-" + self.version
|
||||
|
||||
#Rename to "source_folder" is a convention to simplify later steps
|
||||
os.rename(extracted_dir, self.source_subfolder)
|
||||
|
||||
|
||||
def package(self):
|
||||
include_folder = os.path.join(self.source_subfolder, "parallel_hashmap")
|
||||
self.copy(pattern="LICENSE")
|
||||
self.copy(pattern="*", dst="include/parallel_hashmap", src=include_folder)
|
||||
|
||||
def package_id(self):
|
||||
self.info.header_only()
|
195
src/includes/3thparty/parallel_hashmap/meminfo.h
Normal file
195
src/includes/3thparty/parallel_hashmap/meminfo.h
Normal file
@ -0,0 +1,195 @@
|
||||
#if !defined(spp_memory_h_guard)
|
||||
#define spp_memory_h_guard
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <cstdlib>
|
||||
|
||||
#if defined(_WIN32) || defined( __CYGWIN__)
|
||||
#define SPP_WIN
|
||||
#endif
|
||||
|
||||
#ifdef SPP_WIN
|
||||
#include <windows.h>
|
||||
#include <Psapi.h>
|
||||
#undef min
|
||||
#undef max
|
||||
#elif defined(__linux__)
|
||||
#include <sys/types.h>
|
||||
#include <sys/sysinfo.h>
|
||||
#elif defined(__FreeBSD__)
|
||||
#include <paths.h>
|
||||
#include <fcntl.h>
|
||||
#include <kvm.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/sysctl.h>
|
||||
#include <sys/user.h>
|
||||
#endif
|
||||
|
||||
namespace spp
|
||||
{
|
||||
uint64_t GetSystemMemory();
|
||||
uint64_t GetTotalMemoryUsed();
|
||||
uint64_t GetProcessMemoryUsed();
|
||||
uint64_t GetPhysicalMemory();
|
||||
|
||||
uint64_t GetSystemMemory()
|
||||
{
|
||||
#ifdef SPP_WIN
|
||||
MEMORYSTATUSEX memInfo;
|
||||
memInfo.dwLength = sizeof(MEMORYSTATUSEX);
|
||||
GlobalMemoryStatusEx(&memInfo);
|
||||
return static_cast<uint64_t>(memInfo.ullTotalPageFile);
|
||||
#elif defined(__linux__)
|
||||
struct sysinfo memInfo;
|
||||
sysinfo (&memInfo);
|
||||
auto totalVirtualMem = memInfo.totalram;
|
||||
|
||||
totalVirtualMem += memInfo.totalswap;
|
||||
totalVirtualMem *= memInfo.mem_unit;
|
||||
return static_cast<uint64_t>(totalVirtualMem);
|
||||
#elif defined(__FreeBSD__)
|
||||
kvm_t *kd;
|
||||
u_int pageCnt;
|
||||
size_t pageCntLen = sizeof(pageCnt);
|
||||
u_int pageSize;
|
||||
struct kvm_swap kswap;
|
||||
uint64_t totalVirtualMem;
|
||||
|
||||
pageSize = static_cast<u_int>(getpagesize());
|
||||
|
||||
sysctlbyname("vm.stats.vm.v_page_count", &pageCnt, &pageCntLen, NULL, 0);
|
||||
totalVirtualMem = pageCnt * pageSize;
|
||||
|
||||
kd = kvm_open(NULL, _PATH_DEVNULL, NULL, O_RDONLY, "kvm_open");
|
||||
kvm_getswapinfo(kd, &kswap, 1, 0);
|
||||
kvm_close(kd);
|
||||
totalVirtualMem += kswap.ksw_total * pageSize;
|
||||
|
||||
return totalVirtualMem;
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
uint64_t GetTotalMemoryUsed()
|
||||
{
|
||||
#ifdef SPP_WIN
|
||||
MEMORYSTATUSEX memInfo;
|
||||
memInfo.dwLength = sizeof(MEMORYSTATUSEX);
|
||||
GlobalMemoryStatusEx(&memInfo);
|
||||
return static_cast<uint64_t>(memInfo.ullTotalPageFile - memInfo.ullAvailPageFile);
|
||||
#elif defined(__linux__)
|
||||
struct sysinfo memInfo;
|
||||
sysinfo(&memInfo);
|
||||
auto virtualMemUsed = memInfo.totalram - memInfo.freeram;
|
||||
|
||||
virtualMemUsed += memInfo.totalswap - memInfo.freeswap;
|
||||
virtualMemUsed *= memInfo.mem_unit;
|
||||
|
||||
return static_cast<uint64_t>(virtualMemUsed);
|
||||
#elif defined(__FreeBSD__)
|
||||
kvm_t *kd;
|
||||
u_int pageSize;
|
||||
u_int pageCnt, freeCnt;
|
||||
size_t pageCntLen = sizeof(pageCnt);
|
||||
size_t freeCntLen = sizeof(freeCnt);
|
||||
struct kvm_swap kswap;
|
||||
uint64_t virtualMemUsed;
|
||||
|
||||
pageSize = static_cast<u_int>(getpagesize());
|
||||
|
||||
sysctlbyname("vm.stats.vm.v_page_count", &pageCnt, &pageCntLen, NULL, 0);
|
||||
sysctlbyname("vm.stats.vm.v_free_count", &freeCnt, &freeCntLen, NULL, 0);
|
||||
virtualMemUsed = (pageCnt - freeCnt) * pageSize;
|
||||
|
||||
kd = kvm_open(NULL, _PATH_DEVNULL, NULL, O_RDONLY, "kvm_open");
|
||||
kvm_getswapinfo(kd, &kswap, 1, 0);
|
||||
kvm_close(kd);
|
||||
virtualMemUsed += kswap.ksw_used * pageSize;
|
||||
|
||||
return virtualMemUsed;
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
uint64_t GetProcessMemoryUsed()
|
||||
{
|
||||
#ifdef SPP_WIN
|
||||
PROCESS_MEMORY_COUNTERS_EX pmc;
|
||||
GetProcessMemoryInfo(GetCurrentProcess(), reinterpret_cast<PPROCESS_MEMORY_COUNTERS>(&pmc), sizeof(pmc));
|
||||
return static_cast<uint64_t>(pmc.PrivateUsage);
|
||||
#elif defined(__linux__)
|
||||
auto parseLine =
|
||||
[](char* line)->int
|
||||
{
|
||||
auto i = strlen(line);
|
||||
|
||||
while(*line < '0' || *line > '9')
|
||||
{
|
||||
line++;
|
||||
}
|
||||
|
||||
line[i-3] = '\0';
|
||||
i = atoi(line);
|
||||
return i;
|
||||
};
|
||||
|
||||
auto file = fopen("/proc/self/status", "r");
|
||||
auto result = -1;
|
||||
char line[128];
|
||||
|
||||
while(fgets(line, 128, file) != nullptr)
|
||||
{
|
||||
if(strncmp(line, "VmSize:", 7) == 0)
|
||||
{
|
||||
result = parseLine(line);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
fclose(file);
|
||||
return static_cast<uint64_t>(result) * 1024;
|
||||
#elif defined(__FreeBSD__)
|
||||
struct kinfo_proc info;
|
||||
size_t infoLen = sizeof(info);
|
||||
int mib[] = { CTL_KERN, KERN_PROC, KERN_PROC_PID, getpid() };
|
||||
|
||||
sysctl(mib, sizeof(mib) / sizeof(*mib), &info, &infoLen, NULL, 0);
|
||||
return static_cast<uint64_t>(info.ki_rssize * getpagesize());
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
uint64_t GetPhysicalMemory()
|
||||
{
|
||||
#ifdef SPP_WIN
|
||||
MEMORYSTATUSEX memInfo;
|
||||
memInfo.dwLength = sizeof(MEMORYSTATUSEX);
|
||||
GlobalMemoryStatusEx(&memInfo);
|
||||
return static_cast<uint64_t>(memInfo.ullTotalPhys);
|
||||
#elif defined(__linux__)
|
||||
struct sysinfo memInfo;
|
||||
sysinfo(&memInfo);
|
||||
|
||||
auto totalPhysMem = memInfo.totalram;
|
||||
|
||||
totalPhysMem *= memInfo.mem_unit;
|
||||
return static_cast<uint64_t>(totalPhysMem);
|
||||
#elif defined(__FreeBSD__)
|
||||
u_long physMem;
|
||||
size_t physMemLen = sizeof(physMem);
|
||||
int mib[] = { CTL_HW, HW_PHYSMEM };
|
||||
|
||||
sysctl(mib, sizeof(mib) / sizeof(*mib), &physMem, &physMemLen, NULL, 0);
|
||||
return physMem;
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif // spp_memory_h_guard
|
4382
src/includes/3thparty/parallel_hashmap/phmap.h
Normal file
4382
src/includes/3thparty/parallel_hashmap/phmap.h
Normal file
File diff suppressed because it is too large
Load Diff
5167
src/includes/3thparty/parallel_hashmap/phmap_base.h
Normal file
5167
src/includes/3thparty/parallel_hashmap/phmap_base.h
Normal file
File diff suppressed because it is too large
Load Diff
663
src/includes/3thparty/parallel_hashmap/phmap_bits.h
Normal file
663
src/includes/3thparty/parallel_hashmap/phmap_bits.h
Normal file
@ -0,0 +1,663 @@
|
||||
#if !defined(phmap_bits_h_guard_)
|
||||
#define phmap_bits_h_guard_
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Copyright (c) 2019, Gregory Popovitch - greg7mdp@gmail.com
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
// Includes work from abseil-cpp (https://github.com/abseil/abseil-cpp)
|
||||
// with modifications.
|
||||
//
|
||||
// Copyright 2018 The Abseil Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
// The following guarantees declaration of the byte swap functions
|
||||
#ifdef _MSC_VER
|
||||
#include <stdlib.h> // NOLINT(build/include)
|
||||
#elif defined(__APPLE__)
|
||||
// Mac OS X / Darwin features
|
||||
#include <libkern/OSByteOrder.h>
|
||||
#elif defined(__FreeBSD__)
|
||||
#include <sys/endian.h>
|
||||
#elif defined(__GLIBC__)
|
||||
#include <byteswap.h> // IWYU pragma: export
|
||||
#endif
|
||||
|
||||
#include <string.h>
|
||||
#include <cstdint>
|
||||
#include "phmap_config.h"
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable : 4514) // unreferenced inline function has been removed
|
||||
#endif
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// unaligned APIs
|
||||
// -----------------------------------------------------------------------------
|
||||
// Portable handling of unaligned loads, stores, and copies.
|
||||
// On some platforms, like ARM, the copy functions can be more efficient
|
||||
// then a load and a store.
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
#if defined(ADDRESS_SANITIZER) || defined(THREAD_SANITIZER) ||\
|
||||
defined(MEMORY_SANITIZER)
|
||||
#include <stdint.h>
|
||||
|
||||
extern "C" {
|
||||
uint16_t __sanitizer_unaligned_load16(const void *p);
|
||||
uint32_t __sanitizer_unaligned_load32(const void *p);
|
||||
uint64_t __sanitizer_unaligned_load64(const void *p);
|
||||
void __sanitizer_unaligned_store16(void *p, uint16_t v);
|
||||
void __sanitizer_unaligned_store32(void *p, uint32_t v);
|
||||
void __sanitizer_unaligned_store64(void *p, uint64_t v);
|
||||
} // extern "C"
|
||||
|
||||
namespace phmap {
|
||||
namespace bits {
|
||||
|
||||
inline uint16_t UnalignedLoad16(const void *p) {
|
||||
return __sanitizer_unaligned_load16(p);
|
||||
}
|
||||
|
||||
inline uint32_t UnalignedLoad32(const void *p) {
|
||||
return __sanitizer_unaligned_load32(p);
|
||||
}
|
||||
|
||||
inline uint64_t UnalignedLoad64(const void *p) {
|
||||
return __sanitizer_unaligned_load64(p);
|
||||
}
|
||||
|
||||
inline void UnalignedStore16(void *p, uint16_t v) {
|
||||
__sanitizer_unaligned_store16(p, v);
|
||||
}
|
||||
|
||||
inline void UnalignedStore32(void *p, uint32_t v) {
|
||||
__sanitizer_unaligned_store32(p, v);
|
||||
}
|
||||
|
||||
inline void UnalignedStore64(void *p, uint64_t v) {
|
||||
__sanitizer_unaligned_store64(p, v);
|
||||
}
|
||||
|
||||
} // namespace bits
|
||||
} // namespace phmap
|
||||
|
||||
#define PHMAP_INTERNAL_UNALIGNED_LOAD16(_p) (phmap::bits::UnalignedLoad16(_p))
|
||||
#define PHMAP_INTERNAL_UNALIGNED_LOAD32(_p) (phmap::bits::UnalignedLoad32(_p))
|
||||
#define PHMAP_INTERNAL_UNALIGNED_LOAD64(_p) (phmap::bits::UnalignedLoad64(_p))
|
||||
|
||||
#define PHMAP_INTERNAL_UNALIGNED_STORE16(_p, _val) (phmap::bits::UnalignedStore16(_p, _val))
|
||||
#define PHMAP_INTERNAL_UNALIGNED_STORE32(_p, _val) (phmap::bits::UnalignedStore32(_p, _val))
|
||||
#define PHMAP_INTERNAL_UNALIGNED_STORE64(_p, _val) (phmap::bits::UnalignedStore64(_p, _val))
|
||||
|
||||
#else
|
||||
|
||||
namespace phmap {
|
||||
namespace bits {
|
||||
|
||||
inline uint16_t UnalignedLoad16(const void *p) {
|
||||
uint16_t t;
|
||||
memcpy(&t, p, sizeof t);
|
||||
return t;
|
||||
}
|
||||
|
||||
inline uint32_t UnalignedLoad32(const void *p) {
|
||||
uint32_t t;
|
||||
memcpy(&t, p, sizeof t);
|
||||
return t;
|
||||
}
|
||||
|
||||
inline uint64_t UnalignedLoad64(const void *p) {
|
||||
uint64_t t;
|
||||
memcpy(&t, p, sizeof t);
|
||||
return t;
|
||||
}
|
||||
|
||||
inline void UnalignedStore16(void *p, uint16_t v) { memcpy(p, &v, sizeof v); }
|
||||
|
||||
inline void UnalignedStore32(void *p, uint32_t v) { memcpy(p, &v, sizeof v); }
|
||||
|
||||
inline void UnalignedStore64(void *p, uint64_t v) { memcpy(p, &v, sizeof v); }
|
||||
|
||||
} // namespace bits
|
||||
} // namespace phmap
|
||||
|
||||
#define PHMAP_INTERNAL_UNALIGNED_LOAD16(_p) (phmap::bits::UnalignedLoad16(_p))
|
||||
#define PHMAP_INTERNAL_UNALIGNED_LOAD32(_p) (phmap::bits::UnalignedLoad32(_p))
|
||||
#define PHMAP_INTERNAL_UNALIGNED_LOAD64(_p) (phmap::bits::UnalignedLoad64(_p))
|
||||
|
||||
#define PHMAP_INTERNAL_UNALIGNED_STORE16(_p, _val) (phmap::bits::UnalignedStore16(_p, _val))
|
||||
#define PHMAP_INTERNAL_UNALIGNED_STORE32(_p, _val) (phmap::bits::UnalignedStore32(_p, _val))
|
||||
#define PHMAP_INTERNAL_UNALIGNED_STORE64(_p, _val) (phmap::bits::UnalignedStore64(_p, _val))
|
||||
|
||||
#endif
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// File: optimization.h
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
#if defined(__pnacl__)
|
||||
#define PHMAP_BLOCK_TAIL_CALL_OPTIMIZATION() if (volatile int x = 0) { (void)x; }
|
||||
#elif defined(__clang__)
|
||||
// Clang will not tail call given inline volatile assembly.
|
||||
#define PHMAP_BLOCK_TAIL_CALL_OPTIMIZATION() __asm__ __volatile__("")
|
||||
#elif defined(__GNUC__)
|
||||
// GCC will not tail call given inline volatile assembly.
|
||||
#define PHMAP_BLOCK_TAIL_CALL_OPTIMIZATION() __asm__ __volatile__("")
|
||||
#elif defined(_MSC_VER)
|
||||
#include <intrin.h>
|
||||
// The __nop() intrinsic blocks the optimisation.
|
||||
#define PHMAP_BLOCK_TAIL_CALL_OPTIMIZATION() __nop()
|
||||
#else
|
||||
#define PHMAP_BLOCK_TAIL_CALL_OPTIMIZATION() if (volatile int x = 0) { (void)x; }
|
||||
#endif
|
||||
|
||||
#if defined(__GNUC__)
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wpedantic"
|
||||
#endif
|
||||
|
||||
#ifdef PHMAP_HAVE_INTRINSIC_INT128
|
||||
__extension__ typedef unsigned __int128 phmap_uint128;
|
||||
inline uint64_t umul128(uint64_t a, uint64_t b, uint64_t* high)
|
||||
{
|
||||
auto result = static_cast<phmap_uint128>(a) * static_cast<phmap_uint128>(b);
|
||||
*high = static_cast<uint64_t>(result >> 64);
|
||||
return static_cast<uint64_t>(result);
|
||||
}
|
||||
#define PHMAP_HAS_UMUL128 1
|
||||
#elif (defined(_MSC_VER))
|
||||
#if defined(_M_X64)
|
||||
#pragma intrinsic(_umul128)
|
||||
inline uint64_t umul128(uint64_t a, uint64_t b, uint64_t* high)
|
||||
{
|
||||
return _umul128(a, b, high);
|
||||
}
|
||||
#define PHMAP_HAS_UMUL128 1
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if defined(__GNUC__)
|
||||
#pragma GCC diagnostic pop
|
||||
#endif
|
||||
|
||||
#if defined(__GNUC__)
|
||||
// Cache line alignment
|
||||
#if defined(__i386__) || defined(__x86_64__)
|
||||
#define PHMAP_CACHELINE_SIZE 64
|
||||
#elif defined(__powerpc64__)
|
||||
#define PHMAP_CACHELINE_SIZE 128
|
||||
#elif defined(__aarch64__)
|
||||
// We would need to read special register ctr_el0 to find out L1 dcache size.
|
||||
// This value is a good estimate based on a real aarch64 machine.
|
||||
#define PHMAP_CACHELINE_SIZE 64
|
||||
#elif defined(__arm__)
|
||||
// Cache line sizes for ARM: These values are not strictly correct since
|
||||
// cache line sizes depend on implementations, not architectures. There
|
||||
// are even implementations with cache line sizes configurable at boot
|
||||
// time.
|
||||
#if defined(__ARM_ARCH_5T__)
|
||||
#define PHMAP_CACHELINE_SIZE 32
|
||||
#elif defined(__ARM_ARCH_7A__)
|
||||
#define PHMAP_CACHELINE_SIZE 64
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef PHMAP_CACHELINE_SIZE
|
||||
// A reasonable default guess. Note that overestimates tend to waste more
|
||||
// space, while underestimates tend to waste more time.
|
||||
#define PHMAP_CACHELINE_SIZE 64
|
||||
#endif
|
||||
|
||||
#define PHMAP_CACHELINE_ALIGNED __attribute__((aligned(PHMAP_CACHELINE_SIZE)))
|
||||
#elif defined(_MSC_VER)
|
||||
#define PHMAP_CACHELINE_SIZE 64
|
||||
#define PHMAP_CACHELINE_ALIGNED __declspec(align(PHMAP_CACHELINE_SIZE))
|
||||
#else
|
||||
#define PHMAP_CACHELINE_SIZE 64
|
||||
#define PHMAP_CACHELINE_ALIGNED
|
||||
#endif
|
||||
|
||||
|
||||
#if PHMAP_HAVE_BUILTIN(__builtin_expect) || \
|
||||
(defined(__GNUC__) && !defined(__clang__))
|
||||
#define PHMAP_PREDICT_FALSE(x) (__builtin_expect(x, 0))
|
||||
#define PHMAP_PREDICT_TRUE(x) (__builtin_expect(!!(x), 1))
|
||||
#else
|
||||
#define PHMAP_PREDICT_FALSE(x) (x)
|
||||
#define PHMAP_PREDICT_TRUE(x) (x)
|
||||
#endif
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// File: bits.h
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
// We can achieve something similar to attribute((always_inline)) with MSVC by
|
||||
// using the __forceinline keyword, however this is not perfect. MSVC is
|
||||
// much less aggressive about inlining, and even with the __forceinline keyword.
|
||||
#define PHMAP_BASE_INTERNAL_FORCEINLINE __forceinline
|
||||
#else
|
||||
// Use default attribute inline.
|
||||
#define PHMAP_BASE_INTERNAL_FORCEINLINE inline PHMAP_ATTRIBUTE_ALWAYS_INLINE
|
||||
#endif
|
||||
|
||||
|
||||
namespace phmap {
|
||||
namespace base_internal {
|
||||
|
||||
PHMAP_BASE_INTERNAL_FORCEINLINE int CountLeadingZeros64Slow(uint64_t n) {
|
||||
int zeroes = 60;
|
||||
if (n >> 32) zeroes -= 32, n >>= 32;
|
||||
if (n >> 16) zeroes -= 16, n >>= 16;
|
||||
if (n >> 8) zeroes -= 8, n >>= 8;
|
||||
if (n >> 4) zeroes -= 4, n >>= 4;
|
||||
return "\4\3\2\2\1\1\1\1\0\0\0\0\0\0\0"[n] + zeroes;
|
||||
}
|
||||
|
||||
PHMAP_BASE_INTERNAL_FORCEINLINE int CountLeadingZeros64(uint64_t n) {
|
||||
#if defined(_MSC_VER) && defined(_M_X64)
|
||||
// MSVC does not have __buitin_clzll. Use _BitScanReverse64.
|
||||
unsigned long result = 0; // NOLINT(runtime/int)
|
||||
if (_BitScanReverse64(&result, n)) {
|
||||
return (int)(63 - result);
|
||||
}
|
||||
return 64;
|
||||
#elif defined(_MSC_VER)
|
||||
// MSVC does not have __buitin_clzll. Compose two calls to _BitScanReverse
|
||||
unsigned long result = 0; // NOLINT(runtime/int)
|
||||
if ((n >> 32) && _BitScanReverse(&result, (unsigned long)(n >> 32))) {
|
||||
return 31 - result;
|
||||
}
|
||||
if (_BitScanReverse(&result, (unsigned long)n)) {
|
||||
return 63 - result;
|
||||
}
|
||||
return 64;
|
||||
#elif defined(__GNUC__)
|
||||
// Use __builtin_clzll, which uses the following instructions:
|
||||
// x86: bsr
|
||||
// ARM64: clz
|
||||
// PPC: cntlzd
|
||||
static_assert(sizeof(unsigned long long) == sizeof(n), // NOLINT(runtime/int)
|
||||
"__builtin_clzll does not take 64-bit arg");
|
||||
|
||||
// Handle 0 as a special case because __builtin_clzll(0) is undefined.
|
||||
if (n == 0) {
|
||||
return 64;
|
||||
}
|
||||
return __builtin_clzll(n);
|
||||
#else
|
||||
return CountLeadingZeros64Slow(n);
|
||||
#endif
|
||||
}
|
||||
|
||||
PHMAP_BASE_INTERNAL_FORCEINLINE int CountLeadingZeros32Slow(uint64_t n) {
|
||||
int zeroes = 28;
|
||||
if (n >> 16) zeroes -= 16, n >>= 16;
|
||||
if (n >> 8) zeroes -= 8, n >>= 8;
|
||||
if (n >> 4) zeroes -= 4, n >>= 4;
|
||||
return "\4\3\2\2\1\1\1\1\0\0\0\0\0\0\0"[n] + zeroes;
|
||||
}
|
||||
|
||||
PHMAP_BASE_INTERNAL_FORCEINLINE int CountLeadingZeros32(uint32_t n) {
|
||||
#if defined(_MSC_VER)
|
||||
unsigned long result = 0; // NOLINT(runtime/int)
|
||||
if (_BitScanReverse(&result, n)) {
|
||||
return (int)(31 - result);
|
||||
}
|
||||
return 32;
|
||||
#elif defined(__GNUC__)
|
||||
// Use __builtin_clz, which uses the following instructions:
|
||||
// x86: bsr
|
||||
// ARM64: clz
|
||||
// PPC: cntlzd
|
||||
static_assert(sizeof(int) == sizeof(n),
|
||||
"__builtin_clz does not take 32-bit arg");
|
||||
|
||||
// Handle 0 as a special case because __builtin_clz(0) is undefined.
|
||||
if (n == 0) {
|
||||
return 32;
|
||||
}
|
||||
return __builtin_clz(n);
|
||||
#else
|
||||
return CountLeadingZeros32Slow(n);
|
||||
#endif
|
||||
}
|
||||
|
||||
PHMAP_BASE_INTERNAL_FORCEINLINE int CountTrailingZerosNonZero64Slow(uint64_t n) {
|
||||
int c = 63;
|
||||
n &= ~n + 1;
|
||||
if (n & 0x00000000FFFFFFFF) c -= 32;
|
||||
if (n & 0x0000FFFF0000FFFF) c -= 16;
|
||||
if (n & 0x00FF00FF00FF00FF) c -= 8;
|
||||
if (n & 0x0F0F0F0F0F0F0F0F) c -= 4;
|
||||
if (n & 0x3333333333333333) c -= 2;
|
||||
if (n & 0x5555555555555555) c -= 1;
|
||||
return c;
|
||||
}
|
||||
|
||||
PHMAP_BASE_INTERNAL_FORCEINLINE int CountTrailingZerosNonZero64(uint64_t n) {
|
||||
#if defined(_MSC_VER) && defined(_M_X64)
|
||||
unsigned long result = 0; // NOLINT(runtime/int)
|
||||
_BitScanForward64(&result, n);
|
||||
return (int)result;
|
||||
#elif defined(_MSC_VER)
|
||||
unsigned long result = 0; // NOLINT(runtime/int)
|
||||
if (static_cast<uint32_t>(n) == 0) {
|
||||
_BitScanForward(&result, (unsigned long)(n >> 32));
|
||||
return result + 32;
|
||||
}
|
||||
_BitScanForward(&result, (unsigned long)n);
|
||||
return result;
|
||||
#elif defined(__GNUC__)
|
||||
static_assert(sizeof(unsigned long long) == sizeof(n), // NOLINT(runtime/int)
|
||||
"__builtin_ctzll does not take 64-bit arg");
|
||||
return __builtin_ctzll(n);
|
||||
#else
|
||||
return CountTrailingZerosNonZero64Slow(n);
|
||||
#endif
|
||||
}
|
||||
|
||||
PHMAP_BASE_INTERNAL_FORCEINLINE int CountTrailingZerosNonZero32Slow(uint32_t n) {
|
||||
int c = 31;
|
||||
n &= ~n + 1;
|
||||
if (n & 0x0000FFFF) c -= 16;
|
||||
if (n & 0x00FF00FF) c -= 8;
|
||||
if (n & 0x0F0F0F0F) c -= 4;
|
||||
if (n & 0x33333333) c -= 2;
|
||||
if (n & 0x55555555) c -= 1;
|
||||
return c;
|
||||
}
|
||||
|
||||
PHMAP_BASE_INTERNAL_FORCEINLINE int CountTrailingZerosNonZero32(uint32_t n) {
|
||||
#if defined(_MSC_VER)
|
||||
unsigned long result = 0; // NOLINT(runtime/int)
|
||||
_BitScanForward(&result, n);
|
||||
return (int)result;
|
||||
#elif defined(__GNUC__)
|
||||
static_assert(sizeof(int) == sizeof(n),
|
||||
"__builtin_ctz does not take 32-bit arg");
|
||||
return __builtin_ctz(n);
|
||||
#else
|
||||
return CountTrailingZerosNonZero32Slow(n);
|
||||
#endif
|
||||
}
|
||||
|
||||
#undef PHMAP_BASE_INTERNAL_FORCEINLINE
|
||||
|
||||
} // namespace base_internal
|
||||
} // namespace phmap
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// File: endian.h
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
namespace phmap {
|
||||
|
||||
// Use compiler byte-swapping intrinsics if they are available. 32-bit
|
||||
// and 64-bit versions are available in Clang and GCC as of GCC 4.3.0.
|
||||
// The 16-bit version is available in Clang and GCC only as of GCC 4.8.0.
|
||||
// For simplicity, we enable them all only for GCC 4.8.0 or later.
|
||||
#if defined(__clang__) || \
|
||||
(defined(__GNUC__) && \
|
||||
((__GNUC__ == 4 && __GNUC_MINOR__ >= 8) || __GNUC__ >= 5))
|
||||
|
||||
inline uint64_t gbswap_64(uint64_t host_int) {
|
||||
return __builtin_bswap64(host_int);
|
||||
}
|
||||
inline uint32_t gbswap_32(uint32_t host_int) {
|
||||
return __builtin_bswap32(host_int);
|
||||
}
|
||||
inline uint16_t gbswap_16(uint16_t host_int) {
|
||||
return __builtin_bswap16(host_int);
|
||||
}
|
||||
|
||||
#elif defined(_MSC_VER)
|
||||
|
||||
inline uint64_t gbswap_64(uint64_t host_int) {
|
||||
return _byteswap_uint64(host_int);
|
||||
}
|
||||
inline uint32_t gbswap_32(uint32_t host_int) {
|
||||
return _byteswap_ulong(host_int);
|
||||
}
|
||||
inline uint16_t gbswap_16(uint16_t host_int) {
|
||||
return _byteswap_ushort(host_int);
|
||||
}
|
||||
|
||||
#elif defined(__APPLE__)
|
||||
|
||||
inline uint64_t gbswap_64(uint64_t host_int) { return OSSwapInt16(host_int); }
|
||||
inline uint32_t gbswap_32(uint32_t host_int) { return OSSwapInt32(host_int); }
|
||||
inline uint16_t gbswap_16(uint16_t host_int) { return OSSwapInt64(host_int); }
|
||||
|
||||
#else
|
||||
|
||||
inline uint64_t gbswap_64(uint64_t host_int) {
|
||||
#if defined(__GNUC__) && defined(__x86_64__) && !defined(__APPLE__)
|
||||
// Adapted from /usr/include/byteswap.h. Not available on Mac.
|
||||
if (__builtin_constant_p(host_int)) {
|
||||
return __bswap_constant_64(host_int);
|
||||
} else {
|
||||
uint64_t result;
|
||||
__asm__("bswap %0" : "=r"(result) : "0"(host_int));
|
||||
return result;
|
||||
}
|
||||
#elif defined(__GLIBC__)
|
||||
return bswap_64(host_int);
|
||||
#else
|
||||
return (((host_int & uint64_t{0xFF}) << 56) |
|
||||
((host_int & uint64_t{0xFF00}) << 40) |
|
||||
((host_int & uint64_t{0xFF0000}) << 24) |
|
||||
((host_int & uint64_t{0xFF000000}) << 8) |
|
||||
((host_int & uint64_t{0xFF00000000}) >> 8) |
|
||||
((host_int & uint64_t{0xFF0000000000}) >> 24) |
|
||||
((host_int & uint64_t{0xFF000000000000}) >> 40) |
|
||||
((host_int & uint64_t{0xFF00000000000000}) >> 56));
|
||||
#endif // bswap_64
|
||||
}
|
||||
|
||||
inline uint32_t gbswap_32(uint32_t host_int) {
|
||||
#if defined(__GLIBC__)
|
||||
return bswap_32(host_int);
|
||||
#else
|
||||
return (((host_int & uint32_t{0xFF}) << 24) |
|
||||
((host_int & uint32_t{0xFF00}) << 8) |
|
||||
((host_int & uint32_t{0xFF0000}) >> 8) |
|
||||
((host_int & uint32_t{0xFF000000}) >> 24));
|
||||
#endif
|
||||
}
|
||||
|
||||
inline uint16_t gbswap_16(uint16_t host_int) {
|
||||
#if defined(__GLIBC__)
|
||||
return bswap_16(host_int);
|
||||
#else
|
||||
return (((host_int & uint16_t{0xFF}) << 8) |
|
||||
((host_int & uint16_t{0xFF00}) >> 8));
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif // intrinics available
|
||||
|
||||
#ifdef PHMAP_IS_LITTLE_ENDIAN
|
||||
|
||||
// Definitions for ntohl etc. that don't require us to include
|
||||
// netinet/in.h. We wrap gbswap_32 and gbswap_16 in functions rather
|
||||
// than just #defining them because in debug mode, gcc doesn't
|
||||
// correctly handle the (rather involved) definitions of bswap_32.
|
||||
// gcc guarantees that inline functions are as fast as macros, so
|
||||
// this isn't a performance hit.
|
||||
inline uint16_t ghtons(uint16_t x) { return gbswap_16(x); }
|
||||
inline uint32_t ghtonl(uint32_t x) { return gbswap_32(x); }
|
||||
inline uint64_t ghtonll(uint64_t x) { return gbswap_64(x); }
|
||||
|
||||
#elif defined PHMAP_IS_BIG_ENDIAN
|
||||
|
||||
// These definitions are simpler on big-endian machines
|
||||
// These are functions instead of macros to avoid self-assignment warnings
|
||||
// on calls such as "i = ghtnol(i);". This also provides type checking.
|
||||
inline uint16_t ghtons(uint16_t x) { return x; }
|
||||
inline uint32_t ghtonl(uint32_t x) { return x; }
|
||||
inline uint64_t ghtonll(uint64_t x) { return x; }
|
||||
|
||||
#else
|
||||
#error \
|
||||
"Unsupported byte order: Either PHMAP_IS_BIG_ENDIAN or " \
|
||||
"PHMAP_IS_LITTLE_ENDIAN must be defined"
|
||||
#endif // byte order
|
||||
|
||||
inline uint16_t gntohs(uint16_t x) { return ghtons(x); }
|
||||
inline uint32_t gntohl(uint32_t x) { return ghtonl(x); }
|
||||
inline uint64_t gntohll(uint64_t x) { return ghtonll(x); }
|
||||
|
||||
// Utilities to convert numbers between the current hosts's native byte
|
||||
// order and little-endian byte order
|
||||
//
|
||||
// Load/Store methods are alignment safe
|
||||
namespace little_endian {
|
||||
// Conversion functions.
|
||||
#ifdef PHMAP_IS_LITTLE_ENDIAN
|
||||
|
||||
inline uint16_t FromHost16(uint16_t x) { return x; }
|
||||
inline uint16_t ToHost16(uint16_t x) { return x; }
|
||||
|
||||
inline uint32_t FromHost32(uint32_t x) { return x; }
|
||||
inline uint32_t ToHost32(uint32_t x) { return x; }
|
||||
|
||||
inline uint64_t FromHost64(uint64_t x) { return x; }
|
||||
inline uint64_t ToHost64(uint64_t x) { return x; }
|
||||
|
||||
inline constexpr bool IsLittleEndian() { return true; }
|
||||
|
||||
#elif defined PHMAP_IS_BIG_ENDIAN
|
||||
|
||||
inline uint16_t FromHost16(uint16_t x) { return gbswap_16(x); }
|
||||
inline uint16_t ToHost16(uint16_t x) { return gbswap_16(x); }
|
||||
|
||||
inline uint32_t FromHost32(uint32_t x) { return gbswap_32(x); }
|
||||
inline uint32_t ToHost32(uint32_t x) { return gbswap_32(x); }
|
||||
|
||||
inline uint64_t FromHost64(uint64_t x) { return gbswap_64(x); }
|
||||
inline uint64_t ToHost64(uint64_t x) { return gbswap_64(x); }
|
||||
|
||||
inline constexpr bool IsLittleEndian() { return false; }
|
||||
|
||||
#endif /* ENDIAN */
|
||||
|
||||
// Functions to do unaligned loads and stores in little-endian order.
|
||||
inline uint16_t Load16(const void *p) {
|
||||
return ToHost16(PHMAP_INTERNAL_UNALIGNED_LOAD16(p));
|
||||
}
|
||||
|
||||
inline void Store16(void *p, uint16_t v) {
|
||||
PHMAP_INTERNAL_UNALIGNED_STORE16(p, FromHost16(v));
|
||||
}
|
||||
|
||||
inline uint32_t Load32(const void *p) {
|
||||
return ToHost32(PHMAP_INTERNAL_UNALIGNED_LOAD32(p));
|
||||
}
|
||||
|
||||
inline void Store32(void *p, uint32_t v) {
|
||||
PHMAP_INTERNAL_UNALIGNED_STORE32(p, FromHost32(v));
|
||||
}
|
||||
|
||||
inline uint64_t Load64(const void *p) {
|
||||
return ToHost64(PHMAP_INTERNAL_UNALIGNED_LOAD64(p));
|
||||
}
|
||||
|
||||
inline void Store64(void *p, uint64_t v) {
|
||||
PHMAP_INTERNAL_UNALIGNED_STORE64(p, FromHost64(v));
|
||||
}
|
||||
|
||||
} // namespace little_endian
|
||||
|
||||
// Utilities to convert numbers between the current hosts's native byte
|
||||
// order and big-endian byte order (same as network byte order)
|
||||
//
|
||||
// Load/Store methods are alignment safe
|
||||
namespace big_endian {
|
||||
#ifdef PHMAP_IS_LITTLE_ENDIAN
|
||||
|
||||
inline uint16_t FromHost16(uint16_t x) { return gbswap_16(x); }
|
||||
inline uint16_t ToHost16(uint16_t x) { return gbswap_16(x); }
|
||||
|
||||
inline uint32_t FromHost32(uint32_t x) { return gbswap_32(x); }
|
||||
inline uint32_t ToHost32(uint32_t x) { return gbswap_32(x); }
|
||||
|
||||
inline uint64_t FromHost64(uint64_t x) { return gbswap_64(x); }
|
||||
inline uint64_t ToHost64(uint64_t x) { return gbswap_64(x); }
|
||||
|
||||
inline constexpr bool IsLittleEndian() { return true; }
|
||||
|
||||
#elif defined PHMAP_IS_BIG_ENDIAN
|
||||
|
||||
inline uint16_t FromHost16(uint16_t x) { return x; }
|
||||
inline uint16_t ToHost16(uint16_t x) { return x; }
|
||||
|
||||
inline uint32_t FromHost32(uint32_t x) { return x; }
|
||||
inline uint32_t ToHost32(uint32_t x) { return x; }
|
||||
|
||||
inline uint64_t FromHost64(uint64_t x) { return x; }
|
||||
inline uint64_t ToHost64(uint64_t x) { return x; }
|
||||
|
||||
inline constexpr bool IsLittleEndian() { return false; }
|
||||
|
||||
#endif /* ENDIAN */
|
||||
|
||||
// Functions to do unaligned loads and stores in big-endian order.
|
||||
inline uint16_t Load16(const void *p) {
|
||||
return ToHost16(PHMAP_INTERNAL_UNALIGNED_LOAD16(p));
|
||||
}
|
||||
|
||||
inline void Store16(void *p, uint16_t v) {
|
||||
PHMAP_INTERNAL_UNALIGNED_STORE16(p, FromHost16(v));
|
||||
}
|
||||
|
||||
inline uint32_t Load32(const void *p) {
|
||||
return ToHost32(PHMAP_INTERNAL_UNALIGNED_LOAD32(p));
|
||||
}
|
||||
|
||||
inline void Store32(void *p, uint32_t v) {
|
||||
PHMAP_INTERNAL_UNALIGNED_STORE32(p, FromHost32(v));
|
||||
}
|
||||
|
||||
inline uint64_t Load64(const void *p) {
|
||||
return ToHost64(PHMAP_INTERNAL_UNALIGNED_LOAD64(p));
|
||||
}
|
||||
|
||||
inline void Store64(void *p, uint64_t v) {
|
||||
PHMAP_INTERNAL_UNALIGNED_STORE64(p, FromHost64(v));
|
||||
}
|
||||
|
||||
} // namespace big_endian
|
||||
|
||||
} // namespace phmap
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(pop)
|
||||
#endif
|
||||
|
||||
#endif // phmap_bits_h_guard_
|
753
src/includes/3thparty/parallel_hashmap/phmap_config.h
Normal file
753
src/includes/3thparty/parallel_hashmap/phmap_config.h
Normal file
@ -0,0 +1,753 @@
|
||||
#if !defined(phmap_config_h_guard_)
|
||||
#define phmap_config_h_guard_
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Copyright (c) 2019, Gregory Popovitch - greg7mdp@gmail.com
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
// Includes work from abseil-cpp (https://github.com/abseil/abseil-cpp)
|
||||
// with modifications.
|
||||
//
|
||||
// Copyright 2018 The Abseil Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
#define PHMAP_VERSION_MAJOR 1
|
||||
#define PHMAP_VERSION_MINOR 0
|
||||
#define PHMAP_VERSION_PATCH 0
|
||||
|
||||
// Included for the __GLIBC__ macro (or similar macros on other systems).
|
||||
#include <limits.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
// Included for __GLIBCXX__, _LIBCPP_VERSION
|
||||
#include <cstddef>
|
||||
#endif // __cplusplus
|
||||
|
||||
#if defined(__APPLE__)
|
||||
// Included for TARGET_OS_IPHONE, __IPHONE_OS_VERSION_MIN_REQUIRED,
|
||||
// __IPHONE_8_0.
|
||||
#include <Availability.h>
|
||||
#include <TargetConditionals.h>
|
||||
#endif
|
||||
|
||||
#define PHMAP_XSTR(x) PHMAP_STR(x)
|
||||
#define PHMAP_STR(x) #x
|
||||
#define PHMAP_VAR_NAME_VALUE(var) #var "=" PHMAP_STR(var)
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Some sanity checks
|
||||
// -----------------------------------------------------------------------------
|
||||
//#if defined(__CYGWIN__)
|
||||
// #error "Cygwin is not supported."
|
||||
//#endif
|
||||
|
||||
#if defined(_MSC_FULL_VER) && _MSC_FULL_VER < 190023918 && !defined(__clang__)
|
||||
#error "phmap requires Visual Studio 2015 Update 2 or higher."
|
||||
#endif
|
||||
|
||||
// We support gcc 4.7 and later.
|
||||
#if defined(__GNUC__) && !defined(__clang__)
|
||||
#if __GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 7)
|
||||
#error "phmap requires gcc 4.7 or higher."
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// We support Apple Xcode clang 4.2.1 (version 421.11.65) and later.
|
||||
// This corresponds to Apple Xcode version 4.5.
|
||||
#if defined(__apple_build_version__) && __apple_build_version__ < 4211165
|
||||
#error "phmap requires __apple_build_version__ of 4211165 or higher."
|
||||
#endif
|
||||
|
||||
// Enforce C++11 as the minimum.
|
||||
#if defined(__cplusplus) && !defined(_MSC_VER)
|
||||
#if __cplusplus < 201103L
|
||||
#error "C++ versions less than C++11 are not supported."
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// We have chosen glibc 2.12 as the minimum
|
||||
#if defined(__GLIBC__) && defined(__GLIBC_PREREQ)
|
||||
#if !__GLIBC_PREREQ(2, 12)
|
||||
#error "Minimum required version of glibc is 2.12."
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if defined(_STLPORT_VERSION)
|
||||
#error "STLPort is not supported."
|
||||
#endif
|
||||
|
||||
#if CHAR_BIT != 8
|
||||
#error "phmap assumes CHAR_BIT == 8."
|
||||
#endif
|
||||
|
||||
// phmap currently assumes that an int is 4 bytes.
|
||||
#if INT_MAX < 2147483647
|
||||
#error "phmap assumes that int is at least 4 bytes. "
|
||||
#endif
|
||||
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Compiler Feature Checks
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
#ifdef __has_builtin
|
||||
#define PHMAP_HAVE_BUILTIN(x) __has_builtin(x)
|
||||
#else
|
||||
#define PHMAP_HAVE_BUILTIN(x) 0
|
||||
#endif
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// Checks whether `std::is_trivially_destructible<T>` is supported.
|
||||
// ----------------------------------------------------------------
|
||||
#ifdef PHMAP_HAVE_STD_IS_TRIVIALLY_DESTRUCTIBLE
|
||||
#error PHMAP_HAVE_STD_IS_TRIVIALLY_DESTRUCTIBLE cannot be directly set
|
||||
#elif defined(_LIBCPP_VERSION) || \
|
||||
(!defined(__clang__) && defined(__GNUC__) && defined(__GLIBCXX__) && \
|
||||
(__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8))) || \
|
||||
defined(_MSC_VER)
|
||||
#define PHMAP_HAVE_STD_IS_TRIVIALLY_DESTRUCTIBLE 1
|
||||
#endif
|
||||
|
||||
// --------------------------------------------------------------
|
||||
// Checks whether `std::is_trivially_default_constructible<T>` is
|
||||
// supported.
|
||||
// --------------------------------------------------------------
|
||||
#if defined(PHMAP_HAVE_STD_IS_TRIVIALLY_CONSTRUCTIBLE)
|
||||
#error PHMAP_HAVE_STD_IS_TRIVIALLY_CONSTRUCTIBLE cannot be directly set
|
||||
#elif defined(PHMAP_HAVE_STD_IS_TRIVIALLY_ASSIGNABLE)
|
||||
#error PHMAP_HAVE_STD_IS_TRIVIALLY_ASSIGNABLE cannot directly set
|
||||
#elif (defined(__clang__) && defined(_LIBCPP_VERSION)) || \
|
||||
(!defined(__clang__) && defined(__GNUC__) && \
|
||||
(__GNUC__ > 5 || (__GNUC__ == 5 && __GNUC_MINOR__ >= 1)) && \
|
||||
(defined(_LIBCPP_VERSION) || defined(__GLIBCXX__))) || \
|
||||
(defined(_MSC_VER) && !defined(__NVCC__))
|
||||
#define PHMAP_HAVE_STD_IS_TRIVIALLY_CONSTRUCTIBLE 1
|
||||
#define PHMAP_HAVE_STD_IS_TRIVIALLY_ASSIGNABLE 1
|
||||
#endif
|
||||
|
||||
// -------------------------------------------------------------------
|
||||
// Checks whether C++11's `thread_local` storage duration specifier is
|
||||
// supported.
|
||||
// -------------------------------------------------------------------
|
||||
#ifdef PHMAP_HAVE_THREAD_LOCAL
|
||||
#error PHMAP_HAVE_THREAD_LOCAL cannot be directly set
|
||||
#elif defined(__APPLE__)
|
||||
#if __has_feature(cxx_thread_local) && \
|
||||
!(TARGET_OS_IPHONE && __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_9_0)
|
||||
#define PHMAP_HAVE_THREAD_LOCAL 1
|
||||
#endif
|
||||
#else // !defined(__APPLE__)
|
||||
#define PHMAP_HAVE_THREAD_LOCAL 1
|
||||
#endif
|
||||
|
||||
#if defined(__ANDROID__) && defined(__clang__)
|
||||
|
||||
#if __has_include(<android/ndk-version.h>)
|
||||
#include <android/ndk-version.h>
|
||||
#endif // __has_include(<android/ndk-version.h>)
|
||||
|
||||
#if defined(__ANDROID__) && defined(__clang__) && defined(__NDK_MAJOR__) && \
|
||||
defined(__NDK_MINOR__) && \
|
||||
((__NDK_MAJOR__ < 12) || ((__NDK_MAJOR__ == 12) && (__NDK_MINOR__ < 1)))
|
||||
#undef PHMAP_HAVE_TLS
|
||||
#undef PHMAP_HAVE_THREAD_LOCAL
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// ------------------------------------------------------------
|
||||
// Checks whether the __int128 compiler extension for a 128-bit
|
||||
// integral type is supported.
|
||||
// ------------------------------------------------------------
|
||||
#ifdef PHMAP_HAVE_INTRINSIC_INT128
|
||||
#error PHMAP_HAVE_INTRINSIC_INT128 cannot be directly set
|
||||
#elif defined(__SIZEOF_INT128__)
|
||||
#if (defined(__clang__) && !defined(_WIN32) && !defined(__aarch64__)) || \
|
||||
(defined(__CUDACC__) && __CUDACC_VER_MAJOR__ >= 9) || \
|
||||
(defined(__GNUC__) && !defined(__clang__) && !defined(__CUDACC__))
|
||||
#define PHMAP_HAVE_INTRINSIC_INT128 1
|
||||
#elif defined(__CUDACC__)
|
||||
#if __CUDACC_VER__ >= 70000
|
||||
#define PHMAP_HAVE_INTRINSIC_INT128 1
|
||||
#endif // __CUDACC_VER__ >= 70000
|
||||
#endif // defined(__CUDACC__)
|
||||
#endif
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// Checks whether the compiler both supports and enables exceptions.
|
||||
// ------------------------------------------------------------------
|
||||
#ifdef PHMAP_HAVE_EXCEPTIONS
|
||||
#error PHMAP_HAVE_EXCEPTIONS cannot be directly set.
|
||||
#elif defined(__clang__)
|
||||
#if defined(__EXCEPTIONS) && __has_feature(cxx_exceptions)
|
||||
#define PHMAP_HAVE_EXCEPTIONS 1
|
||||
#endif // defined(__EXCEPTIONS) && __has_feature(cxx_exceptions)
|
||||
#elif !(defined(__GNUC__) && (__GNUC__ < 5) && !defined(__EXCEPTIONS)) && \
|
||||
!(defined(__GNUC__) && (__GNUC__ >= 5) && !defined(__cpp_exceptions)) && \
|
||||
!(defined(_MSC_VER) && !defined(_CPPUNWIND))
|
||||
#define PHMAP_HAVE_EXCEPTIONS 1
|
||||
#endif
|
||||
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// Checks whether the platform has an mmap(2) implementation as defined in
|
||||
// POSIX.1-2001.
|
||||
// -----------------------------------------------------------------------
|
||||
#ifdef PHMAP_HAVE_MMAP
|
||||
#error PHMAP_HAVE_MMAP cannot be directly set
|
||||
#elif defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__) || \
|
||||
defined(__ros__) || defined(__native_client__) || defined(__asmjs__) || \
|
||||
defined(__wasm__) || defined(__Fuchsia__) || defined(__sun) || \
|
||||
defined(__ASYLO__)
|
||||
#define PHMAP_HAVE_MMAP 1
|
||||
#endif
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// Checks the endianness of the platform.
|
||||
// -----------------------------------------------------------------------
|
||||
#if defined(PHMAP_IS_BIG_ENDIAN)
|
||||
#error "PHMAP_IS_BIG_ENDIAN cannot be directly set."
|
||||
#endif
|
||||
|
||||
#if defined(PHMAP_IS_LITTLE_ENDIAN)
|
||||
#error "PHMAP_IS_LITTLE_ENDIAN cannot be directly set."
|
||||
#endif
|
||||
|
||||
#if (defined(__BYTE_ORDER__) && defined(__ORDER_LITTLE_ENDIAN__) && \
|
||||
__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)
|
||||
#define PHMAP_IS_LITTLE_ENDIAN 1
|
||||
#elif defined(__BYTE_ORDER__) && defined(__ORDER_BIG_ENDIAN__) && \
|
||||
__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
|
||||
#define PHMAP_IS_BIG_ENDIAN 1
|
||||
#elif defined(_WIN32)
|
||||
#define PHMAP_IS_LITTLE_ENDIAN 1
|
||||
#else
|
||||
#error "phmap endian detection needs to be set up for your compiler"
|
||||
#endif
|
||||
|
||||
#if defined(__APPLE__) && defined(_LIBCPP_VERSION) && \
|
||||
defined(__MAC_OS_X_VERSION_MIN_REQUIRED__) && \
|
||||
__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ < 101400
|
||||
#define PHMAP_INTERNAL_MACOS_CXX17_TYPES_UNAVAILABLE 1
|
||||
#else
|
||||
#define PHMAP_INTERNAL_MACOS_CXX17_TYPES_UNAVAILABLE 0
|
||||
#endif
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Checks whether C++17 std::any is available by checking whether <any> exists.
|
||||
// ---------------------------------------------------------------------------
|
||||
#ifdef PHMAP_HAVE_STD_ANY
|
||||
#error "PHMAP_HAVE_STD_ANY cannot be directly set."
|
||||
#endif
|
||||
|
||||
#ifdef __has_include
|
||||
#if __has_include(<any>) && __cplusplus >= 201703L && \
|
||||
!PHMAP_INTERNAL_MACOS_CXX17_TYPES_UNAVAILABLE
|
||||
#define PHMAP_HAVE_STD_ANY 1
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef PHMAP_HAVE_STD_OPTIONAL
|
||||
#error "PHMAP_HAVE_STD_OPTIONAL cannot be directly set."
|
||||
#endif
|
||||
|
||||
#ifdef __has_include
|
||||
#if __has_include(<optional>) && __cplusplus >= 201703L && \
|
||||
!PHMAP_INTERNAL_MACOS_CXX17_TYPES_UNAVAILABLE
|
||||
#define PHMAP_HAVE_STD_OPTIONAL 1
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef PHMAP_HAVE_STD_VARIANT
|
||||
#error "PHMAP_HAVE_STD_VARIANT cannot be directly set."
|
||||
#endif
|
||||
|
||||
#ifdef __has_include
|
||||
#if __has_include(<variant>) && __cplusplus >= 201703L && \
|
||||
!PHMAP_INTERNAL_MACOS_CXX17_TYPES_UNAVAILABLE
|
||||
#define PHMAP_HAVE_STD_VARIANT 1
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef PHMAP_HAVE_STD_STRING_VIEW
|
||||
#error "PHMAP_HAVE_STD_STRING_VIEW cannot be directly set."
|
||||
#endif
|
||||
|
||||
#ifdef __has_include
|
||||
#if __has_include(<string_view>) && __cplusplus >= 201703L
|
||||
#define PHMAP_HAVE_STD_STRING_VIEW 1
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// #pragma message(PHMAP_VAR_NAME_VALUE(_MSVC_LANG))
|
||||
|
||||
#if defined(_MSC_VER) && _MSC_VER >= 1910 && \
|
||||
((defined(_MSVC_LANG) && _MSVC_LANG >= 201703) || __cplusplus >= 201703)
|
||||
// #define PHMAP_HAVE_STD_ANY 1
|
||||
#define PHMAP_HAVE_STD_OPTIONAL 1
|
||||
#define PHMAP_HAVE_STD_VARIANT 1
|
||||
#define PHMAP_HAVE_STD_STRING_VIEW 1
|
||||
#endif
|
||||
|
||||
#if (defined(_MSVC_LANG) && _MSVC_LANG >= 201703) || __cplusplus >= 201703
|
||||
#define PHMAP_HAVE_SHARED_MUTEX 1
|
||||
#endif
|
||||
|
||||
#ifndef PHMAP_HAVE_STD_STRING_VIEW
|
||||
#define PHMAP_HAVE_STD_STRING_VIEW 0
|
||||
#endif
|
||||
|
||||
// In debug mode, MSVC 2017's std::variant throws a EXCEPTION_ACCESS_VIOLATION
|
||||
// SEH exception from emplace for variant<SomeStruct> when constructing the
|
||||
// struct can throw. This defeats some of variant_test and
|
||||
// variant_exception_safety_test.
|
||||
#if defined(_MSC_VER) && _MSC_VER >= 1700 && defined(_DEBUG)
|
||||
#define PHMAP_INTERNAL_MSVC_2017_DBG_MODE
|
||||
#endif
|
||||
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Sanitizer Attributes
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// Sanitizer-related attributes are not "defined" in this file (and indeed
|
||||
// are not defined as such in any file). To utilize the following
|
||||
// sanitizer-related attributes within your builds, define the following macros
|
||||
// within your build using a `-D` flag, along with the given value for
|
||||
// `-fsanitize`:
|
||||
//
|
||||
// * `ADDRESS_SANITIZER` + `-fsanitize=address` (Clang, GCC 4.8)
|
||||
// * `MEMORY_SANITIZER` + `-fsanitize=memory` (Clang-only)
|
||||
// * `THREAD_SANITIZER + `-fsanitize=thread` (Clang, GCC 4.8+)
|
||||
// * `UNDEFINED_BEHAVIOR_SANITIZER` + `-fsanitize=undefined` (Clang, GCC 4.9+)
|
||||
// * `CONTROL_FLOW_INTEGRITY` + -fsanitize=cfi (Clang-only)
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// A function-like feature checking macro that is a wrapper around
|
||||
// `__has_attribute`, which is defined by GCC 5+ and Clang and evaluates to a
|
||||
// nonzero constant integer if the attribute is supported or 0 if not.
|
||||
//
|
||||
// It evaluates to zero if `__has_attribute` is not defined by the compiler.
|
||||
// -----------------------------------------------------------------------------
|
||||
#ifdef __has_attribute
|
||||
#define PHMAP_HAVE_ATTRIBUTE(x) __has_attribute(x)
|
||||
#else
|
||||
#define PHMAP_HAVE_ATTRIBUTE(x) 0
|
||||
#endif
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// A function-like feature checking macro that accepts C++11 style attributes.
|
||||
// It's a wrapper around `__has_cpp_attribute`, defined by ISO C++ SD-6
|
||||
// (https://en.cppreference.com/w/cpp/experimental/feature_test). If we don't
|
||||
// find `__has_cpp_attribute`, will evaluate to 0.
|
||||
// -----------------------------------------------------------------------------
|
||||
#if defined(__cplusplus) && defined(__has_cpp_attribute)
|
||||
#define PHMAP_HAVE_CPP_ATTRIBUTE(x) __has_cpp_attribute(x)
|
||||
#else
|
||||
#define PHMAP_HAVE_CPP_ATTRIBUTE(x) 0
|
||||
#endif
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Function Attributes
|
||||
// -----------------------------------------------------------------------------
|
||||
#if PHMAP_HAVE_ATTRIBUTE(format) || (defined(__GNUC__) && !defined(__clang__))
|
||||
#define PHMAP_PRINTF_ATTRIBUTE(string_index, first_to_check) \
|
||||
__attribute__((__format__(__printf__, string_index, first_to_check)))
|
||||
#define PHMAP_SCANF_ATTRIBUTE(string_index, first_to_check) \
|
||||
__attribute__((__format__(__scanf__, string_index, first_to_check)))
|
||||
#else
|
||||
#define PHMAP_PRINTF_ATTRIBUTE(string_index, first_to_check)
|
||||
#define PHMAP_SCANF_ATTRIBUTE(string_index, first_to_check)
|
||||
#endif
|
||||
|
||||
#if PHMAP_HAVE_ATTRIBUTE(always_inline) || \
|
||||
(defined(__GNUC__) && !defined(__clang__))
|
||||
#define PHMAP_ATTRIBUTE_ALWAYS_INLINE __attribute__((always_inline))
|
||||
#define PHMAP_HAVE_ATTRIBUTE_ALWAYS_INLINE 1
|
||||
#else
|
||||
#define PHMAP_ATTRIBUTE_ALWAYS_INLINE
|
||||
#endif
|
||||
|
||||
#if !defined(__INTEL_COMPILER) && (PHMAP_HAVE_ATTRIBUTE(noinline) || (defined(__GNUC__) && !defined(__clang__)))
|
||||
#define PHMAP_ATTRIBUTE_NOINLINE __attribute__((noinline))
|
||||
#define PHMAP_HAVE_ATTRIBUTE_NOINLINE 1
|
||||
#else
|
||||
#define PHMAP_ATTRIBUTE_NOINLINE
|
||||
#endif
|
||||
|
||||
#if PHMAP_HAVE_ATTRIBUTE(disable_tail_calls)
|
||||
#define PHMAP_HAVE_ATTRIBUTE_NO_TAIL_CALL 1
|
||||
#define PHMAP_ATTRIBUTE_NO_TAIL_CALL __attribute__((disable_tail_calls))
|
||||
#elif defined(__GNUC__) && !defined(__clang__)
|
||||
#define PHMAP_HAVE_ATTRIBUTE_NO_TAIL_CALL 1
|
||||
#define PHMAP_ATTRIBUTE_NO_TAIL_CALL \
|
||||
__attribute__((optimize("no-optimize-sibling-calls")))
|
||||
#else
|
||||
#define PHMAP_ATTRIBUTE_NO_TAIL_CALL
|
||||
#define PHMAP_HAVE_ATTRIBUTE_NO_TAIL_CALL 0
|
||||
#endif
|
||||
|
||||
#if (PHMAP_HAVE_ATTRIBUTE(weak) || \
|
||||
(defined(__GNUC__) && !defined(__clang__))) && \
|
||||
!(defined(__llvm__) && defined(_WIN32))
|
||||
#undef PHMAP_ATTRIBUTE_WEAK
|
||||
#define PHMAP_ATTRIBUTE_WEAK __attribute__((weak))
|
||||
#define PHMAP_HAVE_ATTRIBUTE_WEAK 1
|
||||
#else
|
||||
#define PHMAP_ATTRIBUTE_WEAK
|
||||
#define PHMAP_HAVE_ATTRIBUTE_WEAK 0
|
||||
#endif
|
||||
|
||||
#if PHMAP_HAVE_ATTRIBUTE(nonnull) || (defined(__GNUC__) && !defined(__clang__))
|
||||
#define PHMAP_ATTRIBUTE_NONNULL(arg_index) __attribute__((nonnull(arg_index)))
|
||||
#else
|
||||
#define PHMAP_ATTRIBUTE_NONNULL(...)
|
||||
#endif
|
||||
|
||||
#if PHMAP_HAVE_ATTRIBUTE(noreturn) || (defined(__GNUC__) && !defined(__clang__))
|
||||
#define PHMAP_ATTRIBUTE_NORETURN __attribute__((noreturn))
|
||||
#elif defined(_MSC_VER)
|
||||
#define PHMAP_ATTRIBUTE_NORETURN __declspec(noreturn)
|
||||
#else
|
||||
#define PHMAP_ATTRIBUTE_NORETURN
|
||||
#endif
|
||||
|
||||
#if defined(__GNUC__) && defined(ADDRESS_SANITIZER)
|
||||
#define PHMAP_ATTRIBUTE_NO_SANITIZE_ADDRESS __attribute__((no_sanitize_address))
|
||||
#else
|
||||
#define PHMAP_ATTRIBUTE_NO_SANITIZE_ADDRESS
|
||||
#endif
|
||||
|
||||
#if defined(__GNUC__) && defined(MEMORY_SANITIZER)
|
||||
#define PHMAP_ATTRIBUTE_NO_SANITIZE_MEMORY __attribute__((no_sanitize_memory))
|
||||
#else
|
||||
#define PHMAP_ATTRIBUTE_NO_SANITIZE_MEMORY
|
||||
#endif
|
||||
|
||||
#if defined(__GNUC__) && defined(THREAD_SANITIZER)
|
||||
#define PHMAP_ATTRIBUTE_NO_SANITIZE_THREAD __attribute__((no_sanitize_thread))
|
||||
#else
|
||||
#define PHMAP_ATTRIBUTE_NO_SANITIZE_THREAD
|
||||
#endif
|
||||
|
||||
#if defined(__GNUC__) && \
|
||||
(defined(UNDEFINED_BEHAVIOR_SANITIZER) || defined(ADDRESS_SANITIZER))
|
||||
#define PHMAP_ATTRIBUTE_NO_SANITIZE_UNDEFINED \
|
||||
__attribute__((no_sanitize("undefined")))
|
||||
#else
|
||||
#define PHMAP_ATTRIBUTE_NO_SANITIZE_UNDEFINED
|
||||
#endif
|
||||
|
||||
#if defined(__GNUC__) && defined(CONTROL_FLOW_INTEGRITY)
|
||||
#define PHMAP_ATTRIBUTE_NO_SANITIZE_CFI __attribute__((no_sanitize("cfi")))
|
||||
#else
|
||||
#define PHMAP_ATTRIBUTE_NO_SANITIZE_CFI
|
||||
#endif
|
||||
|
||||
#if defined(__GNUC__) && defined(SAFESTACK_SANITIZER)
|
||||
#define PHMAP_ATTRIBUTE_NO_SANITIZE_SAFESTACK \
|
||||
__attribute__((no_sanitize("safe-stack")))
|
||||
#else
|
||||
#define PHMAP_ATTRIBUTE_NO_SANITIZE_SAFESTACK
|
||||
#endif
|
||||
|
||||
#if PHMAP_HAVE_ATTRIBUTE(returns_nonnull) || \
|
||||
(defined(__GNUC__) && \
|
||||
(__GNUC__ > 5 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 9)) && \
|
||||
!defined(__clang__))
|
||||
#define PHMAP_ATTRIBUTE_RETURNS_NONNULL __attribute__((returns_nonnull))
|
||||
#else
|
||||
#define PHMAP_ATTRIBUTE_RETURNS_NONNULL
|
||||
#endif
|
||||
|
||||
#ifdef PHMAP_HAVE_ATTRIBUTE_SECTION
|
||||
#error PHMAP_HAVE_ATTRIBUTE_SECTION cannot be directly set
|
||||
#elif (PHMAP_HAVE_ATTRIBUTE(section) || \
|
||||
(defined(__GNUC__) && !defined(__clang__))) && \
|
||||
!defined(__APPLE__) && PHMAP_HAVE_ATTRIBUTE_WEAK
|
||||
#define PHMAP_HAVE_ATTRIBUTE_SECTION 1
|
||||
#ifndef PHMAP_ATTRIBUTE_SECTION
|
||||
#define PHMAP_ATTRIBUTE_SECTION(name) \
|
||||
__attribute__((section(#name))) __attribute__((noinline))
|
||||
#endif
|
||||
#ifndef PHMAP_ATTRIBUTE_SECTION_VARIABLE
|
||||
#define PHMAP_ATTRIBUTE_SECTION_VARIABLE(name) __attribute__((section(#name)))
|
||||
#endif
|
||||
#ifndef PHMAP_DECLARE_ATTRIBUTE_SECTION_VARS
|
||||
#define PHMAP_DECLARE_ATTRIBUTE_SECTION_VARS(name) \
|
||||
extern char __start_##name[] PHMAP_ATTRIBUTE_WEAK; \
|
||||
extern char __stop_##name[] PHMAP_ATTRIBUTE_WEAK
|
||||
#endif
|
||||
#ifndef PHMAP_DEFINE_ATTRIBUTE_SECTION_VARS
|
||||
#define PHMAP_INIT_ATTRIBUTE_SECTION_VARS(name)
|
||||
#define PHMAP_DEFINE_ATTRIBUTE_SECTION_VARS(name)
|
||||
#endif
|
||||
#define PHMAP_ATTRIBUTE_SECTION_START(name) \
|
||||
(reinterpret_cast<void *>(__start_##name))
|
||||
#define PHMAP_ATTRIBUTE_SECTION_STOP(name) \
|
||||
(reinterpret_cast<void *>(__stop_##name))
|
||||
#else // !PHMAP_HAVE_ATTRIBUTE_SECTION
|
||||
#define PHMAP_HAVE_ATTRIBUTE_SECTION 0
|
||||
#define PHMAP_ATTRIBUTE_SECTION(name)
|
||||
#define PHMAP_ATTRIBUTE_SECTION_VARIABLE(name)
|
||||
#define PHMAP_INIT_ATTRIBUTE_SECTION_VARS(name)
|
||||
#define PHMAP_DEFINE_ATTRIBUTE_SECTION_VARS(name)
|
||||
#define PHMAP_DECLARE_ATTRIBUTE_SECTION_VARS(name)
|
||||
#define PHMAP_ATTRIBUTE_SECTION_START(name) (reinterpret_cast<void *>(0))
|
||||
#define PHMAP_ATTRIBUTE_SECTION_STOP(name) (reinterpret_cast<void *>(0))
|
||||
#endif // PHMAP_ATTRIBUTE_SECTION
|
||||
|
||||
#if PHMAP_HAVE_ATTRIBUTE(force_align_arg_pointer) || \
|
||||
(defined(__GNUC__) && !defined(__clang__))
|
||||
#if defined(__i386__)
|
||||
#define PHMAP_ATTRIBUTE_STACK_ALIGN_FOR_OLD_LIBC \
|
||||
__attribute__((force_align_arg_pointer))
|
||||
#define PHMAP_REQUIRE_STACK_ALIGN_TRAMPOLINE (0)
|
||||
#elif defined(__x86_64__)
|
||||
#define PHMAP_REQUIRE_STACK_ALIGN_TRAMPOLINE (1)
|
||||
#define PHMAP_ATTRIBUTE_STACK_ALIGN_FOR_OLD_LIBC
|
||||
#else // !__i386__ && !__x86_64
|
||||
#define PHMAP_REQUIRE_STACK_ALIGN_TRAMPOLINE (0)
|
||||
#define PHMAP_ATTRIBUTE_STACK_ALIGN_FOR_OLD_LIBC
|
||||
#endif // __i386__
|
||||
#else
|
||||
#define PHMAP_ATTRIBUTE_STACK_ALIGN_FOR_OLD_LIBC
|
||||
#define PHMAP_REQUIRE_STACK_ALIGN_TRAMPOLINE (0)
|
||||
#endif
|
||||
|
||||
#if PHMAP_HAVE_ATTRIBUTE(nodiscard)
|
||||
#define PHMAP_MUST_USE_RESULT [[nodiscard]]
|
||||
#elif defined(__clang__) && PHMAP_HAVE_ATTRIBUTE(warn_unused_result)
|
||||
#define PHMAP_MUST_USE_RESULT __attribute__((warn_unused_result))
|
||||
#else
|
||||
#define PHMAP_MUST_USE_RESULT
|
||||
#endif
|
||||
|
||||
#if PHMAP_HAVE_ATTRIBUTE(hot) || (defined(__GNUC__) && !defined(__clang__))
|
||||
#define PHMAP_ATTRIBUTE_HOT __attribute__((hot))
|
||||
#else
|
||||
#define PHMAP_ATTRIBUTE_HOT
|
||||
#endif
|
||||
|
||||
#if PHMAP_HAVE_ATTRIBUTE(cold) || (defined(__GNUC__) && !defined(__clang__))
|
||||
#define PHMAP_ATTRIBUTE_COLD __attribute__((cold))
|
||||
#else
|
||||
#define PHMAP_ATTRIBUTE_COLD
|
||||
#endif
|
||||
|
||||
#if defined(__clang__)
|
||||
#if PHMAP_HAVE_CPP_ATTRIBUTE(clang::reinitializes)
|
||||
#define PHMAP_ATTRIBUTE_REINITIALIZES [[clang::reinitializes]]
|
||||
#else
|
||||
#define PHMAP_ATTRIBUTE_REINITIALIZES
|
||||
#endif
|
||||
#else
|
||||
#define PHMAP_ATTRIBUTE_REINITIALIZES
|
||||
#endif
|
||||
|
||||
#if PHMAP_HAVE_ATTRIBUTE(unused) || (defined(__GNUC__) && !defined(__clang__))
|
||||
#undef PHMAP_ATTRIBUTE_UNUSED
|
||||
#define PHMAP_ATTRIBUTE_UNUSED __attribute__((__unused__))
|
||||
#else
|
||||
#define PHMAP_ATTRIBUTE_UNUSED
|
||||
#endif
|
||||
|
||||
#if PHMAP_HAVE_ATTRIBUTE(tls_model) || (defined(__GNUC__) && !defined(__clang__))
|
||||
#define PHMAP_ATTRIBUTE_INITIAL_EXEC __attribute__((tls_model("initial-exec")))
|
||||
#else
|
||||
#define PHMAP_ATTRIBUTE_INITIAL_EXEC
|
||||
#endif
|
||||
|
||||
#if PHMAP_HAVE_ATTRIBUTE(packed) || (defined(__GNUC__) && !defined(__clang__))
|
||||
#define PHMAP_ATTRIBUTE_PACKED __attribute__((__packed__))
|
||||
#else
|
||||
#define PHMAP_ATTRIBUTE_PACKED
|
||||
#endif
|
||||
|
||||
#if PHMAP_HAVE_ATTRIBUTE(aligned) || (defined(__GNUC__) && !defined(__clang__))
|
||||
#define PHMAP_ATTRIBUTE_FUNC_ALIGN(bytes) __attribute__((aligned(bytes)))
|
||||
#else
|
||||
#define PHMAP_ATTRIBUTE_FUNC_ALIGN(bytes)
|
||||
#endif
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
// Figure out SSE support
|
||||
// ----------------------------------------------------------------------
|
||||
#ifndef PHMAP_HAVE_SSE2
|
||||
#if defined(__SSE2__) || \
|
||||
(defined(_MSC_VER) && \
|
||||
(defined(_M_X64) || (defined(_M_IX86) && _M_IX86_FP >= 2)))
|
||||
#define PHMAP_HAVE_SSE2 1
|
||||
#else
|
||||
#define PHMAP_HAVE_SSE2 0
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef PHMAP_HAVE_SSSE3
|
||||
#ifdef __SSSE3__
|
||||
#define PHMAP_HAVE_SSSE3 1
|
||||
#else
|
||||
#define PHMAP_HAVE_SSSE3 0
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if PHMAP_HAVE_SSSE3 && !PHMAP_HAVE_SSE2
|
||||
#error "Bad configuration!"
|
||||
#endif
|
||||
|
||||
#if PHMAP_HAVE_SSE2
|
||||
#include <emmintrin.h>
|
||||
#endif
|
||||
|
||||
#if PHMAP_HAVE_SSSE3
|
||||
#include <tmmintrin.h>
|
||||
#endif
|
||||
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
// constexpr if
|
||||
// ----------------------------------------------------------------------
|
||||
#if __cplusplus >= 201703 || (defined(_MSVC_LANG) && _MSVC_LANG >= 201703)
|
||||
#define PHMAP_IF_CONSTEXPR(expr) if constexpr ((expr))
|
||||
#else
|
||||
#define PHMAP_IF_CONSTEXPR(expr) if ((expr))
|
||||
#endif
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
// base/macros.h
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
// PHMAP_ARRAYSIZE()
|
||||
//
|
||||
// Returns the number of elements in an array as a compile-time constant, which
|
||||
// can be used in defining new arrays. If you use this macro on a pointer by
|
||||
// mistake, you will get a compile-time error.
|
||||
#define PHMAP_ARRAYSIZE(array) \
|
||||
(sizeof(::phmap::macros_internal::ArraySizeHelper(array)))
|
||||
|
||||
namespace phmap {
|
||||
namespace macros_internal {
|
||||
// Note: this internal template function declaration is used by PHMAP_ARRAYSIZE.
|
||||
// The function doesn't need a definition, as we only use its type.
|
||||
template <typename T, size_t N>
|
||||
auto ArraySizeHelper(const T (&array)[N]) -> char (&)[N];
|
||||
} // namespace macros_internal
|
||||
} // namespace phmap
|
||||
|
||||
// TODO(zhangxy): Use c++17 standard [[fallthrough]] macro, when supported.
|
||||
#if defined(__clang__) && defined(__has_warning)
|
||||
#if __has_feature(cxx_attributes) && __has_warning("-Wimplicit-fallthrough")
|
||||
#define PHMAP_FALLTHROUGH_INTENDED [[clang::fallthrough]]
|
||||
#endif
|
||||
#elif defined(__GNUC__) && __GNUC__ >= 7
|
||||
#define PHMAP_FALLTHROUGH_INTENDED [[gnu::fallthrough]]
|
||||
#endif
|
||||
|
||||
#ifndef PHMAP_FALLTHROUGH_INTENDED
|
||||
#define PHMAP_FALLTHROUGH_INTENDED \
|
||||
do { } while (0)
|
||||
#endif
|
||||
|
||||
// PHMAP_DEPRECATED()
|
||||
//
|
||||
// Marks a deprecated class, struct, enum, function, method and variable
|
||||
// declarations. The macro argument is used as a custom diagnostic message (e.g.
|
||||
// suggestion of a better alternative).
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// class PHMAP_DEPRECATED("Use Bar instead") Foo {...};
|
||||
// PHMAP_DEPRECATED("Use Baz instead") void Bar() {...}
|
||||
//
|
||||
// Every usage of a deprecated entity will trigger a warning when compiled with
|
||||
// clang's `-Wdeprecated-declarations` option. This option is turned off by
|
||||
// default, but the warnings will be reported by clang-tidy.
|
||||
#if defined(__clang__) && __cplusplus >= 201103L
|
||||
#define PHMAP_DEPRECATED(message) __attribute__((deprecated(message)))
|
||||
#endif
|
||||
|
||||
#ifndef PHMAP_DEPRECATED
|
||||
#define PHMAP_DEPRECATED(message)
|
||||
#endif
|
||||
|
||||
// PHMAP_BAD_CALL_IF()
|
||||
//
|
||||
// Used on a function overload to trap bad calls: any call that matches the
|
||||
// overload will cause a compile-time error. This macro uses a clang-specific
|
||||
// "enable_if" attribute, as described at
|
||||
// http://clang.llvm.org/docs/AttributeReference.html#enable-if
|
||||
//
|
||||
// Overloads which use this macro should be bracketed by
|
||||
// `#ifdef PHMAP_BAD_CALL_IF`.
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// int isdigit(int c);
|
||||
// #ifdef PHMAP_BAD_CALL_IF
|
||||
// int isdigit(int c)
|
||||
// PHMAP_BAD_CALL_IF(c <= -1 || c > 255,
|
||||
// "'c' must have the value of an unsigned char or EOF");
|
||||
// #endif // PHMAP_BAD_CALL_IF
|
||||
|
||||
#if defined(__clang__)
|
||||
#if __has_attribute(enable_if)
|
||||
#define PHMAP_BAD_CALL_IF(expr, msg) \
|
||||
__attribute__((enable_if(expr, "Bad call trap"), unavailable(msg)))
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// PHMAP_ASSERT()
|
||||
//
|
||||
// In C++11, `assert` can't be used portably within constexpr functions.
|
||||
// PHMAP_ASSERT functions as a runtime assert but works in C++11 constexpr
|
||||
// functions. Example:
|
||||
//
|
||||
// constexpr double Divide(double a, double b) {
|
||||
// return PHMAP_ASSERT(b != 0), a / b;
|
||||
// }
|
||||
//
|
||||
// This macro is inspired by
|
||||
// https://akrzemi1.wordpress.com/2017/05/18/asserts-in-constexpr-functions/
|
||||
#if defined(NDEBUG)
|
||||
#define PHMAP_ASSERT(expr) (false ? (void)(expr) : (void)0)
|
||||
#else
|
||||
#define PHMAP_ASSERT(expr) \
|
||||
(PHMAP_PREDICT_TRUE((expr)) ? (void)0 \
|
||||
: [] { assert(false && #expr); }()) // NOLINT
|
||||
#endif
|
||||
|
||||
#ifdef PHMAP_HAVE_EXCEPTIONS
|
||||
#define PHMAP_INTERNAL_TRY try
|
||||
#define PHMAP_INTERNAL_CATCH_ANY catch (...)
|
||||
#define PHMAP_INTERNAL_RETHROW do { throw; } while (false)
|
||||
#else // PHMAP_HAVE_EXCEPTIONS
|
||||
#define PHMAP_INTERNAL_TRY if (true)
|
||||
#define PHMAP_INTERNAL_CATCH_ANY else if (false)
|
||||
#define PHMAP_INTERNAL_RETHROW do {} while (false)
|
||||
#endif // PHMAP_HAVE_EXCEPTIONS
|
||||
|
||||
|
||||
#endif // phmap_config_h_guard_
|
227
src/includes/3thparty/parallel_hashmap/phmap_dump.h
Normal file
227
src/includes/3thparty/parallel_hashmap/phmap_dump.h
Normal file
@ -0,0 +1,227 @@
|
||||
#if !defined(phmap_dump_h_guard_)
|
||||
#define phmap_dump_h_guard_
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Copyright (c) 2019, Gregory Popovitch - greg7mdp@gmail.com
|
||||
//
|
||||
// providing dump/load/mmap_load
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
#include "phmap.h"
|
||||
namespace phmap
|
||||
{
|
||||
|
||||
namespace type_traits_internal {
|
||||
|
||||
#if defined(__GLIBCXX__) && __GLIBCXX__ < 20150801
|
||||
template<typename T> struct IsTriviallyCopyable : public std::integral_constant<bool, __has_trivial_copy(T)> {};
|
||||
#else
|
||||
template<typename T> struct IsTriviallyCopyable : public std::is_trivially_copyable<T> {};
|
||||
#endif
|
||||
|
||||
template <class T1, class T2>
|
||||
struct IsTriviallyCopyable<std::pair<T1, T2>> {
|
||||
static constexpr bool value = IsTriviallyCopyable<T1>::value && IsTriviallyCopyable<T2>::value;
|
||||
};
|
||||
}
|
||||
|
||||
namespace container_internal {
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// dump/load for raw_hash_set
|
||||
// ------------------------------------------------------------------------
|
||||
template <class Policy, class Hash, class Eq, class Alloc>
|
||||
template<typename OutputArchive>
|
||||
bool raw_hash_set<Policy, Hash, Eq, Alloc>::dump(OutputArchive& ar) {
|
||||
static_assert(type_traits_internal::IsTriviallyCopyable<value_type>::value,
|
||||
"value_type should be trivially copyable");
|
||||
|
||||
if (!ar.dump(size_)) {
|
||||
std::cerr << "Failed to dump size_" << std::endl;
|
||||
return false;
|
||||
}
|
||||
if (size_ == 0) {
|
||||
return true;
|
||||
}
|
||||
if (!ar.dump(capacity_)) {
|
||||
std::cerr << "Failed to dump capacity_" << std::endl;
|
||||
return false;
|
||||
}
|
||||
if (!ar.dump(reinterpret_cast<char*>(ctrl_),
|
||||
sizeof(ctrl_t) * (capacity_ + Group::kWidth + 1))) {
|
||||
|
||||
std::cerr << "Failed to dump ctrl_" << std::endl;
|
||||
return false;
|
||||
}
|
||||
if (!ar.dump(reinterpret_cast<char*>(slots_),
|
||||
sizeof(slot_type) * capacity_)) {
|
||||
std::cerr << "Failed to dump slot_" << std::endl;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
template <class Policy, class Hash, class Eq, class Alloc>
|
||||
template<typename InputArchive>
|
||||
bool raw_hash_set<Policy, Hash, Eq, Alloc>::load(InputArchive& ar) {
|
||||
static_assert(type_traits_internal::IsTriviallyCopyable<value_type>::value,
|
||||
"value_type should be trivially copyable");
|
||||
raw_hash_set<Policy, Hash, Eq, Alloc>().swap(*this); // clear any existing content
|
||||
if (!ar.load(&size_)) {
|
||||
std::cerr << "Failed to load size_" << std::endl;
|
||||
return false;
|
||||
}
|
||||
if (size_ == 0) {
|
||||
return true;
|
||||
}
|
||||
if (!ar.load(&capacity_)) {
|
||||
std::cerr << "Failed to load capacity_" << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
// allocate memory for ctrl_ and slots_
|
||||
initialize_slots();
|
||||
if (!ar.load(reinterpret_cast<char*>(ctrl_),
|
||||
sizeof(ctrl_t) * (capacity_ + Group::kWidth + 1))) {
|
||||
std::cerr << "Failed to load ctrl" << std::endl;
|
||||
return false;
|
||||
}
|
||||
if (!ar.load(reinterpret_cast<char*>(slots_),
|
||||
sizeof(slot_type) * capacity_)) {
|
||||
std::cerr << "Failed to load slot" << std::endl;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// dump/load for parallel_hash_set
|
||||
// ------------------------------------------------------------------------
|
||||
template <size_t N,
|
||||
template <class, class, class, class> class RefSet,
|
||||
class Mtx_,
|
||||
class Policy, class Hash, class Eq, class Alloc>
|
||||
template<typename OutputArchive>
|
||||
bool parallel_hash_set<N, RefSet, Mtx_, Policy, Hash, Eq, Alloc>::dump(OutputArchive& ar) {
|
||||
static_assert(type_traits_internal::IsTriviallyCopyable<value_type>::value,
|
||||
"value_type should be trivially copyable");
|
||||
|
||||
if (! ar.dump(subcnt())) {
|
||||
std::cerr << "Failed to dump meta!" << std::endl;
|
||||
return false;
|
||||
}
|
||||
for (size_t i = 0; i < sets_.size(); ++i) {
|
||||
auto& inner = sets_[i];
|
||||
typename Lockable::UniqueLock m(const_cast<Inner&>(inner));
|
||||
if (!inner.set_.dump(ar)) {
|
||||
std::cerr << "Failed to dump submap " << i << std::endl;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
template <size_t N,
|
||||
template <class, class, class, class> class RefSet,
|
||||
class Mtx_,
|
||||
class Policy, class Hash, class Eq, class Alloc>
|
||||
template<typename InputArchive>
|
||||
bool parallel_hash_set<N, RefSet, Mtx_, Policy, Hash, Eq, Alloc>::load(InputArchive& ar) {
|
||||
static_assert(type_traits_internal::IsTriviallyCopyable<value_type>::value,
|
||||
"value_type should be trivially copyable");
|
||||
|
||||
size_t submap_count = 0;
|
||||
if (!ar.load(&submap_count)) {
|
||||
std::cerr << "Failed to load submap count!" << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (submap_count != subcnt()) {
|
||||
std::cerr << "submap count(" << submap_count << ") != N(" << N << ")" << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < submap_count; ++i) {
|
||||
auto& inner = sets_[i];
|
||||
typename Lockable::UniqueLock m(const_cast<Inner&>(inner));
|
||||
if (!inner.set_.load(ar)) {
|
||||
std::cerr << "Failed to load submap " << i << std::endl;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
} // namespace container_internal
|
||||
|
||||
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// BinaryArchive
|
||||
// File is closed when archive object is destroyed
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// ------------------------------------------------------------------------
|
||||
class BinaryOutputArchive {
|
||||
public:
|
||||
BinaryOutputArchive(const char *file_path) {
|
||||
ofs_.open(file_path, std::ios_base::binary);
|
||||
}
|
||||
|
||||
bool dump(const char *p, size_t sz) {
|
||||
ofs_.write(p, sz);
|
||||
return true;
|
||||
}
|
||||
|
||||
template<typename V>
|
||||
typename std::enable_if<type_traits_internal::IsTriviallyCopyable<V>::value, bool>::type
|
||||
dump(const V& v) {
|
||||
ofs_.write(reinterpret_cast<const char *>(&v), sizeof(V));
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
std::ofstream ofs_;
|
||||
};
|
||||
|
||||
|
||||
class BinaryInputArchive {
|
||||
public:
|
||||
BinaryInputArchive(const char * file_path) {
|
||||
ifs_.open(file_path, std::ios_base::binary);
|
||||
}
|
||||
|
||||
bool load(char* p, size_t sz) {
|
||||
ifs_.read(p, sz);
|
||||
return true;
|
||||
}
|
||||
|
||||
template<typename V>
|
||||
typename std::enable_if<type_traits_internal::IsTriviallyCopyable<V>::value, bool>::type
|
||||
load(V* v) {
|
||||
ifs_.read(reinterpret_cast<char *>(v), sizeof(V));
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
std::ifstream ifs_;
|
||||
};
|
||||
|
||||
} // namespace phmap
|
||||
|
||||
#endif // phmap_dump_h_guard_
|
154
src/includes/3thparty/parallel_hashmap/phmap_fwd_decl.h
Normal file
154
src/includes/3thparty/parallel_hashmap/phmap_fwd_decl.h
Normal file
@ -0,0 +1,154 @@
|
||||
#if !defined(phmap_fwd_decl_h_guard_)
|
||||
#define phmap_fwd_decl_h_guard_
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Copyright (c) 2019, Gregory Popovitch - greg7mdp@gmail.com
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable : 4514) // unreferenced inline function has been removed
|
||||
#pragma warning(disable : 4710) // function not inlined
|
||||
#pragma warning(disable : 4711) // selected for automatic inline expansion
|
||||
#endif
|
||||
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
|
||||
#if defined(PHMAP_USE_ABSL_HASH)
|
||||
namespace absl { template <class T> struct Hash; };
|
||||
#endif
|
||||
|
||||
namespace phmap {
|
||||
|
||||
#if defined(PHMAP_USE_ABSL_HASH)
|
||||
template <class T> using Hash = absl::Hash<T>;
|
||||
#else
|
||||
template <class T> struct Hash;
|
||||
#endif
|
||||
|
||||
template <class T> struct EqualTo;
|
||||
template <class T> struct Less;
|
||||
template <class T> using Allocator = typename std::allocator<T>;
|
||||
template<class T1, class T2> using Pair = typename std::pair<T1, T2>;
|
||||
|
||||
class NullMutex;
|
||||
|
||||
namespace container_internal {
|
||||
|
||||
// The hash of an object of type T is computed by using phmap::Hash.
|
||||
template <class T, class E = void>
|
||||
struct HashEq
|
||||
{
|
||||
using Hash = phmap::Hash<T>;
|
||||
using Eq = phmap::EqualTo<T>;
|
||||
};
|
||||
|
||||
template <class T>
|
||||
using hash_default_hash = typename container_internal::HashEq<T>::Hash;
|
||||
|
||||
template <class T>
|
||||
using hash_default_eq = typename container_internal::HashEq<T>::Eq;
|
||||
|
||||
// type alias for std::allocator so we can forward declare without including other headers
|
||||
template <class T>
|
||||
using Allocator = typename phmap::Allocator<T>;
|
||||
|
||||
// type alias for std::pair so we can forward declare without including other headers
|
||||
template<class T1, class T2>
|
||||
using Pair = typename phmap::Pair<T1, T2>;
|
||||
|
||||
} // namespace container_internal
|
||||
|
||||
// ------------- forward declarations for hash containers ----------------------------------
|
||||
template <class T,
|
||||
class Hash = phmap::container_internal::hash_default_hash<T>,
|
||||
class Eq = phmap::container_internal::hash_default_eq<T>,
|
||||
class Alloc = phmap::container_internal::Allocator<T>> // alias for std::allocator
|
||||
class flat_hash_set;
|
||||
|
||||
template <class K, class V,
|
||||
class Hash = phmap::container_internal::hash_default_hash<K>,
|
||||
class Eq = phmap::container_internal::hash_default_eq<K>,
|
||||
class Alloc = phmap::container_internal::Allocator<
|
||||
phmap::container_internal::Pair<const K, V>>> // alias for std::allocator
|
||||
class flat_hash_map;
|
||||
|
||||
template <class T,
|
||||
class Hash = phmap::container_internal::hash_default_hash<T>,
|
||||
class Eq = phmap::container_internal::hash_default_eq<T>,
|
||||
class Alloc = phmap::container_internal::Allocator<T>> // alias for std::allocator
|
||||
class node_hash_set;
|
||||
|
||||
template <class Key, class Value,
|
||||
class Hash = phmap::container_internal::hash_default_hash<Key>,
|
||||
class Eq = phmap::container_internal::hash_default_eq<Key>,
|
||||
class Alloc = phmap::container_internal::Allocator<
|
||||
phmap::container_internal::Pair<const Key, Value>>> // alias for std::allocator
|
||||
class node_hash_map;
|
||||
|
||||
template <class T,
|
||||
class Hash = phmap::container_internal::hash_default_hash<T>,
|
||||
class Eq = phmap::container_internal::hash_default_eq<T>,
|
||||
class Alloc = phmap::container_internal::Allocator<T>, // alias for std::allocator
|
||||
size_t N = 4, // 2**N submaps
|
||||
class Mutex = phmap::NullMutex> // use std::mutex to enable internal locks
|
||||
class parallel_flat_hash_set;
|
||||
|
||||
template <class K, class V,
|
||||
class Hash = phmap::container_internal::hash_default_hash<K>,
|
||||
class Eq = phmap::container_internal::hash_default_eq<K>,
|
||||
class Alloc = phmap::container_internal::Allocator<
|
||||
phmap::container_internal::Pair<const K, V>>, // alias for std::allocator
|
||||
size_t N = 4, // 2**N submaps
|
||||
class Mutex = phmap::NullMutex> // use std::mutex to enable internal locks
|
||||
class parallel_flat_hash_map;
|
||||
|
||||
template <class T,
|
||||
class Hash = phmap::container_internal::hash_default_hash<T>,
|
||||
class Eq = phmap::container_internal::hash_default_eq<T>,
|
||||
class Alloc = phmap::container_internal::Allocator<T>, // alias for std::allocator
|
||||
size_t N = 4, // 2**N submaps
|
||||
class Mutex = phmap::NullMutex> // use std::mutex to enable internal locks
|
||||
class parallel_node_hash_set;
|
||||
|
||||
template <class Key, class Value,
|
||||
class Hash = phmap::container_internal::hash_default_hash<Key>,
|
||||
class Eq = phmap::container_internal::hash_default_eq<Key>,
|
||||
class Alloc = phmap::container_internal::Allocator<
|
||||
phmap::container_internal::Pair<const Key, Value>>, // alias for std::allocator
|
||||
size_t N = 4, // 2**N submaps
|
||||
class Mutex = phmap::NullMutex> // use std::mutex to enable internal locks
|
||||
class parallel_node_hash_map;
|
||||
|
||||
// ------------- forward declarations for btree containers ----------------------------------
|
||||
template <typename Key, typename Compare = phmap::Less<Key>,
|
||||
typename Alloc = phmap::Allocator<Key>>
|
||||
class btree_set;
|
||||
|
||||
template <typename Key, typename Compare = phmap::Less<Key>,
|
||||
typename Alloc = phmap::Allocator<Key>>
|
||||
class btree_multiset;
|
||||
|
||||
template <typename Key, typename Value, typename Compare = phmap::Less<Key>,
|
||||
typename Alloc = phmap::Allocator<phmap::container_internal::Pair<const Key, Value>>>
|
||||
class btree_map;
|
||||
|
||||
template <typename Key, typename Value, typename Compare = phmap::Less<Key>,
|
||||
typename Alloc = phmap::Allocator<phmap::container_internal::Pair<const Key, Value>>>
|
||||
class btree_multimap;
|
||||
|
||||
} // namespace phmap
|
||||
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(pop)
|
||||
#endif
|
||||
|
||||
#endif // phmap_fwd_decl_h_guard_
|
370
src/includes/3thparty/parallel_hashmap/phmap_utils.h
Normal file
370
src/includes/3thparty/parallel_hashmap/phmap_utils.h
Normal file
@ -0,0 +1,370 @@
|
||||
#if !defined(phmap_utils_h_guard_)
|
||||
#define phmap_utils_h_guard_
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Copyright (c) 2019, Gregory Popovitch - greg7mdp@gmail.com
|
||||
//
|
||||
// minimal header providing phmap::HashState
|
||||
//
|
||||
// use as: phmap::HashState().combine(0, _first_name, _last_name, _age);
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable : 4514) // unreferenced inline function has been removed
|
||||
#pragma warning(disable : 4710) // function not inlined
|
||||
#pragma warning(disable : 4711) // selected for automatic inline expansion
|
||||
#endif
|
||||
|
||||
#include <cstdint>
|
||||
#include <functional>
|
||||
#include <tuple>
|
||||
#include "phmap_bits.h"
|
||||
|
||||
namespace phmap
|
||||
{
|
||||
|
||||
// ---------------------------------------------------------------
|
||||
// ---------------------------------------------------------------
|
||||
template<int n>
|
||||
struct phmap_mix
|
||||
{
|
||||
inline size_t operator()(size_t) const;
|
||||
};
|
||||
|
||||
template<>
|
||||
struct phmap_mix<4>
|
||||
{
|
||||
inline size_t operator()(size_t a) const
|
||||
{
|
||||
static constexpr uint64_t kmul = 0xcc9e2d51UL;
|
||||
// static constexpr uint64_t kmul = 0x3B9ACB93UL; // [greg] my own random prime
|
||||
uint64_t l = a * kmul;
|
||||
return static_cast<size_t>(l ^ (l >> 32));
|
||||
}
|
||||
};
|
||||
|
||||
#if defined(PHMAP_HAS_UMUL128)
|
||||
template<>
|
||||
struct phmap_mix<8>
|
||||
{
|
||||
// Very fast mixing (similar to Abseil)
|
||||
inline size_t operator()(size_t a) const
|
||||
{
|
||||
static constexpr uint64_t k = 0xde5fb9d2630458e9ULL;
|
||||
// static constexpr uint64_t k = 0x7C9D0BF0567102A5ULL; // [greg] my own random prime
|
||||
uint64_t h;
|
||||
uint64_t l = umul128(a, k, &h);
|
||||
return static_cast<size_t>(h + l);
|
||||
}
|
||||
};
|
||||
#else
|
||||
template<>
|
||||
struct phmap_mix<8>
|
||||
{
|
||||
inline size_t operator()(size_t a) const
|
||||
{
|
||||
a = (~a) + (a << 21); // a = (a << 21) - a - 1;
|
||||
a = a ^ (a >> 24);
|
||||
a = (a + (a << 3)) + (a << 8); // a * 265
|
||||
a = a ^ (a >> 14);
|
||||
a = (a + (a << 2)) + (a << 4); // a * 21
|
||||
a = a ^ (a >> 28);
|
||||
a = a + (a << 31);
|
||||
return static_cast<size_t>(a);
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
// --------------------------------------------
|
||||
template<int n>
|
||||
struct fold_if_needed
|
||||
{
|
||||
inline size_t operator()(uint64_t) const;
|
||||
};
|
||||
|
||||
template<>
|
||||
struct fold_if_needed<4>
|
||||
{
|
||||
inline size_t operator()(uint64_t a) const
|
||||
{
|
||||
return static_cast<size_t>(a ^ (a >> 32));
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct fold_if_needed<8>
|
||||
{
|
||||
inline size_t operator()(uint64_t a) const
|
||||
{
|
||||
return static_cast<size_t>(a);
|
||||
}
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------
|
||||
// see if class T has a hash_value() friend method
|
||||
// ---------------------------------------------------------------
|
||||
template<typename T>
|
||||
struct has_hash_value
|
||||
{
|
||||
private:
|
||||
typedef std::true_type yes;
|
||||
typedef std::false_type no;
|
||||
|
||||
template<typename U> static auto test(int) -> decltype(hash_value(std::declval<U&>()) == 1, yes());
|
||||
|
||||
template<typename> static no test(...);
|
||||
|
||||
public:
|
||||
static constexpr bool value = std::is_same<decltype(test<T>(0)), yes>::value;
|
||||
};
|
||||
|
||||
#if defined(PHMAP_USE_ABSL_HASH) && !defined(phmap_fwd_decl_h_guard_)
|
||||
namespace absl { template <class T> struct Hash; };
|
||||
template <class T> using Hash = absl::Hash<T>;
|
||||
#else
|
||||
// ---------------------------------------------------------------
|
||||
// phmap::Hash
|
||||
// ---------------------------------------------------------------
|
||||
template <class T>
|
||||
struct Hash
|
||||
{
|
||||
template <class U, typename std::enable_if<has_hash_value<U>::value, int>::type = 0>
|
||||
size_t _hash(const T& val) const
|
||||
{
|
||||
return hash_value(val);
|
||||
}
|
||||
|
||||
template <class U, typename std::enable_if<!has_hash_value<U>::value, int>::type = 0>
|
||||
size_t _hash(const T& val) const
|
||||
{
|
||||
return std::hash<T>()(val);
|
||||
}
|
||||
|
||||
inline size_t operator()(const T& val) const
|
||||
{
|
||||
return _hash<T>(val);
|
||||
}
|
||||
};
|
||||
|
||||
template <class T>
|
||||
struct Hash<T *>
|
||||
{
|
||||
inline size_t operator()(const T *val) const noexcept
|
||||
{
|
||||
return static_cast<size_t>(reinterpret_cast<const uintptr_t>(val));
|
||||
}
|
||||
};
|
||||
|
||||
template<class ArgumentType, class ResultType>
|
||||
struct phmap_unary_function
|
||||
{
|
||||
typedef ArgumentType argument_type;
|
||||
typedef ResultType result_type;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct Hash<bool> : public phmap_unary_function<bool, size_t>
|
||||
{
|
||||
inline size_t operator()(bool val) const noexcept
|
||||
{ return static_cast<size_t>(val); }
|
||||
};
|
||||
|
||||
template <>
|
||||
struct Hash<char> : public phmap_unary_function<char, size_t>
|
||||
{
|
||||
inline size_t operator()(char val) const noexcept
|
||||
{ return static_cast<size_t>(val); }
|
||||
};
|
||||
|
||||
template <>
|
||||
struct Hash<signed char> : public phmap_unary_function<signed char, size_t>
|
||||
{
|
||||
inline size_t operator()(signed char val) const noexcept
|
||||
{ return static_cast<size_t>(val); }
|
||||
};
|
||||
|
||||
template <>
|
||||
struct Hash<unsigned char> : public phmap_unary_function<unsigned char, size_t>
|
||||
{
|
||||
inline size_t operator()(unsigned char val) const noexcept
|
||||
{ return static_cast<size_t>(val); }
|
||||
};
|
||||
|
||||
template <>
|
||||
struct Hash<wchar_t> : public phmap_unary_function<wchar_t, size_t>
|
||||
{
|
||||
inline size_t operator()(wchar_t val) const noexcept
|
||||
{ return static_cast<size_t>(val); }
|
||||
};
|
||||
|
||||
template <>
|
||||
struct Hash<int16_t> : public phmap_unary_function<int16_t, size_t>
|
||||
{
|
||||
inline size_t operator()(int16_t val) const noexcept
|
||||
{ return static_cast<size_t>(val); }
|
||||
};
|
||||
|
||||
template <>
|
||||
struct Hash<uint16_t> : public phmap_unary_function<uint16_t, size_t>
|
||||
{
|
||||
inline size_t operator()(uint16_t val) const noexcept
|
||||
{ return static_cast<size_t>(val); }
|
||||
};
|
||||
|
||||
template <>
|
||||
struct Hash<int32_t> : public phmap_unary_function<int32_t, size_t>
|
||||
{
|
||||
inline size_t operator()(int32_t val) const noexcept
|
||||
{ return static_cast<size_t>(val); }
|
||||
};
|
||||
|
||||
template <>
|
||||
struct Hash<uint32_t> : public phmap_unary_function<uint32_t, size_t>
|
||||
{
|
||||
inline size_t operator()(uint32_t val) const noexcept
|
||||
{ return static_cast<size_t>(val); }
|
||||
};
|
||||
|
||||
template <>
|
||||
struct Hash<int64_t> : public phmap_unary_function<int64_t, size_t>
|
||||
{
|
||||
inline size_t operator()(int64_t val) const noexcept
|
||||
{ return fold_if_needed<sizeof(size_t)>()(static_cast<uint64_t>(val)); }
|
||||
};
|
||||
|
||||
template <>
|
||||
struct Hash<uint64_t> : public phmap_unary_function<uint64_t, size_t>
|
||||
{
|
||||
inline size_t operator()(uint64_t val) const noexcept
|
||||
{ return fold_if_needed<sizeof(size_t)>()(val); }
|
||||
};
|
||||
|
||||
template <>
|
||||
struct Hash<float> : public phmap_unary_function<float, size_t>
|
||||
{
|
||||
inline size_t operator()(float val) const noexcept
|
||||
{
|
||||
// -0.0 and 0.0 should return same hash
|
||||
uint32_t *as_int = reinterpret_cast<uint32_t *>(&val);
|
||||
return (val == 0) ? static_cast<size_t>(0) :
|
||||
static_cast<size_t>(*as_int);
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct Hash<double> : public phmap_unary_function<double, size_t>
|
||||
{
|
||||
inline size_t operator()(double val) const noexcept
|
||||
{
|
||||
// -0.0 and 0.0 should return same hash
|
||||
uint64_t *as_int = reinterpret_cast<uint64_t *>(&val);
|
||||
return (val == 0) ? static_cast<size_t>(0) :
|
||||
fold_if_needed<sizeof(size_t)>()(*as_int);
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
template <class H, int sz> struct Combiner
|
||||
{
|
||||
H operator()(H seed, size_t value);
|
||||
};
|
||||
|
||||
template <class H> struct Combiner<H, 4>
|
||||
{
|
||||
H operator()(H seed, size_t value)
|
||||
{
|
||||
return seed ^ (value + 0x9e3779b9 + (seed << 6) + (seed >> 2));
|
||||
}
|
||||
};
|
||||
|
||||
template <class H> struct Combiner<H, 8>
|
||||
{
|
||||
H operator()(H seed, size_t value)
|
||||
{
|
||||
return seed ^ (value + size_t(0xc6a4a7935bd1e995) + (seed << 6) + (seed >> 2));
|
||||
}
|
||||
};
|
||||
|
||||
// define HashState to combine member hashes... see example below
|
||||
// -----------------------------------------------------------------------------
|
||||
template <typename H>
|
||||
class HashStateBase {
|
||||
public:
|
||||
template <typename T, typename... Ts>
|
||||
static H combine(H state, const T& value, const Ts&... values);
|
||||
|
||||
static H combine(H state) { return state; }
|
||||
};
|
||||
|
||||
template <typename H>
|
||||
template <typename T, typename... Ts>
|
||||
H HashStateBase<H>::combine(H seed, const T& v, const Ts&... vs)
|
||||
{
|
||||
return HashStateBase<H>::combine(Combiner<H, sizeof(H)>()(
|
||||
seed, phmap::Hash<T>()(v)),
|
||||
vs...);
|
||||
}
|
||||
|
||||
using HashState = HashStateBase<size_t>;
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
#if !defined(PHMAP_USE_ABSL_HASH)
|
||||
|
||||
// define Hash for std::pair
|
||||
// -------------------------
|
||||
template<class T1, class T2>
|
||||
struct Hash<std::pair<T1, T2>> {
|
||||
size_t operator()(std::pair<T1, T2> const& p) const noexcept {
|
||||
return phmap::HashState().combine(phmap::Hash<T1>()(p.first), p.second);
|
||||
}
|
||||
};
|
||||
|
||||
// define Hash for std::tuple
|
||||
// --------------------------
|
||||
template<class... T>
|
||||
struct Hash<std::tuple<T...>> {
|
||||
size_t operator()(std::tuple<T...> const& t) const noexcept {
|
||||
return _hash_helper(t);
|
||||
}
|
||||
|
||||
private:
|
||||
template<size_t I = 0, class ...P>
|
||||
typename std::enable_if<I == sizeof...(P), size_t>::type
|
||||
_hash_helper(const std::tuple<P...> &) const noexcept { return 0; }
|
||||
|
||||
template<size_t I = 0, class ...P>
|
||||
typename std::enable_if<I < sizeof...(P), size_t>::type
|
||||
_hash_helper(const std::tuple<P...> &t) const noexcept {
|
||||
const auto &el = std::get<I>(t);
|
||||
using el_type = typename std::remove_cv<typename std::remove_reference<decltype(el)>::type>::type;
|
||||
return Combiner<size_t, sizeof(size_t)>()(
|
||||
phmap::Hash<el_type>()(el), _hash_helper<I + 1>(t));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
} // namespace phmap
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(pop)
|
||||
#endif
|
||||
|
||||
#endif // phmap_utils_h_guard_
|
2184
src/includes/3thparty/robinhood/robin_hood.h
Normal file
2184
src/includes/3thparty/robinhood/robin_hood.h
Normal file
File diff suppressed because it is too large
Load Diff
1
src/includes/3thparty/skarupke
Submodule
1
src/includes/3thparty/skarupke
Submodule
@ -0,0 +1 @@
|
||||
Subproject commit 2c4687431f978f02a3780e24b8b701d22aa32d9c
|
295
src/includes/3thparty/tsl/array_growth_policy.h
Normal file
295
src/includes/3thparty/tsl/array_growth_policy.h
Normal file
@ -0,0 +1,295 @@
|
||||
/**
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2017 Tessil
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
#ifndef TSL_ARRAY_GROWTH_POLICY_H
|
||||
#define TSL_ARRAY_GROWTH_POLICY_H
|
||||
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <climits>
|
||||
#include <cmath>
|
||||
#include <cstddef>
|
||||
#include <iterator>
|
||||
#include <limits>
|
||||
#include <ratio>
|
||||
#include <stdexcept>
|
||||
|
||||
|
||||
namespace tsl {
|
||||
namespace ah {
|
||||
|
||||
/**
|
||||
* Grow the hash table by a factor of GrowthFactor keeping the bucket count to a power of two. It allows
|
||||
* the table to use a mask operation instead of a modulo operation to map a hash to a bucket.
|
||||
*
|
||||
* GrowthFactor must be a power of two >= 2.
|
||||
*/
|
||||
template<std::size_t GrowthFactor>
|
||||
class power_of_two_growth_policy {
|
||||
public:
|
||||
/**
|
||||
* Called on the hash table creation and on rehash. The number of buckets for the table is passed in parameter.
|
||||
* This number is a minimum, the policy may update this value with a higher value if needed (but not lower).
|
||||
*
|
||||
* If 0 is given, min_bucket_count_in_out must still be 0 after the policy creation and
|
||||
* bucket_for_hash must always return 0 in this case.
|
||||
*/
|
||||
explicit power_of_two_growth_policy(std::size_t& min_bucket_count_in_out) {
|
||||
if(min_bucket_count_in_out > max_bucket_count()) {
|
||||
throw std::length_error("The hash table exceeds its maxmimum size.");
|
||||
}
|
||||
|
||||
if(min_bucket_count_in_out > 0) {
|
||||
min_bucket_count_in_out = round_up_to_power_of_two(min_bucket_count_in_out);
|
||||
m_mask = min_bucket_count_in_out - 1;
|
||||
}
|
||||
else {
|
||||
m_mask = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the bucket [0, bucket_count()) to which the hash belongs.
|
||||
* If bucket_count() is 0, it must always return 0.
|
||||
*/
|
||||
std::size_t bucket_for_hash(std::size_t hash) const noexcept {
|
||||
return hash & m_mask;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the number of buckets that should be used on next growth.
|
||||
*/
|
||||
std::size_t next_bucket_count() const {
|
||||
if((m_mask + 1) > max_bucket_count() / GrowthFactor) {
|
||||
throw std::length_error("The hash table exceeds its maxmimum size.");
|
||||
}
|
||||
|
||||
return (m_mask + 1) * GrowthFactor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the maximum number of buckets supported by the policy.
|
||||
*/
|
||||
std::size_t max_bucket_count() const {
|
||||
// Largest power of two.
|
||||
return (std::numeric_limits<std::size_t>::max() / 2) + 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the growth policy as if it was created with a bucket count of 0.
|
||||
* After a clear, the policy must always return 0 when bucket_for_hash is called.
|
||||
*/
|
||||
void clear() noexcept {
|
||||
m_mask = 0;
|
||||
}
|
||||
|
||||
private:
|
||||
static std::size_t round_up_to_power_of_two(std::size_t value) {
|
||||
if(is_power_of_two(value)) {
|
||||
return value;
|
||||
}
|
||||
|
||||
if(value == 0) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
--value;
|
||||
for(std::size_t i = 1; i < sizeof(std::size_t) * CHAR_BIT; i *= 2) {
|
||||
value |= value >> i;
|
||||
}
|
||||
|
||||
return value + 1;
|
||||
}
|
||||
|
||||
static constexpr bool is_power_of_two(std::size_t value) {
|
||||
return value != 0 && (value & (value - 1)) == 0;
|
||||
}
|
||||
|
||||
protected:
|
||||
static_assert(is_power_of_two(GrowthFactor) && GrowthFactor >= 2, "GrowthFactor must be a power of two >= 2.");
|
||||
|
||||
std::size_t m_mask;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Grow the hash table by GrowthFactor::num / GrowthFactor::den and use a modulo to map a hash
|
||||
* to a bucket. Slower but it can be useful if you want a slower growth.
|
||||
*/
|
||||
template<class GrowthFactor = std::ratio<3, 2>>
|
||||
class mod_growth_policy {
|
||||
public:
|
||||
explicit mod_growth_policy(std::size_t& min_bucket_count_in_out) {
|
||||
if(min_bucket_count_in_out > max_bucket_count()) {
|
||||
throw std::length_error("The hash table exceeds its maxmimum size.");
|
||||
}
|
||||
|
||||
if(min_bucket_count_in_out > 0) {
|
||||
m_mod = min_bucket_count_in_out;
|
||||
}
|
||||
else {
|
||||
m_mod = 1;
|
||||
}
|
||||
}
|
||||
|
||||
std::size_t bucket_for_hash(std::size_t hash) const noexcept {
|
||||
return hash % m_mod;
|
||||
}
|
||||
|
||||
std::size_t next_bucket_count() const {
|
||||
if(m_mod == max_bucket_count()) {
|
||||
throw std::length_error("The hash table exceeds its maxmimum size.");
|
||||
}
|
||||
|
||||
const double next_bucket_count = std::ceil(double(m_mod) * REHASH_SIZE_MULTIPLICATION_FACTOR);
|
||||
if(!std::isnormal(next_bucket_count)) {
|
||||
throw std::length_error("The hash table exceeds its maxmimum size.");
|
||||
}
|
||||
|
||||
if(next_bucket_count > double(max_bucket_count())) {
|
||||
return max_bucket_count();
|
||||
}
|
||||
else {
|
||||
return std::size_t(next_bucket_count);
|
||||
}
|
||||
}
|
||||
|
||||
std::size_t max_bucket_count() const {
|
||||
return MAX_BUCKET_COUNT;
|
||||
}
|
||||
|
||||
void clear() noexcept {
|
||||
m_mod = 1;
|
||||
}
|
||||
|
||||
private:
|
||||
static constexpr double REHASH_SIZE_MULTIPLICATION_FACTOR = 1.0 * GrowthFactor::num / GrowthFactor::den;
|
||||
static const std::size_t MAX_BUCKET_COUNT =
|
||||
std::size_t(double(
|
||||
std::numeric_limits<std::size_t>::max() / REHASH_SIZE_MULTIPLICATION_FACTOR
|
||||
));
|
||||
|
||||
static_assert(REHASH_SIZE_MULTIPLICATION_FACTOR >= 1.1, "Growth factor should be >= 1.1.");
|
||||
|
||||
std::size_t m_mod;
|
||||
};
|
||||
|
||||
|
||||
|
||||
namespace detail {
|
||||
|
||||
static constexpr const std::array<std::size_t, 40> PRIMES = {{
|
||||
1ul, 5ul, 17ul, 29ul, 37ul, 53ul, 67ul, 79ul, 97ul, 131ul, 193ul, 257ul, 389ul, 521ul, 769ul, 1031ul,
|
||||
1543ul, 2053ul, 3079ul, 6151ul, 12289ul, 24593ul, 49157ul, 98317ul, 196613ul, 393241ul, 786433ul,
|
||||
1572869ul, 3145739ul, 6291469ul, 12582917ul, 25165843ul, 50331653ul, 100663319ul, 201326611ul,
|
||||
402653189ul, 805306457ul, 1610612741ul, 3221225473ul, 4294967291ul
|
||||
}};
|
||||
|
||||
template<unsigned int IPrime>
|
||||
static constexpr std::size_t mod(std::size_t hash) { return hash % PRIMES[IPrime]; }
|
||||
|
||||
// MOD_PRIME[iprime](hash) returns hash % PRIMES[iprime]. This table allows for faster modulo as the
|
||||
// compiler can optimize the modulo code better with a constant known at the compilation.
|
||||
static constexpr const std::array<std::size_t(*)(std::size_t), 40> MOD_PRIME = {{
|
||||
&mod<0>, &mod<1>, &mod<2>, &mod<3>, &mod<4>, &mod<5>, &mod<6>, &mod<7>, &mod<8>, &mod<9>, &mod<10>,
|
||||
&mod<11>, &mod<12>, &mod<13>, &mod<14>, &mod<15>, &mod<16>, &mod<17>, &mod<18>, &mod<19>, &mod<20>,
|
||||
&mod<21>, &mod<22>, &mod<23>, &mod<24>, &mod<25>, &mod<26>, &mod<27>, &mod<28>, &mod<29>, &mod<30>,
|
||||
&mod<31>, &mod<32>, &mod<33>, &mod<34>, &mod<35>, &mod<36>, &mod<37> , &mod<38>, &mod<39>
|
||||
}};
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Grow the hash table by using prime numbers as bucket count. Slower than tsl::ah::power_of_two_growth_policy in
|
||||
* general but will probably distribute the values around better in the buckets with a poor hash function.
|
||||
*
|
||||
* To allow the compiler to optimize the modulo operation, a lookup table is used with constant primes numbers.
|
||||
*
|
||||
* With a switch the code would look like:
|
||||
* \code
|
||||
* switch(iprime) { // iprime is the current prime of the hash table
|
||||
* case 0: hash % 5ul;
|
||||
* break;
|
||||
* case 1: hash % 17ul;
|
||||
* break;
|
||||
* case 2: hash % 29ul;
|
||||
* break;
|
||||
* ...
|
||||
* }
|
||||
* \endcode
|
||||
*
|
||||
* Due to the constant variable in the modulo the compiler is able to optimize the operation
|
||||
* by a series of multiplications, substractions and shifts.
|
||||
*
|
||||
* The 'hash % 5' could become something like 'hash - (hash * 0xCCCCCCCD) >> 34) * 5' in a 64 bits environement.
|
||||
*/
|
||||
class prime_growth_policy {
|
||||
public:
|
||||
explicit prime_growth_policy(std::size_t& min_bucket_count_in_out) {
|
||||
auto it_prime = std::lower_bound(detail::PRIMES.begin(),
|
||||
detail::PRIMES.end(), min_bucket_count_in_out);
|
||||
if(it_prime == detail::PRIMES.end()) {
|
||||
throw std::length_error("The hash table exceeds its maxmimum size.");
|
||||
}
|
||||
|
||||
m_iprime = static_cast<unsigned int>(std::distance(detail::PRIMES.begin(), it_prime));
|
||||
if(min_bucket_count_in_out > 0) {
|
||||
min_bucket_count_in_out = *it_prime;
|
||||
}
|
||||
else {
|
||||
min_bucket_count_in_out = 0;
|
||||
}
|
||||
}
|
||||
|
||||
std::size_t bucket_for_hash(std::size_t hash) const noexcept {
|
||||
return detail::MOD_PRIME[m_iprime](hash);
|
||||
}
|
||||
|
||||
std::size_t next_bucket_count() const {
|
||||
if(m_iprime + 1 >= detail::PRIMES.size()) {
|
||||
throw std::length_error("The hash table exceeds its maxmimum size.");
|
||||
}
|
||||
|
||||
return detail::PRIMES[m_iprime + 1];
|
||||
}
|
||||
|
||||
std::size_t max_bucket_count() const {
|
||||
return detail::PRIMES.back();
|
||||
}
|
||||
|
||||
void clear() noexcept {
|
||||
m_iprime = 0;
|
||||
}
|
||||
|
||||
private:
|
||||
unsigned int m_iprime;
|
||||
|
||||
static_assert(std::numeric_limits<decltype(m_iprime)>::max() >= detail::PRIMES.size(),
|
||||
"The type of m_iprime is not big enough.");
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
1772
src/includes/3thparty/tsl/array_hash.h
Normal file
1772
src/includes/3thparty/tsl/array_hash.h
Normal file
File diff suppressed because it is too large
Load Diff
863
src/includes/3thparty/tsl/array_map.h
Normal file
863
src/includes/3thparty/tsl/array_map.h
Normal file
@ -0,0 +1,863 @@
|
||||
/**
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2017 Tessil
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
#ifndef TSL_ARRAY_MAP_H
|
||||
#define TSL_ARRAY_MAP_H
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <initializer_list>
|
||||
#include <iterator>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include "array_hash.h"
|
||||
|
||||
namespace tsl {
|
||||
|
||||
|
||||
/**
|
||||
* Implementation of a cache-conscious string hash map.
|
||||
*
|
||||
* The map stores the strings as `const CharT*`. If `StoreNullTerminator` is true,
|
||||
* the strings are stored with the a null-terminator (the `key()` method of the iterators
|
||||
* will return a pointer to this null-terminated string). Otherwise the null character
|
||||
* is not stored (which allow an economy of 1 byte per string).
|
||||
*
|
||||
* The value `T` must be either nothrow move-constructible, copy-constuctible or both.
|
||||
*
|
||||
* The size of a key string is limited to `std::numeric_limits<KeySizeT>::max() - 1`.
|
||||
* That is 65 535 characters by default, but can be raised with the `KeySizeT` template parameter.
|
||||
* See `max_key_size()` for an easy access to this limit.
|
||||
*
|
||||
* The number of elements in the map is limited to `std::numeric_limits<IndexSizeT>::max()`.
|
||||
* That is 4 294 967 296 elements, but can be raised with the `IndexSizeT` template parameter.
|
||||
* See `max_size()` for an easy access to this limit.
|
||||
*
|
||||
* Iterators invalidation:
|
||||
* - clear, operator=: always invalidate the iterators.
|
||||
* - insert, emplace, operator[]: always invalidate the iterators.
|
||||
* - erase: always invalidate the iterators.
|
||||
* - shrink_to_fit: always invalidate the iterators.
|
||||
*/
|
||||
template<class CharT,
|
||||
class T,
|
||||
class Hash = tsl::ah::str_hash<CharT>,
|
||||
class KeyEqual = tsl::ah::str_equal<CharT>,
|
||||
bool StoreNullTerminator = true,
|
||||
class KeySizeT = std::uint16_t,
|
||||
class IndexSizeT = std::uint32_t,
|
||||
class GrowthPolicy = tsl::ah::power_of_two_growth_policy<2>>
|
||||
class array_map {
|
||||
private:
|
||||
template<typename U>
|
||||
using is_iterator = tsl::detail_array_hash::is_iterator<U>;
|
||||
|
||||
using ht = tsl::detail_array_hash::array_hash<CharT, T, Hash, KeyEqual, StoreNullTerminator,
|
||||
KeySizeT, IndexSizeT, GrowthPolicy>;
|
||||
|
||||
public:
|
||||
using char_type = typename ht::char_type;
|
||||
using mapped_type = T;
|
||||
using key_size_type = typename ht::key_size_type;
|
||||
using index_size_type = typename ht::index_size_type;
|
||||
using size_type = typename ht::size_type;
|
||||
using hasher = typename ht::hasher;
|
||||
using key_equal = typename ht::key_equal;
|
||||
using iterator = typename ht::iterator;
|
||||
using const_iterator = typename ht::const_iterator;
|
||||
|
||||
public:
|
||||
array_map(): array_map(ht::DEFAULT_INIT_BUCKET_COUNT) {
|
||||
}
|
||||
|
||||
explicit array_map(size_type bucket_count,
|
||||
const Hash& hash = Hash()): m_ht(bucket_count, hash, ht::DEFAULT_MAX_LOAD_FACTOR)
|
||||
{
|
||||
}
|
||||
|
||||
template<class InputIt, typename std::enable_if<is_iterator<InputIt>::value>::type* = nullptr>
|
||||
array_map(InputIt first, InputIt last,
|
||||
size_type bucket_count = ht::DEFAULT_INIT_BUCKET_COUNT,
|
||||
const Hash& hash = Hash()): array_map(bucket_count, hash)
|
||||
{
|
||||
insert(first, last);
|
||||
}
|
||||
|
||||
|
||||
|
||||
#ifdef TSL_AH_HAS_STRING_VIEW
|
||||
array_map(std::initializer_list<std::pair<std::basic_string_view<CharT>, T>> init,
|
||||
size_type bucket_count = ht::DEFAULT_INIT_BUCKET_COUNT,
|
||||
const Hash& hash = Hash()): array_map(bucket_count, hash)
|
||||
{
|
||||
insert(init);
|
||||
}
|
||||
#else
|
||||
array_map(std::initializer_list<std::pair<const CharT*, T>> init,
|
||||
size_type bucket_count = ht::DEFAULT_INIT_BUCKET_COUNT,
|
||||
const Hash& hash = Hash()): array_map(bucket_count, hash)
|
||||
{
|
||||
insert(init);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
#ifdef TSL_AH_HAS_STRING_VIEW
|
||||
array_map& operator=(std::initializer_list<std::pair<std::basic_string_view<CharT>, T>> ilist) {
|
||||
clear();
|
||||
|
||||
reserve(ilist.size());
|
||||
insert(ilist);
|
||||
|
||||
return *this;
|
||||
}
|
||||
#else
|
||||
array_map& operator=(std::initializer_list<std::pair<const CharT*, T>> ilist) {
|
||||
clear();
|
||||
|
||||
reserve(ilist.size());
|
||||
insert(ilist);
|
||||
|
||||
return *this;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Iterators
|
||||
*/
|
||||
iterator begin() noexcept { return m_ht.begin(); }
|
||||
const_iterator begin() const noexcept { return m_ht.begin(); }
|
||||
const_iterator cbegin() const noexcept { return m_ht.cbegin(); }
|
||||
|
||||
iterator end() noexcept { return m_ht.end(); }
|
||||
const_iterator end() const noexcept { return m_ht.end(); }
|
||||
const_iterator cend() const noexcept { return m_ht.cend(); }
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Capacity
|
||||
*/
|
||||
bool empty() const noexcept { return m_ht.empty(); }
|
||||
size_type size() const noexcept { return m_ht.size(); }
|
||||
size_type max_size() const noexcept { return m_ht.max_size(); }
|
||||
size_type max_key_size() const noexcept { return m_ht.max_key_size(); }
|
||||
void shrink_to_fit() { m_ht.shrink_to_fit(); }
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Modifiers
|
||||
*/
|
||||
void clear() noexcept { m_ht.clear(); }
|
||||
|
||||
|
||||
|
||||
#ifdef TSL_AH_HAS_STRING_VIEW
|
||||
std::pair<iterator, bool> insert(const std::basic_string_view<CharT>& key, const T& value) {
|
||||
return m_ht.emplace(key.data(), key.size(), value);
|
||||
}
|
||||
#else
|
||||
std::pair<iterator, bool> insert(const CharT* key, const T& value) {
|
||||
return m_ht.emplace(key, std::char_traits<CharT>::length(key), value);
|
||||
}
|
||||
|
||||
std::pair<iterator, bool> insert(const std::basic_string<CharT>& key, const T& value) {
|
||||
return m_ht.emplace(key.data(), key.size(), value);
|
||||
}
|
||||
#endif
|
||||
std::pair<iterator, bool> insert_ks(const CharT* key, size_type key_size, const T& value) {
|
||||
return m_ht.emplace(key, key_size, value);
|
||||
}
|
||||
|
||||
|
||||
|
||||
#ifdef TSL_AH_HAS_STRING_VIEW
|
||||
std::pair<iterator, bool> insert(const std::basic_string_view<CharT>& key, T&& value) {
|
||||
return m_ht.emplace(key.data(), key.size(), std::move(value));
|
||||
}
|
||||
#else
|
||||
std::pair<iterator, bool> insert(const CharT* key, T&& value) {
|
||||
return m_ht.emplace(key, std::char_traits<CharT>::length(key), std::move(value));
|
||||
}
|
||||
|
||||
std::pair<iterator, bool> insert(const std::basic_string<CharT>& key, T&& value) {
|
||||
return m_ht.emplace(key.data(), key.size(), std::move(value));
|
||||
}
|
||||
#endif
|
||||
std::pair<iterator, bool> insert_ks(const CharT* key, size_type key_size, T&& value) {
|
||||
return m_ht.emplace(key, key_size, std::move(value));
|
||||
}
|
||||
|
||||
|
||||
|
||||
template<class InputIt, typename std::enable_if<is_iterator<InputIt>::value>::type* = nullptr>
|
||||
void insert(InputIt first, InputIt last) {
|
||||
if(std::is_base_of<std::forward_iterator_tag,
|
||||
typename std::iterator_traits<InputIt>::iterator_category>::value)
|
||||
{
|
||||
const auto nb_elements_insert = std::distance(first, last);
|
||||
const std::size_t nb_free_buckets = std::size_t(float(bucket_count())*max_load_factor()) - size();
|
||||
|
||||
if(nb_elements_insert > 0 && nb_free_buckets < std::size_t(nb_elements_insert)) {
|
||||
reserve(size() + std::size_t(nb_elements_insert));
|
||||
}
|
||||
}
|
||||
|
||||
for(auto it = first; it != last; ++it) {
|
||||
insert_pair(*it);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
#ifdef TSL_AH_HAS_STRING_VIEW
|
||||
void insert(std::initializer_list<std::pair<std::basic_string_view<CharT>, T>> ilist) {
|
||||
insert(ilist.begin(), ilist.end());
|
||||
}
|
||||
#else
|
||||
void insert(std::initializer_list<std::pair<const CharT*, T>> ilist) {
|
||||
insert(ilist.begin(), ilist.end());
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
#ifdef TSL_AH_HAS_STRING_VIEW
|
||||
template<class M>
|
||||
std::pair<iterator, bool> insert_or_assign(const std::basic_string_view<CharT>& key, M&& obj) {
|
||||
return m_ht.insert_or_assign(key.data(), key.size(), std::forward<M>(obj));
|
||||
}
|
||||
#else
|
||||
template<class M>
|
||||
std::pair<iterator, bool> insert_or_assign(const CharT* key, M&& obj) {
|
||||
return m_ht.insert_or_assign(key, std::char_traits<CharT>::length(key), std::forward<M>(obj));
|
||||
}
|
||||
|
||||
template<class M>
|
||||
std::pair<iterator, bool> insert_or_assign(const std::basic_string<CharT>& key, M&& obj) {
|
||||
return m_ht.insert_or_assign(key.data(), key.size(), std::forward<M>(obj));
|
||||
}
|
||||
#endif
|
||||
template<class M>
|
||||
std::pair<iterator, bool> insert_or_assign_ks(const CharT* key, size_type key_size, M&& obj) {
|
||||
return m_ht.insert_or_assign(key, key_size, std::forward<M>(obj));
|
||||
}
|
||||
|
||||
|
||||
|
||||
#ifdef TSL_AH_HAS_STRING_VIEW
|
||||
template<class... Args>
|
||||
std::pair<iterator, bool> emplace(const std::basic_string_view<CharT>& key, Args&&... args) {
|
||||
return m_ht.emplace(key.data(), key.size(), std::forward<Args>(args)...);
|
||||
}
|
||||
#else
|
||||
template<class... Args>
|
||||
std::pair<iterator, bool> emplace(const CharT* key, Args&&... args) {
|
||||
return m_ht.emplace(key, std::char_traits<CharT>::length(key), std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template<class... Args>
|
||||
std::pair<iterator, bool> emplace(const std::basic_string<CharT>& key, Args&&... args) {
|
||||
return m_ht.emplace(key.data(), key.size(), std::forward<Args>(args)...);
|
||||
}
|
||||
#endif
|
||||
template<class... Args>
|
||||
std::pair<iterator, bool> emplace_ks(const CharT* key, size_type key_size, Args&&... args) {
|
||||
return m_ht.emplace(key, key_size, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Erase has an amortized O(1) runtime complexity, but even if it removes the key immediatly,
|
||||
* it doesn't do the same for the associated value T.
|
||||
*
|
||||
* T will only be removed when the ratio between the size of the map and
|
||||
* the size of the map + the number of deleted values still stored is low enough.
|
||||
*
|
||||
* To force the deletion you can call shrink_to_fit.
|
||||
*/
|
||||
iterator erase(const_iterator pos) { return m_ht.erase(pos); }
|
||||
|
||||
/**
|
||||
* @copydoc erase(const_iterator pos)
|
||||
*/
|
||||
iterator erase(const_iterator first, const_iterator last) { return m_ht.erase(first, last); }
|
||||
|
||||
|
||||
|
||||
#ifdef TSL_AH_HAS_STRING_VIEW
|
||||
/**
|
||||
* @copydoc erase(const_iterator pos)
|
||||
*/
|
||||
size_type erase(const std::basic_string_view<CharT>& key) {
|
||||
return m_ht.erase(key.data(), key.size());
|
||||
}
|
||||
#else
|
||||
/**
|
||||
* @copydoc erase(const_iterator pos)
|
||||
*/
|
||||
size_type erase(const CharT* key) {
|
||||
return m_ht.erase(key, std::char_traits<CharT>::length(key));
|
||||
}
|
||||
|
||||
/**
|
||||
* @copydoc erase(const_iterator pos)
|
||||
*/
|
||||
size_type erase(const std::basic_string<CharT>& key) {
|
||||
return m_ht.erase(key.data(), key.size());
|
||||
}
|
||||
#endif
|
||||
/**
|
||||
* @copydoc erase(const_iterator pos)
|
||||
*/
|
||||
size_type erase_ks(const CharT* key, size_type key_size) {
|
||||
return m_ht.erase(key, key_size);
|
||||
}
|
||||
|
||||
|
||||
|
||||
#ifdef TSL_AH_HAS_STRING_VIEW
|
||||
/**
|
||||
* @copydoc erase_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash)
|
||||
*/
|
||||
size_type erase(const std::basic_string_view<CharT>& key, std::size_t precalculated_hash) {
|
||||
return m_ht.erase(key.data(), key.size(), precalculated_hash);
|
||||
}
|
||||
#else
|
||||
/**
|
||||
* @copydoc erase_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash)
|
||||
*/
|
||||
size_type erase(const CharT* key, std::size_t precalculated_hash) {
|
||||
return m_ht.erase(key, std::char_traits<CharT>::length(key), precalculated_hash);
|
||||
}
|
||||
|
||||
/**
|
||||
* @copydoc erase_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash)
|
||||
*/
|
||||
size_type erase(const std::basic_string<CharT>& key, std::size_t precalculated_hash) {
|
||||
return m_ht.erase(key.data(), key.size(), precalculated_hash);
|
||||
}
|
||||
#endif
|
||||
/**
|
||||
* @copydoc erase(const_iterator pos)
|
||||
*
|
||||
* Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same
|
||||
* as hash_function()(key). Usefull to speed-up the lookup to the value if you already have the hash.
|
||||
*/
|
||||
size_type erase_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash) {
|
||||
return m_ht.erase(key, key_size, precalculated_hash);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void swap(array_map& other) { other.m_ht.swap(m_ht); }
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Lookup
|
||||
*/
|
||||
#ifdef TSL_AH_HAS_STRING_VIEW
|
||||
T& at(const std::basic_string_view<CharT>& key) {
|
||||
return m_ht.at(key.data(), key.size());
|
||||
}
|
||||
|
||||
const T& at(const std::basic_string_view<CharT>& key) const {
|
||||
return m_ht.at(key.data(), key.size());
|
||||
}
|
||||
#else
|
||||
T& at(const CharT* key) {
|
||||
return m_ht.at(key, std::char_traits<CharT>::length(key));
|
||||
}
|
||||
|
||||
const T& at(const CharT* key) const {
|
||||
return m_ht.at(key, std::char_traits<CharT>::length(key));
|
||||
}
|
||||
|
||||
T& at(const std::basic_string<CharT>& key) {
|
||||
return m_ht.at(key.data(), key.size());
|
||||
}
|
||||
|
||||
const T& at(const std::basic_string<CharT>& key) const {
|
||||
return m_ht.at(key.data(), key.size());
|
||||
}
|
||||
#endif
|
||||
T& at_ks(const CharT* key, size_type key_size) {
|
||||
return m_ht.at(key, key_size);
|
||||
}
|
||||
|
||||
const T& at_ks(const CharT* key, size_type key_size) const {
|
||||
return m_ht.at(key, key_size);
|
||||
}
|
||||
|
||||
|
||||
|
||||
#ifdef TSL_AH_HAS_STRING_VIEW
|
||||
/**
|
||||
* @copydoc at_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash)
|
||||
*/
|
||||
T& at(const std::basic_string_view<CharT>& key, std::size_t precalculated_hash) {
|
||||
return m_ht.at(key.data(), key.size(), precalculated_hash);
|
||||
}
|
||||
|
||||
/**
|
||||
* @copydoc at_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash)
|
||||
*/
|
||||
const T& at(const std::basic_string_view<CharT>& key, std::size_t precalculated_hash) const {
|
||||
return m_ht.at(key.data(), key.size(), precalculated_hash);
|
||||
}
|
||||
#else
|
||||
/**
|
||||
* @copydoc at_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash)
|
||||
*/
|
||||
T& at(const CharT* key, std::size_t precalculated_hash) {
|
||||
return m_ht.at(key, std::char_traits<CharT>::length(key), precalculated_hash);
|
||||
}
|
||||
|
||||
/**
|
||||
* @copydoc at_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash)
|
||||
*/
|
||||
const T& at(const CharT* key, std::size_t precalculated_hash) const {
|
||||
return m_ht.at(key, std::char_traits<CharT>::length(key), precalculated_hash);
|
||||
}
|
||||
|
||||
/**
|
||||
* @copydoc at_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash)
|
||||
*/
|
||||
T& at(const std::basic_string<CharT>& key, std::size_t precalculated_hash) {
|
||||
return m_ht.at(key.data(), key.size(), precalculated_hash);
|
||||
}
|
||||
|
||||
/**
|
||||
* @copydoc at_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash)
|
||||
*/
|
||||
const T& at(const std::basic_string<CharT>& key, std::size_t precalculated_hash) const {
|
||||
return m_ht.at(key.data(), key.size(), precalculated_hash);
|
||||
}
|
||||
#endif
|
||||
/**
|
||||
* Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same
|
||||
* as hash_function()(key). Usefull to speed-up the lookup to the value if you already have the hash.
|
||||
*/
|
||||
T& at_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash) {
|
||||
return m_ht.at(key, key_size, precalculated_hash);
|
||||
}
|
||||
|
||||
/**
|
||||
* @copydoc at_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash)
|
||||
*/
|
||||
const T& at_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash) const {
|
||||
return m_ht.at(key, key_size, precalculated_hash);
|
||||
}
|
||||
|
||||
|
||||
|
||||
#ifdef TSL_AH_HAS_STRING_VIEW
|
||||
T& operator[](const std::basic_string_view<CharT>& key) { return m_ht.access_operator(key.data(), key.size()); }
|
||||
#else
|
||||
T& operator[](const CharT* key) { return m_ht.access_operator(key, std::char_traits<CharT>::length(key)); }
|
||||
T& operator[](const std::basic_string<CharT>& key) { return m_ht.access_operator(key.data(), key.size()); }
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
#ifdef TSL_AH_HAS_STRING_VIEW
|
||||
size_type count(const std::basic_string_view<CharT>& key) const {
|
||||
return m_ht.count(key.data(), key.size());
|
||||
}
|
||||
#else
|
||||
size_type count(const CharT* key) const {
|
||||
return m_ht.count(key, std::char_traits<CharT>::length(key));
|
||||
}
|
||||
|
||||
size_type count(const std::basic_string<CharT>& key) const {
|
||||
return m_ht.count(key.data(), key.size());
|
||||
}
|
||||
#endif
|
||||
size_type count_ks(const CharT* key, size_type key_size) const {
|
||||
return m_ht.count(key, key_size);
|
||||
}
|
||||
|
||||
|
||||
|
||||
#ifdef TSL_AH_HAS_STRING_VIEW
|
||||
/**
|
||||
* @copydoc count_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash) const
|
||||
*/
|
||||
size_type count(const std::basic_string_view<CharT>& key, std::size_t precalculated_hash) const {
|
||||
return m_ht.count(key.data(), key.size(), precalculated_hash);
|
||||
}
|
||||
#else
|
||||
/**
|
||||
* @copydoc count_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash) const
|
||||
*/
|
||||
size_type count(const CharT* key, std::size_t precalculated_hash) const {
|
||||
return m_ht.count(key, std::char_traits<CharT>::length(key), precalculated_hash);
|
||||
}
|
||||
|
||||
/**
|
||||
* @copydoc count_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash) const
|
||||
*/
|
||||
size_type count(const std::basic_string<CharT>& key, std::size_t precalculated_hash) const {
|
||||
return m_ht.count(key.data(), key.size(), precalculated_hash);
|
||||
}
|
||||
#endif
|
||||
/**
|
||||
* Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same
|
||||
* as hash_function()(key). Usefull to speed-up the lookup to the value if you already have the hash.
|
||||
*/
|
||||
size_type count_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash) const {
|
||||
return m_ht.count(key, key_size, precalculated_hash);
|
||||
}
|
||||
|
||||
|
||||
|
||||
#ifdef TSL_AH_HAS_STRING_VIEW
|
||||
iterator find(const std::basic_string_view<CharT>& key) {
|
||||
return m_ht.find(key.data(), key.size());
|
||||
}
|
||||
|
||||
const_iterator find(const std::basic_string_view<CharT>& key) const {
|
||||
return m_ht.find(key.data(), key.size());
|
||||
}
|
||||
#else
|
||||
iterator find(const CharT* key) {
|
||||
return m_ht.find(key, std::char_traits<CharT>::length(key));
|
||||
}
|
||||
|
||||
const_iterator find(const CharT* key) const {
|
||||
return m_ht.find(key, std::char_traits<CharT>::length(key));
|
||||
}
|
||||
|
||||
iterator find(const std::basic_string<CharT>& key) {
|
||||
return m_ht.find(key.data(), key.size());
|
||||
}
|
||||
|
||||
const_iterator find(const std::basic_string<CharT>& key) const {
|
||||
return m_ht.find(key.data(), key.size());
|
||||
}
|
||||
#endif
|
||||
iterator find_ks(const CharT* key, size_type key_size) {
|
||||
return m_ht.find(key, key_size);
|
||||
}
|
||||
|
||||
const_iterator find_ks(const CharT* key, size_type key_size) const {
|
||||
return m_ht.find(key, key_size);
|
||||
}
|
||||
|
||||
|
||||
|
||||
#ifdef TSL_AH_HAS_STRING_VIEW
|
||||
/**
|
||||
* @copydoc find_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash)
|
||||
*/
|
||||
iterator find(const std::basic_string_view<CharT>& key, std::size_t precalculated_hash) {
|
||||
return m_ht.find(key.data(), key.size(), precalculated_hash);
|
||||
}
|
||||
|
||||
/**
|
||||
* @copydoc find_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash)
|
||||
*/
|
||||
const_iterator find(const std::basic_string_view<CharT>& key, std::size_t precalculated_hash) const {
|
||||
return m_ht.find(key.data(), key.size(), precalculated_hash);
|
||||
}
|
||||
#else
|
||||
/**
|
||||
* @copydoc find_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash)
|
||||
*/
|
||||
iterator find(const CharT* key, std::size_t precalculated_hash) {
|
||||
return m_ht.find(key, std::char_traits<CharT>::length(key), precalculated_hash);
|
||||
}
|
||||
|
||||
/**
|
||||
* @copydoc find_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash)
|
||||
*/
|
||||
const_iterator find(const CharT* key, std::size_t precalculated_hash) const {
|
||||
return m_ht.find(key, std::char_traits<CharT>::length(key), precalculated_hash);
|
||||
}
|
||||
|
||||
/**
|
||||
* @copydoc find_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash)
|
||||
*/
|
||||
iterator find(const std::basic_string<CharT>& key, std::size_t precalculated_hash) {
|
||||
return m_ht.find(key.data(), key.size(), precalculated_hash);
|
||||
}
|
||||
|
||||
/**
|
||||
* @copydoc find_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash)
|
||||
*/
|
||||
const_iterator find(const std::basic_string<CharT>& key, std::size_t precalculated_hash) const {
|
||||
return m_ht.find(key.data(), key.size(), precalculated_hash);
|
||||
}
|
||||
#endif
|
||||
/**
|
||||
* Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same
|
||||
* as hash_function()(key). Usefull to speed-up the lookup to the value if you already have the hash.
|
||||
*/
|
||||
iterator find_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash) {
|
||||
return m_ht.find(key, key_size, precalculated_hash);
|
||||
}
|
||||
|
||||
/**
|
||||
* @copydoc find_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash)
|
||||
*/
|
||||
const_iterator find_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash) const {
|
||||
return m_ht.find(key, key_size, precalculated_hash);
|
||||
}
|
||||
|
||||
|
||||
|
||||
#ifdef TSL_AH_HAS_STRING_VIEW
|
||||
std::pair<iterator, iterator> equal_range(const std::basic_string_view<CharT>& key) {
|
||||
return m_ht.equal_range(key.data(), key.size());
|
||||
}
|
||||
|
||||
std::pair<const_iterator, const_iterator> equal_range(const std::basic_string_view<CharT>& key) const {
|
||||
return m_ht.equal_range(key.data(), key.size());
|
||||
}
|
||||
#else
|
||||
std::pair<iterator, iterator> equal_range(const CharT* key) {
|
||||
return m_ht.equal_range(key, std::char_traits<CharT>::length(key));
|
||||
}
|
||||
|
||||
std::pair<const_iterator, const_iterator> equal_range(const CharT* key) const {
|
||||
return m_ht.equal_range(key, std::char_traits<CharT>::length(key));
|
||||
}
|
||||
|
||||
std::pair<iterator, iterator> equal_range(const std::basic_string<CharT>& key) {
|
||||
return m_ht.equal_range(key.data(), key.size());
|
||||
}
|
||||
|
||||
std::pair<const_iterator, const_iterator> equal_range(const std::basic_string<CharT>& key) const {
|
||||
return m_ht.equal_range(key.data(), key.size());
|
||||
}
|
||||
#endif
|
||||
std::pair<iterator, iterator> equal_range_ks(const CharT* key, size_type key_size) {
|
||||
return m_ht.equal_range(key, key_size);
|
||||
}
|
||||
|
||||
std::pair<const_iterator, const_iterator> equal_range_ks(const CharT* key, size_type key_size) const {
|
||||
return m_ht.equal_range(key, key_size);
|
||||
}
|
||||
|
||||
|
||||
|
||||
#ifdef TSL_AH_HAS_STRING_VIEW
|
||||
/**
|
||||
* @copydoc equal_range_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash)
|
||||
*/
|
||||
std::pair<iterator, iterator> equal_range(const std::basic_string_view<CharT>& key, std::size_t precalculated_hash) {
|
||||
return m_ht.equal_range(key.data(), key.size(), precalculated_hash);
|
||||
}
|
||||
|
||||
/**
|
||||
* @copydoc equal_range_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash)
|
||||
*/
|
||||
std::pair<const_iterator, const_iterator> equal_range(const std::basic_string_view<CharT>& key, std::size_t precalculated_hash) const {
|
||||
return m_ht.equal_range(key.data(), key.size(), precalculated_hash);
|
||||
}
|
||||
#else
|
||||
/**
|
||||
* @copydoc equal_range_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash)
|
||||
*/
|
||||
std::pair<iterator, iterator> equal_range(const CharT* key, std::size_t precalculated_hash) {
|
||||
return m_ht.equal_range(key, std::char_traits<CharT>::length(key), precalculated_hash);
|
||||
}
|
||||
|
||||
/**
|
||||
* @copydoc equal_range_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash)
|
||||
*/
|
||||
std::pair<const_iterator, const_iterator> equal_range(const CharT* key, std::size_t precalculated_hash) const {
|
||||
return m_ht.equal_range(key, std::char_traits<CharT>::length(key), precalculated_hash);
|
||||
}
|
||||
|
||||
/**
|
||||
* @copydoc equal_range_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash)
|
||||
*/
|
||||
std::pair<iterator, iterator> equal_range(const std::basic_string<CharT>& key, std::size_t precalculated_hash) {
|
||||
return m_ht.equal_range(key.data(), key.size(), precalculated_hash);
|
||||
}
|
||||
|
||||
/**
|
||||
* @copydoc equal_range_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash)
|
||||
*/
|
||||
std::pair<const_iterator, const_iterator> equal_range(const std::basic_string<CharT>& key, std::size_t precalculated_hash) const {
|
||||
return m_ht.equal_range(key.data(), key.size(), precalculated_hash);
|
||||
}
|
||||
#endif
|
||||
/**
|
||||
* Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same
|
||||
* as hash_function()(key). Usefull to speed-up the lookup to the value if you already have the hash.
|
||||
*/
|
||||
std::pair<iterator, iterator> equal_range_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash) {
|
||||
return m_ht.equal_range(key, key_size, precalculated_hash);
|
||||
}
|
||||
|
||||
/**
|
||||
* @copydoc equal_range_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash)
|
||||
*/
|
||||
std::pair<const_iterator, const_iterator> equal_range_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash) const {
|
||||
return m_ht.equal_range(key, key_size, precalculated_hash);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Bucket interface
|
||||
*/
|
||||
size_type bucket_count() const { return m_ht.bucket_count(); }
|
||||
size_type max_bucket_count() const { return m_ht.max_bucket_count(); }
|
||||
|
||||
|
||||
/*
|
||||
* Hash policy
|
||||
*/
|
||||
float load_factor() const { return m_ht.load_factor(); }
|
||||
float max_load_factor() const { return m_ht.max_load_factor(); }
|
||||
void max_load_factor(float ml) { m_ht.max_load_factor(ml); }
|
||||
|
||||
void rehash(size_type count) { m_ht.rehash(count); }
|
||||
void reserve(size_type count) { m_ht.reserve(count); }
|
||||
|
||||
|
||||
/*
|
||||
* Observers
|
||||
*/
|
||||
hasher hash_function() const { return m_ht.hash_function(); }
|
||||
key_equal key_eq() const { return m_ht.key_eq(); }
|
||||
|
||||
|
||||
/*
|
||||
* Other
|
||||
*/
|
||||
/**
|
||||
* Return the `const_iterator it` as an `iterator`.
|
||||
*/
|
||||
iterator mutable_iterator(const_iterator it) noexcept { return m_ht.mutable_iterator(it); }
|
||||
|
||||
/**
|
||||
* Serialize the map through the `serializer` parameter.
|
||||
*
|
||||
* The `serializer` parameter must be a function object that supports the following calls:
|
||||
* - `template<typename U> void operator()(const U& value);` where the types `std::uint64_t`, `float` and `T` must be supported for U.
|
||||
* - `void operator()(const CharT* value, std::size_t value_size);`
|
||||
*
|
||||
* The implementation leaves binary compatibilty (endianness, IEEE 754 for floats, ...) of the types it serializes
|
||||
* in the hands of the `Serializer` function object if compatibilty is required.
|
||||
*/
|
||||
template<class Serializer>
|
||||
void serialize(Serializer& serializer) const {
|
||||
m_ht.serialize(serializer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deserialize a previouly serialized map through the `deserializer` parameter.
|
||||
*
|
||||
* The `deserializer` parameter must be a function object that supports the following calls:
|
||||
* - `template<typename U> U operator()();` where the types `std::uint64_t`, `float` and `T` must be supported for U.
|
||||
* - `void operator()(CharT* value_out, std::size_t value_size);`
|
||||
*
|
||||
* If the deserialized hash map type is hash compatible with the serialized map, the deserialization process can be
|
||||
* sped up by setting `hash_compatible` to true. To be hash compatible, the Hash (take care of the 32-bits vs 64 bits),
|
||||
* KeyEqual, GrowthPolicy, StoreNullTerminator, KeySizeT and IndexSizeT must behave the same than the ones used on the
|
||||
* serialized map. Otherwise the behaviour is undefined with `hash_compatible` sets to true.
|
||||
*
|
||||
* The behaviour is undefined if the type `CharT` and `T` of the `array_map` are not the same as the
|
||||
* types used during serialization.
|
||||
*
|
||||
* The implementation leaves binary compatibilty (endianness, IEEE 754 for floats, size of int, ...) of the types it
|
||||
* deserializes in the hands of the `Deserializer` function object if compatibilty is required.
|
||||
*/
|
||||
template<class Deserializer>
|
||||
static array_map deserialize(Deserializer& deserializer, bool hash_compatible = false) {
|
||||
array_map map(0);
|
||||
map.m_ht.deserialize(deserializer, hash_compatible);
|
||||
|
||||
return map;
|
||||
}
|
||||
|
||||
friend bool operator==(const array_map& lhs, const array_map& rhs) {
|
||||
if(lhs.size() != rhs.size()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for(auto it = lhs.cbegin(); it != lhs.cend(); ++it) {
|
||||
const auto it_element_rhs = rhs.find_ks(it.key(), it.key_size());
|
||||
if(it_element_rhs == rhs.cend() || it.value() != it_element_rhs.value()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
friend bool operator!=(const array_map& lhs, const array_map& rhs) {
|
||||
return !operator==(lhs, rhs);
|
||||
}
|
||||
|
||||
friend void swap(array_map& lhs, array_map& rhs) {
|
||||
lhs.swap(rhs);
|
||||
}
|
||||
|
||||
private:
|
||||
template<class U, class V>
|
||||
void insert_pair(const std::pair<U, V>& value) {
|
||||
insert(value.first, value.second);
|
||||
}
|
||||
|
||||
template<class U, class V>
|
||||
void insert_pair(std::pair<U, V>&& value) {
|
||||
insert(value.first, std::move(value.second));
|
||||
}
|
||||
|
||||
public:
|
||||
static const size_type MAX_KEY_SIZE = ht::MAX_KEY_SIZE;
|
||||
|
||||
private:
|
||||
ht m_ht;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Same as
|
||||
* `tsl::array_map<CharT, T, Hash, KeyEqual, StoreNullTerminator, KeySizeT, IndexSizeT, tsl::ah::prime_growth_policy>`.
|
||||
*/
|
||||
template<class CharT,
|
||||
class T,
|
||||
class Hash = tsl::ah::str_hash<CharT>,
|
||||
class KeyEqual = tsl::ah::str_equal<CharT>,
|
||||
bool StoreNullTerminator = true,
|
||||
class KeySizeT = std::uint16_t,
|
||||
class IndexSizeT = std::uint32_t>
|
||||
using array_pg_map = array_map<CharT, T, Hash, KeyEqual, StoreNullTerminator,
|
||||
KeySizeT, IndexSizeT, tsl::ah::prime_growth_policy>;
|
||||
|
||||
} //end namespace tsl
|
||||
|
||||
#endif
|
664
src/includes/3thparty/tsl/array_set.h
Normal file
664
src/includes/3thparty/tsl/array_set.h
Normal file
@ -0,0 +1,664 @@
|
||||
/**
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2017 Tessil
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
#ifndef TSL_ARRAY_SET_H
|
||||
#define TSL_ARRAY_SET_H
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <initializer_list>
|
||||
#include <iterator>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include "array_hash.h"
|
||||
|
||||
namespace tsl {
|
||||
|
||||
/**
|
||||
* Implementation of a cache-conscious string hash set.
|
||||
*
|
||||
* The set stores the strings as `const CharT*`. If `StoreNullTerminator` is true,
|
||||
* the strings are stored with the a null-terminator (the `key()` method of the iterators
|
||||
* will return a pointer to this null-terminated string). Otherwise the null character
|
||||
* is not stored (which allow an economy of 1 byte per string).
|
||||
*
|
||||
* The size of a key string is limited to `std::numeric_limits<KeySizeT>::max() - 1`.
|
||||
* That is 65 535 characters by default, but can be raised with the `KeySizeT` template parameter.
|
||||
* See `max_key_size()` for an easy access to this limit.
|
||||
*
|
||||
* The number of elements in the set is limited to `std::numeric_limits<IndexSizeT>::max()`.
|
||||
* That is 4 294 967 296 elements, but can be raised with the `IndexSizeT` template parameter.
|
||||
* See `max_size()` for an easy access to this limit.
|
||||
*
|
||||
* Iterators invalidation:
|
||||
* - clear, operator=: always invalidate the iterators.
|
||||
* - insert, emplace, operator[]: always invalidate the iterators.
|
||||
* - erase: always invalidate the iterators.
|
||||
* - shrink_to_fit: always invalidate the iterators.
|
||||
*/
|
||||
template<class CharT,
|
||||
class Hash = tsl::ah::str_hash<CharT>,
|
||||
class KeyEqual = tsl::ah::str_equal<CharT>,
|
||||
bool StoreNullTerminator = true,
|
||||
class KeySizeT = std::uint16_t,
|
||||
class IndexSizeT = std::uint32_t,
|
||||
class GrowthPolicy = tsl::ah::power_of_two_growth_policy<2>>
|
||||
class array_set {
|
||||
private:
|
||||
template<typename U>
|
||||
using is_iterator = tsl::detail_array_hash::is_iterator<U>;
|
||||
|
||||
using ht = tsl::detail_array_hash::array_hash<CharT, void, Hash, KeyEqual, StoreNullTerminator,
|
||||
KeySizeT, IndexSizeT, GrowthPolicy>;
|
||||
|
||||
public:
|
||||
using char_type = typename ht::char_type;
|
||||
using key_size_type = typename ht::key_size_type;
|
||||
using index_size_type = typename ht::index_size_type;
|
||||
using size_type = typename ht::size_type;
|
||||
using hasher = typename ht::hasher;
|
||||
using key_equal = typename ht::key_equal;
|
||||
using iterator = typename ht::iterator;
|
||||
using const_iterator = typename ht::const_iterator;
|
||||
|
||||
array_set(): array_set(ht::DEFAULT_INIT_BUCKET_COUNT) {
|
||||
}
|
||||
|
||||
explicit array_set(size_type bucket_count,
|
||||
const Hash& hash = Hash()): m_ht(bucket_count, hash, ht::DEFAULT_MAX_LOAD_FACTOR)
|
||||
{
|
||||
}
|
||||
|
||||
template<class InputIt, typename std::enable_if<is_iterator<InputIt>::value>::type* = nullptr>
|
||||
array_set(InputIt first, InputIt last,
|
||||
size_type bucket_count = ht::DEFAULT_INIT_BUCKET_COUNT,
|
||||
const Hash& hash = Hash()): array_set(bucket_count, hash)
|
||||
{
|
||||
insert(first, last);
|
||||
}
|
||||
|
||||
|
||||
#ifdef TSL_AH_HAS_STRING_VIEW
|
||||
array_set(std::initializer_list<std::basic_string_view<CharT>> init,
|
||||
size_type bucket_count = ht::DEFAULT_INIT_BUCKET_COUNT,
|
||||
const Hash& hash = Hash()): array_set(bucket_count, hash)
|
||||
{
|
||||
insert(init);
|
||||
}
|
||||
#else
|
||||
array_set(std::initializer_list<const CharT*> init,
|
||||
size_type bucket_count = ht::DEFAULT_INIT_BUCKET_COUNT,
|
||||
const Hash& hash = Hash()): array_set(bucket_count, hash)
|
||||
{
|
||||
insert(init);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
#ifdef TSL_AH_HAS_STRING_VIEW
|
||||
array_set& operator=(std::initializer_list<std::basic_string_view<CharT>> ilist) {
|
||||
clear();
|
||||
|
||||
reserve(ilist.size());
|
||||
insert(ilist);
|
||||
|
||||
return *this;
|
||||
}
|
||||
#else
|
||||
array_set& operator=(std::initializer_list<const CharT*> ilist) {
|
||||
clear();
|
||||
|
||||
reserve(ilist.size());
|
||||
insert(ilist);
|
||||
|
||||
return *this;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Iterators
|
||||
*/
|
||||
iterator begin() noexcept { return m_ht.begin(); }
|
||||
const_iterator begin() const noexcept { return m_ht.begin(); }
|
||||
const_iterator cbegin() const noexcept { return m_ht.cbegin(); }
|
||||
|
||||
iterator end() noexcept { return m_ht.end(); }
|
||||
const_iterator end() const noexcept { return m_ht.end(); }
|
||||
const_iterator cend() const noexcept { return m_ht.cend(); }
|
||||
|
||||
|
||||
/*
|
||||
* Capacity
|
||||
*/
|
||||
bool empty() const noexcept { return m_ht.empty(); }
|
||||
size_type size() const noexcept { return m_ht.size(); }
|
||||
size_type max_size() const noexcept { return m_ht.max_size(); }
|
||||
size_type max_key_size() const noexcept { return m_ht.max_key_size(); }
|
||||
void shrink_to_fit() { m_ht.shrink_to_fit(); }
|
||||
|
||||
|
||||
/*
|
||||
* Modifiers
|
||||
*/
|
||||
void clear() noexcept { m_ht.clear(); }
|
||||
|
||||
|
||||
|
||||
#ifdef TSL_AH_HAS_STRING_VIEW
|
||||
std::pair<iterator, bool> insert(const std::basic_string_view<CharT>& key) {
|
||||
return m_ht.emplace(key.data(), key.size());
|
||||
}
|
||||
#else
|
||||
std::pair<iterator, bool> insert(const CharT* key) {
|
||||
return m_ht.emplace(key, std::char_traits<CharT>::length(key));
|
||||
}
|
||||
|
||||
std::pair<iterator, bool> insert(const std::basic_string<CharT>& key) {
|
||||
return m_ht.emplace(key.data(), key.size());
|
||||
}
|
||||
#endif
|
||||
std::pair<iterator, bool> insert_ks(const CharT* key, size_type key_size) {
|
||||
return m_ht.emplace(key, key_size);
|
||||
}
|
||||
|
||||
|
||||
|
||||
template<class InputIt, typename std::enable_if<is_iterator<InputIt>::value>::type* = nullptr>
|
||||
void insert(InputIt first, InputIt last) {
|
||||
if(std::is_base_of<std::forward_iterator_tag,
|
||||
typename std::iterator_traits<InputIt>::iterator_category>::value)
|
||||
{
|
||||
const auto nb_elements_insert = std::distance(first, last);
|
||||
const std::size_t nb_free_buckets = std::size_t(float(bucket_count())*max_load_factor()) - size();
|
||||
|
||||
if(nb_elements_insert > 0 && nb_free_buckets < std::size_t(nb_elements_insert)) {
|
||||
reserve(size() + std::size_t(nb_elements_insert));
|
||||
}
|
||||
}
|
||||
|
||||
for(auto it = first; it != last; ++it) {
|
||||
insert(*it);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
#ifdef TSL_AH_HAS_STRING_VIEW
|
||||
void insert(std::initializer_list<std::basic_string_view<CharT>> ilist) {
|
||||
insert(ilist.begin(), ilist.end());
|
||||
}
|
||||
#else
|
||||
void insert(std::initializer_list<const CharT*> ilist) {
|
||||
insert(ilist.begin(), ilist.end());
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
#ifdef TSL_AH_HAS_STRING_VIEW
|
||||
/**
|
||||
* @copydoc emplace_ks(const CharT* key, size_type key_size)
|
||||
*/
|
||||
std::pair<iterator, bool> emplace(const std::basic_string_view<CharT>& key) {
|
||||
return m_ht.emplace(key.data(), key.size());
|
||||
}
|
||||
#else
|
||||
/**
|
||||
* @copydoc emplace_ks(const CharT* key, size_type key_size)
|
||||
*/
|
||||
std::pair<iterator, bool> emplace(const CharT* key) {
|
||||
return m_ht.emplace(key, std::char_traits<CharT>::length(key));
|
||||
}
|
||||
|
||||
/**
|
||||
* @copydoc emplace_ks(const CharT* key, size_type key_size)
|
||||
*/
|
||||
std::pair<iterator, bool> emplace(const std::basic_string<CharT>& key) {
|
||||
return m_ht.emplace(key.data(), key.size());
|
||||
}
|
||||
#endif
|
||||
/**
|
||||
* No difference compared to the insert method. Mainly here for coherence with array_map.
|
||||
*/
|
||||
std::pair<iterator, bool> emplace_ks(const CharT* key, size_type key_size) {
|
||||
return m_ht.emplace(key, key_size);
|
||||
}
|
||||
|
||||
|
||||
|
||||
iterator erase(const_iterator pos) { return m_ht.erase(pos); }
|
||||
iterator erase(const_iterator first, const_iterator last) { return m_ht.erase(first, last); }
|
||||
|
||||
#ifdef TSL_AH_HAS_STRING_VIEW
|
||||
size_type erase(const std::basic_string_view<CharT>& key) {
|
||||
return m_ht.erase(key.data(), key.size());
|
||||
}
|
||||
#else
|
||||
size_type erase(const CharT* key) {
|
||||
return m_ht.erase(key, std::char_traits<CharT>::length(key));
|
||||
}
|
||||
|
||||
size_type erase(const std::basic_string<CharT>& key) {
|
||||
return m_ht.erase(key.data(), key.size());
|
||||
}
|
||||
#endif
|
||||
size_type erase_ks(const CharT* key, size_type key_size) {
|
||||
return m_ht.erase(key, key_size);
|
||||
}
|
||||
|
||||
|
||||
|
||||
#ifdef TSL_AH_HAS_STRING_VIEW
|
||||
/**
|
||||
* @copydoc erase_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash)
|
||||
*/
|
||||
size_type erase(const std::basic_string_view<CharT>& key, std::size_t precalculated_hash) {
|
||||
return m_ht.erase(key.data(), key.size(), precalculated_hash);
|
||||
}
|
||||
#else
|
||||
/**
|
||||
* @copydoc erase_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash)
|
||||
*/
|
||||
size_type erase(const CharT* key, std::size_t precalculated_hash) {
|
||||
return m_ht.erase(key, std::char_traits<CharT>::length(key), precalculated_hash);
|
||||
}
|
||||
|
||||
/**
|
||||
* @copydoc erase_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash)
|
||||
*/
|
||||
size_type erase(const std::basic_string<CharT>& key, std::size_t precalculated_hash) {
|
||||
return m_ht.erase(key.data(), key.size(), precalculated_hash);
|
||||
}
|
||||
#endif
|
||||
/**
|
||||
* Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same
|
||||
* as hash_function()(key). Usefull to speed-up the lookup to the value if you already have the hash.
|
||||
*/
|
||||
size_type erase_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash) {
|
||||
return m_ht.erase(key, key_size, precalculated_hash);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void swap(array_set& other) { other.m_ht.swap(m_ht); }
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Lookup
|
||||
*/
|
||||
#ifdef TSL_AH_HAS_STRING_VIEW
|
||||
size_type count(const std::basic_string_view<CharT>& key) const { return m_ht.count(key.data(), key.size()); }
|
||||
#else
|
||||
size_type count(const CharT* key) const { return m_ht.count(key, std::char_traits<CharT>::length(key)); }
|
||||
size_type count(const std::basic_string<CharT>& key) const { return m_ht.count(key.data(), key.size()); }
|
||||
#endif
|
||||
size_type count_ks(const CharT* key, size_type key_size) const { return m_ht.count(key, key_size); }
|
||||
|
||||
|
||||
|
||||
#ifdef TSL_AH_HAS_STRING_VIEW
|
||||
/**
|
||||
* @copydoc count_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash) const
|
||||
*/
|
||||
size_type count(const std::basic_string_view<CharT>& key, std::size_t precalculated_hash) const {
|
||||
return m_ht.count(key.data(), key.size(), precalculated_hash);
|
||||
}
|
||||
#else
|
||||
/**
|
||||
* @copydoc count_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash) const
|
||||
*/
|
||||
size_type count(const CharT* key, std::size_t precalculated_hash) const {
|
||||
return m_ht.count(key, std::char_traits<CharT>::length(key), precalculated_hash);
|
||||
}
|
||||
|
||||
/**
|
||||
* @copydoc count_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash) const
|
||||
*/
|
||||
size_type count(const std::basic_string<CharT>& key, std::size_t precalculated_hash) const {
|
||||
return m_ht.count(key.data(), key.size(), precalculated_hash);
|
||||
}
|
||||
#endif
|
||||
/**
|
||||
* Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same
|
||||
* as hash_function()(key). Usefull to speed-up the lookup to the value if you already have the hash.
|
||||
*/
|
||||
size_type count_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash) const {
|
||||
return m_ht.count(key, key_size, precalculated_hash);
|
||||
}
|
||||
|
||||
|
||||
|
||||
#ifdef TSL_AH_HAS_STRING_VIEW
|
||||
iterator find(const std::basic_string_view<CharT>& key) {
|
||||
return m_ht.find(key.data(), key.size());
|
||||
}
|
||||
|
||||
const_iterator find(const std::basic_string_view<CharT>& key) const {
|
||||
return m_ht.find(key.data(), key.size());
|
||||
}
|
||||
#else
|
||||
iterator find(const CharT* key) {
|
||||
return m_ht.find(key, std::char_traits<CharT>::length(key));
|
||||
}
|
||||
|
||||
const_iterator find(const CharT* key) const {
|
||||
return m_ht.find(key, std::char_traits<CharT>::length(key));
|
||||
}
|
||||
|
||||
iterator find(const std::basic_string<CharT>& key) {
|
||||
return m_ht.find(key.data(), key.size());
|
||||
}
|
||||
|
||||
const_iterator find(const std::basic_string<CharT>& key) const {
|
||||
return m_ht.find(key.data(), key.size());
|
||||
}
|
||||
#endif
|
||||
iterator find_ks(const CharT* key, size_type key_size) {
|
||||
return m_ht.find(key, key_size);
|
||||
}
|
||||
|
||||
const_iterator find_ks(const CharT* key, size_type key_size) const {
|
||||
return m_ht.find(key, key_size);
|
||||
}
|
||||
|
||||
|
||||
|
||||
#ifdef TSL_AH_HAS_STRING_VIEW
|
||||
/**
|
||||
* @copydoc find_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash)
|
||||
*/
|
||||
iterator find(const std::basic_string_view<CharT>& key, std::size_t precalculated_hash) {
|
||||
return m_ht.find(key.data(), key.size(), precalculated_hash);
|
||||
}
|
||||
|
||||
/**
|
||||
* @copydoc find_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash)
|
||||
*/
|
||||
const_iterator find(const std::basic_string_view<CharT>& key, std::size_t precalculated_hash) const {
|
||||
return m_ht.find(key.data(), key.size(), precalculated_hash);
|
||||
}
|
||||
#else
|
||||
/**
|
||||
* @copydoc find_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash)
|
||||
*/
|
||||
iterator find(const CharT* key, std::size_t precalculated_hash) {
|
||||
return m_ht.find(key, std::char_traits<CharT>::length(key), precalculated_hash);
|
||||
}
|
||||
|
||||
/**
|
||||
* @copydoc find_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash)
|
||||
*/
|
||||
const_iterator find(const CharT* key, std::size_t precalculated_hash) const {
|
||||
return m_ht.find(key, std::char_traits<CharT>::length(key), precalculated_hash);
|
||||
}
|
||||
|
||||
/**
|
||||
* @copydoc find_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash)
|
||||
*/
|
||||
iterator find(const std::basic_string<CharT>& key, std::size_t precalculated_hash) {
|
||||
return m_ht.find(key.data(), key.size(), precalculated_hash);
|
||||
}
|
||||
|
||||
/**
|
||||
* @copydoc find_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash)
|
||||
*/
|
||||
const_iterator find(const std::basic_string<CharT>& key, std::size_t precalculated_hash) const {
|
||||
return m_ht.find(key.data(), key.size(), precalculated_hash);
|
||||
}
|
||||
#endif
|
||||
/**
|
||||
* Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same
|
||||
* as hash_function()(key). Usefull to speed-up the lookup to the value if you already have the hash.
|
||||
*/
|
||||
iterator find_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash) {
|
||||
return m_ht.find(key, key_size, precalculated_hash);
|
||||
}
|
||||
|
||||
/**
|
||||
* @copydoc find_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash)
|
||||
*/
|
||||
const_iterator find_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash) const {
|
||||
return m_ht.find(key, key_size, precalculated_hash);
|
||||
}
|
||||
|
||||
|
||||
|
||||
#ifdef TSL_AH_HAS_STRING_VIEW
|
||||
std::pair<iterator, iterator> equal_range(const std::basic_string_view<CharT>& key) {
|
||||
return m_ht.equal_range(key.data(), key.size());
|
||||
}
|
||||
|
||||
std::pair<const_iterator, const_iterator> equal_range(const std::basic_string_view<CharT>& key) const {
|
||||
return m_ht.equal_range(key.data(), key.size());
|
||||
}
|
||||
#else
|
||||
std::pair<iterator, iterator> equal_range(const CharT* key) {
|
||||
return m_ht.equal_range(key, std::char_traits<CharT>::length(key));
|
||||
}
|
||||
|
||||
std::pair<const_iterator, const_iterator> equal_range(const CharT* key) const {
|
||||
return m_ht.equal_range(key, std::char_traits<CharT>::length(key));
|
||||
}
|
||||
|
||||
std::pair<iterator, iterator> equal_range(const std::basic_string<CharT>& key) {
|
||||
return m_ht.equal_range(key.data(), key.size());
|
||||
}
|
||||
|
||||
std::pair<const_iterator, const_iterator> equal_range(const std::basic_string<CharT>& key) const {
|
||||
return m_ht.equal_range(key.data(), key.size());
|
||||
}
|
||||
#endif
|
||||
std::pair<iterator, iterator> equal_range_ks(const CharT* key, size_type key_size) {
|
||||
return m_ht.equal_range(key, key_size);
|
||||
}
|
||||
|
||||
std::pair<const_iterator, const_iterator> equal_range_ks(const CharT* key, size_type key_size) const {
|
||||
return m_ht.equal_range(key, key_size);
|
||||
}
|
||||
|
||||
|
||||
|
||||
#ifdef TSL_AH_HAS_STRING_VIEW
|
||||
/**
|
||||
* @copydoc equal_range_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash)
|
||||
*/
|
||||
std::pair<iterator, iterator> equal_range(const std::basic_string_view<CharT>& key, std::size_t precalculated_hash) {
|
||||
return m_ht.equal_range(key.data(), key.size(), precalculated_hash);
|
||||
}
|
||||
|
||||
/**
|
||||
* @copydoc equal_range_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash)
|
||||
*/
|
||||
std::pair<const_iterator, const_iterator> equal_range(const std::basic_string_view<CharT>& key, std::size_t precalculated_hash) const {
|
||||
return m_ht.equal_range(key.data(), key.size(), precalculated_hash);
|
||||
}
|
||||
#else
|
||||
/**
|
||||
* @copydoc equal_range_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash)
|
||||
*/
|
||||
std::pair<iterator, iterator> equal_range(const CharT* key, std::size_t precalculated_hash) {
|
||||
return m_ht.equal_range(key, std::char_traits<CharT>::length(key), precalculated_hash);
|
||||
}
|
||||
|
||||
/**
|
||||
* @copydoc equal_range_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash)
|
||||
*/
|
||||
std::pair<const_iterator, const_iterator> equal_range(const CharT* key, std::size_t precalculated_hash) const {
|
||||
return m_ht.equal_range(key, std::char_traits<CharT>::length(key), precalculated_hash);
|
||||
}
|
||||
|
||||
/**
|
||||
* @copydoc equal_range_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash)
|
||||
*/
|
||||
std::pair<iterator, iterator> equal_range(const std::basic_string<CharT>& key, std::size_t precalculated_hash) {
|
||||
return m_ht.equal_range(key.data(), key.size(), precalculated_hash);
|
||||
}
|
||||
|
||||
/**
|
||||
* @copydoc equal_range_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash)
|
||||
*/
|
||||
std::pair<const_iterator, const_iterator> equal_range(const std::basic_string<CharT>& key, std::size_t precalculated_hash) const {
|
||||
return m_ht.equal_range(key.data(), key.size(), precalculated_hash);
|
||||
}
|
||||
#endif
|
||||
/**
|
||||
* Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same
|
||||
* as hash_function()(key). Usefull to speed-up the lookup to the value if you already have the hash.
|
||||
*/
|
||||
std::pair<iterator, iterator> equal_range_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash) {
|
||||
return m_ht.equal_range(key, key_size, precalculated_hash);
|
||||
}
|
||||
|
||||
/**
|
||||
* @copydoc equal_range_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash)
|
||||
*/
|
||||
std::pair<const_iterator, const_iterator> equal_range_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash) const {
|
||||
return m_ht.equal_range(key, key_size, precalculated_hash);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Bucket interface
|
||||
*/
|
||||
size_type bucket_count() const { return m_ht.bucket_count(); }
|
||||
size_type max_bucket_count() const { return m_ht.max_bucket_count(); }
|
||||
|
||||
|
||||
/*
|
||||
* Hash policy
|
||||
*/
|
||||
float load_factor() const { return m_ht.load_factor(); }
|
||||
float max_load_factor() const { return m_ht.max_load_factor(); }
|
||||
void max_load_factor(float ml) { m_ht.max_load_factor(ml); }
|
||||
|
||||
void rehash(size_type count) { m_ht.rehash(count); }
|
||||
void reserve(size_type count) { m_ht.reserve(count); }
|
||||
|
||||
|
||||
/*
|
||||
* Observers
|
||||
*/
|
||||
hasher hash_function() const { return m_ht.hash_function(); }
|
||||
key_equal key_eq() const { return m_ht.key_eq(); }
|
||||
|
||||
|
||||
/*
|
||||
* Other
|
||||
*/
|
||||
/**
|
||||
* Return the `const_iterator it` as an `iterator`.
|
||||
*/
|
||||
iterator mutable_iterator(const_iterator it) noexcept { return m_ht.mutable_iterator(it); }
|
||||
|
||||
/**
|
||||
* Serialize the set through the `serializer` parameter.
|
||||
*
|
||||
* The `serializer` parameter must be a function object that supports the following calls:
|
||||
* - `template<typename U> void operator()(const U& value);` where the types `std::uint64_t` and `float` must be supported for U.
|
||||
* - `void operator()(const CharT* value, std::size_t value_size);`
|
||||
*
|
||||
* The implementation leaves binary compatibilty (endianness, IEEE 754 for floats, ...) of the types it serializes
|
||||
* in the hands of the `Serializer` function object if compatibilty is required.
|
||||
*/
|
||||
template<class Serializer>
|
||||
void serialize(Serializer& serializer) const {
|
||||
m_ht.serialize(serializer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deserialize a previouly serialized set through the `deserializer` parameter.
|
||||
*
|
||||
* The `deserializer` parameter must be a function object that supports the following calls:
|
||||
* - `template<typename U> U operator()();` where the types `std::uint64_t` and `float` must be supported for U.
|
||||
* - `void operator()(CharT* value_out, std::size_t value_size);`
|
||||
*
|
||||
* If the deserialized hash set type is hash compatible with the serialized set, the deserialization process can be
|
||||
* sped up by setting `hash_compatible` to true. To be hash compatible, the Hash (take care of the 32-bits vs 64 bits),
|
||||
* KeyEqual, GrowthPolicy, StoreNullTerminator, KeySizeT and IndexSizeT must behave the same than the ones used on the
|
||||
* serialized set. Otherwise the behaviour is undefined with `hash_compatible` sets to true.
|
||||
*
|
||||
* The behaviour is undefined if the type `CharT` of the `array_set` is not the same as the
|
||||
* type used during serialization.
|
||||
*
|
||||
* The implementation leaves binary compatibilty (endianness, IEEE 754 for floats, size of int, ...) of the types it
|
||||
* deserializes in the hands of the `Deserializer` function object if compatibilty is required.
|
||||
*/
|
||||
template<class Deserializer>
|
||||
static array_set deserialize(Deserializer& deserializer, bool hash_compatible = false) {
|
||||
array_set set(0);
|
||||
set.m_ht.deserialize(deserializer, hash_compatible);
|
||||
|
||||
return set;
|
||||
}
|
||||
|
||||
friend bool operator==(const array_set& lhs, const array_set& rhs) {
|
||||
if(lhs.size() != rhs.size()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for(auto it = lhs.cbegin(); it != lhs.cend(); ++it) {
|
||||
const auto it_element_rhs = rhs.find_ks(it.key(), it.key_size());
|
||||
if(it_element_rhs == rhs.cend()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
friend bool operator!=(const array_set& lhs, const array_set& rhs) {
|
||||
return !operator==(lhs, rhs);
|
||||
}
|
||||
|
||||
friend void swap(array_set& lhs, array_set& rhs) {
|
||||
lhs.swap(rhs);
|
||||
}
|
||||
|
||||
public:
|
||||
static const size_type MAX_KEY_SIZE = ht::MAX_KEY_SIZE;
|
||||
|
||||
private:
|
||||
ht m_ht;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Same as
|
||||
* `tsl::array_set<CharT, Hash, KeyEqual, StoreNullTerminator, KeySizeT, IndexSizeT, tsl::ah::prime_growth_policy>`.
|
||||
*/
|
||||
template<class CharT,
|
||||
class Hash = tsl::ah::str_hash<CharT>,
|
||||
class KeyEqual = tsl::ah::str_equal<CharT>,
|
||||
bool StoreNullTerminator = true,
|
||||
class KeySizeT = std::uint16_t,
|
||||
class IndexSizeT = std::uint32_t>
|
||||
using array_pg_set = array_set<CharT, Hash, KeyEqual, StoreNullTerminator,
|
||||
KeySizeT, IndexSizeT, tsl::ah::prime_growth_policy>;
|
||||
|
||||
} //end namespace tsl
|
||||
|
||||
#endif
|
706
src/includes/3thparty/tsl/bhopscotch_map.h
Normal file
706
src/includes/3thparty/tsl/bhopscotch_map.h
Normal file
@ -0,0 +1,706 @@
|
||||
/**
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2017 Tessil
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
#ifndef TSL_BHOPSCOTCH_MAP_H
|
||||
#define TSL_BHOPSCOTCH_MAP_H
|
||||
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstddef>
|
||||
#include <functional>
|
||||
#include <initializer_list>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include "hopscotch_hash.h"
|
||||
|
||||
|
||||
namespace tsl {
|
||||
|
||||
|
||||
/**
|
||||
* Similar to tsl::hopscotch_map but instead of using a list for overflowing elements it uses
|
||||
* a binary search tree. It thus needs an additional template parameter Compare. Compare should
|
||||
* be arithmetically coherent with KeyEqual.
|
||||
*
|
||||
* The binary search tree allows the map to have a worst-case scenario of O(log n) for search
|
||||
* and delete, even if the hash function maps all the elements to the same bucket.
|
||||
* For insert, the amortized worst case is O(log n), but the worst case is O(n) in case of rehash.
|
||||
*
|
||||
* This makes the map resistant to DoS attacks (but doesn't preclude you to have a good hash function,
|
||||
* as an element in the bucket array is faster to retrieve than in the tree).
|
||||
*
|
||||
* @copydoc hopscotch_map
|
||||
*/
|
||||
template<class Key,
|
||||
class T,
|
||||
class Hash = std::hash<Key>,
|
||||
class KeyEqual = std::equal_to<Key>,
|
||||
class Compare = std::less<Key>,
|
||||
class Allocator = std::allocator<std::pair<const Key, T>>,
|
||||
unsigned int NeighborhoodSize = 62,
|
||||
bool StoreHash = false,
|
||||
class GrowthPolicy = tsl::hh::power_of_two_growth_policy<2>>
|
||||
class bhopscotch_map {
|
||||
private:
|
||||
template<typename U>
|
||||
using has_is_transparent = tsl::detail_hopscotch_hash::has_is_transparent<U>;
|
||||
|
||||
class KeySelect {
|
||||
public:
|
||||
using key_type = Key;
|
||||
|
||||
const key_type& operator()(const std::pair<const Key, T>& key_value) const {
|
||||
return key_value.first;
|
||||
}
|
||||
|
||||
const key_type& operator()(std::pair<const Key, T>& key_value) {
|
||||
return key_value.first;
|
||||
}
|
||||
};
|
||||
|
||||
class ValueSelect {
|
||||
public:
|
||||
using value_type = T;
|
||||
|
||||
const value_type& operator()(const std::pair<const Key, T>& key_value) const {
|
||||
return key_value.second;
|
||||
}
|
||||
|
||||
value_type& operator()(std::pair<const Key, T>& key_value) {
|
||||
return key_value.second;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// TODO Not optimal as we have to use std::pair<const Key, T> as ValueType which forbid
|
||||
// us to move the key in the bucket array, we have to use copy. Optimize.
|
||||
using overflow_container_type = std::map<Key, T, Compare, Allocator>;
|
||||
using ht = detail_hopscotch_hash::hopscotch_hash<std::pair<const Key, T>, KeySelect, ValueSelect,
|
||||
Hash, KeyEqual,
|
||||
Allocator, NeighborhoodSize,
|
||||
StoreHash, GrowthPolicy,
|
||||
overflow_container_type>;
|
||||
|
||||
public:
|
||||
using key_type = typename ht::key_type;
|
||||
using mapped_type = T;
|
||||
using value_type = typename ht::value_type;
|
||||
using size_type = typename ht::size_type;
|
||||
using difference_type = typename ht::difference_type;
|
||||
using hasher = typename ht::hasher;
|
||||
using key_equal = typename ht::key_equal;
|
||||
using key_compare = Compare;
|
||||
using allocator_type = typename ht::allocator_type;
|
||||
using reference = typename ht::reference;
|
||||
using const_reference = typename ht::const_reference;
|
||||
using pointer = typename ht::pointer;
|
||||
using const_pointer = typename ht::const_pointer;
|
||||
using iterator = typename ht::iterator;
|
||||
using const_iterator = typename ht::const_iterator;
|
||||
|
||||
|
||||
/*
|
||||
* Constructors
|
||||
*/
|
||||
bhopscotch_map() : bhopscotch_map(ht::DEFAULT_INIT_BUCKETS_SIZE) {
|
||||
}
|
||||
|
||||
explicit bhopscotch_map(size_type bucket_count,
|
||||
const Hash& hash = Hash(),
|
||||
const KeyEqual& equal = KeyEqual(),
|
||||
const Allocator& alloc = Allocator(),
|
||||
const Compare& comp = Compare()) :
|
||||
m_ht(bucket_count, hash, equal, alloc, ht::DEFAULT_MAX_LOAD_FACTOR, comp)
|
||||
{
|
||||
}
|
||||
|
||||
bhopscotch_map(size_type bucket_count,
|
||||
const Allocator& alloc) : bhopscotch_map(bucket_count, Hash(), KeyEqual(), alloc)
|
||||
{
|
||||
}
|
||||
|
||||
bhopscotch_map(size_type bucket_count,
|
||||
const Hash& hash,
|
||||
const Allocator& alloc) : bhopscotch_map(bucket_count, hash, KeyEqual(), alloc)
|
||||
{
|
||||
}
|
||||
|
||||
explicit bhopscotch_map(const Allocator& alloc) : bhopscotch_map(ht::DEFAULT_INIT_BUCKETS_SIZE, alloc) {
|
||||
}
|
||||
|
||||
template<class InputIt>
|
||||
bhopscotch_map(InputIt first, InputIt last,
|
||||
size_type bucket_count = ht::DEFAULT_INIT_BUCKETS_SIZE,
|
||||
const Hash& hash = Hash(),
|
||||
const KeyEqual& equal = KeyEqual(),
|
||||
const Allocator& alloc = Allocator()) : bhopscotch_map(bucket_count, hash, equal, alloc)
|
||||
{
|
||||
insert(first, last);
|
||||
}
|
||||
|
||||
template<class InputIt>
|
||||
bhopscotch_map(InputIt first, InputIt last,
|
||||
size_type bucket_count,
|
||||
const Allocator& alloc) : bhopscotch_map(first, last, bucket_count, Hash(), KeyEqual(), alloc)
|
||||
{
|
||||
}
|
||||
|
||||
template<class InputIt>
|
||||
bhopscotch_map(InputIt first, InputIt last,
|
||||
size_type bucket_count,
|
||||
const Hash& hash,
|
||||
const Allocator& alloc) : bhopscotch_map(first, last, bucket_count, hash, KeyEqual(), alloc)
|
||||
{
|
||||
}
|
||||
|
||||
bhopscotch_map(std::initializer_list<value_type> init,
|
||||
size_type bucket_count = ht::DEFAULT_INIT_BUCKETS_SIZE,
|
||||
const Hash& hash = Hash(),
|
||||
const KeyEqual& equal = KeyEqual(),
|
||||
const Allocator& alloc = Allocator()) :
|
||||
bhopscotch_map(init.begin(), init.end(), bucket_count, hash, equal, alloc)
|
||||
{
|
||||
}
|
||||
|
||||
bhopscotch_map(std::initializer_list<value_type> init,
|
||||
size_type bucket_count,
|
||||
const Allocator& alloc) :
|
||||
bhopscotch_map(init.begin(), init.end(), bucket_count, Hash(), KeyEqual(), alloc)
|
||||
{
|
||||
}
|
||||
|
||||
bhopscotch_map(std::initializer_list<value_type> init,
|
||||
size_type bucket_count,
|
||||
const Hash& hash,
|
||||
const Allocator& alloc) :
|
||||
bhopscotch_map(init.begin(), init.end(), bucket_count, hash, KeyEqual(), alloc)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
bhopscotch_map& operator=(std::initializer_list<value_type> ilist) {
|
||||
m_ht.clear();
|
||||
|
||||
m_ht.reserve(ilist.size());
|
||||
m_ht.insert(ilist.begin(), ilist.end());
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
allocator_type get_allocator() const { return m_ht.get_allocator(); }
|
||||
|
||||
|
||||
/*
|
||||
* Iterators
|
||||
*/
|
||||
iterator begin() noexcept { return m_ht.begin(); }
|
||||
const_iterator begin() const noexcept { return m_ht.begin(); }
|
||||
const_iterator cbegin() const noexcept { return m_ht.cbegin(); }
|
||||
|
||||
iterator end() noexcept { return m_ht.end(); }
|
||||
const_iterator end() const noexcept { return m_ht.end(); }
|
||||
const_iterator cend() const noexcept { return m_ht.cend(); }
|
||||
|
||||
|
||||
/*
|
||||
* Capacity
|
||||
*/
|
||||
bool empty() const noexcept { return m_ht.empty(); }
|
||||
size_type size() const noexcept { return m_ht.size(); }
|
||||
size_type max_size() const noexcept { return m_ht.max_size(); }
|
||||
|
||||
/*
|
||||
* Modifiers
|
||||
*/
|
||||
void clear() noexcept { m_ht.clear(); }
|
||||
|
||||
|
||||
|
||||
|
||||
std::pair<iterator, bool> insert(const value_type& value) {
|
||||
return m_ht.insert(value);
|
||||
}
|
||||
|
||||
template<class P, typename std::enable_if<std::is_constructible<value_type, P&&>::value>::type* = nullptr>
|
||||
std::pair<iterator, bool> insert(P&& value) {
|
||||
return m_ht.insert(std::forward<P>(value));
|
||||
}
|
||||
|
||||
std::pair<iterator, bool> insert(value_type&& value) {
|
||||
return m_ht.insert(std::move(value));
|
||||
}
|
||||
|
||||
|
||||
iterator insert(const_iterator hint, const value_type& value) {
|
||||
return m_ht.insert(hint, value);
|
||||
}
|
||||
|
||||
template<class P, typename std::enable_if<std::is_constructible<value_type, P&&>::value>::type* = nullptr>
|
||||
iterator insert(const_iterator hint, P&& value) {
|
||||
return m_ht.insert(hint, std::forward<P>(value));
|
||||
}
|
||||
|
||||
iterator insert(const_iterator hint, value_type&& value) {
|
||||
return m_ht.insert(hint, std::move(value));
|
||||
}
|
||||
|
||||
|
||||
template<class InputIt>
|
||||
void insert(InputIt first, InputIt last) {
|
||||
m_ht.insert(first, last);
|
||||
}
|
||||
|
||||
void insert(std::initializer_list<value_type> ilist) {
|
||||
m_ht.insert(ilist.begin(), ilist.end());
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
template<class M>
|
||||
std::pair<iterator, bool> insert_or_assign(const key_type& k, M&& obj) {
|
||||
return m_ht.insert_or_assign(k, std::forward<M>(obj));
|
||||
}
|
||||
|
||||
template<class M>
|
||||
std::pair<iterator, bool> insert_or_assign(key_type&& k, M&& obj) {
|
||||
return m_ht.insert_or_assign(std::move(k), std::forward<M>(obj));
|
||||
}
|
||||
|
||||
template<class M>
|
||||
iterator insert_or_assign(const_iterator hint, const key_type& k, M&& obj) {
|
||||
return m_ht.insert_or_assign(hint, k, std::forward<M>(obj));
|
||||
}
|
||||
|
||||
template<class M>
|
||||
iterator insert_or_assign(const_iterator hint, key_type&& k, M&& obj) {
|
||||
return m_ht.insert_or_assign(hint, std::move(k), std::forward<M>(obj));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Due to the way elements are stored, emplace will need to move or copy the key-value once.
|
||||
* The method is equivalent to insert(value_type(std::forward<Args>(args)...));
|
||||
*
|
||||
* Mainly here for compatibility with the std::unordered_map interface.
|
||||
*/
|
||||
template<class... Args>
|
||||
std::pair<iterator, bool> emplace(Args&&... args) {
|
||||
return m_ht.emplace(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Due to the way elements are stored, emplace_hint will need to move or copy the key-value once.
|
||||
* The method is equivalent to insert(hint, value_type(std::forward<Args>(args)...));
|
||||
*
|
||||
* Mainly here for compatibility with the std::unordered_map interface.
|
||||
*/
|
||||
template<class... Args>
|
||||
iterator emplace_hint(const_iterator hint, Args&&... args) {
|
||||
return m_ht.emplace_hint(hint, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
template<class... Args>
|
||||
std::pair<iterator, bool> try_emplace(const key_type& k, Args&&... args) {
|
||||
return m_ht.try_emplace(k, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template<class... Args>
|
||||
std::pair<iterator, bool> try_emplace(key_type&& k, Args&&... args) {
|
||||
return m_ht.try_emplace(std::move(k), std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template<class... Args>
|
||||
iterator try_emplace(const_iterator hint, const key_type& k, Args&&... args) {
|
||||
return m_ht.try_emplace(hint, k, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template<class... Args>
|
||||
iterator try_emplace(const_iterator hint, key_type&& k, Args&&... args) {
|
||||
return m_ht.try_emplace(hint, std::move(k), std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
iterator erase(iterator pos) { return m_ht.erase(pos); }
|
||||
iterator erase(const_iterator pos) { return m_ht.erase(pos); }
|
||||
iterator erase(const_iterator first, const_iterator last) { return m_ht.erase(first, last); }
|
||||
size_type erase(const key_type& key) { return m_ht.erase(key); }
|
||||
|
||||
/**
|
||||
* Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same
|
||||
* as hash_function()(key). Usefull to speed-up the lookup to the value if you already have the hash.
|
||||
*/
|
||||
size_type erase(const key_type& key, std::size_t precalculated_hash) {
|
||||
return m_ht.erase(key, precalculated_hash);
|
||||
}
|
||||
|
||||
/**
|
||||
* This overload only participates in the overload resolution if the typedef KeyEqual::is_transparent
|
||||
* and Compare::is_transparent exist.
|
||||
* If so, K must be hashable and comparable to Key.
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, class CP = Compare,
|
||||
typename std::enable_if<has_is_transparent<KE>::value && has_is_transparent<CP>::value>::type* = nullptr>
|
||||
size_type erase(const K& key) { return m_ht.erase(key); }
|
||||
|
||||
/**
|
||||
* @copydoc erase(const K& key)
|
||||
*
|
||||
* Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same
|
||||
* as hash_function()(key). Usefull to speed-up the lookup to the value if you already have the hash.
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, class CP = Compare,
|
||||
typename std::enable_if<has_is_transparent<KE>::value && has_is_transparent<CP>::value>::type* = nullptr>
|
||||
size_type erase(const K& key, std::size_t precalculated_hash) { return m_ht.erase(key, precalculated_hash); }
|
||||
|
||||
|
||||
|
||||
|
||||
void swap(bhopscotch_map& other) { other.m_ht.swap(m_ht); }
|
||||
|
||||
/*
|
||||
* Lookup
|
||||
*/
|
||||
T& at(const Key& key) { return m_ht.at(key); }
|
||||
|
||||
/**
|
||||
* Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same
|
||||
* as hash_function()(key). Usefull to speed-up the lookup if you already have the hash.
|
||||
*/
|
||||
T& at(const Key& key, std::size_t precalculated_hash) { return m_ht.at(key, precalculated_hash); }
|
||||
|
||||
const T& at(const Key& key) const { return m_ht.at(key); }
|
||||
|
||||
/**
|
||||
* @copydoc at(const Key& key, std::size_t precalculated_hash)
|
||||
*/
|
||||
const T& at(const Key& key, std::size_t precalculated_hash) const { return m_ht.at(key, precalculated_hash); }
|
||||
|
||||
/**
|
||||
* This overload only participates in the overload resolution if the typedef KeyEqual::is_transparent
|
||||
* and Compare::is_transparent exist.
|
||||
* If so, K must be hashable and comparable to Key.
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, class CP = Compare,
|
||||
typename std::enable_if<has_is_transparent<KE>::value && has_is_transparent<CP>::value>::type* = nullptr>
|
||||
T& at(const K& key) { return m_ht.at(key); }
|
||||
|
||||
/**
|
||||
* @copydoc at(const K& key)
|
||||
*
|
||||
* Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same
|
||||
* as hash_function()(key). Usefull to speed-up the lookup if you already have the hash.
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, class CP = Compare,
|
||||
typename std::enable_if<has_is_transparent<KE>::value && has_is_transparent<CP>::value>::type* = nullptr>
|
||||
T& at(const K& key, std::size_t precalculated_hash) { return m_ht.at(key, precalculated_hash); }
|
||||
|
||||
/**
|
||||
* @copydoc at(const K& key)
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, class CP = Compare,
|
||||
typename std::enable_if<has_is_transparent<KE>::value && has_is_transparent<CP>::value>::type* = nullptr>
|
||||
const T& at(const K& key) const { return m_ht.at(key); }
|
||||
|
||||
/**
|
||||
* @copydoc at(const K& key, std::size_t precalculated_hash)
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, class CP = Compare,
|
||||
typename std::enable_if<has_is_transparent<KE>::value && has_is_transparent<CP>::value>::type* = nullptr>
|
||||
const T& at(const K& key, std::size_t precalculated_hash) const { return m_ht.at(key, precalculated_hash); }
|
||||
|
||||
|
||||
|
||||
|
||||
T& operator[](const Key& key) { return m_ht[key]; }
|
||||
T& operator[](Key&& key) { return m_ht[std::move(key)]; }
|
||||
|
||||
|
||||
|
||||
|
||||
size_type count(const Key& key) const { return m_ht.count(key); }
|
||||
|
||||
/**
|
||||
* Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same
|
||||
* as hash_function()(key). Usefull to speed-up the lookup if you already have the hash.
|
||||
*/
|
||||
size_type count(const Key& key, std::size_t precalculated_hash) const { return m_ht.count(key, precalculated_hash); }
|
||||
|
||||
/**
|
||||
* This overload only participates in the overload resolution if the typedef KeyEqual::is_transparent
|
||||
* and Compare::is_transparent exist.
|
||||
* If so, K must be hashable and comparable to Key.
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, class CP = Compare,
|
||||
typename std::enable_if<has_is_transparent<KE>::value && has_is_transparent<CP>::value>::type* = nullptr>
|
||||
size_type count(const K& key) const { return m_ht.count(key); }
|
||||
|
||||
/**
|
||||
* @copydoc count(const K& key) const
|
||||
*
|
||||
* Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same
|
||||
* as hash_function()(key). Usefull to speed-up the lookup if you already have the hash.
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, class CP = Compare,
|
||||
typename std::enable_if<has_is_transparent<KE>::value && has_is_transparent<CP>::value>::type* = nullptr>
|
||||
size_type count(const K& key, std::size_t precalculated_hash) const { return m_ht.count(key, precalculated_hash); }
|
||||
|
||||
|
||||
|
||||
|
||||
iterator find(const Key& key) { return m_ht.find(key); }
|
||||
|
||||
/**
|
||||
* Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same
|
||||
* as hash_function()(key). Usefull to speed-up the lookup if you already have the hash.
|
||||
*/
|
||||
iterator find(const Key& key, std::size_t precalculated_hash) { return m_ht.find(key, precalculated_hash); }
|
||||
|
||||
const_iterator find(const Key& key) const { return m_ht.find(key); }
|
||||
|
||||
/**
|
||||
* @copydoc find(const Key& key, std::size_t precalculated_hash)
|
||||
*/
|
||||
const_iterator find(const Key& key, std::size_t precalculated_hash) const { return m_ht.find(key, precalculated_hash); }
|
||||
|
||||
/**
|
||||
* This overload only participates in the overload resolution if the typedef KeyEqual::is_transparent
|
||||
* and Compare::is_transparent exist.
|
||||
* If so, K must be hashable and comparable to Key.
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, class CP = Compare,
|
||||
typename std::enable_if<has_is_transparent<KE>::value && has_is_transparent<CP>::value>::type* = nullptr>
|
||||
iterator find(const K& key) { return m_ht.find(key); }
|
||||
|
||||
/**
|
||||
* @copydoc find(const K& key)
|
||||
*
|
||||
* Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same
|
||||
* as hash_function()(key). Usefull to speed-up the lookup if you already have the hash.
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, class CP = Compare,
|
||||
typename std::enable_if<has_is_transparent<KE>::value && has_is_transparent<CP>::value>::type* = nullptr>
|
||||
iterator find(const K& key, std::size_t precalculated_hash) { return m_ht.find(key, precalculated_hash); }
|
||||
|
||||
/**
|
||||
* @copydoc find(const K& key)
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, class CP = Compare,
|
||||
typename std::enable_if<has_is_transparent<KE>::value && has_is_transparent<CP>::value>::type* = nullptr>
|
||||
const_iterator find(const K& key) const { return m_ht.find(key); }
|
||||
|
||||
/**
|
||||
* @copydoc find(const K& key, std::size_t precalculated_hash)
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, class CP = Compare,
|
||||
typename std::enable_if<has_is_transparent<KE>::value && has_is_transparent<CP>::value>::type* = nullptr>
|
||||
const_iterator find(const K& key, std::size_t precalculated_hash) const { return m_ht.find(key, precalculated_hash); }
|
||||
|
||||
|
||||
|
||||
|
||||
bool contains(const Key& key) const { return m_ht.contains(key); }
|
||||
|
||||
/**
|
||||
* Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same
|
||||
* as hash_function()(key). Usefull to speed-up the lookup if you already have the hash.
|
||||
*/
|
||||
bool contains(const Key& key, std::size_t precalculated_hash) const {
|
||||
return m_ht.contains(key, precalculated_hash);
|
||||
}
|
||||
|
||||
/**
|
||||
* This overload only participates in the overload resolution if the typedef KeyEqual::is_transparent exists.
|
||||
* If so, K must be hashable and comparable to Key.
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
|
||||
bool contains(const K& key) const { return m_ht.contains(key); }
|
||||
|
||||
/**
|
||||
* @copydoc contains(const K& key) const
|
||||
*
|
||||
* Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same
|
||||
* as hash_function()(key). Usefull to speed-up the lookup if you already have the hash.
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
|
||||
bool contains(const K& key, std::size_t precalculated_hash) const {
|
||||
return m_ht.contains(key, precalculated_hash);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
std::pair<iterator, iterator> equal_range(const Key& key) { return m_ht.equal_range(key); }
|
||||
|
||||
/**
|
||||
* Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same
|
||||
* as hash_function()(key). Usefull to speed-up the lookup if you already have the hash.
|
||||
*/
|
||||
std::pair<iterator, iterator> equal_range(const Key& key, std::size_t precalculated_hash) {
|
||||
return m_ht.equal_range(key, precalculated_hash);
|
||||
}
|
||||
|
||||
std::pair<const_iterator, const_iterator> equal_range(const Key& key) const { return m_ht.equal_range(key); }
|
||||
|
||||
/**
|
||||
* @copydoc equal_range(const Key& key, std::size_t precalculated_hash)
|
||||
*/
|
||||
std::pair<const_iterator, const_iterator> equal_range(const Key& key, std::size_t precalculated_hash) const {
|
||||
return m_ht.equal_range(key, precalculated_hash);
|
||||
}
|
||||
|
||||
/**
|
||||
* This overload only participates in the overload resolution if the typedef KeyEqual::is_transparent
|
||||
* and Compare::is_transparent exist.
|
||||
* If so, K must be hashable and comparable to Key.
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, class CP = Compare,
|
||||
typename std::enable_if<has_is_transparent<KE>::value && has_is_transparent<CP>::value>::type* = nullptr>
|
||||
std::pair<iterator, iterator> equal_range(const K& key) { return m_ht.equal_range(key); }
|
||||
|
||||
/**
|
||||
* @copydoc equal_range(const K& key)
|
||||
*
|
||||
* Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same
|
||||
* as hash_function()(key). Usefull to speed-up the lookup if you already have the hash.
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, class CP = Compare,
|
||||
typename std::enable_if<has_is_transparent<KE>::value && has_is_transparent<CP>::value>::type* = nullptr>
|
||||
std::pair<iterator, iterator> equal_range(const K& key, std::size_t precalculated_hash) {
|
||||
return m_ht.equal_range(key, precalculated_hash);
|
||||
}
|
||||
|
||||
/**
|
||||
* @copydoc equal_range(const K& key)
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, class CP = Compare,
|
||||
typename std::enable_if<has_is_transparent<KE>::value && has_is_transparent<CP>::value>::type* = nullptr>
|
||||
std::pair<const_iterator, const_iterator> equal_range(const K& key) const { return m_ht.equal_range(key); }
|
||||
|
||||
/**
|
||||
* @copydoc equal_range(const K& key, std::size_t precalculated_hash)
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, class CP = Compare,
|
||||
typename std::enable_if<has_is_transparent<KE>::value && has_is_transparent<CP>::value>::type* = nullptr>
|
||||
std::pair<const_iterator, const_iterator> equal_range(const K& key, std::size_t precalculated_hash) const {
|
||||
return m_ht.equal_range(key, precalculated_hash);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Bucket interface
|
||||
*/
|
||||
size_type bucket_count() const { return m_ht.bucket_count(); }
|
||||
size_type max_bucket_count() const { return m_ht.max_bucket_count(); }
|
||||
|
||||
|
||||
/*
|
||||
* Hash policy
|
||||
*/
|
||||
float load_factor() const { return m_ht.load_factor(); }
|
||||
float max_load_factor() const { return m_ht.max_load_factor(); }
|
||||
void max_load_factor(float ml) { m_ht.max_load_factor(ml); }
|
||||
|
||||
void rehash(size_type count_) { m_ht.rehash(count_); }
|
||||
void reserve(size_type count_) { m_ht.reserve(count_); }
|
||||
|
||||
|
||||
/*
|
||||
* Observers
|
||||
*/
|
||||
hasher hash_function() const { return m_ht.hash_function(); }
|
||||
key_equal key_eq() const { return m_ht.key_eq(); }
|
||||
key_compare key_comp() const { return m_ht.key_comp(); }
|
||||
|
||||
/*
|
||||
* Other
|
||||
*/
|
||||
|
||||
/**
|
||||
* Convert a const_iterator to an iterator.
|
||||
*/
|
||||
iterator mutable_iterator(const_iterator pos) {
|
||||
return m_ht.mutable_iterator(pos);
|
||||
}
|
||||
|
||||
size_type overflow_size() const noexcept { return m_ht.overflow_size(); }
|
||||
|
||||
friend bool operator==(const bhopscotch_map& lhs, const bhopscotch_map& rhs) {
|
||||
if(lhs.size() != rhs.size()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for(const auto& element_lhs : lhs) {
|
||||
const auto it_element_rhs = rhs.find(element_lhs.first);
|
||||
if(it_element_rhs == rhs.cend() || element_lhs.second != it_element_rhs->second) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
friend bool operator!=(const bhopscotch_map& lhs, const bhopscotch_map& rhs) {
|
||||
return !operator==(lhs, rhs);
|
||||
}
|
||||
|
||||
friend void swap(bhopscotch_map& lhs, bhopscotch_map& rhs) {
|
||||
lhs.swap(rhs);
|
||||
}
|
||||
|
||||
|
||||
|
||||
private:
|
||||
ht m_ht;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Same as `tsl::bhopscotch_map<Key, T, Hash, KeyEqual, Compare, Allocator, NeighborhoodSize, StoreHash, tsl::hh::prime_growth_policy>`.
|
||||
*/
|
||||
template<class Key,
|
||||
class T,
|
||||
class Hash = std::hash<Key>,
|
||||
class KeyEqual = std::equal_to<Key>,
|
||||
class Compare = std::less<Key>,
|
||||
class Allocator = std::allocator<std::pair<const Key, T>>,
|
||||
unsigned int NeighborhoodSize = 62,
|
||||
bool StoreHash = false>
|
||||
using bhopscotch_pg_map = bhopscotch_map<Key, T, Hash, KeyEqual, Compare, Allocator, NeighborhoodSize, StoreHash, tsl::hh::prime_growth_policy>;
|
||||
|
||||
} // end namespace tsl
|
||||
|
||||
#endif
|
560
src/includes/3thparty/tsl/bhopscotch_set.h
Normal file
560
src/includes/3thparty/tsl/bhopscotch_set.h
Normal file
@ -0,0 +1,560 @@
|
||||
/**
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2017 Tessil
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
#ifndef TSL_BHOPSCOTCH_SET_H
|
||||
#define TSL_BHOPSCOTCH_SET_H
|
||||
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstddef>
|
||||
#include <functional>
|
||||
#include <initializer_list>
|
||||
#include <memory>
|
||||
#include <set>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include "hopscotch_hash.h"
|
||||
|
||||
|
||||
namespace tsl {
|
||||
|
||||
|
||||
/**
|
||||
* Similar to tsl::hopscotch_set but instead of using a list for overflowing elements it uses
|
||||
* a binary search tree. It thus needs an additional template parameter Compare. Compare should
|
||||
* be arithmetically coherent with KeyEqual.
|
||||
*
|
||||
* The binary search tree allows the set to have a worst-case scenario of O(log n) for search
|
||||
* and delete, even if the hash function maps all the elements to the same bucket.
|
||||
* For insert, the amortized worst case is O(log n), but the worst case is O(n) in case of rehash.
|
||||
*
|
||||
* This makes the set resistant to DoS attacks (but doesn't preclude you to have a good hash function,
|
||||
* as an element in the bucket array is faster to retrieve than in the tree).
|
||||
*
|
||||
* @copydoc hopscotch_set
|
||||
*/
|
||||
template<class Key,
|
||||
class Hash = std::hash<Key>,
|
||||
class KeyEqual = std::equal_to<Key>,
|
||||
class Compare = std::less<Key>,
|
||||
class Allocator = std::allocator<Key>,
|
||||
unsigned int NeighborhoodSize = 62,
|
||||
bool StoreHash = false,
|
||||
class GrowthPolicy = tsl::hh::power_of_two_growth_policy<2>>
|
||||
class bhopscotch_set {
|
||||
private:
|
||||
template<typename U>
|
||||
using has_is_transparent = tsl::detail_hopscotch_hash::has_is_transparent<U>;
|
||||
|
||||
class KeySelect {
|
||||
public:
|
||||
using key_type = Key;
|
||||
|
||||
const key_type& operator()(const Key& key) const {
|
||||
return key;
|
||||
}
|
||||
|
||||
key_type& operator()(Key& key) {
|
||||
return key;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
using overflow_container_type = std::set<Key, Compare, Allocator>;
|
||||
using ht = tsl::detail_hopscotch_hash::hopscotch_hash<Key, KeySelect, void,
|
||||
Hash, KeyEqual,
|
||||
Allocator, NeighborhoodSize,
|
||||
StoreHash, GrowthPolicy,
|
||||
overflow_container_type>;
|
||||
|
||||
public:
|
||||
using key_type = typename ht::key_type;
|
||||
using value_type = typename ht::value_type;
|
||||
using size_type = typename ht::size_type;
|
||||
using difference_type = typename ht::difference_type;
|
||||
using hasher = typename ht::hasher;
|
||||
using key_equal = typename ht::key_equal;
|
||||
using key_compare = Compare;
|
||||
using allocator_type = typename ht::allocator_type;
|
||||
using reference = typename ht::reference;
|
||||
using const_reference = typename ht::const_reference;
|
||||
using pointer = typename ht::pointer;
|
||||
using const_pointer = typename ht::const_pointer;
|
||||
using iterator = typename ht::iterator;
|
||||
using const_iterator = typename ht::const_iterator;
|
||||
|
||||
|
||||
/*
|
||||
* Constructors
|
||||
*/
|
||||
bhopscotch_set() : bhopscotch_set(ht::DEFAULT_INIT_BUCKETS_SIZE) {
|
||||
}
|
||||
|
||||
explicit bhopscotch_set(size_type bucket_count,
|
||||
const Hash& hash = Hash(),
|
||||
const KeyEqual& equal = KeyEqual(),
|
||||
const Allocator& alloc = Allocator(),
|
||||
const Compare& comp = Compare()) :
|
||||
m_ht(bucket_count, hash, equal, alloc, ht::DEFAULT_MAX_LOAD_FACTOR, comp)
|
||||
{
|
||||
}
|
||||
|
||||
bhopscotch_set(size_type bucket_count,
|
||||
const Allocator& alloc) : bhopscotch_set(bucket_count, Hash(), KeyEqual(), alloc)
|
||||
{
|
||||
}
|
||||
|
||||
bhopscotch_set(size_type bucket_count,
|
||||
const Hash& hash,
|
||||
const Allocator& alloc) : bhopscotch_set(bucket_count, hash, KeyEqual(), alloc)
|
||||
{
|
||||
}
|
||||
|
||||
explicit bhopscotch_set(const Allocator& alloc) : bhopscotch_set(ht::DEFAULT_INIT_BUCKETS_SIZE, alloc) {
|
||||
}
|
||||
|
||||
template<class InputIt>
|
||||
bhopscotch_set(InputIt first, InputIt last,
|
||||
size_type bucket_count = ht::DEFAULT_INIT_BUCKETS_SIZE,
|
||||
const Hash& hash = Hash(),
|
||||
const KeyEqual& equal = KeyEqual(),
|
||||
const Allocator& alloc = Allocator()) : bhopscotch_set(bucket_count, hash, equal, alloc)
|
||||
{
|
||||
insert(first, last);
|
||||
}
|
||||
|
||||
template<class InputIt>
|
||||
bhopscotch_set(InputIt first, InputIt last,
|
||||
size_type bucket_count,
|
||||
const Allocator& alloc) : bhopscotch_set(first, last, bucket_count, Hash(), KeyEqual(), alloc)
|
||||
{
|
||||
}
|
||||
|
||||
template<class InputIt>
|
||||
bhopscotch_set(InputIt first, InputIt last,
|
||||
size_type bucket_count,
|
||||
const Hash& hash,
|
||||
const Allocator& alloc) : bhopscotch_set(first, last, bucket_count, hash, KeyEqual(), alloc)
|
||||
{
|
||||
}
|
||||
|
||||
bhopscotch_set(std::initializer_list<value_type> init,
|
||||
size_type bucket_count = ht::DEFAULT_INIT_BUCKETS_SIZE,
|
||||
const Hash& hash = Hash(),
|
||||
const KeyEqual& equal = KeyEqual(),
|
||||
const Allocator& alloc = Allocator()) :
|
||||
bhopscotch_set(init.begin(), init.end(), bucket_count, hash, equal, alloc)
|
||||
{
|
||||
}
|
||||
|
||||
bhopscotch_set(std::initializer_list<value_type> init,
|
||||
size_type bucket_count,
|
||||
const Allocator& alloc) :
|
||||
bhopscotch_set(init.begin(), init.end(), bucket_count, Hash(), KeyEqual(), alloc)
|
||||
{
|
||||
}
|
||||
|
||||
bhopscotch_set(std::initializer_list<value_type> init,
|
||||
size_type bucket_count,
|
||||
const Hash& hash,
|
||||
const Allocator& alloc) :
|
||||
bhopscotch_set(init.begin(), init.end(), bucket_count, hash, KeyEqual(), alloc)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
bhopscotch_set& operator=(std::initializer_list<value_type> ilist) {
|
||||
m_ht.clear();
|
||||
|
||||
m_ht.reserve(ilist.size());
|
||||
m_ht.insert(ilist.begin(), ilist.end());
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
allocator_type get_allocator() const { return m_ht.get_allocator(); }
|
||||
|
||||
|
||||
/*
|
||||
* Iterators
|
||||
*/
|
||||
iterator begin() noexcept { return m_ht.begin(); }
|
||||
const_iterator begin() const noexcept { return m_ht.begin(); }
|
||||
const_iterator cbegin() const noexcept { return m_ht.cbegin(); }
|
||||
|
||||
iterator end() noexcept { return m_ht.end(); }
|
||||
const_iterator end() const noexcept { return m_ht.end(); }
|
||||
const_iterator cend() const noexcept { return m_ht.cend(); }
|
||||
|
||||
|
||||
/*
|
||||
* Capacity
|
||||
*/
|
||||
bool empty() const noexcept { return m_ht.empty(); }
|
||||
size_type size() const noexcept { return m_ht.size(); }
|
||||
size_type max_size() const noexcept { return m_ht.max_size(); }
|
||||
|
||||
/*
|
||||
* Modifiers
|
||||
*/
|
||||
void clear() noexcept { m_ht.clear(); }
|
||||
|
||||
|
||||
|
||||
|
||||
std::pair<iterator, bool> insert(const value_type& value) { return m_ht.insert(value); }
|
||||
std::pair<iterator, bool> insert(value_type&& value) { return m_ht.insert(std::move(value)); }
|
||||
|
||||
iterator insert(const_iterator hint, const value_type& value) { return m_ht.insert(hint, value); }
|
||||
iterator insert(const_iterator hint, value_type&& value) { return m_ht.insert(hint, std::move(value)); }
|
||||
|
||||
template<class InputIt>
|
||||
void insert(InputIt first, InputIt last) { m_ht.insert(first, last); }
|
||||
void insert(std::initializer_list<value_type> ilist) { m_ht.insert(ilist.begin(), ilist.end()); }
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Due to the way elements are stored, emplace will need to move or copy the key-value once.
|
||||
* The method is equivalent to insert(value_type(std::forward<Args>(args)...));
|
||||
*
|
||||
* Mainly here for compatibility with the std::unordered_map interface.
|
||||
*/
|
||||
template<class... Args>
|
||||
std::pair<iterator, bool> emplace(Args&&... args) { return m_ht.emplace(std::forward<Args>(args)...); }
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Due to the way elements are stored, emplace_hint will need to move or copy the key-value once.
|
||||
* The method is equivalent to insert(hint, value_type(std::forward<Args>(args)...));
|
||||
*
|
||||
* Mainly here for compatibility with the std::unordered_map interface.
|
||||
*/
|
||||
template<class... Args>
|
||||
iterator emplace_hint(const_iterator hint, Args&&... args) {
|
||||
return m_ht.emplace_hint(hint, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
iterator erase(iterator pos) { return m_ht.erase(pos); }
|
||||
iterator erase(const_iterator pos) { return m_ht.erase(pos); }
|
||||
iterator erase(const_iterator first, const_iterator last) { return m_ht.erase(first, last); }
|
||||
size_type erase(const key_type& key) { return m_ht.erase(key); }
|
||||
|
||||
/**
|
||||
* Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same
|
||||
* as hash_function()(key). Usefull to speed-up the lookup to the value if you already have the hash.
|
||||
*/
|
||||
size_type erase(const key_type& key, std::size_t precalculated_hash) {
|
||||
return m_ht.erase(key, precalculated_hash);
|
||||
}
|
||||
|
||||
/**
|
||||
* This overload only participates in the overload resolution if the typedef KeyEqual::is_transparent
|
||||
* and Compare::is_transparent exist.
|
||||
* If so, K must be hashable and comparable to Key.
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, class CP = Compare,
|
||||
typename std::enable_if<has_is_transparent<KE>::value && has_is_transparent<CP>::value>::type* = nullptr>
|
||||
size_type erase(const K& key) { return m_ht.erase(key); }
|
||||
|
||||
/**
|
||||
* @copydoc erase(const K& key)
|
||||
*
|
||||
* Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same
|
||||
* as hash_function()(key). Usefull to speed-up the lookup to the value if you already have the hash.
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, class CP = Compare,
|
||||
typename std::enable_if<has_is_transparent<KE>::value && has_is_transparent<CP>::value>::type* = nullptr>
|
||||
size_type erase(const K& key, std::size_t precalculated_hash) { return m_ht.erase(key, precalculated_hash); }
|
||||
|
||||
|
||||
|
||||
|
||||
void swap(bhopscotch_set& other) { other.m_ht.swap(m_ht); }
|
||||
|
||||
|
||||
/*
|
||||
* Lookup
|
||||
*/
|
||||
size_type count(const Key& key) const { return m_ht.count(key); }
|
||||
|
||||
/**
|
||||
* Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same
|
||||
* as hash_function()(key). Usefull to speed-up the lookup if you already have the hash.
|
||||
*/
|
||||
size_type count(const Key& key, std::size_t precalculated_hash) const { return m_ht.count(key, precalculated_hash); }
|
||||
|
||||
/**
|
||||
* This overload only participates in the overload resolution if the typedef KeyEqual::is_transparent
|
||||
* and Compare::is_transparent exist.
|
||||
* If so, K must be hashable and comparable to Key.
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, class CP = Compare,
|
||||
typename std::enable_if<has_is_transparent<KE>::value && has_is_transparent<CP>::value>::type* = nullptr>
|
||||
size_type count(const K& key) const { return m_ht.count(key); }
|
||||
|
||||
/**
|
||||
* @copydoc count(const K& key) const
|
||||
*
|
||||
* Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same
|
||||
* as hash_function()(key). Usefull to speed-up the lookup if you already have the hash.
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, class CP = Compare,
|
||||
typename std::enable_if<has_is_transparent<KE>::value && has_is_transparent<CP>::value>::type* = nullptr>
|
||||
size_type count(const K& key, std::size_t precalculated_hash) const { return m_ht.count(key, precalculated_hash); }
|
||||
|
||||
|
||||
|
||||
|
||||
iterator find(const Key& key) { return m_ht.find(key); }
|
||||
|
||||
/**
|
||||
* Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same
|
||||
* as hash_function()(key). Usefull to speed-up the lookup if you already have the hash.
|
||||
*/
|
||||
iterator find(const Key& key, std::size_t precalculated_hash) { return m_ht.find(key, precalculated_hash); }
|
||||
|
||||
const_iterator find(const Key& key) const { return m_ht.find(key); }
|
||||
|
||||
/**
|
||||
* @copydoc find(const Key& key, std::size_t precalculated_hash)
|
||||
*/
|
||||
const_iterator find(const Key& key, std::size_t precalculated_hash) const { return m_ht.find(key, precalculated_hash); }
|
||||
|
||||
/**
|
||||
* This overload only participates in the overload resolution if the typedef KeyEqual::is_transparent
|
||||
* and Compare::is_transparent exist.
|
||||
* If so, K must be hashable and comparable to Key.
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, class CP = Compare,
|
||||
typename std::enable_if<has_is_transparent<KE>::value && has_is_transparent<CP>::value>::type* = nullptr>
|
||||
iterator find(const K& key) { return m_ht.find(key); }
|
||||
|
||||
/**
|
||||
* @copydoc find(const K& key)
|
||||
*
|
||||
* Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same
|
||||
* as hash_function()(key). Usefull to speed-up the lookup if you already have the hash.
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, class CP = Compare,
|
||||
typename std::enable_if<has_is_transparent<KE>::value && has_is_transparent<CP>::value>::type* = nullptr>
|
||||
iterator find(const K& key, std::size_t precalculated_hash) { return m_ht.find(key, precalculated_hash); }
|
||||
|
||||
/**
|
||||
* @copydoc find(const K& key)
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, class CP = Compare,
|
||||
typename std::enable_if<has_is_transparent<KE>::value && has_is_transparent<CP>::value>::type* = nullptr>
|
||||
const_iterator find(const K& key) const { return m_ht.find(key); }
|
||||
|
||||
/**
|
||||
* @copydoc find(const K& key)
|
||||
*
|
||||
* Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same
|
||||
* as hash_function()(key). Usefull to speed-up the lookup if you already have the hash.
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, class CP = Compare,
|
||||
typename std::enable_if<has_is_transparent<KE>::value && has_is_transparent<CP>::value>::type* = nullptr>
|
||||
const_iterator find(const K& key, std::size_t precalculated_hash) const { return m_ht.find(key, precalculated_hash); }
|
||||
|
||||
|
||||
|
||||
|
||||
bool contains(const Key& key) const { return m_ht.contains(key); }
|
||||
|
||||
/**
|
||||
* Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same
|
||||
* as hash_function()(key). Usefull to speed-up the lookup if you already have the hash.
|
||||
*/
|
||||
bool contains(const Key& key, std::size_t precalculated_hash) const {
|
||||
return m_ht.contains(key, precalculated_hash);
|
||||
}
|
||||
|
||||
/**
|
||||
* This overload only participates in the overload resolution if the typedef KeyEqual::is_transparent exists.
|
||||
* If so, K must be hashable and comparable to Key.
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
|
||||
bool contains(const K& key) const { return m_ht.contains(key); }
|
||||
|
||||
/**
|
||||
* @copydoc contains(const K& key) const
|
||||
*
|
||||
* Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same
|
||||
* as hash_function()(key). Usefull to speed-up the lookup if you already have the hash.
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
|
||||
bool contains(const K& key, std::size_t precalculated_hash) const {
|
||||
return m_ht.contains(key, precalculated_hash);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
std::pair<iterator, iterator> equal_range(const Key& key) { return m_ht.equal_range(key); }
|
||||
|
||||
/**
|
||||
* Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same
|
||||
* as hash_function()(key). Usefull to speed-up the lookup if you already have the hash.
|
||||
*/
|
||||
std::pair<iterator, iterator> equal_range(const Key& key, std::size_t precalculated_hash) {
|
||||
return m_ht.equal_range(key, precalculated_hash);
|
||||
}
|
||||
|
||||
std::pair<const_iterator, const_iterator> equal_range(const Key& key) const { return m_ht.equal_range(key); }
|
||||
|
||||
/**
|
||||
* @copydoc equal_range(const Key& key, std::size_t precalculated_hash)
|
||||
*/
|
||||
std::pair<const_iterator, const_iterator> equal_range(const Key& key, std::size_t precalculated_hash) const {
|
||||
return m_ht.equal_range(key, precalculated_hash);
|
||||
}
|
||||
|
||||
/**
|
||||
* This overload only participates in the overload resolution if the typedef KeyEqual::is_transparent
|
||||
* and Compare::is_transparent exist.
|
||||
* If so, K must be hashable and comparable to Key.
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, class CP = Compare,
|
||||
typename std::enable_if<has_is_transparent<KE>::value && has_is_transparent<CP>::value>::type* = nullptr>
|
||||
std::pair<iterator, iterator> equal_range(const K& key) { return m_ht.equal_range(key); }
|
||||
|
||||
/**
|
||||
* @copydoc equal_range(const K& key)
|
||||
*
|
||||
* Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same
|
||||
* as hash_function()(key). Usefull to speed-up the lookup if you already have the hash.
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, class CP = Compare,
|
||||
typename std::enable_if<has_is_transparent<KE>::value && has_is_transparent<CP>::value>::type* = nullptr>
|
||||
std::pair<iterator, iterator> equal_range(const K& key, std::size_t precalculated_hash) {
|
||||
return m_ht.equal_range(key, precalculated_hash);
|
||||
}
|
||||
|
||||
/**
|
||||
* @copydoc equal_range(const K& key)
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, class CP = Compare,
|
||||
typename std::enable_if<has_is_transparent<KE>::value && has_is_transparent<CP>::value>::type* = nullptr>
|
||||
std::pair<const_iterator, const_iterator> equal_range(const K& key) const { return m_ht.equal_range(key); }
|
||||
|
||||
/**
|
||||
* @copydoc equal_range(const K& key, std::size_t precalculated_hash)
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, class CP = Compare,
|
||||
typename std::enable_if<has_is_transparent<KE>::value && has_is_transparent<CP>::value>::type* = nullptr>
|
||||
std::pair<const_iterator, const_iterator> equal_range(const K& key, std::size_t precalculated_hash) const {
|
||||
return m_ht.equal_range(key, precalculated_hash);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Bucket interface
|
||||
*/
|
||||
size_type bucket_count() const { return m_ht.bucket_count(); }
|
||||
size_type max_bucket_count() const { return m_ht.max_bucket_count(); }
|
||||
|
||||
|
||||
/*
|
||||
* Hash policy
|
||||
*/
|
||||
float load_factor() const { return m_ht.load_factor(); }
|
||||
float max_load_factor() const { return m_ht.max_load_factor(); }
|
||||
void max_load_factor(float ml) { m_ht.max_load_factor(ml); }
|
||||
|
||||
void rehash(size_type count_) { m_ht.rehash(count_); }
|
||||
void reserve(size_type count_) { m_ht.reserve(count_); }
|
||||
|
||||
|
||||
/*
|
||||
* Observers
|
||||
*/
|
||||
hasher hash_function() const { return m_ht.hash_function(); }
|
||||
key_equal key_eq() const { return m_ht.key_eq(); }
|
||||
key_compare key_comp() const { return m_ht.key_comp(); }
|
||||
|
||||
|
||||
/*
|
||||
* Other
|
||||
*/
|
||||
|
||||
/**
|
||||
* Convert a const_iterator to an iterator.
|
||||
*/
|
||||
iterator mutable_iterator(const_iterator pos) {
|
||||
return m_ht.mutable_iterator(pos);
|
||||
}
|
||||
|
||||
size_type overflow_size() const noexcept { return m_ht.overflow_size(); }
|
||||
|
||||
friend bool operator==(const bhopscotch_set& lhs, const bhopscotch_set& rhs) {
|
||||
if(lhs.size() != rhs.size()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for(const auto& element_lhs : lhs) {
|
||||
const auto it_element_rhs = rhs.find(element_lhs);
|
||||
if(it_element_rhs == rhs.cend()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
friend bool operator!=(const bhopscotch_set& lhs, const bhopscotch_set& rhs) {
|
||||
return !operator==(lhs, rhs);
|
||||
}
|
||||
|
||||
friend void swap(bhopscotch_set& lhs, bhopscotch_set& rhs) {
|
||||
lhs.swap(rhs);
|
||||
}
|
||||
|
||||
private:
|
||||
ht m_ht;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Same as `tsl::bhopscotch_set<Key, Hash, KeyEqual, Compare, Allocator, NeighborhoodSize, StoreHash, tsl::hh::prime_growth_policy>`.
|
||||
*/
|
||||
template<class Key,
|
||||
class Hash = std::hash<Key>,
|
||||
class KeyEqual = std::equal_to<Key>,
|
||||
class Compare = std::less<Key>,
|
||||
class Allocator = std::allocator<Key>,
|
||||
unsigned int NeighborhoodSize = 62,
|
||||
bool StoreHash = false>
|
||||
using bhopscotch_pg_set = bhopscotch_set<Key, Hash, KeyEqual, Compare, Allocator, NeighborhoodSize, StoreHash, tsl::hh::prime_growth_policy>;
|
||||
|
||||
} // end namespace tsl
|
||||
|
||||
#endif
|
295
src/includes/3thparty/tsl/hopscotch_growth_policy.h
Normal file
295
src/includes/3thparty/tsl/hopscotch_growth_policy.h
Normal file
@ -0,0 +1,295 @@
|
||||
/**
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2018 Tessil
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
#ifndef TSL_HOPSCOTCH_GROWTH_POLICY_H
|
||||
#define TSL_HOPSCOTCH_GROWTH_POLICY_H
|
||||
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <climits>
|
||||
#include <cmath>
|
||||
#include <cstddef>
|
||||
#include <iterator>
|
||||
#include <limits>
|
||||
#include <ratio>
|
||||
#include <stdexcept>
|
||||
|
||||
|
||||
namespace tsl {
|
||||
namespace hh {
|
||||
|
||||
/**
|
||||
* Grow the hash table by a factor of GrowthFactor keeping the bucket count to a power of two. It allows
|
||||
* the table to use a mask operation instead of a modulo operation to map a hash to a bucket.
|
||||
*
|
||||
* GrowthFactor must be a power of two >= 2.
|
||||
*/
|
||||
template<std::size_t GrowthFactor>
|
||||
class power_of_two_growth_policy {
|
||||
public:
|
||||
/**
|
||||
* Called on the hash table creation and on rehash. The number of buckets for the table is passed in parameter.
|
||||
* This number is a minimum, the policy may update this value with a higher value if needed (but not lower).
|
||||
*
|
||||
* If 0 is given, min_bucket_count_in_out must still be 0 after the policy creation and
|
||||
* bucket_for_hash must always return 0 in this case.
|
||||
*/
|
||||
explicit power_of_two_growth_policy(std::size_t& min_bucket_count_in_out) {
|
||||
if(min_bucket_count_in_out > max_bucket_count()) {
|
||||
throw std::length_error("The hash table exceeds its maxmimum size.");
|
||||
}
|
||||
|
||||
if(min_bucket_count_in_out > 0) {
|
||||
min_bucket_count_in_out = round_up_to_power_of_two(min_bucket_count_in_out);
|
||||
m_mask = min_bucket_count_in_out - 1;
|
||||
}
|
||||
else {
|
||||
m_mask = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the bucket [0, bucket_count()) to which the hash belongs.
|
||||
* If bucket_count() is 0, it must always return 0.
|
||||
*/
|
||||
std::size_t bucket_for_hash(std::size_t hash) const noexcept {
|
||||
return hash & m_mask;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the bucket count to use when the bucket array grows on rehash.
|
||||
*/
|
||||
std::size_t next_bucket_count() const {
|
||||
if((m_mask + 1) > max_bucket_count() / GrowthFactor) {
|
||||
throw std::length_error("The hash table exceeds its maxmimum size.");
|
||||
}
|
||||
|
||||
return (m_mask + 1) * GrowthFactor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the maximum number of buckets supported by the policy.
|
||||
*/
|
||||
std::size_t max_bucket_count() const {
|
||||
// Largest power of two.
|
||||
return (std::numeric_limits<std::size_t>::max() / 2) + 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the growth policy as if it was created with a bucket count of 0.
|
||||
* After a clear, the policy must always return 0 when bucket_for_hash is called.
|
||||
*/
|
||||
void clear() noexcept {
|
||||
m_mask = 0;
|
||||
}
|
||||
|
||||
private:
|
||||
static std::size_t round_up_to_power_of_two(std::size_t value) {
|
||||
if(is_power_of_two(value)) {
|
||||
return value;
|
||||
}
|
||||
|
||||
if(value == 0) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
--value;
|
||||
for(std::size_t i = 1; i < sizeof(std::size_t) * CHAR_BIT; i *= 2) {
|
||||
value |= value >> i;
|
||||
}
|
||||
|
||||
return value + 1;
|
||||
}
|
||||
|
||||
static constexpr bool is_power_of_two(std::size_t value) {
|
||||
return value != 0 && (value & (value - 1)) == 0;
|
||||
}
|
||||
|
||||
private:
|
||||
static_assert(is_power_of_two(GrowthFactor) && GrowthFactor >= 2, "GrowthFactor must be a power of two >= 2.");
|
||||
|
||||
std::size_t m_mask;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Grow the hash table by GrowthFactor::num / GrowthFactor::den and use a modulo to map a hash
|
||||
* to a bucket. Slower but it can be useful if you want a slower growth.
|
||||
*/
|
||||
template<class GrowthFactor = std::ratio<3, 2>>
|
||||
class mod_growth_policy {
|
||||
public:
|
||||
explicit mod_growth_policy(std::size_t& min_bucket_count_in_out) {
|
||||
if(min_bucket_count_in_out > max_bucket_count()) {
|
||||
throw std::length_error("The hash table exceeds its maxmimum size.");
|
||||
}
|
||||
|
||||
if(min_bucket_count_in_out > 0) {
|
||||
m_mod = min_bucket_count_in_out;
|
||||
}
|
||||
else {
|
||||
m_mod = 1;
|
||||
}
|
||||
}
|
||||
|
||||
std::size_t bucket_for_hash(std::size_t hash) const noexcept {
|
||||
return hash % m_mod;
|
||||
}
|
||||
|
||||
std::size_t next_bucket_count() const {
|
||||
if(m_mod == max_bucket_count()) {
|
||||
throw std::length_error("The hash table exceeds its maxmimum size.");
|
||||
}
|
||||
|
||||
const double next_bucket_count = std::ceil(double(m_mod) * REHASH_SIZE_MULTIPLICATION_FACTOR);
|
||||
if(!std::isnormal(next_bucket_count)) {
|
||||
throw std::length_error("The hash table exceeds its maxmimum size.");
|
||||
}
|
||||
|
||||
if(next_bucket_count > double(max_bucket_count())) {
|
||||
return max_bucket_count();
|
||||
}
|
||||
else {
|
||||
return std::size_t(next_bucket_count);
|
||||
}
|
||||
}
|
||||
|
||||
std::size_t max_bucket_count() const {
|
||||
return MAX_BUCKET_COUNT;
|
||||
}
|
||||
|
||||
void clear() noexcept {
|
||||
m_mod = 1;
|
||||
}
|
||||
|
||||
private:
|
||||
static constexpr double REHASH_SIZE_MULTIPLICATION_FACTOR = 1.0 * GrowthFactor::num / GrowthFactor::den;
|
||||
static const std::size_t MAX_BUCKET_COUNT =
|
||||
std::size_t(double(
|
||||
std::numeric_limits<std::size_t>::max() / REHASH_SIZE_MULTIPLICATION_FACTOR
|
||||
));
|
||||
|
||||
static_assert(REHASH_SIZE_MULTIPLICATION_FACTOR >= 1.1, "Growth factor should be >= 1.1.");
|
||||
|
||||
std::size_t m_mod;
|
||||
};
|
||||
|
||||
|
||||
|
||||
namespace detail {
|
||||
|
||||
static constexpr const std::array<std::size_t, 40> PRIMES = {{
|
||||
1ul, 5ul, 17ul, 29ul, 37ul, 53ul, 67ul, 79ul, 97ul, 131ul, 193ul, 257ul, 389ul, 521ul, 769ul, 1031ul,
|
||||
1543ul, 2053ul, 3079ul, 6151ul, 12289ul, 24593ul, 49157ul, 98317ul, 196613ul, 393241ul, 786433ul,
|
||||
1572869ul, 3145739ul, 6291469ul, 12582917ul, 25165843ul, 50331653ul, 100663319ul, 201326611ul,
|
||||
402653189ul, 805306457ul, 1610612741ul, 3221225473ul, 4294967291ul
|
||||
}};
|
||||
|
||||
template<unsigned int IPrime>
|
||||
static constexpr std::size_t mod(std::size_t hash) { return hash % PRIMES[IPrime]; }
|
||||
|
||||
// MOD_PRIME[iprime](hash) returns hash % PRIMES[iprime]. This table allows for faster modulo as the
|
||||
// compiler can optimize the modulo code better with a constant known at the compilation.
|
||||
static constexpr const std::array<std::size_t(*)(std::size_t), 40> MOD_PRIME = {{
|
||||
&mod<0>, &mod<1>, &mod<2>, &mod<3>, &mod<4>, &mod<5>, &mod<6>, &mod<7>, &mod<8>, &mod<9>, &mod<10>,
|
||||
&mod<11>, &mod<12>, &mod<13>, &mod<14>, &mod<15>, &mod<16>, &mod<17>, &mod<18>, &mod<19>, &mod<20>,
|
||||
&mod<21>, &mod<22>, &mod<23>, &mod<24>, &mod<25>, &mod<26>, &mod<27>, &mod<28>, &mod<29>, &mod<30>,
|
||||
&mod<31>, &mod<32>, &mod<33>, &mod<34>, &mod<35>, &mod<36>, &mod<37> , &mod<38>, &mod<39>
|
||||
}};
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Grow the hash table by using prime numbers as bucket count. Slower than tsl::hh::power_of_two_growth_policy in
|
||||
* general but will probably distribute the values around better in the buckets with a poor hash function.
|
||||
*
|
||||
* To allow the compiler to optimize the modulo operation, a lookup table is used with constant primes numbers.
|
||||
*
|
||||
* With a switch the code would look like:
|
||||
* \code
|
||||
* switch(iprime) { // iprime is the current prime of the hash table
|
||||
* case 0: hash % 5ul;
|
||||
* break;
|
||||
* case 1: hash % 17ul;
|
||||
* break;
|
||||
* case 2: hash % 29ul;
|
||||
* break;
|
||||
* ...
|
||||
* }
|
||||
* \endcode
|
||||
*
|
||||
* Due to the constant variable in the modulo the compiler is able to optimize the operation
|
||||
* by a series of multiplications, substractions and shifts.
|
||||
*
|
||||
* The 'hash % 5' could become something like 'hash - (hash * 0xCCCCCCCD) >> 34) * 5' in a 64 bits environement.
|
||||
*/
|
||||
class prime_growth_policy {
|
||||
public:
|
||||
explicit prime_growth_policy(std::size_t& min_bucket_count_in_out) {
|
||||
auto it_prime = std::lower_bound(detail::PRIMES.begin(),
|
||||
detail::PRIMES.end(), min_bucket_count_in_out);
|
||||
if(it_prime == detail::PRIMES.end()) {
|
||||
throw std::length_error("The hash table exceeds its maxmimum size.");
|
||||
}
|
||||
|
||||
m_iprime = static_cast<unsigned int>(std::distance(detail::PRIMES.begin(), it_prime));
|
||||
if(min_bucket_count_in_out > 0) {
|
||||
min_bucket_count_in_out = *it_prime;
|
||||
}
|
||||
else {
|
||||
min_bucket_count_in_out = 0;
|
||||
}
|
||||
}
|
||||
|
||||
std::size_t bucket_for_hash(std::size_t hash) const noexcept {
|
||||
return detail::MOD_PRIME[m_iprime](hash);
|
||||
}
|
||||
|
||||
std::size_t next_bucket_count() const {
|
||||
if(m_iprime + 1 >= detail::PRIMES.size()) {
|
||||
throw std::length_error("The hash table exceeds its maxmimum size.");
|
||||
}
|
||||
|
||||
return detail::PRIMES[m_iprime + 1];
|
||||
}
|
||||
|
||||
std::size_t max_bucket_count() const {
|
||||
return detail::PRIMES.back();
|
||||
}
|
||||
|
||||
void clear() noexcept {
|
||||
m_iprime = 0;
|
||||
}
|
||||
|
||||
private:
|
||||
unsigned int m_iprime;
|
||||
|
||||
static_assert(std::numeric_limits<decltype(m_iprime)>::max() >= detail::PRIMES.size(),
|
||||
"The type of m_iprime is not big enough.");
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
1822
src/includes/3thparty/tsl/hopscotch_hash.h
Normal file
1822
src/includes/3thparty/tsl/hopscotch_hash.h
Normal file
File diff suppressed because it is too large
Load Diff
710
src/includes/3thparty/tsl/hopscotch_map.h
Normal file
710
src/includes/3thparty/tsl/hopscotch_map.h
Normal file
@ -0,0 +1,710 @@
|
||||
/**
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2017 Tessil
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
#ifndef TSL_HOPSCOTCH_MAP_H
|
||||
#define TSL_HOPSCOTCH_MAP_H
|
||||
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstddef>
|
||||
#include <functional>
|
||||
#include <initializer_list>
|
||||
#include <list>
|
||||
#include <memory>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include "hopscotch_hash.h"
|
||||
|
||||
|
||||
namespace tsl {
|
||||
|
||||
/**
|
||||
* Implementation of a hash map using the hopscotch hashing algorithm.
|
||||
*
|
||||
* The Key and the value T must be either nothrow move-constructible, copy-constuctible or both.
|
||||
*
|
||||
* The size of the neighborhood (NeighborhoodSize) must be > 0 and <= 62 if StoreHash is false.
|
||||
* When StoreHash is true, 32-bits of the hash will be stored alongside the neighborhood limiting
|
||||
* the NeighborhoodSize to <= 30. There is no memory usage difference between
|
||||
* 'NeighborhoodSize 62; StoreHash false' and 'NeighborhoodSize 30; StoreHash true'.
|
||||
*
|
||||
* Storing the hash may improve performance on insert during the rehash process if the hash takes time
|
||||
* to compute. It may also improve read performance if the KeyEqual function takes time (or incurs a cache-miss).
|
||||
* If used with simple Hash and KeyEqual it may slow things down.
|
||||
*
|
||||
* StoreHash can only be set if the GrowthPolicy is set to tsl::power_of_two_growth_policy.
|
||||
*
|
||||
* GrowthPolicy defines how the map grows and consequently how a hash value is mapped to a bucket.
|
||||
* By default the map uses tsl::power_of_two_growth_policy. This policy keeps the number of buckets
|
||||
* to a power of two and uses a mask to map the hash to a bucket instead of the slow modulo.
|
||||
* You may define your own growth policy, check tsl::power_of_two_growth_policy for the interface.
|
||||
*
|
||||
* If the destructors of Key or T throw an exception, behaviour of the class is undefined.
|
||||
*
|
||||
* Iterators invalidation:
|
||||
* - clear, operator=, reserve, rehash: always invalidate the iterators.
|
||||
* - insert, emplace, emplace_hint, operator[]: if there is an effective insert, invalidate the iterators
|
||||
* if a displacement is needed to resolve a collision (which mean that most of the time,
|
||||
* insert will invalidate the iterators). Or if there is a rehash.
|
||||
* - erase: iterator on the erased element is the only one which become invalid.
|
||||
*/
|
||||
template<class Key,
|
||||
class T,
|
||||
class Hash = std::hash<Key>,
|
||||
class KeyEqual = std::equal_to<Key>,
|
||||
class Allocator = std::allocator<std::pair<Key, T>>,
|
||||
unsigned int NeighborhoodSize = 62,
|
||||
bool StoreHash = false,
|
||||
class GrowthPolicy = tsl::hh::power_of_two_growth_policy<2>>
|
||||
class hopscotch_map {
|
||||
private:
|
||||
template<typename U>
|
||||
using has_is_transparent = tsl::detail_hopscotch_hash::has_is_transparent<U>;
|
||||
|
||||
class KeySelect {
|
||||
public:
|
||||
using key_type = Key;
|
||||
|
||||
const key_type& operator()(const std::pair<Key, T>& key_value) const {
|
||||
return key_value.first;
|
||||
}
|
||||
|
||||
key_type& operator()(std::pair<Key, T>& key_value) {
|
||||
return key_value.first;
|
||||
}
|
||||
};
|
||||
|
||||
class ValueSelect {
|
||||
public:
|
||||
using value_type = T;
|
||||
|
||||
const value_type& operator()(const std::pair<Key, T>& key_value) const {
|
||||
return key_value.second;
|
||||
}
|
||||
|
||||
value_type& operator()(std::pair<Key, T>& key_value) {
|
||||
return key_value.second;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
using overflow_container_type = std::list<std::pair<Key, T>, Allocator>;
|
||||
using ht = detail_hopscotch_hash::hopscotch_hash<std::pair<Key, T>, KeySelect, ValueSelect,
|
||||
Hash, KeyEqual,
|
||||
Allocator, NeighborhoodSize,
|
||||
StoreHash, GrowthPolicy,
|
||||
overflow_container_type>;
|
||||
|
||||
public:
|
||||
using key_type = typename ht::key_type;
|
||||
using mapped_type = T;
|
||||
using value_type = typename ht::value_type;
|
||||
using size_type = typename ht::size_type;
|
||||
using difference_type = typename ht::difference_type;
|
||||
using hasher = typename ht::hasher;
|
||||
using key_equal = typename ht::key_equal;
|
||||
using allocator_type = typename ht::allocator_type;
|
||||
using reference = typename ht::reference;
|
||||
using const_reference = typename ht::const_reference;
|
||||
using pointer = typename ht::pointer;
|
||||
using const_pointer = typename ht::const_pointer;
|
||||
using iterator = typename ht::iterator;
|
||||
using const_iterator = typename ht::const_iterator;
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Constructors
|
||||
*/
|
||||
hopscotch_map() : hopscotch_map(ht::DEFAULT_INIT_BUCKETS_SIZE) {
|
||||
}
|
||||
|
||||
explicit hopscotch_map(size_type bucket_count,
|
||||
const Hash& hash = Hash(),
|
||||
const KeyEqual& equal = KeyEqual(),
|
||||
const Allocator& alloc = Allocator()) :
|
||||
m_ht(bucket_count, hash, equal, alloc, ht::DEFAULT_MAX_LOAD_FACTOR)
|
||||
{
|
||||
}
|
||||
|
||||
hopscotch_map(size_type bucket_count,
|
||||
const Allocator& alloc) : hopscotch_map(bucket_count, Hash(), KeyEqual(), alloc)
|
||||
{
|
||||
}
|
||||
|
||||
hopscotch_map(size_type bucket_count,
|
||||
const Hash& hash,
|
||||
const Allocator& alloc) : hopscotch_map(bucket_count, hash, KeyEqual(), alloc)
|
||||
{
|
||||
}
|
||||
|
||||
explicit hopscotch_map(const Allocator& alloc) : hopscotch_map(ht::DEFAULT_INIT_BUCKETS_SIZE, alloc) {
|
||||
}
|
||||
|
||||
template<class InputIt>
|
||||
hopscotch_map(InputIt first, InputIt last,
|
||||
size_type bucket_count = ht::DEFAULT_INIT_BUCKETS_SIZE,
|
||||
const Hash& hash = Hash(),
|
||||
const KeyEqual& equal = KeyEqual(),
|
||||
const Allocator& alloc = Allocator()) : hopscotch_map(bucket_count, hash, equal, alloc)
|
||||
{
|
||||
insert(first, last);
|
||||
}
|
||||
|
||||
template<class InputIt>
|
||||
hopscotch_map(InputIt first, InputIt last,
|
||||
size_type bucket_count,
|
||||
const Allocator& alloc) : hopscotch_map(first, last, bucket_count, Hash(), KeyEqual(), alloc)
|
||||
{
|
||||
}
|
||||
|
||||
template<class InputIt>
|
||||
hopscotch_map(InputIt first, InputIt last,
|
||||
size_type bucket_count,
|
||||
const Hash& hash,
|
||||
const Allocator& alloc) : hopscotch_map(first, last, bucket_count, hash, KeyEqual(), alloc)
|
||||
{
|
||||
}
|
||||
|
||||
hopscotch_map(std::initializer_list<value_type> init,
|
||||
size_type bucket_count = ht::DEFAULT_INIT_BUCKETS_SIZE,
|
||||
const Hash& hash = Hash(),
|
||||
const KeyEqual& equal = KeyEqual(),
|
||||
const Allocator& alloc = Allocator()) :
|
||||
hopscotch_map(init.begin(), init.end(), bucket_count, hash, equal, alloc)
|
||||
{
|
||||
}
|
||||
|
||||
hopscotch_map(std::initializer_list<value_type> init,
|
||||
size_type bucket_count,
|
||||
const Allocator& alloc) :
|
||||
hopscotch_map(init.begin(), init.end(), bucket_count, Hash(), KeyEqual(), alloc)
|
||||
{
|
||||
}
|
||||
|
||||
hopscotch_map(std::initializer_list<value_type> init,
|
||||
size_type bucket_count,
|
||||
const Hash& hash,
|
||||
const Allocator& alloc) :
|
||||
hopscotch_map(init.begin(), init.end(), bucket_count, hash, KeyEqual(), alloc)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
hopscotch_map& operator=(std::initializer_list<value_type> ilist) {
|
||||
m_ht.clear();
|
||||
|
||||
m_ht.reserve(ilist.size());
|
||||
m_ht.insert(ilist.begin(), ilist.end());
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
allocator_type get_allocator() const { return m_ht.get_allocator(); }
|
||||
|
||||
|
||||
/*
|
||||
* Iterators
|
||||
*/
|
||||
iterator begin() noexcept { return m_ht.begin(); }
|
||||
const_iterator begin() const noexcept { return m_ht.begin(); }
|
||||
const_iterator cbegin() const noexcept { return m_ht.cbegin(); }
|
||||
|
||||
iterator end() noexcept { return m_ht.end(); }
|
||||
const_iterator end() const noexcept { return m_ht.end(); }
|
||||
const_iterator cend() const noexcept { return m_ht.cend(); }
|
||||
|
||||
|
||||
/*
|
||||
* Capacity
|
||||
*/
|
||||
bool empty() const noexcept { return m_ht.empty(); }
|
||||
size_type size() const noexcept { return m_ht.size(); }
|
||||
size_type max_size() const noexcept { return m_ht.max_size(); }
|
||||
|
||||
/*
|
||||
* Modifiers
|
||||
*/
|
||||
void clear() noexcept { m_ht.clear(); }
|
||||
|
||||
|
||||
|
||||
|
||||
std::pair<iterator, bool> insert(const value_type& value) {
|
||||
return m_ht.insert(value);
|
||||
}
|
||||
|
||||
template<class P, typename std::enable_if<std::is_constructible<value_type, P&&>::value>::type* = nullptr>
|
||||
std::pair<iterator, bool> insert(P&& value) {
|
||||
return m_ht.insert(std::forward<P>(value));
|
||||
}
|
||||
|
||||
std::pair<iterator, bool> insert(value_type&& value) {
|
||||
return m_ht.insert(std::move(value));
|
||||
}
|
||||
|
||||
|
||||
iterator insert(const_iterator hint, const value_type& value) {
|
||||
return m_ht.insert(hint, value);
|
||||
}
|
||||
|
||||
template<class P, typename std::enable_if<std::is_constructible<value_type, P&&>::value>::type* = nullptr>
|
||||
iterator insert(const_iterator hint, P&& value) {
|
||||
return m_ht.insert(hint, std::forward<P>(value));
|
||||
}
|
||||
|
||||
iterator insert(const_iterator hint, value_type&& value) {
|
||||
return m_ht.insert(hint, std::move(value));
|
||||
}
|
||||
|
||||
|
||||
template<class InputIt>
|
||||
void insert(InputIt first, InputIt last) {
|
||||
m_ht.insert(first, last);
|
||||
}
|
||||
|
||||
void insert(std::initializer_list<value_type> ilist) {
|
||||
m_ht.insert(ilist.begin(), ilist.end());
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
template<class M>
|
||||
std::pair<iterator, bool> insert_or_assign(const key_type& k, M&& obj) {
|
||||
return m_ht.insert_or_assign(k, std::forward<M>(obj));
|
||||
}
|
||||
|
||||
template<class M>
|
||||
std::pair<iterator, bool> insert_or_assign(key_type&& k, M&& obj) {
|
||||
return m_ht.insert_or_assign(std::move(k), std::forward<M>(obj));
|
||||
}
|
||||
|
||||
template<class M>
|
||||
iterator insert_or_assign(const_iterator hint, const key_type& k, M&& obj) {
|
||||
return m_ht.insert_or_assign(hint, k, std::forward<M>(obj));
|
||||
}
|
||||
|
||||
template<class M>
|
||||
iterator insert_or_assign(const_iterator hint, key_type&& k, M&& obj) {
|
||||
return m_ht.insert_or_assign(hint, std::move(k), std::forward<M>(obj));
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Due to the way elements are stored, emplace will need to move or copy the key-value once.
|
||||
* The method is equivalent to insert(value_type(std::forward<Args>(args)...));
|
||||
*
|
||||
* Mainly here for compatibility with the std::unordered_map interface.
|
||||
*/
|
||||
template<class... Args>
|
||||
std::pair<iterator, bool> emplace(Args&&... args) {
|
||||
return m_ht.emplace(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Due to the way elements are stored, emplace_hint will need to move or copy the key-value once.
|
||||
* The method is equivalent to insert(hint, value_type(std::forward<Args>(args)...));
|
||||
*
|
||||
* Mainly here for compatibility with the std::unordered_map interface.
|
||||
*/
|
||||
template<class... Args>
|
||||
iterator emplace_hint(const_iterator hint, Args&&... args) {
|
||||
return m_ht.emplace_hint(hint, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
template<class... Args>
|
||||
std::pair<iterator, bool> try_emplace(const key_type& k, Args&&... args) {
|
||||
return m_ht.try_emplace(k, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template<class... Args>
|
||||
std::pair<iterator, bool> try_emplace(key_type&& k, Args&&... args) {
|
||||
return m_ht.try_emplace(std::move(k), std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template<class... Args>
|
||||
iterator try_emplace(const_iterator hint, const key_type& k, Args&&... args) {
|
||||
return m_ht.try_emplace(hint, k, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template<class... Args>
|
||||
iterator try_emplace(const_iterator hint, key_type&& k, Args&&... args) {
|
||||
return m_ht.try_emplace(hint, std::move(k), std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
iterator erase(iterator pos) { return m_ht.erase(pos); }
|
||||
iterator erase(const_iterator pos) { return m_ht.erase(pos); }
|
||||
iterator erase(const_iterator first, const_iterator last) { return m_ht.erase(first, last); }
|
||||
size_type erase(const key_type& key) { return m_ht.erase(key); }
|
||||
|
||||
/**
|
||||
* Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same
|
||||
* as hash_function()(key). Usefull to speed-up the lookup to the value if you already have the hash.
|
||||
*/
|
||||
size_type erase(const key_type& key, std::size_t precalculated_hash) {
|
||||
return m_ht.erase(key, precalculated_hash);
|
||||
}
|
||||
|
||||
/**
|
||||
* This overload only participates in the overload resolution if the typedef KeyEqual::is_transparent exists.
|
||||
* If so, K must be hashable and comparable to Key.
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
|
||||
size_type erase(const K& key) { return m_ht.erase(key); }
|
||||
|
||||
/**
|
||||
* @copydoc erase(const K& key)
|
||||
*
|
||||
* Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same
|
||||
* as hash_function()(key). Usefull to speed-up the lookup to the value if you already have the hash.
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
|
||||
size_type erase(const K& key, std::size_t precalculated_hash) {
|
||||
return m_ht.erase(key, precalculated_hash);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void swap(hopscotch_map& other) { other.m_ht.swap(m_ht); }
|
||||
|
||||
/*
|
||||
* Lookup
|
||||
*/
|
||||
T& at(const Key& key) { return m_ht.at(key); }
|
||||
|
||||
/**
|
||||
* Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same
|
||||
* as hash_function()(key). Usefull to speed-up the lookup if you already have the hash.
|
||||
*/
|
||||
T& at(const Key& key, std::size_t precalculated_hash) { return m_ht.at(key, precalculated_hash); }
|
||||
|
||||
|
||||
const T& at(const Key& key) const { return m_ht.at(key); }
|
||||
|
||||
/**
|
||||
* @copydoc at(const Key& key, std::size_t precalculated_hash)
|
||||
*/
|
||||
const T& at(const Key& key, std::size_t precalculated_hash) const { return m_ht.at(key, precalculated_hash); }
|
||||
|
||||
|
||||
/**
|
||||
* This overload only participates in the overload resolution if the typedef KeyEqual::is_transparent exists.
|
||||
* If so, K must be hashable and comparable to Key.
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
|
||||
T& at(const K& key) { return m_ht.at(key); }
|
||||
|
||||
/**
|
||||
* @copydoc at(const K& key)
|
||||
*
|
||||
* Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same
|
||||
* as hash_function()(key). Usefull to speed-up the lookup if you already have the hash.
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
|
||||
T& at(const K& key, std::size_t precalculated_hash) { return m_ht.at(key, precalculated_hash); }
|
||||
|
||||
|
||||
/**
|
||||
* @copydoc at(const K& key)
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
|
||||
const T& at(const K& key) const { return m_ht.at(key); }
|
||||
|
||||
/**
|
||||
* @copydoc at(const K& key, std::size_t precalculated_hash)
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
|
||||
const T& at(const K& key, std::size_t precalculated_hash) const { return m_ht.at(key, precalculated_hash); }
|
||||
|
||||
|
||||
|
||||
|
||||
T& operator[](const Key& key) { return m_ht[key]; }
|
||||
T& operator[](Key&& key) { return m_ht[std::move(key)]; }
|
||||
|
||||
|
||||
|
||||
|
||||
size_type count(const Key& key) const { return m_ht.count(key); }
|
||||
|
||||
/**
|
||||
* Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same
|
||||
* as hash_function()(key). Usefull to speed-up the lookup if you already have the hash.
|
||||
*/
|
||||
size_type count(const Key& key, std::size_t precalculated_hash) const {
|
||||
return m_ht.count(key, precalculated_hash);
|
||||
}
|
||||
|
||||
/**
|
||||
* This overload only participates in the overload resolution if the typedef KeyEqual::is_transparent exists.
|
||||
* If so, K must be hashable and comparable to Key.
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
|
||||
size_type count(const K& key) const { return m_ht.count(key); }
|
||||
|
||||
/**
|
||||
* @copydoc count(const K& key) const
|
||||
*
|
||||
* Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same
|
||||
* as hash_function()(key). Usefull to speed-up the lookup if you already have the hash.
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
|
||||
size_type count(const K& key, std::size_t precalculated_hash) const { return m_ht.count(key, precalculated_hash); }
|
||||
|
||||
|
||||
|
||||
|
||||
iterator find(const Key& key) { return m_ht.find(key); }
|
||||
|
||||
/**
|
||||
* Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same
|
||||
* as hash_function()(key). Usefull to speed-up the lookup if you already have the hash.
|
||||
*/
|
||||
iterator find(const Key& key, std::size_t precalculated_hash) { return m_ht.find(key, precalculated_hash); }
|
||||
|
||||
const_iterator find(const Key& key) const { return m_ht.find(key); }
|
||||
|
||||
/**
|
||||
* @copydoc find(const Key& key, std::size_t precalculated_hash)
|
||||
*/
|
||||
const_iterator find(const Key& key, std::size_t precalculated_hash) const {
|
||||
return m_ht.find(key, precalculated_hash);
|
||||
}
|
||||
|
||||
/**
|
||||
* This overload only participates in the overload resolution if the typedef KeyEqual::is_transparent exists.
|
||||
* If so, K must be hashable and comparable to Key.
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
|
||||
iterator find(const K& key) { return m_ht.find(key); }
|
||||
|
||||
/**
|
||||
* @copydoc find(const K& key)
|
||||
*
|
||||
* Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same
|
||||
* as hash_function()(key). Usefull to speed-up the lookup if you already have the hash.
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
|
||||
iterator find(const K& key, std::size_t precalculated_hash) { return m_ht.find(key, precalculated_hash); }
|
||||
|
||||
/**
|
||||
* @copydoc find(const K& key)
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
|
||||
const_iterator find(const K& key) const { return m_ht.find(key); }
|
||||
|
||||
/**
|
||||
* @copydoc find(const K& key)
|
||||
*
|
||||
* Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same
|
||||
* as hash_function()(key). Usefull to speed-up the lookup if you already have the hash.
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
|
||||
const_iterator find(const K& key, std::size_t precalculated_hash) const {
|
||||
return m_ht.find(key, precalculated_hash);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
bool contains(const Key& key) const { return m_ht.contains(key); }
|
||||
|
||||
/**
|
||||
* Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same
|
||||
* as hash_function()(key). Usefull to speed-up the lookup if you already have the hash.
|
||||
*/
|
||||
bool contains(const Key& key, std::size_t precalculated_hash) const {
|
||||
return m_ht.contains(key, precalculated_hash);
|
||||
}
|
||||
|
||||
/**
|
||||
* This overload only participates in the overload resolution if the typedef KeyEqual::is_transparent exists.
|
||||
* If so, K must be hashable and comparable to Key.
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
|
||||
bool contains(const K& key) const { return m_ht.contains(key); }
|
||||
|
||||
/**
|
||||
* @copydoc contains(const K& key) const
|
||||
*
|
||||
* Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same
|
||||
* as hash_function()(key). Usefull to speed-up the lookup if you already have the hash.
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
|
||||
bool contains(const K& key, std::size_t precalculated_hash) const {
|
||||
return m_ht.contains(key, precalculated_hash);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
std::pair<iterator, iterator> equal_range(const Key& key) { return m_ht.equal_range(key); }
|
||||
|
||||
/**
|
||||
* Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same
|
||||
* as hash_function()(key). Usefull to speed-up the lookup if you already have the hash.
|
||||
*/
|
||||
std::pair<iterator, iterator> equal_range(const Key& key, std::size_t precalculated_hash) {
|
||||
return m_ht.equal_range(key, precalculated_hash);
|
||||
}
|
||||
|
||||
std::pair<const_iterator, const_iterator> equal_range(const Key& key) const { return m_ht.equal_range(key); }
|
||||
|
||||
/**
|
||||
* @copydoc equal_range(const Key& key, std::size_t precalculated_hash)
|
||||
*/
|
||||
std::pair<const_iterator, const_iterator> equal_range(const Key& key, std::size_t precalculated_hash) const {
|
||||
return m_ht.equal_range(key, precalculated_hash);
|
||||
}
|
||||
|
||||
/**
|
||||
* This overload only participates in the overload resolution if the typedef KeyEqual::is_transparent exists.
|
||||
* If so, K must be hashable and comparable to Key.
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
|
||||
std::pair<iterator, iterator> equal_range(const K& key) { return m_ht.equal_range(key); }
|
||||
|
||||
|
||||
/**
|
||||
* @copydoc equal_range(const K& key)
|
||||
*
|
||||
* Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same
|
||||
* as hash_function()(key). Usefull to speed-up the lookup if you already have the hash.
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
|
||||
std::pair<iterator, iterator> equal_range(const K& key, std::size_t precalculated_hash) {
|
||||
return m_ht.equal_range(key, precalculated_hash);
|
||||
}
|
||||
|
||||
/**
|
||||
* @copydoc equal_range(const K& key)
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
|
||||
std::pair<const_iterator, const_iterator> equal_range(const K& key) const { return m_ht.equal_range(key); }
|
||||
|
||||
/**
|
||||
* @copydoc equal_range(const K& key, std::size_t precalculated_hash)
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
|
||||
std::pair<const_iterator, const_iterator> equal_range(const K& key, std::size_t precalculated_hash) const {
|
||||
return m_ht.equal_range(key, precalculated_hash);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Bucket interface
|
||||
*/
|
||||
size_type bucket_count() const { return m_ht.bucket_count(); }
|
||||
size_type max_bucket_count() const { return m_ht.max_bucket_count(); }
|
||||
|
||||
|
||||
/*
|
||||
* Hash policy
|
||||
*/
|
||||
float load_factor() const { return m_ht.load_factor(); }
|
||||
float max_load_factor() const { return m_ht.max_load_factor(); }
|
||||
void max_load_factor(float ml) { m_ht.max_load_factor(ml); }
|
||||
|
||||
void rehash(size_type count_) { m_ht.rehash(count_); }
|
||||
void reserve(size_type count_) { m_ht.reserve(count_); }
|
||||
|
||||
|
||||
/*
|
||||
* Observers
|
||||
*/
|
||||
hasher hash_function() const { return m_ht.hash_function(); }
|
||||
key_equal key_eq() const { return m_ht.key_eq(); }
|
||||
|
||||
/*
|
||||
* Other
|
||||
*/
|
||||
|
||||
/**
|
||||
* Convert a const_iterator to an iterator.
|
||||
*/
|
||||
iterator mutable_iterator(const_iterator pos) {
|
||||
return m_ht.mutable_iterator(pos);
|
||||
}
|
||||
|
||||
size_type overflow_size() const noexcept { return m_ht.overflow_size(); }
|
||||
|
||||
friend bool operator==(const hopscotch_map& lhs, const hopscotch_map& rhs) {
|
||||
if(lhs.size() != rhs.size()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for(const auto& element_lhs : lhs) {
|
||||
const auto it_element_rhs = rhs.find(element_lhs.first);
|
||||
if(it_element_rhs == rhs.cend() || element_lhs.second != it_element_rhs->second) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
friend bool operator!=(const hopscotch_map& lhs, const hopscotch_map& rhs) {
|
||||
return !operator==(lhs, rhs);
|
||||
}
|
||||
|
||||
friend void swap(hopscotch_map& lhs, hopscotch_map& rhs) {
|
||||
lhs.swap(rhs);
|
||||
}
|
||||
|
||||
|
||||
|
||||
private:
|
||||
ht m_ht;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Same as `tsl::hopscotch_map<Key, T, Hash, KeyEqual, Allocator, NeighborhoodSize, StoreHash, tsl::hh::prime_growth_policy>`.
|
||||
*/
|
||||
template<class Key,
|
||||
class T,
|
||||
class Hash = std::hash<Key>,
|
||||
class KeyEqual = std::equal_to<Key>,
|
||||
class Allocator = std::allocator<std::pair<Key, T>>,
|
||||
unsigned int NeighborhoodSize = 62,
|
||||
bool StoreHash = false>
|
||||
using hopscotch_pg_map = hopscotch_map<Key, T, Hash, KeyEqual, Allocator, NeighborhoodSize, StoreHash, tsl::hh::prime_growth_policy>;
|
||||
|
||||
} // end namespace tsl
|
||||
|
||||
#endif
|
556
src/includes/3thparty/tsl/hopscotch_set.h
Normal file
556
src/includes/3thparty/tsl/hopscotch_set.h
Normal file
@ -0,0 +1,556 @@
|
||||
/**
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2017 Tessil
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
#ifndef TSL_HOPSCOTCH_SET_H
|
||||
#define TSL_HOPSCOTCH_SET_H
|
||||
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstddef>
|
||||
#include <functional>
|
||||
#include <initializer_list>
|
||||
#include <list>
|
||||
#include <memory>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include "hopscotch_hash.h"
|
||||
|
||||
|
||||
namespace tsl {
|
||||
|
||||
/**
|
||||
* Implementation of a hash set using the hopscotch hashing algorithm.
|
||||
*
|
||||
* The Key must be either nothrow move-constructible, copy-constuctible or both.
|
||||
*
|
||||
* The size of the neighborhood (NeighborhoodSize) must be > 0 and <= 62 if StoreHash is false.
|
||||
* When StoreHash is true, 32-bits of the hash will be stored alongside the neighborhood limiting
|
||||
* the NeighborhoodSize to <= 30. There is no memory usage difference between
|
||||
* 'NeighborhoodSize 62; StoreHash false' and 'NeighborhoodSize 30; StoreHash true'.
|
||||
*
|
||||
* Storing the hash may improve performance on insert during the rehash process if the hash takes time
|
||||
* to compute. It may also improve read performance if the KeyEqual function takes time (or incurs a cache-miss).
|
||||
* If used with simple Hash and KeyEqual it may slow things down.
|
||||
*
|
||||
* StoreHash can only be set if the GrowthPolicy is set to tsl::power_of_two_growth_policy.
|
||||
*
|
||||
* GrowthPolicy defines how the set grows and consequently how a hash value is mapped to a bucket.
|
||||
* By default the set uses tsl::power_of_two_growth_policy. This policy keeps the number of buckets
|
||||
* to a power of two and uses a mask to set the hash to a bucket instead of the slow modulo.
|
||||
* You may define your own growth policy, check tsl::power_of_two_growth_policy for the interface.
|
||||
*
|
||||
* If the destructor of Key throws an exception, behaviour of the class is undefined.
|
||||
*
|
||||
* Iterators invalidation:
|
||||
* - clear, operator=, reserve, rehash: always invalidate the iterators.
|
||||
* - insert, emplace, emplace_hint, operator[]: if there is an effective insert, invalidate the iterators
|
||||
* if a displacement is needed to resolve a collision (which mean that most of the time,
|
||||
* insert will invalidate the iterators). Or if there is a rehash.
|
||||
* - erase: iterator on the erased element is the only one which become invalid.
|
||||
*/
|
||||
template<class Key,
|
||||
class Hash = std::hash<Key>,
|
||||
class KeyEqual = std::equal_to<Key>,
|
||||
class Allocator = std::allocator<Key>,
|
||||
unsigned int NeighborhoodSize = 62,
|
||||
bool StoreHash = false,
|
||||
class GrowthPolicy = tsl::hh::power_of_two_growth_policy<2>>
|
||||
class hopscotch_set {
|
||||
private:
|
||||
template<typename U>
|
||||
using has_is_transparent = tsl::detail_hopscotch_hash::has_is_transparent<U>;
|
||||
|
||||
class KeySelect {
|
||||
public:
|
||||
using key_type = Key;
|
||||
|
||||
const key_type& operator()(const Key& key) const {
|
||||
return key;
|
||||
}
|
||||
|
||||
key_type& operator()(Key& key) {
|
||||
return key;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
using overflow_container_type = std::list<Key, Allocator>;
|
||||
using ht = detail_hopscotch_hash::hopscotch_hash<Key, KeySelect, void,
|
||||
Hash, KeyEqual,
|
||||
Allocator, NeighborhoodSize,
|
||||
StoreHash, GrowthPolicy,
|
||||
overflow_container_type>;
|
||||
|
||||
public:
|
||||
using key_type = typename ht::key_type;
|
||||
using value_type = typename ht::value_type;
|
||||
using size_type = typename ht::size_type;
|
||||
using difference_type = typename ht::difference_type;
|
||||
using hasher = typename ht::hasher;
|
||||
using key_equal = typename ht::key_equal;
|
||||
using allocator_type = typename ht::allocator_type;
|
||||
using reference = typename ht::reference;
|
||||
using const_reference = typename ht::const_reference;
|
||||
using pointer = typename ht::pointer;
|
||||
using const_pointer = typename ht::const_pointer;
|
||||
using iterator = typename ht::iterator;
|
||||
using const_iterator = typename ht::const_iterator;
|
||||
|
||||
|
||||
/*
|
||||
* Constructors
|
||||
*/
|
||||
hopscotch_set() : hopscotch_set(ht::DEFAULT_INIT_BUCKETS_SIZE) {
|
||||
}
|
||||
|
||||
explicit hopscotch_set(size_type bucket_count,
|
||||
const Hash& hash = Hash(),
|
||||
const KeyEqual& equal = KeyEqual(),
|
||||
const Allocator& alloc = Allocator()) :
|
||||
m_ht(bucket_count, hash, equal, alloc, ht::DEFAULT_MAX_LOAD_FACTOR)
|
||||
{
|
||||
}
|
||||
|
||||
hopscotch_set(size_type bucket_count,
|
||||
const Allocator& alloc) : hopscotch_set(bucket_count, Hash(), KeyEqual(), alloc)
|
||||
{
|
||||
}
|
||||
|
||||
hopscotch_set(size_type bucket_count,
|
||||
const Hash& hash,
|
||||
const Allocator& alloc) : hopscotch_set(bucket_count, hash, KeyEqual(), alloc)
|
||||
{
|
||||
}
|
||||
|
||||
explicit hopscotch_set(const Allocator& alloc) : hopscotch_set(ht::DEFAULT_INIT_BUCKETS_SIZE, alloc) {
|
||||
}
|
||||
|
||||
template<class InputIt>
|
||||
hopscotch_set(InputIt first, InputIt last,
|
||||
size_type bucket_count = ht::DEFAULT_INIT_BUCKETS_SIZE,
|
||||
const Hash& hash = Hash(),
|
||||
const KeyEqual& equal = KeyEqual(),
|
||||
const Allocator& alloc = Allocator()) : hopscotch_set(bucket_count, hash, equal, alloc)
|
||||
{
|
||||
insert(first, last);
|
||||
}
|
||||
|
||||
template<class InputIt>
|
||||
hopscotch_set(InputIt first, InputIt last,
|
||||
size_type bucket_count,
|
||||
const Allocator& alloc) : hopscotch_set(first, last, bucket_count, Hash(), KeyEqual(), alloc)
|
||||
{
|
||||
}
|
||||
|
||||
template<class InputIt>
|
||||
hopscotch_set(InputIt first, InputIt last,
|
||||
size_type bucket_count,
|
||||
const Hash& hash,
|
||||
const Allocator& alloc) : hopscotch_set(first, last, bucket_count, hash, KeyEqual(), alloc)
|
||||
{
|
||||
}
|
||||
|
||||
hopscotch_set(std::initializer_list<value_type> init,
|
||||
size_type bucket_count = ht::DEFAULT_INIT_BUCKETS_SIZE,
|
||||
const Hash& hash = Hash(),
|
||||
const KeyEqual& equal = KeyEqual(),
|
||||
const Allocator& alloc = Allocator()) :
|
||||
hopscotch_set(init.begin(), init.end(), bucket_count, hash, equal, alloc)
|
||||
{
|
||||
}
|
||||
|
||||
hopscotch_set(std::initializer_list<value_type> init,
|
||||
size_type bucket_count,
|
||||
const Allocator& alloc) :
|
||||
hopscotch_set(init.begin(), init.end(), bucket_count, Hash(), KeyEqual(), alloc)
|
||||
{
|
||||
}
|
||||
|
||||
hopscotch_set(std::initializer_list<value_type> init,
|
||||
size_type bucket_count,
|
||||
const Hash& hash,
|
||||
const Allocator& alloc) :
|
||||
hopscotch_set(init.begin(), init.end(), bucket_count, hash, KeyEqual(), alloc)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
hopscotch_set& operator=(std::initializer_list<value_type> ilist) {
|
||||
m_ht.clear();
|
||||
|
||||
m_ht.reserve(ilist.size());
|
||||
m_ht.insert(ilist.begin(), ilist.end());
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
allocator_type get_allocator() const { return m_ht.get_allocator(); }
|
||||
|
||||
|
||||
/*
|
||||
* Iterators
|
||||
*/
|
||||
iterator begin() noexcept { return m_ht.begin(); }
|
||||
const_iterator begin() const noexcept { return m_ht.begin(); }
|
||||
const_iterator cbegin() const noexcept { return m_ht.cbegin(); }
|
||||
|
||||
iterator end() noexcept { return m_ht.end(); }
|
||||
const_iterator end() const noexcept { return m_ht.end(); }
|
||||
const_iterator cend() const noexcept { return m_ht.cend(); }
|
||||
|
||||
|
||||
/*
|
||||
* Capacity
|
||||
*/
|
||||
bool empty() const noexcept { return m_ht.empty(); }
|
||||
size_type size() const noexcept { return m_ht.size(); }
|
||||
size_type max_size() const noexcept { return m_ht.max_size(); }
|
||||
|
||||
/*
|
||||
* Modifiers
|
||||
*/
|
||||
void clear() noexcept { m_ht.clear(); }
|
||||
|
||||
|
||||
|
||||
|
||||
std::pair<iterator, bool> insert(const value_type& value) { return m_ht.insert(value); }
|
||||
std::pair<iterator, bool> insert(value_type&& value) { return m_ht.insert(std::move(value)); }
|
||||
|
||||
iterator insert(const_iterator hint, const value_type& value) { return m_ht.insert(hint, value); }
|
||||
iterator insert(const_iterator hint, value_type&& value) { return m_ht.insert(hint, std::move(value)); }
|
||||
|
||||
template<class InputIt>
|
||||
void insert(InputIt first, InputIt last) { m_ht.insert(first, last); }
|
||||
void insert(std::initializer_list<value_type> ilist) { m_ht.insert(ilist.begin(), ilist.end()); }
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Due to the way elements are stored, emplace will need to move or copy the key-value once.
|
||||
* The method is equivalent to insert(value_type(std::forward<Args>(args)...));
|
||||
*
|
||||
* Mainly here for compatibility with the std::unordered_map interface.
|
||||
*/
|
||||
template<class... Args>
|
||||
std::pair<iterator, bool> emplace(Args&&... args) { return m_ht.emplace(std::forward<Args>(args)...); }
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Due to the way elements are stored, emplace_hint will need to move or copy the key-value once.
|
||||
* The method is equivalent to insert(hint, value_type(std::forward<Args>(args)...));
|
||||
*
|
||||
* Mainly here for compatibility with the std::unordered_map interface.
|
||||
*/
|
||||
template<class... Args>
|
||||
iterator emplace_hint(const_iterator hint, Args&&... args) {
|
||||
return m_ht.emplace_hint(hint, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
iterator erase(iterator pos) { return m_ht.erase(pos); }
|
||||
iterator erase(const_iterator pos) { return m_ht.erase(pos); }
|
||||
iterator erase(const_iterator first, const_iterator last) { return m_ht.erase(first, last); }
|
||||
size_type erase(const key_type& key) { return m_ht.erase(key); }
|
||||
|
||||
/**
|
||||
* Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same
|
||||
* as hash_function()(key). Usefull to speed-up the lookup to the value if you already have the hash.
|
||||
*/
|
||||
size_type erase(const key_type& key, std::size_t precalculated_hash) {
|
||||
return m_ht.erase(key, precalculated_hash);
|
||||
}
|
||||
|
||||
/**
|
||||
* This overload only participates in the overload resolution if the typedef KeyEqual::is_transparent exists.
|
||||
* If so, K must be hashable and comparable to Key.
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
|
||||
size_type erase(const K& key) { return m_ht.erase(key); }
|
||||
|
||||
/**
|
||||
* @copydoc erase(const K& key)
|
||||
*
|
||||
* Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same
|
||||
* as hash_function()(key). Usefull to speed-up the lookup to the value if you already have the hash.
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
|
||||
size_type erase(const K& key, std::size_t precalculated_hash) {
|
||||
return m_ht.erase(key, precalculated_hash);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void swap(hopscotch_set& other) { other.m_ht.swap(m_ht); }
|
||||
|
||||
|
||||
/*
|
||||
* Lookup
|
||||
*/
|
||||
size_type count(const Key& key) const { return m_ht.count(key); }
|
||||
|
||||
/**
|
||||
* Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same
|
||||
* as hash_function()(key). Usefull to speed-up the lookup if you already have the hash.
|
||||
*/
|
||||
size_type count(const Key& key, std::size_t precalculated_hash) const { return m_ht.count(key, precalculated_hash); }
|
||||
|
||||
/**
|
||||
* This overload only participates in the overload resolution if the typedef KeyEqual::is_transparent exists.
|
||||
* If so, K must be hashable and comparable to Key.
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
|
||||
size_type count(const K& key) const { return m_ht.count(key); }
|
||||
|
||||
/**
|
||||
* @copydoc count(const K& key) const
|
||||
*
|
||||
* Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same
|
||||
* as hash_function()(key). Usefull to speed-up the lookup if you already have the hash.
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
|
||||
size_type count(const K& key, std::size_t precalculated_hash) const { return m_ht.count(key, precalculated_hash); }
|
||||
|
||||
|
||||
|
||||
|
||||
iterator find(const Key& key) { return m_ht.find(key); }
|
||||
|
||||
/**
|
||||
* Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same
|
||||
* as hash_function()(key). Usefull to speed-up the lookup if you already have the hash.
|
||||
*/
|
||||
iterator find(const Key& key, std::size_t precalculated_hash) { return m_ht.find(key, precalculated_hash); }
|
||||
|
||||
const_iterator find(const Key& key) const { return m_ht.find(key); }
|
||||
|
||||
/**
|
||||
* @copydoc find(const Key& key, std::size_t precalculated_hash)
|
||||
*/
|
||||
const_iterator find(const Key& key, std::size_t precalculated_hash) const { return m_ht.find(key, precalculated_hash); }
|
||||
|
||||
/**
|
||||
* This overload only participates in the overload resolution if the typedef KeyEqual::is_transparent exists.
|
||||
* If so, K must be hashable and comparable to Key.
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
|
||||
iterator find(const K& key) { return m_ht.find(key); }
|
||||
|
||||
/**
|
||||
* @copydoc find(const K& key)
|
||||
*
|
||||
* Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same
|
||||
* as hash_function()(key). Usefull to speed-up the lookup if you already have the hash.
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
|
||||
iterator find(const K& key, std::size_t precalculated_hash) { return m_ht.find(key, precalculated_hash); }
|
||||
|
||||
/**
|
||||
* @copydoc find(const K& key)
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
|
||||
const_iterator find(const K& key) const { return m_ht.find(key); }
|
||||
|
||||
/**
|
||||
* @copydoc find(const K& key)
|
||||
*
|
||||
* Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same
|
||||
* as hash_function()(key). Usefull to speed-up the lookup if you already have the hash.
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
|
||||
const_iterator find(const K& key, std::size_t precalculated_hash) const { return m_ht.find(key, precalculated_hash); }
|
||||
|
||||
|
||||
|
||||
|
||||
bool contains(const Key& key) const { return m_ht.contains(key); }
|
||||
|
||||
/**
|
||||
* Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same
|
||||
* as hash_function()(key). Usefull to speed-up the lookup if you already have the hash.
|
||||
*/
|
||||
bool contains(const Key& key, std::size_t precalculated_hash) const {
|
||||
return m_ht.contains(key, precalculated_hash);
|
||||
}
|
||||
|
||||
/**
|
||||
* This overload only participates in the overload resolution if the typedef KeyEqual::is_transparent exists.
|
||||
* If so, K must be hashable and comparable to Key.
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
|
||||
bool contains(const K& key) const { return m_ht.contains(key); }
|
||||
|
||||
/**
|
||||
* @copydoc contains(const K& key) const
|
||||
*
|
||||
* Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same
|
||||
* as hash_function()(key). Usefull to speed-up the lookup if you already have the hash.
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
|
||||
bool contains(const K& key, std::size_t precalculated_hash) const {
|
||||
return m_ht.contains(key, precalculated_hash);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
std::pair<iterator, iterator> equal_range(const Key& key) { return m_ht.equal_range(key); }
|
||||
|
||||
/**
|
||||
* Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same
|
||||
* as hash_function()(key). Usefull to speed-up the lookup if you already have the hash.
|
||||
*/
|
||||
std::pair<iterator, iterator> equal_range(const Key& key, std::size_t precalculated_hash) {
|
||||
return m_ht.equal_range(key, precalculated_hash);
|
||||
}
|
||||
|
||||
std::pair<const_iterator, const_iterator> equal_range(const Key& key) const { return m_ht.equal_range(key); }
|
||||
|
||||
/**
|
||||
* @copydoc equal_range(const Key& key, std::size_t precalculated_hash)
|
||||
*/
|
||||
std::pair<const_iterator, const_iterator> equal_range(const Key& key, std::size_t precalculated_hash) const {
|
||||
return m_ht.equal_range(key, precalculated_hash);
|
||||
}
|
||||
|
||||
/**
|
||||
* This overload only participates in the overload resolution if the typedef KeyEqual::is_transparent exists.
|
||||
* If so, K must be hashable and comparable to Key.
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
|
||||
std::pair<iterator, iterator> equal_range(const K& key) { return m_ht.equal_range(key); }
|
||||
|
||||
/**
|
||||
* @copydoc equal_range(const K& key)
|
||||
*
|
||||
* Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same
|
||||
* as hash_function()(key). Usefull to speed-up the lookup if you already have the hash.
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
|
||||
std::pair<iterator, iterator> equal_range(const K& key, std::size_t precalculated_hash) {
|
||||
return m_ht.equal_range(key, precalculated_hash);
|
||||
}
|
||||
|
||||
/**
|
||||
* @copydoc equal_range(const K& key)
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
|
||||
std::pair<const_iterator, const_iterator> equal_range(const K& key) const { return m_ht.equal_range(key); }
|
||||
|
||||
/**
|
||||
* @copydoc equal_range(const K& key, std::size_t precalculated_hash)
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
|
||||
std::pair<const_iterator, const_iterator> equal_range(const K& key, std::size_t precalculated_hash) const {
|
||||
return m_ht.equal_range(key, precalculated_hash);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Bucket interface
|
||||
*/
|
||||
size_type bucket_count() const { return m_ht.bucket_count(); }
|
||||
size_type max_bucket_count() const { return m_ht.max_bucket_count(); }
|
||||
|
||||
|
||||
/*
|
||||
* Hash policy
|
||||
*/
|
||||
float load_factor() const { return m_ht.load_factor(); }
|
||||
float max_load_factor() const { return m_ht.max_load_factor(); }
|
||||
void max_load_factor(float ml) { m_ht.max_load_factor(ml); }
|
||||
|
||||
void rehash(size_type count_) { m_ht.rehash(count_); }
|
||||
void reserve(size_type count_) { m_ht.reserve(count_); }
|
||||
|
||||
|
||||
/*
|
||||
* Observers
|
||||
*/
|
||||
hasher hash_function() const { return m_ht.hash_function(); }
|
||||
key_equal key_eq() const { return m_ht.key_eq(); }
|
||||
|
||||
|
||||
/*
|
||||
* Other
|
||||
*/
|
||||
|
||||
/**
|
||||
* Convert a const_iterator to an iterator.
|
||||
*/
|
||||
iterator mutable_iterator(const_iterator pos) {
|
||||
return m_ht.mutable_iterator(pos);
|
||||
}
|
||||
|
||||
size_type overflow_size() const noexcept { return m_ht.overflow_size(); }
|
||||
|
||||
friend bool operator==(const hopscotch_set& lhs, const hopscotch_set& rhs) {
|
||||
if(lhs.size() != rhs.size()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for(const auto& element_lhs : lhs) {
|
||||
const auto it_element_rhs = rhs.find(element_lhs);
|
||||
if(it_element_rhs == rhs.cend()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
friend bool operator!=(const hopscotch_set& lhs, const hopscotch_set& rhs) {
|
||||
return !operator==(lhs, rhs);
|
||||
}
|
||||
|
||||
friend void swap(hopscotch_set& lhs, hopscotch_set& rhs) {
|
||||
lhs.swap(rhs);
|
||||
}
|
||||
|
||||
private:
|
||||
ht m_ht;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Same as `tsl::hopscotch_set<Key, Hash, KeyEqual, Allocator, NeighborhoodSize, StoreHash, tsl::hh::prime_growth_policy>`.
|
||||
*/
|
||||
template<class Key,
|
||||
class Hash = std::hash<Key>,
|
||||
class KeyEqual = std::equal_to<Key>,
|
||||
class Allocator = std::allocator<Key>,
|
||||
unsigned int NeighborhoodSize = 62,
|
||||
bool StoreHash = false>
|
||||
using hopscotch_pg_set = hopscotch_set<Key, Hash, KeyEqual, Allocator, NeighborhoodSize, StoreHash, tsl::hh::prime_growth_policy>;
|
||||
|
||||
} // end namespace tsl
|
||||
|
||||
#endif
|
1614
src/includes/3thparty/tsl/ordered_hash.h
Normal file
1614
src/includes/3thparty/tsl/ordered_hash.h
Normal file
File diff suppressed because it is too large
Load Diff
833
src/includes/3thparty/tsl/ordered_map.h
Normal file
833
src/includes/3thparty/tsl/ordered_map.h
Normal file
@ -0,0 +1,833 @@
|
||||
/**
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2017 Tessil
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
#ifndef TSL_ORDERED_MAP_H
|
||||
#define TSL_ORDERED_MAP_H
|
||||
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <deque>
|
||||
#include <functional>
|
||||
#include <initializer_list>
|
||||
#include <memory>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#include "ordered_hash.h"
|
||||
|
||||
|
||||
namespace tsl {
|
||||
|
||||
|
||||
/**
|
||||
* Implementation of an hash map using open adressing with robin hood with backshift delete to resolve collisions.
|
||||
*
|
||||
* The particularity of this hash map is that it remembers the order in which the elements were added and
|
||||
* provide a way to access the structure which stores these values through the 'values_container()' method.
|
||||
* The used container is defined by ValueTypeContainer, by default a std::deque is used (grows faster) but
|
||||
* a std::vector may be used. In this case the map provides a 'data()' method which give a direct access
|
||||
* to the memory used to store the values (which can be usefull to communicate with C API's).
|
||||
*
|
||||
* The Key and T must be copy constructible and/or move constructible. To use `unordered_erase` they both
|
||||
* must be swappable.
|
||||
*
|
||||
* The behaviour of the hash map is undefinded if the destructor of Key or T throws an exception.
|
||||
*
|
||||
* By default the maximum size of a map is limited to 2^32 - 1 values, if needed this can be changed through
|
||||
* the IndexType template parameter. Using an `uint64_t` will raise this limit to 2^64 - 1 values but each
|
||||
* bucket will use 16 bytes instead of 8 bytes in addition to the space needed to store the values.
|
||||
*
|
||||
* Iterators invalidation:
|
||||
* - clear, operator=, reserve, rehash: always invalidate the iterators (also invalidate end()).
|
||||
* - insert, emplace, emplace_hint, operator[]: when a std::vector is used as ValueTypeContainer
|
||||
* and if size() < capacity(), only end().
|
||||
* Otherwise all the iterators are invalidated if an insert occurs.
|
||||
* - erase, unordered_erase: when a std::vector is used as ValueTypeContainer invalidate the iterator of
|
||||
* the erased element and all the ones after the erased element (including end()).
|
||||
* Otherwise all the iterators are invalidated if an erase occurs.
|
||||
*/
|
||||
template<class Key,
|
||||
class T,
|
||||
class Hash = std::hash<Key>,
|
||||
class KeyEqual = std::equal_to<Key>,
|
||||
class Allocator = std::allocator<std::pair<Key, T>>,
|
||||
class ValueTypeContainer = std::deque<std::pair<Key, T>, Allocator>,
|
||||
class IndexType = std::uint_least32_t>
|
||||
class ordered_map {
|
||||
private:
|
||||
template<typename U>
|
||||
using has_is_transparent = tsl::detail_ordered_hash::has_is_transparent<U>;
|
||||
|
||||
class KeySelect {
|
||||
public:
|
||||
using key_type = Key;
|
||||
|
||||
const key_type& operator()(const std::pair<Key, T>& key_value) const noexcept {
|
||||
return key_value.first;
|
||||
}
|
||||
|
||||
key_type& operator()(std::pair<Key, T>& key_value) noexcept {
|
||||
return key_value.first;
|
||||
}
|
||||
};
|
||||
|
||||
class ValueSelect {
|
||||
public:
|
||||
using value_type = T;
|
||||
|
||||
const value_type& operator()(const std::pair<Key, T>& key_value) const noexcept {
|
||||
return key_value.second;
|
||||
}
|
||||
|
||||
value_type& operator()(std::pair<Key, T>& key_value) noexcept {
|
||||
return key_value.second;
|
||||
}
|
||||
};
|
||||
|
||||
using ht = detail_ordered_hash::ordered_hash<std::pair<Key, T>, KeySelect, ValueSelect,
|
||||
Hash, KeyEqual, Allocator, ValueTypeContainer, IndexType>;
|
||||
|
||||
public:
|
||||
using key_type = typename ht::key_type;
|
||||
using mapped_type = T;
|
||||
using value_type = typename ht::value_type;
|
||||
using size_type = typename ht::size_type;
|
||||
using difference_type = typename ht::difference_type;
|
||||
using hasher = typename ht::hasher;
|
||||
using key_equal = typename ht::key_equal;
|
||||
using allocator_type = typename ht::allocator_type;
|
||||
using reference = typename ht::reference;
|
||||
using const_reference = typename ht::const_reference;
|
||||
using pointer = typename ht::pointer;
|
||||
using const_pointer = typename ht::const_pointer;
|
||||
using iterator = typename ht::iterator;
|
||||
using const_iterator = typename ht::const_iterator;
|
||||
using reverse_iterator = typename ht::reverse_iterator;
|
||||
using const_reverse_iterator = typename ht::const_reverse_iterator;
|
||||
|
||||
using values_container_type = typename ht::values_container_type;
|
||||
|
||||
|
||||
/*
|
||||
* Constructors
|
||||
*/
|
||||
ordered_map(): ordered_map(ht::DEFAULT_INIT_BUCKETS_SIZE) {
|
||||
}
|
||||
|
||||
explicit ordered_map(size_type bucket_count,
|
||||
const Hash& hash = Hash(),
|
||||
const KeyEqual& equal = KeyEqual(),
|
||||
const Allocator& alloc = Allocator()):
|
||||
m_ht(bucket_count, hash, equal, alloc, ht::DEFAULT_MAX_LOAD_FACTOR)
|
||||
{
|
||||
}
|
||||
|
||||
ordered_map(size_type bucket_count,
|
||||
const Allocator& alloc): ordered_map(bucket_count, Hash(), KeyEqual(), alloc)
|
||||
{
|
||||
}
|
||||
|
||||
ordered_map(size_type bucket_count,
|
||||
const Hash& hash,
|
||||
const Allocator& alloc): ordered_map(bucket_count, hash, KeyEqual(), alloc)
|
||||
{
|
||||
}
|
||||
|
||||
explicit ordered_map(const Allocator& alloc): ordered_map(ht::DEFAULT_INIT_BUCKETS_SIZE, alloc) {
|
||||
}
|
||||
|
||||
template<class InputIt>
|
||||
ordered_map(InputIt first, InputIt last,
|
||||
size_type bucket_count = ht::DEFAULT_INIT_BUCKETS_SIZE,
|
||||
const Hash& hash = Hash(),
|
||||
const KeyEqual& equal = KeyEqual(),
|
||||
const Allocator& alloc = Allocator()): ordered_map(bucket_count, hash, equal, alloc)
|
||||
{
|
||||
insert(first, last);
|
||||
}
|
||||
|
||||
template<class InputIt>
|
||||
ordered_map(InputIt first, InputIt last,
|
||||
size_type bucket_count,
|
||||
const Allocator& alloc): ordered_map(first, last, bucket_count, Hash(), KeyEqual(), alloc)
|
||||
{
|
||||
}
|
||||
|
||||
template<class InputIt>
|
||||
ordered_map(InputIt first, InputIt last,
|
||||
size_type bucket_count,
|
||||
const Hash& hash,
|
||||
const Allocator& alloc): ordered_map(first, last, bucket_count, hash, KeyEqual(), alloc)
|
||||
{
|
||||
}
|
||||
|
||||
ordered_map(std::initializer_list<value_type> init,
|
||||
size_type bucket_count = ht::DEFAULT_INIT_BUCKETS_SIZE,
|
||||
const Hash& hash = Hash(),
|
||||
const KeyEqual& equal = KeyEqual(),
|
||||
const Allocator& alloc = Allocator()):
|
||||
ordered_map(init.begin(), init.end(), bucket_count, hash, equal, alloc)
|
||||
{
|
||||
}
|
||||
|
||||
ordered_map(std::initializer_list<value_type> init,
|
||||
size_type bucket_count,
|
||||
const Allocator& alloc):
|
||||
ordered_map(init.begin(), init.end(), bucket_count, Hash(), KeyEqual(), alloc)
|
||||
{
|
||||
}
|
||||
|
||||
ordered_map(std::initializer_list<value_type> init,
|
||||
size_type bucket_count,
|
||||
const Hash& hash,
|
||||
const Allocator& alloc):
|
||||
ordered_map(init.begin(), init.end(), bucket_count, hash, KeyEqual(), alloc)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
ordered_map& operator=(std::initializer_list<value_type> ilist) {
|
||||
m_ht.clear();
|
||||
|
||||
m_ht.reserve(ilist.size());
|
||||
m_ht.insert(ilist.begin(), ilist.end());
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
allocator_type get_allocator() const { return m_ht.get_allocator(); }
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Iterators
|
||||
*/
|
||||
iterator begin() noexcept { return m_ht.begin(); }
|
||||
const_iterator begin() const noexcept { return m_ht.begin(); }
|
||||
const_iterator cbegin() const noexcept { return m_ht.cbegin(); }
|
||||
|
||||
iterator end() noexcept { return m_ht.end(); }
|
||||
const_iterator end() const noexcept { return m_ht.end(); }
|
||||
const_iterator cend() const noexcept { return m_ht.cend(); }
|
||||
|
||||
reverse_iterator rbegin() noexcept { return m_ht.rbegin(); }
|
||||
const_reverse_iterator rbegin() const noexcept { return m_ht.rbegin(); }
|
||||
const_reverse_iterator rcbegin() const noexcept { return m_ht.rcbegin(); }
|
||||
|
||||
reverse_iterator rend() noexcept { return m_ht.rend(); }
|
||||
const_reverse_iterator rend() const noexcept { return m_ht.rend(); }
|
||||
const_reverse_iterator rcend() const noexcept { return m_ht.rcend(); }
|
||||
|
||||
|
||||
/*
|
||||
* Capacity
|
||||
*/
|
||||
bool empty() const noexcept { return m_ht.empty(); }
|
||||
size_type size() const noexcept { return m_ht.size(); }
|
||||
size_type max_size() const noexcept { return m_ht.max_size(); }
|
||||
|
||||
/*
|
||||
* Modifiers
|
||||
*/
|
||||
void clear() noexcept { m_ht.clear(); }
|
||||
|
||||
|
||||
|
||||
std::pair<iterator, bool> insert(const value_type& value) { return m_ht.insert(value); }
|
||||
|
||||
template<class P, typename std::enable_if<std::is_constructible<value_type, P&&>::value>::type* = nullptr>
|
||||
std::pair<iterator, bool> insert(P&& value) { return m_ht.emplace(std::forward<P>(value)); }
|
||||
|
||||
std::pair<iterator, bool> insert(value_type&& value) { return m_ht.insert(std::move(value)); }
|
||||
|
||||
|
||||
iterator insert(const_iterator hint, const value_type& value) {
|
||||
return m_ht.insert_hint(hint, value);
|
||||
}
|
||||
|
||||
template<class P, typename std::enable_if<std::is_constructible<value_type, P&&>::value>::type* = nullptr>
|
||||
iterator insert(const_iterator hint, P&& value) {
|
||||
return m_ht.emplace_hint(hint, std::forward<P>(value));
|
||||
}
|
||||
|
||||
iterator insert(const_iterator hint, value_type&& value) {
|
||||
return m_ht.insert_hint(hint, std::move(value));
|
||||
}
|
||||
|
||||
|
||||
template<class InputIt>
|
||||
void insert(InputIt first, InputIt last) { m_ht.insert(first, last); }
|
||||
void insert(std::initializer_list<value_type> ilist) { m_ht.insert(ilist.begin(), ilist.end()); }
|
||||
|
||||
|
||||
|
||||
|
||||
template<class M>
|
||||
std::pair<iterator, bool> insert_or_assign(const key_type& k, M&& obj) {
|
||||
return m_ht.insert_or_assign(k, std::forward<M>(obj));
|
||||
}
|
||||
|
||||
template<class M>
|
||||
std::pair<iterator, bool> insert_or_assign(key_type&& k, M&& obj) {
|
||||
return m_ht.insert_or_assign(std::move(k), std::forward<M>(obj));
|
||||
}
|
||||
|
||||
|
||||
template<class M>
|
||||
iterator insert_or_assign(const_iterator hint, const key_type& k, M&& obj) {
|
||||
return m_ht.insert_or_assign(hint, k, std::forward<M>(obj));
|
||||
}
|
||||
|
||||
template<class M>
|
||||
iterator insert_or_assign(const_iterator hint, key_type&& k, M&& obj) {
|
||||
return m_ht.insert_or_assign(hint, std::move(k), std::forward<M>(obj));
|
||||
}
|
||||
|
||||
/**
|
||||
* Due to the way elements are stored, emplace will need to move or copy the key-value once.
|
||||
* The method is equivalent to insert(value_type(std::forward<Args>(args)...));
|
||||
*
|
||||
* Mainly here for compatibility with the std::unordered_map interface.
|
||||
*/
|
||||
template<class... Args>
|
||||
std::pair<iterator, bool> emplace(Args&&... args) { return m_ht.emplace(std::forward<Args>(args)...); }
|
||||
|
||||
/**
|
||||
* Due to the way elements are stored, emplace_hint will need to move or copy the key-value once.
|
||||
* The method is equivalent to insert(hint, value_type(std::forward<Args>(args)...));
|
||||
*
|
||||
* Mainly here for compatibility with the std::unordered_map interface.
|
||||
*/
|
||||
template <class... Args>
|
||||
iterator emplace_hint(const_iterator hint, Args&&... args) {
|
||||
return m_ht.emplace_hint(hint, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
template<class... Args>
|
||||
std::pair<iterator, bool> try_emplace(const key_type& k, Args&&... args) {
|
||||
return m_ht.try_emplace(k, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template<class... Args>
|
||||
std::pair<iterator, bool> try_emplace(key_type&& k, Args&&... args) {
|
||||
return m_ht.try_emplace(std::move(k), std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template<class... Args>
|
||||
iterator try_emplace(const_iterator hint, const key_type& k, Args&&... args) {
|
||||
return m_ht.try_emplace_hint(hint, k, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template<class... Args>
|
||||
iterator try_emplace(const_iterator hint, key_type&& k, Args&&... args) {
|
||||
return m_ht.try_emplace_hint(hint, std::move(k), std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* When erasing an element, the insert order will be preserved and no holes will be present in the container
|
||||
* returned by 'values_container()'.
|
||||
*
|
||||
* The method is in O(n), if the order is not important 'unordered_erase(...)' method is faster with an O(1)
|
||||
* average complexity.
|
||||
*/
|
||||
iterator erase(iterator pos) { return m_ht.erase(pos); }
|
||||
|
||||
/**
|
||||
* @copydoc erase(iterator pos)
|
||||
*/
|
||||
iterator erase(const_iterator pos) { return m_ht.erase(pos); }
|
||||
|
||||
/**
|
||||
* @copydoc erase(iterator pos)
|
||||
*/
|
||||
iterator erase(const_iterator first, const_iterator last) { return m_ht.erase(first, last); }
|
||||
|
||||
/**
|
||||
* @copydoc erase(iterator pos)
|
||||
*/
|
||||
size_type erase(const key_type& key) { return m_ht.erase(key); }
|
||||
|
||||
/**
|
||||
* @copydoc erase(iterator pos)
|
||||
*
|
||||
* Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same
|
||||
* as hash_function()(key). Usefull to speed-up the lookup to the value if you already have the hash.
|
||||
*/
|
||||
size_type erase(const key_type& key, std::size_t precalculated_hash) {
|
||||
return m_ht.erase(key, precalculated_hash);
|
||||
}
|
||||
|
||||
/**
|
||||
* @copydoc erase(iterator pos)
|
||||
*
|
||||
* This overload only participates in the overload resolution if the typedef KeyEqual::is_transparent exists.
|
||||
* If so, K must be hashable and comparable to Key.
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
|
||||
size_type erase(const K& key) { return m_ht.erase(key); }
|
||||
|
||||
/**
|
||||
* @copydoc erase(const key_type& key, std::size_t precalculated_hash)
|
||||
*
|
||||
* This overload only participates in the overload resolution if the typedef KeyEqual::is_transparent exists.
|
||||
* If so, K must be hashable and comparable to Key.
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
|
||||
size_type erase(const K& key, std::size_t precalculated_hash) {
|
||||
return m_ht.erase(key, precalculated_hash);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void swap(ordered_map& other) { other.m_ht.swap(m_ht); }
|
||||
|
||||
/*
|
||||
* Lookup
|
||||
*/
|
||||
T& at(const Key& key) { return m_ht.at(key); }
|
||||
|
||||
/**
|
||||
* Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same
|
||||
* as hash_function()(key). Usefull to speed-up the lookup if you already have the hash.
|
||||
*/
|
||||
T& at(const Key& key, std::size_t precalculated_hash) { return m_ht.at(key, precalculated_hash); }
|
||||
|
||||
|
||||
const T& at(const Key& key) const { return m_ht.at(key); }
|
||||
|
||||
/**
|
||||
* @copydoc at(const Key& key, std::size_t precalculated_hash)
|
||||
*/
|
||||
const T& at(const Key& key, std::size_t precalculated_hash) const { return m_ht.at(key, precalculated_hash); }
|
||||
|
||||
|
||||
/**
|
||||
* This overload only participates in the overload resolution if the typedef KeyEqual::is_transparent exists.
|
||||
* If so, K must be hashable and comparable to Key.
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
|
||||
T& at(const K& key) { return m_ht.at(key); }
|
||||
|
||||
/**
|
||||
* @copydoc at(const K& key)
|
||||
*
|
||||
* Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same
|
||||
* as hash_function()(key). Usefull to speed-up the lookup if you already have the hash.
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
|
||||
T& at(const K& key, std::size_t precalculated_hash) { return m_ht.at(key, precalculated_hash); }
|
||||
|
||||
/**
|
||||
* @copydoc at(const K& key)
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
|
||||
const T& at(const K& key) const { return m_ht.at(key); }
|
||||
|
||||
/**
|
||||
* @copydoc at(const K& key, std::size_t precalculated_hash)
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
|
||||
const T& at(const K& key, std::size_t precalculated_hash) const { return m_ht.at(key, precalculated_hash); }
|
||||
|
||||
|
||||
|
||||
T& operator[](const Key& key) { return m_ht[key]; }
|
||||
T& operator[](Key&& key) { return m_ht[std::move(key)]; }
|
||||
|
||||
|
||||
|
||||
size_type count(const Key& key) const { return m_ht.count(key); }
|
||||
|
||||
/**
|
||||
* Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same
|
||||
* as hash_function()(key). Usefull to speed-up the lookup if you already have the hash.
|
||||
*/
|
||||
size_type count(const Key& key, std::size_t precalculated_hash) const {
|
||||
return m_ht.count(key, precalculated_hash);
|
||||
}
|
||||
|
||||
/**
|
||||
* This overload only participates in the overload resolution if the typedef KeyEqual::is_transparent exists.
|
||||
* If so, K must be hashable and comparable to Key.
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
|
||||
size_type count(const K& key) const { return m_ht.count(key); }
|
||||
|
||||
/**
|
||||
* @copydoc count(const K& key) const
|
||||
*
|
||||
* Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same
|
||||
* as hash_function()(key). Usefull to speed-up the lookup if you already have the hash.
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
|
||||
size_type count(const K& key, std::size_t precalculated_hash) const {
|
||||
return m_ht.count(key, precalculated_hash);
|
||||
}
|
||||
|
||||
|
||||
|
||||
iterator find(const Key& key) { return m_ht.find(key); }
|
||||
|
||||
/**
|
||||
* Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same
|
||||
* as hash_function()(key). Usefull to speed-up the lookup if you already have the hash.
|
||||
*/
|
||||
iterator find(const Key& key, std::size_t precalculated_hash) { return m_ht.find(key, precalculated_hash); }
|
||||
|
||||
const_iterator find(const Key& key) const { return m_ht.find(key); }
|
||||
|
||||
/**
|
||||
* @copydoc find(const Key& key, std::size_t precalculated_hash)
|
||||
*/
|
||||
const_iterator find(const Key& key, std::size_t precalculated_hash) const {
|
||||
return m_ht.find(key, precalculated_hash);
|
||||
}
|
||||
|
||||
/**
|
||||
* This overload only participates in the overload resolution if the typedef KeyEqual::is_transparent exists.
|
||||
* If so, K must be hashable and comparable to Key.
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
|
||||
iterator find(const K& key) { return m_ht.find(key); }
|
||||
|
||||
/**
|
||||
* @copydoc find(const K& key)
|
||||
*
|
||||
* Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same
|
||||
* as hash_function()(key). Usefull to speed-up the lookup if you already have the hash.
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
|
||||
iterator find(const K& key, std::size_t precalculated_hash) { return m_ht.find(key, precalculated_hash); }
|
||||
|
||||
/**
|
||||
* @copydoc find(const K& key)
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
|
||||
const_iterator find(const K& key) const { return m_ht.find(key); }
|
||||
|
||||
/**
|
||||
* @copydoc find(const K& key)
|
||||
*
|
||||
* Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same
|
||||
* as hash_function()(key). Usefull to speed-up the lookup if you already have the hash.
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
|
||||
const_iterator find(const K& key, std::size_t precalculated_hash) const {
|
||||
return m_ht.find(key, precalculated_hash);
|
||||
}
|
||||
|
||||
|
||||
|
||||
std::pair<iterator, iterator> equal_range(const Key& key) { return m_ht.equal_range(key); }
|
||||
|
||||
/**
|
||||
* Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same
|
||||
* as hash_function()(key). Usefull to speed-up the lookup if you already have the hash.
|
||||
*/
|
||||
std::pair<iterator, iterator> equal_range(const Key& key, std::size_t precalculated_hash) {
|
||||
return m_ht.equal_range(key, precalculated_hash);
|
||||
}
|
||||
|
||||
std::pair<const_iterator, const_iterator> equal_range(const Key& key) const { return m_ht.equal_range(key); }
|
||||
|
||||
/**
|
||||
* @copydoc equal_range(const Key& key, std::size_t precalculated_hash)
|
||||
*/
|
||||
std::pair<const_iterator, const_iterator> equal_range(const Key& key, std::size_t precalculated_hash) const {
|
||||
return m_ht.equal_range(key, precalculated_hash);
|
||||
}
|
||||
|
||||
/**
|
||||
* This overload only participates in the overload resolution if the typedef KeyEqual::is_transparent exists.
|
||||
* If so, K must be hashable and comparable to Key.
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
|
||||
std::pair<iterator, iterator> equal_range(const K& key) { return m_ht.equal_range(key); }
|
||||
|
||||
/**
|
||||
* @copydoc equal_range(const K& key)
|
||||
*
|
||||
* Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same
|
||||
* as hash_function()(key). Usefull to speed-up the lookup if you already have the hash.
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
|
||||
std::pair<iterator, iterator> equal_range(const K& key, std::size_t precalculated_hash) {
|
||||
return m_ht.equal_range(key, precalculated_hash);
|
||||
}
|
||||
|
||||
/**
|
||||
* @copydoc equal_range(const K& key)
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
|
||||
std::pair<const_iterator, const_iterator> equal_range(const K& key) const { return m_ht.equal_range(key); }
|
||||
|
||||
/**
|
||||
* @copydoc equal_range(const K& key, std::size_t precalculated_hash)
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
|
||||
std::pair<const_iterator, const_iterator> equal_range(const K& key, std::size_t precalculated_hash) const {
|
||||
return m_ht.equal_range(key, precalculated_hash);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Bucket interface
|
||||
*/
|
||||
size_type bucket_count() const { return m_ht.bucket_count(); }
|
||||
size_type max_bucket_count() const { return m_ht.max_bucket_count(); }
|
||||
|
||||
|
||||
/*
|
||||
* Hash policy
|
||||
*/
|
||||
float load_factor() const { return m_ht.load_factor(); }
|
||||
float max_load_factor() const { return m_ht.max_load_factor(); }
|
||||
void max_load_factor(float ml) { m_ht.max_load_factor(ml); }
|
||||
|
||||
void rehash(size_type count) { m_ht.rehash(count); }
|
||||
void reserve(size_type count) { m_ht.reserve(count); }
|
||||
|
||||
|
||||
/*
|
||||
* Observers
|
||||
*/
|
||||
hasher hash_function() const { return m_ht.hash_function(); }
|
||||
key_equal key_eq() const { return m_ht.key_eq(); }
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Other
|
||||
*/
|
||||
|
||||
/**
|
||||
* Convert a const_iterator to an iterator.
|
||||
*/
|
||||
iterator mutable_iterator(const_iterator pos) {
|
||||
return m_ht.mutable_iterator(pos);
|
||||
}
|
||||
|
||||
/**
|
||||
* Requires index <= size().
|
||||
*
|
||||
* Return an iterator to the element at index. Return end() if index == size().
|
||||
*/
|
||||
iterator nth(size_type index) { return m_ht.nth(index); }
|
||||
|
||||
/**
|
||||
* @copydoc nth(size_type index)
|
||||
*/
|
||||
const_iterator nth(size_type index) const { return m_ht.nth(index); }
|
||||
|
||||
|
||||
/**
|
||||
* Return const_reference to the first element. Requires the container to not be empty.
|
||||
*/
|
||||
const_reference front() const { return m_ht.front(); }
|
||||
|
||||
/**
|
||||
* Return const_reference to the last element. Requires the container to not be empty.
|
||||
*/
|
||||
const_reference back() const { return m_ht.back(); }
|
||||
|
||||
|
||||
/**
|
||||
* Only available if ValueTypeContainer is a std::vector. Same as calling 'values_container().data()'.
|
||||
*/
|
||||
template<class U = values_container_type, typename std::enable_if<tsl::detail_ordered_hash::is_vector<U>::value>::type* = nullptr>
|
||||
const typename values_container_type::value_type* data() const noexcept { return m_ht.data(); }
|
||||
|
||||
/**
|
||||
* Return the container in which the values are stored. The values are in the same order as the insertion order
|
||||
* and are contiguous in the structure, no holes (size() == values_container().size()).
|
||||
*/
|
||||
const values_container_type& values_container() const noexcept { return m_ht.values_container(); }
|
||||
|
||||
template<class U = values_container_type, typename std::enable_if<tsl::detail_ordered_hash::is_vector<U>::value>::type* = nullptr>
|
||||
size_type capacity() const noexcept { return m_ht.capacity(); }
|
||||
|
||||
void shrink_to_fit() { m_ht.shrink_to_fit(); }
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Insert the value before pos shifting all the elements on the right of pos (including pos) one position
|
||||
* to the right.
|
||||
*
|
||||
* Amortized linear time-complexity in the distance between pos and end().
|
||||
*/
|
||||
std::pair<iterator, bool> insert_at_position(const_iterator pos, const value_type& value) {
|
||||
return m_ht.insert_at_position(pos, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @copydoc insert_at_position(const_iterator pos, const value_type& value)
|
||||
*/
|
||||
std::pair<iterator, bool> insert_at_position(const_iterator pos, value_type&& value) {
|
||||
return m_ht.insert_at_position(pos, std::move(value));
|
||||
}
|
||||
|
||||
/**
|
||||
* @copydoc insert_at_position(const_iterator pos, const value_type& value)
|
||||
*
|
||||
* Same as insert_at_position(pos, value_type(std::forward<Args>(args)...), mainly
|
||||
* here for coherence.
|
||||
*/
|
||||
template<class... Args>
|
||||
std::pair<iterator, bool> emplace_at_position(const_iterator pos, Args&&... args) {
|
||||
return m_ht.emplace_at_position(pos, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
/**
|
||||
* @copydoc insert_at_position(const_iterator pos, const value_type& value)
|
||||
*/
|
||||
template<class... Args>
|
||||
std::pair<iterator, bool> try_emplace_at_position(const_iterator pos, const key_type& k, Args&&... args) {
|
||||
return m_ht.try_emplace_at_position(pos, k, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
/**
|
||||
* @copydoc insert_at_position(const_iterator pos, const value_type& value)
|
||||
*/
|
||||
template<class... Args>
|
||||
std::pair<iterator, bool> try_emplace_at_position(const_iterator pos, key_type&& k, Args&&... args) {
|
||||
return m_ht.try_emplace_at_position(pos, std::move(k), std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void pop_back() { m_ht.pop_back(); }
|
||||
|
||||
/**
|
||||
* Faster erase operation with an O(1) average complexity but it doesn't preserve the insertion order.
|
||||
*
|
||||
* If an erasure occurs, the last element of the map will take the place of the erased element.
|
||||
*/
|
||||
iterator unordered_erase(iterator pos) { return m_ht.unordered_erase(pos); }
|
||||
|
||||
/**
|
||||
* @copydoc unordered_erase(iterator pos)
|
||||
*/
|
||||
iterator unordered_erase(const_iterator pos) { return m_ht.unordered_erase(pos); }
|
||||
|
||||
/**
|
||||
* @copydoc unordered_erase(iterator pos)
|
||||
*/
|
||||
size_type unordered_erase(const key_type& key) { return m_ht.unordered_erase(key); }
|
||||
|
||||
/**
|
||||
* @copydoc unordered_erase(iterator pos)
|
||||
*
|
||||
* Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same
|
||||
* as hash_function()(key). Usefull to speed-up the lookup if you already have the hash.
|
||||
*/
|
||||
size_type unordered_erase(const key_type& key, std::size_t precalculated_hash) {
|
||||
return m_ht.unordered_erase(key, precalculated_hash);
|
||||
}
|
||||
|
||||
/**
|
||||
* @copydoc unordered_erase(iterator pos)
|
||||
*
|
||||
* This overload only participates in the overload resolution if the typedef KeyEqual::is_transparent exists.
|
||||
* If so, K must be hashable and comparable to Key.
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
|
||||
size_type unordered_erase(const K& key) { return m_ht.unordered_erase(key); }
|
||||
|
||||
/**
|
||||
* @copydoc unordered_erase(const K& key)
|
||||
*
|
||||
* Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same
|
||||
* as hash_function()(key). Usefull to speed-up the lookup if you already have the hash.
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
|
||||
size_type unordered_erase(const K& key, std::size_t precalculated_hash) {
|
||||
return m_ht.unordered_erase(key, precalculated_hash);
|
||||
}
|
||||
|
||||
/**
|
||||
* Serialize the map through the `serializer` parameter.
|
||||
*
|
||||
* The `serializer` parameter must be a function object that supports the following call:
|
||||
* - `template<typename U> void operator()(const U& value);` where the types `std::uint64_t`, `float` and `std::pair<Key, T>` must be supported for U.
|
||||
*
|
||||
* The implementation leaves binary compatibilty (endianness, IEEE 754 for floats, ...) of the types it serializes
|
||||
* in the hands of the `Serializer` function object if compatibilty is required.
|
||||
*/
|
||||
template<class Serializer>
|
||||
void serialize(Serializer& serializer) const {
|
||||
m_ht.serialize(serializer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deserialize a previouly serialized map through the `deserializer` parameter.
|
||||
*
|
||||
* The `deserializer` parameter must be a function object that supports the following calls:
|
||||
* - `template<typename U> U operator()();` where the types `std::uint64_t`, `float` and `std::pair<Key, T>` must be supported for U.
|
||||
*
|
||||
* If the deserialized hash map type is hash compatible with the serialized map, the deserialization process can be
|
||||
* sped up by setting `hash_compatible` to true. To be hash compatible, the Hash and KeyEqual must behave the same way
|
||||
* than the ones used on the serialized map. The `std::size_t` must also be of the same size as the one on the platform used
|
||||
* to serialize the map, the same apply for `IndexType`. If these criteria are not met, the behaviour is undefined with
|
||||
* `hash_compatible` sets to true.
|
||||
*
|
||||
* The behaviour is undefined if the type `Key` and `T` of the `ordered_map` are not the same as the
|
||||
* types used during serialization.
|
||||
*
|
||||
* The implementation leaves binary compatibilty (endianness, IEEE 754 for floats, size of int, ...) of the types it
|
||||
* deserializes in the hands of the `Deserializer` function object if compatibilty is required.
|
||||
*/
|
||||
template<class Deserializer>
|
||||
static ordered_map deserialize(Deserializer& deserializer, bool hash_compatible = false) {
|
||||
ordered_map map(0);
|
||||
map.m_ht.deserialize(deserializer, hash_compatible);
|
||||
|
||||
return map;
|
||||
}
|
||||
|
||||
|
||||
|
||||
friend bool operator==(const ordered_map& lhs, const ordered_map& rhs) { return lhs.m_ht == rhs.m_ht; }
|
||||
friend bool operator!=(const ordered_map& lhs, const ordered_map& rhs) { return lhs.m_ht != rhs.m_ht; }
|
||||
friend bool operator<(const ordered_map& lhs, const ordered_map& rhs) { return lhs.m_ht < rhs.m_ht; }
|
||||
friend bool operator<=(const ordered_map& lhs, const ordered_map& rhs) { return lhs.m_ht <= rhs.m_ht; }
|
||||
friend bool operator>(const ordered_map& lhs, const ordered_map& rhs) { return lhs.m_ht > rhs.m_ht; }
|
||||
friend bool operator>=(const ordered_map& lhs, const ordered_map& rhs) { return lhs.m_ht >= rhs.m_ht; }
|
||||
|
||||
friend void swap(ordered_map& lhs, ordered_map& rhs) { lhs.swap(rhs); }
|
||||
|
||||
private:
|
||||
ht m_ht;
|
||||
};
|
||||
|
||||
} // end namespace tsl
|
||||
|
||||
#endif
|
688
src/includes/3thparty/tsl/ordered_set.h
Normal file
688
src/includes/3thparty/tsl/ordered_set.h
Normal file
@ -0,0 +1,688 @@
|
||||
/**
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2017 Tessil
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
#ifndef TSL_ORDERED_SET_H
|
||||
#define TSL_ORDERED_SET_H
|
||||
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <deque>
|
||||
#include <functional>
|
||||
#include <initializer_list>
|
||||
#include <memory>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#include "ordered_hash.h"
|
||||
|
||||
|
||||
namespace tsl {
|
||||
|
||||
|
||||
/**
|
||||
* Implementation of an hash set using open adressing with robin hood with backshift delete to resolve collisions.
|
||||
*
|
||||
* The particularity of this hash set is that it remembers the order in which the elements were added and
|
||||
* provide a way to access the structure which stores these values through the 'values_container()' method.
|
||||
* The used container is defined by ValueTypeContainer, by default a std::deque is used (grows faster) but
|
||||
* a std::vector may be used. In this case the set provides a 'data()' method which give a direct access
|
||||
* to the memory used to store the values (which can be usefull to communicate with C API's).
|
||||
*
|
||||
* The Key must be copy constructible and/or move constructible. To use `unordered_erase` it also must be swappable.
|
||||
*
|
||||
* The behaviour of the hash set is undefinded if the destructor of Key throws an exception.
|
||||
*
|
||||
* By default the maximum size of a set is limited to 2^32 - 1 values, if needed this can be changed through
|
||||
* the IndexType template parameter. Using an `uint64_t` will raise this limit to 2^64 - 1 values but each
|
||||
* bucket will use 16 bytes instead of 8 bytes in addition to the space needed to store the values.
|
||||
*
|
||||
* Iterators invalidation:
|
||||
* - clear, operator=, reserve, rehash: always invalidate the iterators (also invalidate end()).
|
||||
* - insert, emplace, emplace_hint, operator[]: when a std::vector is used as ValueTypeContainer
|
||||
* and if size() < capacity(), only end().
|
||||
* Otherwise all the iterators are invalidated if an insert occurs.
|
||||
* - erase, unordered_erase: when a std::vector is used as ValueTypeContainer invalidate the iterator of
|
||||
* the erased element and all the ones after the erased element (including end()).
|
||||
* Otherwise all the iterators are invalidated if an erase occurs.
|
||||
*/
|
||||
template<class Key,
|
||||
class Hash = std::hash<Key>,
|
||||
class KeyEqual = std::equal_to<Key>,
|
||||
class Allocator = std::allocator<Key>,
|
||||
class ValueTypeContainer = std::deque<Key, Allocator>,
|
||||
class IndexType = std::uint_least32_t>
|
||||
class ordered_set {
|
||||
private:
|
||||
template<typename U>
|
||||
using has_is_transparent = tsl::detail_ordered_hash::has_is_transparent<U>;
|
||||
|
||||
class KeySelect {
|
||||
public:
|
||||
using key_type = Key;
|
||||
|
||||
const key_type& operator()(const Key& key) const noexcept {
|
||||
return key;
|
||||
}
|
||||
|
||||
key_type& operator()(Key& key) noexcept {
|
||||
return key;
|
||||
}
|
||||
};
|
||||
|
||||
using ht = detail_ordered_hash::ordered_hash<Key, KeySelect, void,
|
||||
Hash, KeyEqual, Allocator, ValueTypeContainer, IndexType>;
|
||||
|
||||
public:
|
||||
using key_type = typename ht::key_type;
|
||||
using value_type = typename ht::value_type;
|
||||
using size_type = typename ht::size_type;
|
||||
using difference_type = typename ht::difference_type;
|
||||
using hasher = typename ht::hasher;
|
||||
using key_equal = typename ht::key_equal;
|
||||
using allocator_type = typename ht::allocator_type;
|
||||
using reference = typename ht::reference;
|
||||
using const_reference = typename ht::const_reference;
|
||||
using pointer = typename ht::pointer;
|
||||
using const_pointer = typename ht::const_pointer;
|
||||
using iterator = typename ht::iterator;
|
||||
using const_iterator = typename ht::const_iterator;
|
||||
using reverse_iterator = typename ht::reverse_iterator;
|
||||
using const_reverse_iterator = typename ht::const_reverse_iterator;
|
||||
|
||||
using values_container_type = typename ht::values_container_type;
|
||||
|
||||
|
||||
/*
|
||||
* Constructors
|
||||
*/
|
||||
ordered_set(): ordered_set(ht::DEFAULT_INIT_BUCKETS_SIZE) {
|
||||
}
|
||||
|
||||
explicit ordered_set(size_type bucket_count,
|
||||
const Hash& hash = Hash(),
|
||||
const KeyEqual& equal = KeyEqual(),
|
||||
const Allocator& alloc = Allocator()):
|
||||
m_ht(bucket_count, hash, equal, alloc, ht::DEFAULT_MAX_LOAD_FACTOR)
|
||||
{
|
||||
}
|
||||
|
||||
ordered_set(size_type bucket_count,
|
||||
const Allocator& alloc): ordered_set(bucket_count, Hash(), KeyEqual(), alloc)
|
||||
{
|
||||
}
|
||||
|
||||
ordered_set(size_type bucket_count,
|
||||
const Hash& hash,
|
||||
const Allocator& alloc): ordered_set(bucket_count, hash, KeyEqual(), alloc)
|
||||
{
|
||||
}
|
||||
|
||||
explicit ordered_set(const Allocator& alloc): ordered_set(ht::DEFAULT_INIT_BUCKETS_SIZE, alloc) {
|
||||
}
|
||||
|
||||
template<class InputIt>
|
||||
ordered_set(InputIt first, InputIt last,
|
||||
size_type bucket_count = ht::DEFAULT_INIT_BUCKETS_SIZE,
|
||||
const Hash& hash = Hash(),
|
||||
const KeyEqual& equal = KeyEqual(),
|
||||
const Allocator& alloc = Allocator()): ordered_set(bucket_count, hash, equal, alloc)
|
||||
{
|
||||
insert(first, last);
|
||||
}
|
||||
|
||||
template<class InputIt>
|
||||
ordered_set(InputIt first, InputIt last,
|
||||
size_type bucket_count,
|
||||
const Allocator& alloc): ordered_set(first, last, bucket_count, Hash(), KeyEqual(), alloc)
|
||||
{
|
||||
}
|
||||
|
||||
template<class InputIt>
|
||||
ordered_set(InputIt first, InputIt last,
|
||||
size_type bucket_count,
|
||||
const Hash& hash,
|
||||
const Allocator& alloc): ordered_set(first, last, bucket_count, hash, KeyEqual(), alloc)
|
||||
{
|
||||
}
|
||||
|
||||
ordered_set(std::initializer_list<value_type> init,
|
||||
size_type bucket_count = ht::DEFAULT_INIT_BUCKETS_SIZE,
|
||||
const Hash& hash = Hash(),
|
||||
const KeyEqual& equal = KeyEqual(),
|
||||
const Allocator& alloc = Allocator()):
|
||||
ordered_set(init.begin(), init.end(), bucket_count, hash, equal, alloc)
|
||||
{
|
||||
}
|
||||
|
||||
ordered_set(std::initializer_list<value_type> init,
|
||||
size_type bucket_count,
|
||||
const Allocator& alloc):
|
||||
ordered_set(init.begin(), init.end(), bucket_count, Hash(), KeyEqual(), alloc)
|
||||
{
|
||||
}
|
||||
|
||||
ordered_set(std::initializer_list<value_type> init,
|
||||
size_type bucket_count,
|
||||
const Hash& hash,
|
||||
const Allocator& alloc):
|
||||
ordered_set(init.begin(), init.end(), bucket_count, hash, KeyEqual(), alloc)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
ordered_set& operator=(std::initializer_list<value_type> ilist) {
|
||||
m_ht.clear();
|
||||
|
||||
m_ht.reserve(ilist.size());
|
||||
m_ht.insert(ilist.begin(), ilist.end());
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
allocator_type get_allocator() const { return m_ht.get_allocator(); }
|
||||
|
||||
|
||||
/*
|
||||
* Iterators
|
||||
*/
|
||||
iterator begin() noexcept { return m_ht.begin(); }
|
||||
const_iterator begin() const noexcept { return m_ht.begin(); }
|
||||
const_iterator cbegin() const noexcept { return m_ht.cbegin(); }
|
||||
|
||||
iterator end() noexcept { return m_ht.end(); }
|
||||
const_iterator end() const noexcept { return m_ht.end(); }
|
||||
const_iterator cend() const noexcept { return m_ht.cend(); }
|
||||
|
||||
reverse_iterator rbegin() noexcept { return m_ht.rbegin(); }
|
||||
const_reverse_iterator rbegin() const noexcept { return m_ht.rbegin(); }
|
||||
const_reverse_iterator rcbegin() const noexcept { return m_ht.rcbegin(); }
|
||||
|
||||
reverse_iterator rend() noexcept { return m_ht.rend(); }
|
||||
const_reverse_iterator rend() const noexcept { return m_ht.rend(); }
|
||||
const_reverse_iterator rcend() const noexcept { return m_ht.rcend(); }
|
||||
|
||||
|
||||
/*
|
||||
* Capacity
|
||||
*/
|
||||
bool empty() const noexcept { return m_ht.empty(); }
|
||||
size_type size() const noexcept { return m_ht.size(); }
|
||||
size_type max_size() const noexcept { return m_ht.max_size(); }
|
||||
|
||||
/*
|
||||
* Modifiers
|
||||
*/
|
||||
void clear() noexcept { m_ht.clear(); }
|
||||
|
||||
|
||||
|
||||
std::pair<iterator, bool> insert(const value_type& value) { return m_ht.insert(value); }
|
||||
std::pair<iterator, bool> insert(value_type&& value) { return m_ht.insert(std::move(value)); }
|
||||
|
||||
iterator insert(const_iterator hint, const value_type& value) {
|
||||
return m_ht.insert_hint(hint, value);
|
||||
}
|
||||
|
||||
iterator insert(const_iterator hint, value_type&& value) {
|
||||
return m_ht.insert_hint(hint, std::move(value));
|
||||
}
|
||||
|
||||
template<class InputIt>
|
||||
void insert(InputIt first, InputIt last) { m_ht.insert(first, last); }
|
||||
void insert(std::initializer_list<value_type> ilist) { m_ht.insert(ilist.begin(), ilist.end()); }
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Due to the way elements are stored, emplace will need to move or copy the key-value once.
|
||||
* The method is equivalent to insert(value_type(std::forward<Args>(args)...));
|
||||
*
|
||||
* Mainly here for compatibility with the std::unordered_map interface.
|
||||
*/
|
||||
template<class... Args>
|
||||
std::pair<iterator, bool> emplace(Args&&... args) { return m_ht.emplace(std::forward<Args>(args)...); }
|
||||
|
||||
/**
|
||||
* Due to the way elements are stored, emplace_hint will need to move or copy the key-value once.
|
||||
* The method is equivalent to insert(hint, value_type(std::forward<Args>(args)...));
|
||||
*
|
||||
* Mainly here for compatibility with the std::unordered_map interface.
|
||||
*/
|
||||
template<class... Args>
|
||||
iterator emplace_hint(const_iterator hint, Args&&... args) {
|
||||
return m_ht.emplace_hint(hint, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
/**
|
||||
* When erasing an element, the insert order will be preserved and no holes will be present in the container
|
||||
* returned by 'values_container()'.
|
||||
*
|
||||
* The method is in O(n), if the order is not important 'unordered_erase(...)' method is faster with an O(1)
|
||||
* average complexity.
|
||||
*/
|
||||
iterator erase(iterator pos) { return m_ht.erase(pos); }
|
||||
|
||||
/**
|
||||
* @copydoc erase(iterator pos)
|
||||
*/
|
||||
iterator erase(const_iterator pos) { return m_ht.erase(pos); }
|
||||
|
||||
/**
|
||||
* @copydoc erase(iterator pos)
|
||||
*/
|
||||
iterator erase(const_iterator first, const_iterator last) { return m_ht.erase(first, last); }
|
||||
|
||||
/**
|
||||
* @copydoc erase(iterator pos)
|
||||
*/
|
||||
size_type erase(const key_type& key) { return m_ht.erase(key); }
|
||||
|
||||
/**
|
||||
* @copydoc erase(iterator pos)
|
||||
*
|
||||
* Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same
|
||||
* as hash_function()(key). Usefull to speed-up the lookup to the value if you already have the hash.
|
||||
*/
|
||||
size_type erase(const key_type& key, std::size_t precalculated_hash) {
|
||||
return m_ht.erase(key, precalculated_hash);
|
||||
}
|
||||
|
||||
/**
|
||||
* @copydoc erase(iterator pos)
|
||||
*
|
||||
* This overload only participates in the overload resolution if the typedef KeyEqual::is_transparent exists.
|
||||
* If so, K must be hashable and comparable to Key.
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
|
||||
size_type erase(const K& key) { return m_ht.erase(key); }
|
||||
|
||||
/**
|
||||
* @copydoc erase(const key_type& key, std::size_t precalculated_hash)
|
||||
*
|
||||
* This overload only participates in the overload resolution if the typedef KeyEqual::is_transparent exists.
|
||||
* If so, K must be hashable and comparable to Key.
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
|
||||
size_type erase(const K& key, std::size_t precalculated_hash) {
|
||||
return m_ht.erase(key, precalculated_hash);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void swap(ordered_set& other) { other.m_ht.swap(m_ht); }
|
||||
|
||||
/*
|
||||
* Lookup
|
||||
*/
|
||||
size_type count(const Key& key) const { return m_ht.count(key); }
|
||||
|
||||
/**
|
||||
* Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same
|
||||
* as hash_function()(key). Usefull to speed-up the lookup if you already have the hash.
|
||||
*/
|
||||
size_type count(const Key& key, std::size_t precalculated_hash) const {
|
||||
return m_ht.count(key, precalculated_hash);
|
||||
}
|
||||
|
||||
/**
|
||||
* This overload only participates in the overload resolution if the typedef KeyEqual::is_transparent exists.
|
||||
* If so, K must be hashable and comparable to Key.
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
|
||||
size_type count(const K& key) const { return m_ht.count(key); }
|
||||
|
||||
/**
|
||||
* @copydoc count(const K& key) const
|
||||
*
|
||||
* Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same
|
||||
* as hash_function()(key). Usefull to speed-up the lookup if you already have the hash.
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
|
||||
size_type count(const K& key, std::size_t precalculated_hash) const {
|
||||
return m_ht.count(key, precalculated_hash);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
iterator find(const Key& key) { return m_ht.find(key); }
|
||||
|
||||
/**
|
||||
* Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same
|
||||
* as hash_function()(key). Usefull to speed-up the lookup if you already have the hash.
|
||||
*/
|
||||
iterator find(const Key& key, std::size_t precalculated_hash) { return m_ht.find(key, precalculated_hash); }
|
||||
|
||||
const_iterator find(const Key& key) const { return m_ht.find(key); }
|
||||
|
||||
/**
|
||||
* @copydoc find(const Key& key, std::size_t precalculated_hash)
|
||||
*/
|
||||
const_iterator find(const Key& key, std::size_t precalculated_hash) const {
|
||||
return m_ht.find(key, precalculated_hash);
|
||||
}
|
||||
|
||||
/**
|
||||
* This overload only participates in the overload resolution if the typedef KeyEqual::is_transparent exists.
|
||||
* If so, K must be hashable and comparable to Key.
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
|
||||
iterator find(const K& key) { return m_ht.find(key); }
|
||||
|
||||
/**
|
||||
* @copydoc find(const K& key)
|
||||
*
|
||||
* Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same
|
||||
* as hash_function()(key). Usefull to speed-up the lookup if you already have the hash.
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
|
||||
iterator find(const K& key, std::size_t precalculated_hash) { return m_ht.find(key, precalculated_hash); }
|
||||
|
||||
/**
|
||||
* @copydoc find(const K& key)
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
|
||||
const_iterator find(const K& key) const { return m_ht.find(key); }
|
||||
|
||||
/**
|
||||
* @copydoc find(const K& key)
|
||||
*
|
||||
* Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same
|
||||
* as hash_function()(key). Usefull to speed-up the lookup if you already have the hash.
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
|
||||
const_iterator find(const K& key, std::size_t precalculated_hash) const {
|
||||
return m_ht.find(key, precalculated_hash);
|
||||
}
|
||||
|
||||
|
||||
|
||||
std::pair<iterator, iterator> equal_range(const Key& key) { return m_ht.equal_range(key); }
|
||||
|
||||
/**
|
||||
* Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same
|
||||
* as hash_function()(key). Usefull to speed-up the lookup if you already have the hash.
|
||||
*/
|
||||
std::pair<iterator, iterator> equal_range(const Key& key, std::size_t precalculated_hash) {
|
||||
return m_ht.equal_range(key, precalculated_hash);
|
||||
}
|
||||
|
||||
std::pair<const_iterator, const_iterator> equal_range(const Key& key) const { return m_ht.equal_range(key); }
|
||||
|
||||
/**
|
||||
* @copydoc equal_range(const Key& key, std::size_t precalculated_hash)
|
||||
*/
|
||||
std::pair<const_iterator, const_iterator> equal_range(const Key& key, std::size_t precalculated_hash) const {
|
||||
return m_ht.equal_range(key, precalculated_hash);
|
||||
}
|
||||
|
||||
/**
|
||||
* This overload only participates in the overload resolution if the typedef KeyEqual::is_transparent exists.
|
||||
* If so, K must be hashable and comparable to Key.
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
|
||||
std::pair<iterator, iterator> equal_range(const K& key) { return m_ht.equal_range(key); }
|
||||
|
||||
/**
|
||||
* @copydoc equal_range(const K& key)
|
||||
*
|
||||
* Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same
|
||||
* as hash_function()(key). Usefull to speed-up the lookup if you already have the hash.
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
|
||||
std::pair<iterator, iterator> equal_range(const K& key, std::size_t precalculated_hash) {
|
||||
return m_ht.equal_range(key, precalculated_hash);
|
||||
}
|
||||
|
||||
/**
|
||||
* @copydoc equal_range(const K& key)
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
|
||||
std::pair<const_iterator, const_iterator> equal_range(const K& key) const { return m_ht.equal_range(key); }
|
||||
|
||||
/**
|
||||
* @copydoc equal_range(const K& key, std::size_t precalculated_hash)
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
|
||||
std::pair<const_iterator, const_iterator> equal_range(const K& key, std::size_t precalculated_hash) const {
|
||||
return m_ht.equal_range(key, precalculated_hash);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Bucket interface
|
||||
*/
|
||||
size_type bucket_count() const { return m_ht.bucket_count(); }
|
||||
size_type max_bucket_count() const { return m_ht.max_bucket_count(); }
|
||||
|
||||
|
||||
/*
|
||||
* Hash policy
|
||||
*/
|
||||
float load_factor() const { return m_ht.load_factor(); }
|
||||
float max_load_factor() const { return m_ht.max_load_factor(); }
|
||||
void max_load_factor(float ml) { m_ht.max_load_factor(ml); }
|
||||
|
||||
void rehash(size_type count) { m_ht.rehash(count); }
|
||||
void reserve(size_type count) { m_ht.reserve(count); }
|
||||
|
||||
|
||||
/*
|
||||
* Observers
|
||||
*/
|
||||
hasher hash_function() const { return m_ht.hash_function(); }
|
||||
key_equal key_eq() const { return m_ht.key_eq(); }
|
||||
|
||||
|
||||
/*
|
||||
* Other
|
||||
*/
|
||||
|
||||
/**
|
||||
* Convert a const_iterator to an iterator.
|
||||
*/
|
||||
iterator mutable_iterator(const_iterator pos) {
|
||||
return m_ht.mutable_iterator(pos);
|
||||
}
|
||||
|
||||
/**
|
||||
* Requires index <= size().
|
||||
*
|
||||
* Return an iterator to the element at index. Return end() if index == size().
|
||||
*/
|
||||
iterator nth(size_type index) { return m_ht.nth(index); }
|
||||
|
||||
/**
|
||||
* @copydoc nth(size_type index)
|
||||
*/
|
||||
const_iterator nth(size_type index) const { return m_ht.nth(index); }
|
||||
|
||||
|
||||
/**
|
||||
* Return const_reference to the first element. Requires the container to not be empty.
|
||||
*/
|
||||
const_reference front() const { return m_ht.front(); }
|
||||
|
||||
/**
|
||||
* Return const_reference to the last element. Requires the container to not be empty.
|
||||
*/
|
||||
const_reference back() const { return m_ht.back(); }
|
||||
|
||||
|
||||
/**
|
||||
* Only available if ValueTypeContainer is a std::vector. Same as calling 'values_container().data()'.
|
||||
*/
|
||||
template<class U = values_container_type, typename std::enable_if<tsl::detail_ordered_hash::is_vector<U>::value>::type* = nullptr>
|
||||
const typename values_container_type::value_type* data() const noexcept { return m_ht.data(); }
|
||||
|
||||
/**
|
||||
* Return the container in which the values are stored. The values are in the same order as the insertion order
|
||||
* and are contiguous in the structure, no holes (size() == values_container().size()).
|
||||
*/
|
||||
const values_container_type& values_container() const noexcept { return m_ht.values_container(); }
|
||||
|
||||
template<class U = values_container_type, typename std::enable_if<tsl::detail_ordered_hash::is_vector<U>::value>::type* = nullptr>
|
||||
size_type capacity() const noexcept { return m_ht.capacity(); }
|
||||
|
||||
void shrink_to_fit() { m_ht.shrink_to_fit(); }
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Insert the value before pos shifting all the elements on the right of pos (including pos) one position
|
||||
* to the right.
|
||||
*
|
||||
* Amortized linear time-complexity in the distance between pos and end().
|
||||
*/
|
||||
std::pair<iterator, bool> insert_at_position(const_iterator pos, const value_type& value) {
|
||||
return m_ht.insert_at_position(pos, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @copydoc insert_at_position(const_iterator pos, const value_type& value)
|
||||
*/
|
||||
std::pair<iterator, bool> insert_at_position(const_iterator pos, value_type&& value) {
|
||||
return m_ht.insert_at_position(pos, std::move(value));
|
||||
}
|
||||
|
||||
/**
|
||||
* @copydoc insert_at_position(const_iterator pos, const value_type& value)
|
||||
*
|
||||
* Same as insert_at_position(pos, value_type(std::forward<Args>(args)...), mainly
|
||||
* here for coherence.
|
||||
*/
|
||||
template<class... Args>
|
||||
std::pair<iterator, bool> emplace_at_position(const_iterator pos, Args&&... args) {
|
||||
return m_ht.emplace_at_position(pos, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void pop_back() { m_ht.pop_back(); }
|
||||
|
||||
/**
|
||||
* Faster erase operation with an O(1) average complexity but it doesn't preserve the insertion order.
|
||||
*
|
||||
* If an erasure occurs, the last element of the map will take the place of the erased element.
|
||||
*/
|
||||
iterator unordered_erase(iterator pos) { return m_ht.unordered_erase(pos); }
|
||||
|
||||
/**
|
||||
* @copydoc unordered_erase(iterator pos)
|
||||
*/
|
||||
iterator unordered_erase(const_iterator pos) { return m_ht.unordered_erase(pos); }
|
||||
|
||||
/**
|
||||
* @copydoc unordered_erase(iterator pos)
|
||||
*/
|
||||
size_type unordered_erase(const key_type& key) { return m_ht.unordered_erase(key); }
|
||||
|
||||
/**
|
||||
* @copydoc unordered_erase(iterator pos)
|
||||
*
|
||||
* Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same
|
||||
* as hash_function()(key). Usefull to speed-up the lookup if you already have the hash.
|
||||
*/
|
||||
size_type unordered_erase(const key_type& key, std::size_t precalculated_hash) {
|
||||
return m_ht.unordered_erase(key, precalculated_hash);
|
||||
}
|
||||
|
||||
/**
|
||||
* @copydoc unordered_erase(iterator pos)
|
||||
*
|
||||
* This overload only participates in the overload resolution if the typedef KeyEqual::is_transparent exists.
|
||||
* If so, K must be hashable and comparable to Key.
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
|
||||
size_type unordered_erase(const K& key) { return m_ht.unordered_erase(key); }
|
||||
|
||||
/**
|
||||
* @copydoc unordered_erase(const K& key)
|
||||
*
|
||||
* Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same
|
||||
* as hash_function()(key). Usefull to speed-up the lookup if you already have the hash.
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
|
||||
size_type unordered_erase(const K& key, std::size_t precalculated_hash) {
|
||||
return m_ht.unordered_erase(key, precalculated_hash);
|
||||
}
|
||||
|
||||
/**
|
||||
* Serialize the set through the `serializer` parameter.
|
||||
*
|
||||
* The `serializer` parameter must be a function object that supports the following call:
|
||||
* - `void operator()(const U& value);` where the types `std::uint64_t`, `float` and `Key` must be supported for U.
|
||||
*
|
||||
* The implementation leaves binary compatibilty (endianness, IEEE 754 for floats, ...) of the types it serializes
|
||||
* in the hands of the `Serializer` function object if compatibilty is required.
|
||||
*/
|
||||
template<class Serializer>
|
||||
void serialize(Serializer& serializer) const {
|
||||
m_ht.serialize(serializer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deserialize a previouly serialized set through the `deserializer` parameter.
|
||||
*
|
||||
* The `deserializer` parameter must be a function object that supports the following calls:
|
||||
* - `template<typename U> U operator()();` where the types `std::uint64_t`, `float` and `Key` must be supported for U.
|
||||
*
|
||||
* If the deserialized hash set type is hash compatible with the serialized set, the deserialization process can be
|
||||
* sped up by setting `hash_compatible` to true. To be hash compatible, the Hash and KeyEqual must behave the same way
|
||||
* than the ones used on the serialized map. The `std::size_t` must also be of the same size as the one on the platform used
|
||||
* to serialize the map, the same apply for `IndexType`. If these criteria are not met, the behaviour is undefined with
|
||||
* `hash_compatible` sets to true.
|
||||
*
|
||||
* The behaviour is undefined if the type `Key` of the `ordered_set` is not the same as the
|
||||
* type used during serialization.
|
||||
*
|
||||
* The implementation leaves binary compatibilty (endianness, IEEE 754 for floats, size of int, ...) of the types it
|
||||
* deserializes in the hands of the `Deserializer` function object if compatibilty is required.
|
||||
*/
|
||||
template<class Deserializer>
|
||||
static ordered_set deserialize(Deserializer& deserializer, bool hash_compatible = false) {
|
||||
ordered_set set(0);
|
||||
set.m_ht.deserialize(deserializer, hash_compatible);
|
||||
|
||||
return set;
|
||||
}
|
||||
|
||||
|
||||
|
||||
friend bool operator==(const ordered_set& lhs, const ordered_set& rhs) { return lhs.m_ht == rhs.m_ht; }
|
||||
friend bool operator!=(const ordered_set& lhs, const ordered_set& rhs) { return lhs.m_ht != rhs.m_ht; }
|
||||
friend bool operator<(const ordered_set& lhs, const ordered_set& rhs) { return lhs.m_ht < rhs.m_ht; }
|
||||
friend bool operator<=(const ordered_set& lhs, const ordered_set& rhs) { return lhs.m_ht <= rhs.m_ht; }
|
||||
friend bool operator>(const ordered_set& lhs, const ordered_set& rhs) { return lhs.m_ht > rhs.m_ht; }
|
||||
friend bool operator>=(const ordered_set& lhs, const ordered_set& rhs) { return lhs.m_ht >= rhs.m_ht; }
|
||||
|
||||
friend void swap(ordered_set& lhs, ordered_set& rhs) { lhs.swap(rhs); }
|
||||
|
||||
private:
|
||||
ht m_ht;
|
||||
};
|
||||
|
||||
} // end namespace tsl
|
||||
|
||||
#endif
|
348
src/includes/3thparty/tsl/robin_growth_policy.h
Normal file
348
src/includes/3thparty/tsl/robin_growth_policy.h
Normal file
@ -0,0 +1,348 @@
|
||||
/**
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2017 Tessil
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
#ifndef TSL_ROBIN_GROWTH_POLICY_H
|
||||
#define TSL_ROBIN_GROWTH_POLICY_H
|
||||
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <climits>
|
||||
#include <cmath>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <iterator>
|
||||
#include <limits>
|
||||
#include <ratio>
|
||||
#include <stdexcept>
|
||||
|
||||
|
||||
#ifdef TSL_DEBUG
|
||||
# define tsl_rh_assert(expr) assert(expr)
|
||||
#else
|
||||
# define tsl_rh_assert(expr) (static_cast<void>(0))
|
||||
#endif
|
||||
|
||||
|
||||
/**
|
||||
* If exceptions are enabled, throw the exception passed in parameter, otherwise call std::terminate.
|
||||
*/
|
||||
#if (defined(__cpp_exceptions) || defined(__EXCEPTIONS) || (defined (_MSC_VER) && defined (_CPPUNWIND))) && !defined(TSL_NO_EXCEPTIONS)
|
||||
# define TSL_RH_THROW_OR_TERMINATE(ex, msg) throw ex(msg)
|
||||
#else
|
||||
# define TSL_RH_NO_EXCEPTIONS
|
||||
# ifdef NDEBUG
|
||||
# define TSL_RH_THROW_OR_TERMINATE(ex, msg) std::terminate()
|
||||
# else
|
||||
# include <iostream>
|
||||
# define TSL_RH_THROW_OR_TERMINATE(ex, msg) do { std::cerr << msg << std::endl; std::terminate(); } while(0)
|
||||
# endif
|
||||
#endif
|
||||
|
||||
|
||||
#if defined(__GNUC__) || defined(__clang__)
|
||||
# define TSL_RH_LIKELY(exp) (__builtin_expect(!!(exp), true))
|
||||
#else
|
||||
# define TSL_RH_LIKELY(exp) (exp)
|
||||
#endif
|
||||
|
||||
|
||||
namespace tsl {
|
||||
namespace rh {
|
||||
|
||||
/**
|
||||
* Grow the hash table by a factor of GrowthFactor keeping the bucket count to a power of two. It allows
|
||||
* the table to use a mask operation instead of a modulo operation to map a hash to a bucket.
|
||||
*
|
||||
* GrowthFactor must be a power of two >= 2.
|
||||
*/
|
||||
template<std::size_t GrowthFactor>
|
||||
class power_of_two_growth_policy {
|
||||
public:
|
||||
/**
|
||||
* Called on the hash table creation and on rehash. The number of buckets for the table is passed in parameter.
|
||||
* This number is a minimum, the policy may update this value with a higher value if needed (but not lower).
|
||||
*
|
||||
* If 0 is given, min_bucket_count_in_out must still be 0 after the policy creation and
|
||||
* bucket_for_hash must always return 0 in this case.
|
||||
*/
|
||||
explicit power_of_two_growth_policy(std::size_t& min_bucket_count_in_out) {
|
||||
if(min_bucket_count_in_out > max_bucket_count()) {
|
||||
TSL_RH_THROW_OR_TERMINATE(std::length_error, "The hash table exceeds its maxmimum size.");
|
||||
}
|
||||
|
||||
if(min_bucket_count_in_out > 0) {
|
||||
min_bucket_count_in_out = round_up_to_power_of_two(min_bucket_count_in_out);
|
||||
m_mask = min_bucket_count_in_out - 1;
|
||||
}
|
||||
else {
|
||||
m_mask = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the bucket [0, bucket_count()) to which the hash belongs.
|
||||
* If bucket_count() is 0, it must always return 0.
|
||||
*/
|
||||
std::size_t bucket_for_hash(std::size_t hash) const noexcept {
|
||||
return hash & m_mask;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the number of buckets that should be used on next growth.
|
||||
*/
|
||||
std::size_t next_bucket_count() const {
|
||||
if((m_mask + 1) > max_bucket_count() / GrowthFactor) {
|
||||
TSL_RH_THROW_OR_TERMINATE(std::length_error, "The hash table exceeds its maxmimum size.");
|
||||
}
|
||||
|
||||
return (m_mask + 1) * GrowthFactor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the maximum number of buckets supported by the policy.
|
||||
*/
|
||||
std::size_t max_bucket_count() const {
|
||||
// Largest power of two.
|
||||
return (std::numeric_limits<std::size_t>::max() / 2) + 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the growth policy as if it was created with a bucket count of 0.
|
||||
* After a clear, the policy must always return 0 when bucket_for_hash is called.
|
||||
*/
|
||||
void clear() noexcept {
|
||||
m_mask = 0;
|
||||
}
|
||||
|
||||
private:
|
||||
static std::size_t round_up_to_power_of_two(std::size_t value) {
|
||||
if(is_power_of_two(value)) {
|
||||
return value;
|
||||
}
|
||||
|
||||
if(value == 0) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
--value;
|
||||
for(std::size_t i = 1; i < sizeof(std::size_t) * CHAR_BIT; i *= 2) {
|
||||
value |= value >> i;
|
||||
}
|
||||
|
||||
return value + 1;
|
||||
}
|
||||
|
||||
static constexpr bool is_power_of_two(std::size_t value) {
|
||||
return value != 0 && (value & (value - 1)) == 0;
|
||||
}
|
||||
|
||||
protected:
|
||||
static_assert(is_power_of_two(GrowthFactor) && GrowthFactor >= 2, "GrowthFactor must be a power of two >= 2.");
|
||||
|
||||
std::size_t m_mask;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Grow the hash table by GrowthFactor::num / GrowthFactor::den and use a modulo to map a hash
|
||||
* to a bucket. Slower but it can be useful if you want a slower growth.
|
||||
*/
|
||||
template<class GrowthFactor = std::ratio<3, 2>>
|
||||
class mod_growth_policy {
|
||||
public:
|
||||
explicit mod_growth_policy(std::size_t& min_bucket_count_in_out) {
|
||||
if(min_bucket_count_in_out > max_bucket_count()) {
|
||||
TSL_RH_THROW_OR_TERMINATE(std::length_error, "The hash table exceeds its maxmimum size.");
|
||||
}
|
||||
|
||||
if(min_bucket_count_in_out > 0) {
|
||||
m_mod = min_bucket_count_in_out;
|
||||
}
|
||||
else {
|
||||
m_mod = 1;
|
||||
}
|
||||
}
|
||||
|
||||
std::size_t bucket_for_hash(std::size_t hash) const noexcept {
|
||||
return hash % m_mod;
|
||||
}
|
||||
|
||||
std::size_t next_bucket_count() const {
|
||||
if(m_mod == max_bucket_count()) {
|
||||
TSL_RH_THROW_OR_TERMINATE(std::length_error, "The hash table exceeds its maxmimum size.");
|
||||
}
|
||||
|
||||
const double next_bucket_count = std::ceil(double(m_mod) * REHASH_SIZE_MULTIPLICATION_FACTOR);
|
||||
if(!std::isnormal(next_bucket_count)) {
|
||||
TSL_RH_THROW_OR_TERMINATE(std::length_error, "The hash table exceeds its maxmimum size.");
|
||||
}
|
||||
|
||||
if(next_bucket_count > double(max_bucket_count())) {
|
||||
return max_bucket_count();
|
||||
}
|
||||
else {
|
||||
return std::size_t(next_bucket_count);
|
||||
}
|
||||
}
|
||||
|
||||
std::size_t max_bucket_count() const {
|
||||
return MAX_BUCKET_COUNT;
|
||||
}
|
||||
|
||||
void clear() noexcept {
|
||||
m_mod = 1;
|
||||
}
|
||||
|
||||
private:
|
||||
static constexpr double REHASH_SIZE_MULTIPLICATION_FACTOR = 1.0 * GrowthFactor::num / GrowthFactor::den;
|
||||
static const std::size_t MAX_BUCKET_COUNT =
|
||||
std::size_t(double(
|
||||
std::numeric_limits<std::size_t>::max() / REHASH_SIZE_MULTIPLICATION_FACTOR
|
||||
));
|
||||
|
||||
static_assert(REHASH_SIZE_MULTIPLICATION_FACTOR >= 1.1, "Growth factor should be >= 1.1.");
|
||||
|
||||
std::size_t m_mod;
|
||||
};
|
||||
|
||||
|
||||
|
||||
namespace detail {
|
||||
|
||||
#if SIZE_MAX >= ULLONG_MAX
|
||||
#define TSL_RH_NB_PRIMES 51
|
||||
#elif SIZE_MAX >= ULONG_MAX
|
||||
#define TSL_RH_NB_PRIMES 40
|
||||
#else
|
||||
#define TSL_RH_NB_PRIMES 23
|
||||
#endif
|
||||
|
||||
static constexpr const std::array<std::size_t, TSL_RH_NB_PRIMES> PRIMES = {{
|
||||
1u, 5u, 17u, 29u, 37u, 53u, 67u, 79u, 97u, 131u, 193u, 257u, 389u, 521u, 769u, 1031u,
|
||||
1543u, 2053u, 3079u, 6151u, 12289u, 24593u, 49157u,
|
||||
#if SIZE_MAX >= ULONG_MAX
|
||||
98317ul, 196613ul, 393241ul, 786433ul, 1572869ul, 3145739ul, 6291469ul, 12582917ul,
|
||||
25165843ul, 50331653ul, 100663319ul, 201326611ul, 402653189ul, 805306457ul, 1610612741ul,
|
||||
3221225473ul, 4294967291ul,
|
||||
#endif
|
||||
#if SIZE_MAX >= ULLONG_MAX
|
||||
6442450939ull, 12884901893ull, 25769803751ull, 51539607551ull, 103079215111ull, 206158430209ull,
|
||||
412316860441ull, 824633720831ull, 1649267441651ull, 3298534883309ull, 6597069766657ull,
|
||||
#endif
|
||||
}};
|
||||
|
||||
template<unsigned int IPrime>
|
||||
static constexpr std::size_t mod(std::size_t hash) { return hash % PRIMES[IPrime]; }
|
||||
|
||||
// MOD_PRIME[iprime](hash) returns hash % PRIMES[iprime]. This table allows for faster modulo as the
|
||||
// compiler can optimize the modulo code better with a constant known at the compilation.
|
||||
static constexpr const std::array<std::size_t(*)(std::size_t), TSL_RH_NB_PRIMES> MOD_PRIME = {{
|
||||
&mod<0>, &mod<1>, &mod<2>, &mod<3>, &mod<4>, &mod<5>, &mod<6>, &mod<7>, &mod<8>, &mod<9>, &mod<10>,
|
||||
&mod<11>, &mod<12>, &mod<13>, &mod<14>, &mod<15>, &mod<16>, &mod<17>, &mod<18>, &mod<19>, &mod<20>,
|
||||
&mod<21>, &mod<22>,
|
||||
#if SIZE_MAX >= ULONG_MAX
|
||||
&mod<23>, &mod<24>, &mod<25>, &mod<26>, &mod<27>, &mod<28>, &mod<29>, &mod<30>, &mod<31>, &mod<32>,
|
||||
&mod<33>, &mod<34>, &mod<35>, &mod<36>, &mod<37> , &mod<38>, &mod<39>,
|
||||
#endif
|
||||
#if SIZE_MAX >= ULLONG_MAX
|
||||
&mod<40>, &mod<41>, &mod<42>, &mod<43>, &mod<44>, &mod<45>, &mod<46>, &mod<47>, &mod<48>, &mod<49>,
|
||||
&mod<50>,
|
||||
#endif
|
||||
}};
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Grow the hash table by using prime numbers as bucket count. Slower than tsl::rh::power_of_two_growth_policy in
|
||||
* general but will probably distribute the values around better in the buckets with a poor hash function.
|
||||
*
|
||||
* To allow the compiler to optimize the modulo operation, a lookup table is used with constant primes numbers.
|
||||
*
|
||||
* With a switch the code would look like:
|
||||
* \code
|
||||
* switch(iprime) { // iprime is the current prime of the hash table
|
||||
* case 0: hash % 5ul;
|
||||
* break;
|
||||
* case 1: hash % 17ul;
|
||||
* break;
|
||||
* case 2: hash % 29ul;
|
||||
* break;
|
||||
* ...
|
||||
* }
|
||||
* \endcode
|
||||
*
|
||||
* Due to the constant variable in the modulo the compiler is able to optimize the operation
|
||||
* by a series of multiplications, substractions and shifts.
|
||||
*
|
||||
* The 'hash % 5' could become something like 'hash - (hash * 0xCCCCCCCD) >> 34) * 5' in a 64 bits environement.
|
||||
*/
|
||||
class prime_growth_policy {
|
||||
public:
|
||||
explicit prime_growth_policy(std::size_t& min_bucket_count_in_out) {
|
||||
auto it_prime = std::lower_bound(detail::PRIMES.begin(),
|
||||
detail::PRIMES.end(), min_bucket_count_in_out);
|
||||
if(it_prime == detail::PRIMES.end()) {
|
||||
TSL_RH_THROW_OR_TERMINATE(std::length_error, "The hash table exceeds its maxmimum size.");
|
||||
}
|
||||
|
||||
m_iprime = static_cast<unsigned int>(std::distance(detail::PRIMES.begin(), it_prime));
|
||||
if(min_bucket_count_in_out > 0) {
|
||||
min_bucket_count_in_out = *it_prime;
|
||||
}
|
||||
else {
|
||||
min_bucket_count_in_out = 0;
|
||||
}
|
||||
}
|
||||
|
||||
std::size_t bucket_for_hash(std::size_t hash) const noexcept {
|
||||
return detail::MOD_PRIME[m_iprime](hash);
|
||||
}
|
||||
|
||||
std::size_t next_bucket_count() const {
|
||||
if(m_iprime + 1 >= detail::PRIMES.size()) {
|
||||
TSL_RH_THROW_OR_TERMINATE(std::length_error, "The hash table exceeds its maxmimum size.");
|
||||
}
|
||||
|
||||
return detail::PRIMES[m_iprime + 1];
|
||||
}
|
||||
|
||||
std::size_t max_bucket_count() const {
|
||||
return detail::PRIMES.back();
|
||||
}
|
||||
|
||||
void clear() noexcept {
|
||||
m_iprime = 0;
|
||||
}
|
||||
|
||||
private:
|
||||
unsigned int m_iprime;
|
||||
|
||||
static_assert(std::numeric_limits<decltype(m_iprime)>::max() >= detail::PRIMES.size(),
|
||||
"The type of m_iprime is not big enough.");
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
1441
src/includes/3thparty/tsl/robin_hash.h
Normal file
1441
src/includes/3thparty/tsl/robin_hash.h
Normal file
File diff suppressed because it is too large
Load Diff
715
src/includes/3thparty/tsl/robin_map.h
Normal file
715
src/includes/3thparty/tsl/robin_map.h
Normal file
@ -0,0 +1,715 @@
|
||||
/**
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2017 Tessil
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
#ifndef TSL_ROBIN_MAP_H
|
||||
#define TSL_ROBIN_MAP_H
|
||||
|
||||
|
||||
#include <cstddef>
|
||||
#include <functional>
|
||||
#include <initializer_list>
|
||||
#include <memory>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include "robin_hash.h"
|
||||
|
||||
|
||||
namespace tsl {
|
||||
|
||||
|
||||
/**
|
||||
* Implementation of a hash map using open-adressing and the robin hood hashing algorithm with backward shift deletion.
|
||||
*
|
||||
* For operations modifying the hash map (insert, erase, rehash, ...), the strong exception guarantee
|
||||
* is only guaranteed when the expression `std::is_nothrow_swappable<std::pair<Key, T>>::value &&
|
||||
* std::is_nothrow_move_constructible<std::pair<Key, T>>::value` is true, otherwise if an exception
|
||||
* is thrown during the swap or the move, the hash map may end up in a undefined state. Per the standard
|
||||
* a `Key` or `T` with a noexcept copy constructor and no move constructor also satisfies the
|
||||
* `std::is_nothrow_move_constructible<std::pair<Key, T>>::value` criterion (and will thus guarantee the
|
||||
* strong exception for the map).
|
||||
*
|
||||
* When `StoreHash` is true, 32 bits of the hash are stored alongside the values. It can improve
|
||||
* the performance during lookups if the `KeyEqual` function takes time (if it engenders a cache-miss for example)
|
||||
* as we then compare the stored hashes before comparing the keys. When `tsl::rh::power_of_two_growth_policy` is used
|
||||
* as `GrowthPolicy`, it may also speed-up the rehash process as we can avoid to recalculate the hash.
|
||||
* When it is detected that storing the hash will not incur any memory penality due to alignement (i.e.
|
||||
* `sizeof(tsl::detail_robin_hash::bucket_entry<ValueType, true>) ==
|
||||
* sizeof(tsl::detail_robin_hash::bucket_entry<ValueType, false>)`) and `tsl::rh::power_of_two_growth_policy` is
|
||||
* used, the hash will be stored even if `StoreHash` is false so that we can speed-up the rehash (but it will
|
||||
* not be used on lookups unless `StoreHash` is true).
|
||||
*
|
||||
* `GrowthPolicy` defines how the map grows and consequently how a hash value is mapped to a bucket.
|
||||
* By default the map uses `tsl::rh::power_of_two_growth_policy`. This policy keeps the number of buckets
|
||||
* to a power of two and uses a mask to map the hash to a bucket instead of the slow modulo.
|
||||
* Other growth policies are available and you may define your own growth policy,
|
||||
* check `tsl::rh::power_of_two_growth_policy` for the interface.
|
||||
*
|
||||
* `std::pair<Key, T>` must be swappable.
|
||||
*
|
||||
* `Key` and `T` must be copy and/or move constructible.
|
||||
*
|
||||
* If the destructor of `Key` or `T` throws an exception, the behaviour of the class is undefined.
|
||||
*
|
||||
* Iterators invalidation:
|
||||
* - clear, operator=, reserve, rehash: always invalidate the iterators.
|
||||
* - insert, emplace, emplace_hint, operator[]: if there is an effective insert, invalidate the iterators.
|
||||
* - erase: always invalidate the iterators.
|
||||
*/
|
||||
template<class Key,
|
||||
class T,
|
||||
class Hash = std::hash<Key>,
|
||||
class KeyEqual = std::equal_to<Key>,
|
||||
class Allocator = std::allocator<std::pair<Key, T>>,
|
||||
bool StoreHash = false,
|
||||
class GrowthPolicy = tsl::rh::power_of_two_growth_policy<2>>
|
||||
class robin_map {
|
||||
private:
|
||||
template<typename U>
|
||||
using has_is_transparent = tsl::detail_robin_hash::has_is_transparent<U>;
|
||||
|
||||
class KeySelect {
|
||||
public:
|
||||
using key_type = Key;
|
||||
|
||||
const key_type& operator()(const std::pair<Key, T>& key_value) const noexcept {
|
||||
return key_value.first;
|
||||
}
|
||||
|
||||
key_type& operator()(std::pair<Key, T>& key_value) noexcept {
|
||||
return key_value.first;
|
||||
}
|
||||
};
|
||||
|
||||
class ValueSelect {
|
||||
public:
|
||||
using value_type = T;
|
||||
|
||||
const value_type& operator()(const std::pair<Key, T>& key_value) const noexcept {
|
||||
return key_value.second;
|
||||
}
|
||||
|
||||
value_type& operator()(std::pair<Key, T>& key_value) noexcept {
|
||||
return key_value.second;
|
||||
}
|
||||
};
|
||||
|
||||
using ht = detail_robin_hash::robin_hash<std::pair<Key, T>, KeySelect, ValueSelect,
|
||||
Hash, KeyEqual, Allocator, StoreHash, GrowthPolicy>;
|
||||
|
||||
public:
|
||||
using key_type = typename ht::key_type;
|
||||
using mapped_type = T;
|
||||
using value_type = typename ht::value_type;
|
||||
using size_type = typename ht::size_type;
|
||||
using difference_type = typename ht::difference_type;
|
||||
using hasher = typename ht::hasher;
|
||||
using key_equal = typename ht::key_equal;
|
||||
using allocator_type = typename ht::allocator_type;
|
||||
using reference = typename ht::reference;
|
||||
using const_reference = typename ht::const_reference;
|
||||
using pointer = typename ht::pointer;
|
||||
using const_pointer = typename ht::const_pointer;
|
||||
using iterator = typename ht::iterator;
|
||||
using const_iterator = typename ht::const_iterator;
|
||||
|
||||
|
||||
public:
|
||||
/*
|
||||
* Constructors
|
||||
*/
|
||||
robin_map(): robin_map(ht::DEFAULT_INIT_BUCKETS_SIZE) {
|
||||
}
|
||||
|
||||
explicit robin_map(size_type bucket_count,
|
||||
const Hash& hash = Hash(),
|
||||
const KeyEqual& equal = KeyEqual(),
|
||||
const Allocator& alloc = Allocator()):
|
||||
m_ht(bucket_count, hash, equal, alloc)
|
||||
{
|
||||
}
|
||||
|
||||
robin_map(size_type bucket_count,
|
||||
const Allocator& alloc): robin_map(bucket_count, Hash(), KeyEqual(), alloc)
|
||||
{
|
||||
}
|
||||
|
||||
robin_map(size_type bucket_count,
|
||||
const Hash& hash,
|
||||
const Allocator& alloc): robin_map(bucket_count, hash, KeyEqual(), alloc)
|
||||
{
|
||||
}
|
||||
|
||||
explicit robin_map(const Allocator& alloc): robin_map(ht::DEFAULT_INIT_BUCKETS_SIZE, alloc) {
|
||||
}
|
||||
|
||||
template<class InputIt>
|
||||
robin_map(InputIt first, InputIt last,
|
||||
size_type bucket_count = ht::DEFAULT_INIT_BUCKETS_SIZE,
|
||||
const Hash& hash = Hash(),
|
||||
const KeyEqual& equal = KeyEqual(),
|
||||
const Allocator& alloc = Allocator()): robin_map(bucket_count, hash, equal, alloc)
|
||||
{
|
||||
insert(first, last);
|
||||
}
|
||||
|
||||
template<class InputIt>
|
||||
robin_map(InputIt first, InputIt last,
|
||||
size_type bucket_count,
|
||||
const Allocator& alloc): robin_map(first, last, bucket_count, Hash(), KeyEqual(), alloc)
|
||||
{
|
||||
}
|
||||
|
||||
template<class InputIt>
|
||||
robin_map(InputIt first, InputIt last,
|
||||
size_type bucket_count,
|
||||
const Hash& hash,
|
||||
const Allocator& alloc): robin_map(first, last, bucket_count, hash, KeyEqual(), alloc)
|
||||
{
|
||||
}
|
||||
|
||||
robin_map(std::initializer_list<value_type> init,
|
||||
size_type bucket_count = ht::DEFAULT_INIT_BUCKETS_SIZE,
|
||||
const Hash& hash = Hash(),
|
||||
const KeyEqual& equal = KeyEqual(),
|
||||
const Allocator& alloc = Allocator()):
|
||||
robin_map(init.begin(), init.end(), bucket_count, hash, equal, alloc)
|
||||
{
|
||||
}
|
||||
|
||||
robin_map(std::initializer_list<value_type> init,
|
||||
size_type bucket_count,
|
||||
const Allocator& alloc):
|
||||
robin_map(init.begin(), init.end(), bucket_count, Hash(), KeyEqual(), alloc)
|
||||
{
|
||||
}
|
||||
|
||||
robin_map(std::initializer_list<value_type> init,
|
||||
size_type bucket_count,
|
||||
const Hash& hash,
|
||||
const Allocator& alloc):
|
||||
robin_map(init.begin(), init.end(), bucket_count, hash, KeyEqual(), alloc)
|
||||
{
|
||||
}
|
||||
|
||||
robin_map& operator=(std::initializer_list<value_type> ilist) {
|
||||
m_ht.clear();
|
||||
|
||||
m_ht.reserve(ilist.size());
|
||||
m_ht.insert(ilist.begin(), ilist.end());
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
allocator_type get_allocator() const { return m_ht.get_allocator(); }
|
||||
|
||||
|
||||
/*
|
||||
* Iterators
|
||||
*/
|
||||
iterator begin() noexcept { return m_ht.begin(); }
|
||||
const_iterator begin() const noexcept { return m_ht.begin(); }
|
||||
const_iterator cbegin() const noexcept { return m_ht.cbegin(); }
|
||||
|
||||
iterator end() noexcept { return m_ht.end(); }
|
||||
const_iterator end() const noexcept { return m_ht.end(); }
|
||||
const_iterator cend() const noexcept { return m_ht.cend(); }
|
||||
|
||||
|
||||
/*
|
||||
* Capacity
|
||||
*/
|
||||
bool empty() const noexcept { return m_ht.empty(); }
|
||||
size_type size() const noexcept { return m_ht.size(); }
|
||||
size_type max_size() const noexcept { return m_ht.max_size(); }
|
||||
|
||||
/*
|
||||
* Modifiers
|
||||
*/
|
||||
void clear() noexcept { m_ht.clear(); }
|
||||
|
||||
|
||||
|
||||
std::pair<iterator, bool> insert(const value_type& value) {
|
||||
return m_ht.insert(value);
|
||||
}
|
||||
|
||||
template<class P, typename std::enable_if<std::is_constructible<value_type, P&&>::value>::type* = nullptr>
|
||||
std::pair<iterator, bool> insert(P&& value) {
|
||||
return m_ht.emplace(std::forward<P>(value));
|
||||
}
|
||||
|
||||
std::pair<iterator, bool> insert(value_type&& value) {
|
||||
return m_ht.insert(std::move(value));
|
||||
}
|
||||
|
||||
|
||||
iterator insert(const_iterator hint, const value_type& value) {
|
||||
return m_ht.insert_hint(hint, value);
|
||||
}
|
||||
|
||||
template<class P, typename std::enable_if<std::is_constructible<value_type, P&&>::value>::type* = nullptr>
|
||||
iterator insert(const_iterator hint, P&& value) {
|
||||
return m_ht.emplace_hint(hint, std::forward<P>(value));
|
||||
}
|
||||
|
||||
iterator insert(const_iterator hint, value_type&& value) {
|
||||
return m_ht.insert_hint(hint, std::move(value));
|
||||
}
|
||||
|
||||
|
||||
template<class InputIt>
|
||||
void insert(InputIt first, InputIt last) {
|
||||
m_ht.insert(first, last);
|
||||
}
|
||||
|
||||
void insert(std::initializer_list<value_type> ilist) {
|
||||
m_ht.insert(ilist.begin(), ilist.end());
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
template<class M>
|
||||
std::pair<iterator, bool> insert_or_assign(const key_type& k, M&& obj) {
|
||||
return m_ht.insert_or_assign(k, std::forward<M>(obj));
|
||||
}
|
||||
|
||||
template<class M>
|
||||
std::pair<iterator, bool> insert_or_assign(key_type&& k, M&& obj) {
|
||||
return m_ht.insert_or_assign(std::move(k), std::forward<M>(obj));
|
||||
}
|
||||
|
||||
template<class M>
|
||||
iterator insert_or_assign(const_iterator hint, const key_type& k, M&& obj) {
|
||||
return m_ht.insert_or_assign(hint, k, std::forward<M>(obj));
|
||||
}
|
||||
|
||||
template<class M>
|
||||
iterator insert_or_assign(const_iterator hint, key_type&& k, M&& obj) {
|
||||
return m_ht.insert_or_assign(hint, std::move(k), std::forward<M>(obj));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Due to the way elements are stored, emplace will need to move or copy the key-value once.
|
||||
* The method is equivalent to insert(value_type(std::forward<Args>(args)...));
|
||||
*
|
||||
* Mainly here for compatibility with the std::unordered_map interface.
|
||||
*/
|
||||
template<class... Args>
|
||||
std::pair<iterator, bool> emplace(Args&&... args) {
|
||||
return m_ht.emplace(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Due to the way elements are stored, emplace_hint will need to move or copy the key-value once.
|
||||
* The method is equivalent to insert(hint, value_type(std::forward<Args>(args)...));
|
||||
*
|
||||
* Mainly here for compatibility with the std::unordered_map interface.
|
||||
*/
|
||||
template<class... Args>
|
||||
iterator emplace_hint(const_iterator hint, Args&&... args) {
|
||||
return m_ht.emplace_hint(hint, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
template<class... Args>
|
||||
std::pair<iterator, bool> try_emplace(const key_type& k, Args&&... args) {
|
||||
return m_ht.try_emplace(k, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template<class... Args>
|
||||
std::pair<iterator, bool> try_emplace(key_type&& k, Args&&... args) {
|
||||
return m_ht.try_emplace(std::move(k), std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template<class... Args>
|
||||
iterator try_emplace(const_iterator hint, const key_type& k, Args&&... args) {
|
||||
return m_ht.try_emplace_hint(hint, k, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template<class... Args>
|
||||
iterator try_emplace(const_iterator hint, key_type&& k, Args&&... args) {
|
||||
return m_ht.try_emplace_hint(hint, std::move(k), std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
iterator erase(iterator pos) { return m_ht.erase(pos); }
|
||||
iterator erase(const_iterator pos) { return m_ht.erase(pos); }
|
||||
iterator erase(const_iterator first, const_iterator last) { return m_ht.erase(first, last); }
|
||||
size_type erase(const key_type& key) { return m_ht.erase(key); }
|
||||
|
||||
/**
|
||||
* Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same
|
||||
* as hash_function()(key). Usefull to speed-up the lookup to the value if you already have the hash.
|
||||
*/
|
||||
size_type erase(const key_type& key, std::size_t precalculated_hash) {
|
||||
return m_ht.erase(key, precalculated_hash);
|
||||
}
|
||||
|
||||
/**
|
||||
* This overload only participates in the overload resolution if the typedef KeyEqual::is_transparent exists.
|
||||
* If so, K must be hashable and comparable to Key.
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
|
||||
size_type erase(const K& key) { return m_ht.erase(key); }
|
||||
|
||||
/**
|
||||
* @copydoc erase(const K& key)
|
||||
*
|
||||
* Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same
|
||||
* as hash_function()(key). Usefull to speed-up the lookup to the value if you already have the hash.
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
|
||||
size_type erase(const K& key, std::size_t precalculated_hash) {
|
||||
return m_ht.erase(key, precalculated_hash);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void swap(robin_map& other) { other.m_ht.swap(m_ht); }
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Lookup
|
||||
*/
|
||||
T& at(const Key& key) { return m_ht.at(key); }
|
||||
|
||||
/**
|
||||
* Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same
|
||||
* as hash_function()(key). Usefull to speed-up the lookup if you already have the hash.
|
||||
*/
|
||||
T& at(const Key& key, std::size_t precalculated_hash) { return m_ht.at(key, precalculated_hash); }
|
||||
|
||||
|
||||
const T& at(const Key& key) const { return m_ht.at(key); }
|
||||
|
||||
/**
|
||||
* @copydoc at(const Key& key, std::size_t precalculated_hash)
|
||||
*/
|
||||
const T& at(const Key& key, std::size_t precalculated_hash) const { return m_ht.at(key, precalculated_hash); }
|
||||
|
||||
|
||||
/**
|
||||
* This overload only participates in the overload resolution if the typedef KeyEqual::is_transparent exists.
|
||||
* If so, K must be hashable and comparable to Key.
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
|
||||
T& at(const K& key) { return m_ht.at(key); }
|
||||
|
||||
/**
|
||||
* @copydoc at(const K& key)
|
||||
*
|
||||
* Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same
|
||||
* as hash_function()(key). Usefull to speed-up the lookup if you already have the hash.
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
|
||||
T& at(const K& key, std::size_t precalculated_hash) { return m_ht.at(key, precalculated_hash); }
|
||||
|
||||
|
||||
/**
|
||||
* @copydoc at(const K& key)
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
|
||||
const T& at(const K& key) const { return m_ht.at(key); }
|
||||
|
||||
/**
|
||||
* @copydoc at(const K& key, std::size_t precalculated_hash)
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
|
||||
const T& at(const K& key, std::size_t precalculated_hash) const { return m_ht.at(key, precalculated_hash); }
|
||||
|
||||
|
||||
|
||||
|
||||
T& operator[](const Key& key) { return m_ht[key]; }
|
||||
T& operator[](Key&& key) { return m_ht[std::move(key)]; }
|
||||
|
||||
|
||||
|
||||
|
||||
size_type count(const Key& key) const { return m_ht.count(key); }
|
||||
|
||||
/**
|
||||
* Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same
|
||||
* as hash_function()(key). Usefull to speed-up the lookup if you already have the hash.
|
||||
*/
|
||||
size_type count(const Key& key, std::size_t precalculated_hash) const {
|
||||
return m_ht.count(key, precalculated_hash);
|
||||
}
|
||||
|
||||
/**
|
||||
* This overload only participates in the overload resolution if the typedef KeyEqual::is_transparent exists.
|
||||
* If so, K must be hashable and comparable to Key.
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
|
||||
size_type count(const K& key) const { return m_ht.count(key); }
|
||||
|
||||
/**
|
||||
* @copydoc count(const K& key) const
|
||||
*
|
||||
* Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same
|
||||
* as hash_function()(key). Usefull to speed-up the lookup if you already have the hash.
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
|
||||
size_type count(const K& key, std::size_t precalculated_hash) const { return m_ht.count(key, precalculated_hash); }
|
||||
|
||||
|
||||
|
||||
|
||||
iterator find(const Key& key) { return m_ht.find(key); }
|
||||
|
||||
/**
|
||||
* Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same
|
||||
* as hash_function()(key). Usefull to speed-up the lookup if you already have the hash.
|
||||
*/
|
||||
iterator find(const Key& key, std::size_t precalculated_hash) { return m_ht.find(key, precalculated_hash); }
|
||||
|
||||
const_iterator find(const Key& key) const { return m_ht.find(key); }
|
||||
|
||||
/**
|
||||
* @copydoc find(const Key& key, std::size_t precalculated_hash)
|
||||
*/
|
||||
const_iterator find(const Key& key, std::size_t precalculated_hash) const {
|
||||
return m_ht.find(key, precalculated_hash);
|
||||
}
|
||||
|
||||
/**
|
||||
* This overload only participates in the overload resolution if the typedef KeyEqual::is_transparent exists.
|
||||
* If so, K must be hashable and comparable to Key.
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
|
||||
iterator find(const K& key) { return m_ht.find(key); }
|
||||
|
||||
/**
|
||||
* @copydoc find(const K& key)
|
||||
*
|
||||
* Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same
|
||||
* as hash_function()(key). Usefull to speed-up the lookup if you already have the hash.
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
|
||||
iterator find(const K& key, std::size_t precalculated_hash) { return m_ht.find(key, precalculated_hash); }
|
||||
|
||||
/**
|
||||
* @copydoc find(const K& key)
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
|
||||
const_iterator find(const K& key) const { return m_ht.find(key); }
|
||||
|
||||
/**
|
||||
* @copydoc find(const K& key)
|
||||
*
|
||||
* Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same
|
||||
* as hash_function()(key). Usefull to speed-up the lookup if you already have the hash.
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
|
||||
const_iterator find(const K& key, std::size_t precalculated_hash) const {
|
||||
return m_ht.find(key, precalculated_hash);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
bool contains(const Key& key) const { return m_ht.contains(key); }
|
||||
|
||||
/**
|
||||
* Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same
|
||||
* as hash_function()(key). Usefull to speed-up the lookup if you already have the hash.
|
||||
*/
|
||||
bool contains(const Key& key, std::size_t precalculated_hash) const {
|
||||
return m_ht.contains(key, precalculated_hash);
|
||||
}
|
||||
|
||||
/**
|
||||
* This overload only participates in the overload resolution if the typedef KeyEqual::is_transparent exists.
|
||||
* If so, K must be hashable and comparable to Key.
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
|
||||
bool contains(const K& key) const { return m_ht.contains(key); }
|
||||
|
||||
/**
|
||||
* @copydoc contains(const K& key) const
|
||||
*
|
||||
* Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same
|
||||
* as hash_function()(key). Usefull to speed-up the lookup if you already have the hash.
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
|
||||
bool contains(const K& key, std::size_t precalculated_hash) const {
|
||||
return m_ht.contains(key, precalculated_hash);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
std::pair<iterator, iterator> equal_range(const Key& key) { return m_ht.equal_range(key); }
|
||||
|
||||
/**
|
||||
* Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same
|
||||
* as hash_function()(key). Usefull to speed-up the lookup if you already have the hash.
|
||||
*/
|
||||
std::pair<iterator, iterator> equal_range(const Key& key, std::size_t precalculated_hash) {
|
||||
return m_ht.equal_range(key, precalculated_hash);
|
||||
}
|
||||
|
||||
std::pair<const_iterator, const_iterator> equal_range(const Key& key) const { return m_ht.equal_range(key); }
|
||||
|
||||
/**
|
||||
* @copydoc equal_range(const Key& key, std::size_t precalculated_hash)
|
||||
*/
|
||||
std::pair<const_iterator, const_iterator> equal_range(const Key& key, std::size_t precalculated_hash) const {
|
||||
return m_ht.equal_range(key, precalculated_hash);
|
||||
}
|
||||
|
||||
/**
|
||||
* This overload only participates in the overload resolution if the typedef KeyEqual::is_transparent exists.
|
||||
* If so, K must be hashable and comparable to Key.
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
|
||||
std::pair<iterator, iterator> equal_range(const K& key) { return m_ht.equal_range(key); }
|
||||
|
||||
|
||||
/**
|
||||
* @copydoc equal_range(const K& key)
|
||||
*
|
||||
* Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same
|
||||
* as hash_function()(key). Usefull to speed-up the lookup if you already have the hash.
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
|
||||
std::pair<iterator, iterator> equal_range(const K& key, std::size_t precalculated_hash) {
|
||||
return m_ht.equal_range(key, precalculated_hash);
|
||||
}
|
||||
|
||||
/**
|
||||
* @copydoc equal_range(const K& key)
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
|
||||
std::pair<const_iterator, const_iterator> equal_range(const K& key) const { return m_ht.equal_range(key); }
|
||||
|
||||
/**
|
||||
* @copydoc equal_range(const K& key, std::size_t precalculated_hash)
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
|
||||
std::pair<const_iterator, const_iterator> equal_range(const K& key, std::size_t precalculated_hash) const {
|
||||
return m_ht.equal_range(key, precalculated_hash);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Bucket interface
|
||||
*/
|
||||
size_type bucket_count() const { return m_ht.bucket_count(); }
|
||||
size_type max_bucket_count() const { return m_ht.max_bucket_count(); }
|
||||
|
||||
|
||||
/*
|
||||
* Hash policy
|
||||
*/
|
||||
float load_factor() const { return m_ht.load_factor(); }
|
||||
|
||||
float min_load_factor() const { return m_ht.min_load_factor(); }
|
||||
float max_load_factor() const { return m_ht.max_load_factor(); }
|
||||
|
||||
/**
|
||||
* Set the `min_load_factor` to `ml`. When the `load_factor` of the map goes
|
||||
* below `min_load_factor` after some erase operations, the map will be
|
||||
* shrunk when an insertion occurs. The erase method itself never shrinks
|
||||
* the map.
|
||||
*
|
||||
* The default value of `min_load_factor` is 0.0f, the map never shrinks by default.
|
||||
*/
|
||||
void min_load_factor(float ml) { m_ht.min_load_factor(ml); }
|
||||
void max_load_factor(float ml) { m_ht.max_load_factor(ml); }
|
||||
|
||||
void rehash(size_type count) { m_ht.rehash(count); }
|
||||
void reserve(size_type count) { m_ht.reserve(count); }
|
||||
|
||||
|
||||
/*
|
||||
* Observers
|
||||
*/
|
||||
hasher hash_function() const { return m_ht.hash_function(); }
|
||||
key_equal key_eq() const { return m_ht.key_eq(); }
|
||||
|
||||
/*
|
||||
* Other
|
||||
*/
|
||||
|
||||
/**
|
||||
* Convert a const_iterator to an iterator.
|
||||
*/
|
||||
iterator mutable_iterator(const_iterator pos) {
|
||||
return m_ht.mutable_iterator(pos);
|
||||
}
|
||||
|
||||
friend bool operator==(const robin_map& lhs, const robin_map& rhs) {
|
||||
if(lhs.size() != rhs.size()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for(const auto& element_lhs: lhs) {
|
||||
const auto it_element_rhs = rhs.find(element_lhs.first);
|
||||
if(it_element_rhs == rhs.cend() || element_lhs.second != it_element_rhs->second) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
friend bool operator!=(const robin_map& lhs, const robin_map& rhs) {
|
||||
return !operator==(lhs, rhs);
|
||||
}
|
||||
|
||||
friend void swap(robin_map& lhs, robin_map& rhs) {
|
||||
lhs.swap(rhs);
|
||||
}
|
||||
|
||||
private:
|
||||
ht m_ht;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Same as `tsl::robin_map<Key, T, Hash, KeyEqual, Allocator, StoreHash, tsl::rh::prime_growth_policy>`.
|
||||
*/
|
||||
template<class Key,
|
||||
class T,
|
||||
class Hash = std::hash<Key>,
|
||||
class KeyEqual = std::equal_to<Key>,
|
||||
class Allocator = std::allocator<std::pair<Key, T>>,
|
||||
bool StoreHash = false>
|
||||
using robin_pg_map = robin_map<Key, T, Hash, KeyEqual, Allocator, StoreHash, tsl::rh::prime_growth_policy>;
|
||||
|
||||
} // end namespace tsl
|
||||
|
||||
#endif
|
582
src/includes/3thparty/tsl/robin_set.h
Normal file
582
src/includes/3thparty/tsl/robin_set.h
Normal file
@ -0,0 +1,582 @@
|
||||
/**
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2017 Tessil
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
#ifndef TSL_ROBIN_SET_H
|
||||
#define TSL_ROBIN_SET_H
|
||||
|
||||
|
||||
#include <cstddef>
|
||||
#include <functional>
|
||||
#include <initializer_list>
|
||||
#include <memory>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include "robin_hash.h"
|
||||
|
||||
|
||||
namespace tsl {
|
||||
|
||||
|
||||
/**
|
||||
* Implementation of a hash set using open-adressing and the robin hood hashing algorithm with backward shift deletion.
|
||||
*
|
||||
* For operations modifying the hash set (insert, erase, rehash, ...), the strong exception guarantee
|
||||
* is only guaranteed when the expression `std::is_nothrow_swappable<Key>::value &&
|
||||
* std::is_nothrow_move_constructible<Key>::value` is true, otherwise if an exception
|
||||
* is thrown during the swap or the move, the hash set may end up in a undefined state. Per the standard
|
||||
* a `Key` with a noexcept copy constructor and no move constructor also satisfies the
|
||||
* `std::is_nothrow_move_constructible<Key>::value` criterion (and will thus guarantee the
|
||||
* strong exception for the set).
|
||||
*
|
||||
* When `StoreHash` is true, 32 bits of the hash are stored alongside the values. It can improve
|
||||
* the performance during lookups if the `KeyEqual` function takes time (or engenders a cache-miss for example)
|
||||
* as we then compare the stored hashes before comparing the keys. When `tsl::rh::power_of_two_growth_policy` is used
|
||||
* as `GrowthPolicy`, it may also speed-up the rehash process as we can avoid to recalculate the hash.
|
||||
* When it is detected that storing the hash will not incur any memory penality due to alignement (i.e.
|
||||
* `sizeof(tsl::detail_robin_hash::bucket_entry<ValueType, true>) ==
|
||||
* sizeof(tsl::detail_robin_hash::bucket_entry<ValueType, false>)`) and `tsl::rh::power_of_two_growth_policy` is
|
||||
* used, the hash will be stored even if `StoreHash` is false so that we can speed-up the rehash (but it will
|
||||
* not be used on lookups unless `StoreHash` is true).
|
||||
*
|
||||
* `GrowthPolicy` defines how the set grows and consequently how a hash value is mapped to a bucket.
|
||||
* By default the set uses `tsl::rh::power_of_two_growth_policy`. This policy keeps the number of buckets
|
||||
* to a power of two and uses a mask to set the hash to a bucket instead of the slow modulo.
|
||||
* Other growth policies are available and you may define your own growth policy,
|
||||
* check `tsl::rh::power_of_two_growth_policy` for the interface.
|
||||
*
|
||||
* `Key` must be swappable.
|
||||
*
|
||||
* `Key` must be copy and/or move constructible.
|
||||
*
|
||||
* If the destructor of `Key` throws an exception, the behaviour of the class is undefined.
|
||||
*
|
||||
* Iterators invalidation:
|
||||
* - clear, operator=, reserve, rehash: always invalidate the iterators.
|
||||
* - insert, emplace, emplace_hint, operator[]: if there is an effective insert, invalidate the iterators.
|
||||
* - erase: always invalidate the iterators.
|
||||
*/
|
||||
template<class Key,
|
||||
class Hash = std::hash<Key>,
|
||||
class KeyEqual = std::equal_to<Key>,
|
||||
class Allocator = std::allocator<Key>,
|
||||
bool StoreHash = false,
|
||||
class GrowthPolicy = tsl::rh::power_of_two_growth_policy<2>>
|
||||
class robin_set {
|
||||
private:
|
||||
template<typename U>
|
||||
using has_is_transparent = tsl::detail_robin_hash::has_is_transparent<U>;
|
||||
|
||||
class KeySelect {
|
||||
public:
|
||||
using key_type = Key;
|
||||
|
||||
const key_type& operator()(const Key& key) const noexcept {
|
||||
return key;
|
||||
}
|
||||
|
||||
key_type& operator()(Key& key) noexcept {
|
||||
return key;
|
||||
}
|
||||
};
|
||||
|
||||
using ht = detail_robin_hash::robin_hash<Key, KeySelect, void,
|
||||
Hash, KeyEqual, Allocator, StoreHash, GrowthPolicy>;
|
||||
|
||||
public:
|
||||
using key_type = typename ht::key_type;
|
||||
using value_type = typename ht::value_type;
|
||||
using size_type = typename ht::size_type;
|
||||
using difference_type = typename ht::difference_type;
|
||||
using hasher = typename ht::hasher;
|
||||
using key_equal = typename ht::key_equal;
|
||||
using allocator_type = typename ht::allocator_type;
|
||||
using reference = typename ht::reference;
|
||||
using const_reference = typename ht::const_reference;
|
||||
using pointer = typename ht::pointer;
|
||||
using const_pointer = typename ht::const_pointer;
|
||||
using iterator = typename ht::iterator;
|
||||
using const_iterator = typename ht::const_iterator;
|
||||
|
||||
|
||||
/*
|
||||
* Constructors
|
||||
*/
|
||||
robin_set(): robin_set(ht::DEFAULT_INIT_BUCKETS_SIZE) {
|
||||
}
|
||||
|
||||
explicit robin_set(size_type bucket_count,
|
||||
const Hash& hash = Hash(),
|
||||
const KeyEqual& equal = KeyEqual(),
|
||||
const Allocator& alloc = Allocator()):
|
||||
m_ht(bucket_count, hash, equal, alloc)
|
||||
{
|
||||
}
|
||||
|
||||
robin_set(size_type bucket_count,
|
||||
const Allocator& alloc): robin_set(bucket_count, Hash(), KeyEqual(), alloc)
|
||||
{
|
||||
}
|
||||
|
||||
robin_set(size_type bucket_count,
|
||||
const Hash& hash,
|
||||
const Allocator& alloc): robin_set(bucket_count, hash, KeyEqual(), alloc)
|
||||
{
|
||||
}
|
||||
|
||||
explicit robin_set(const Allocator& alloc): robin_set(ht::DEFAULT_INIT_BUCKETS_SIZE, alloc) {
|
||||
}
|
||||
|
||||
template<class InputIt>
|
||||
robin_set(InputIt first, InputIt last,
|
||||
size_type bucket_count = ht::DEFAULT_INIT_BUCKETS_SIZE,
|
||||
const Hash& hash = Hash(),
|
||||
const KeyEqual& equal = KeyEqual(),
|
||||
const Allocator& alloc = Allocator()): robin_set(bucket_count, hash, equal, alloc)
|
||||
{
|
||||
insert(first, last);
|
||||
}
|
||||
|
||||
template<class InputIt>
|
||||
robin_set(InputIt first, InputIt last,
|
||||
size_type bucket_count,
|
||||
const Allocator& alloc): robin_set(first, last, bucket_count, Hash(), KeyEqual(), alloc)
|
||||
{
|
||||
}
|
||||
|
||||
template<class InputIt>
|
||||
robin_set(InputIt first, InputIt last,
|
||||
size_type bucket_count,
|
||||
const Hash& hash,
|
||||
const Allocator& alloc): robin_set(first, last, bucket_count, hash, KeyEqual(), alloc)
|
||||
{
|
||||
}
|
||||
|
||||
robin_set(std::initializer_list<value_type> init,
|
||||
size_type bucket_count = ht::DEFAULT_INIT_BUCKETS_SIZE,
|
||||
const Hash& hash = Hash(),
|
||||
const KeyEqual& equal = KeyEqual(),
|
||||
const Allocator& alloc = Allocator()):
|
||||
robin_set(init.begin(), init.end(), bucket_count, hash, equal, alloc)
|
||||
{
|
||||
}
|
||||
|
||||
robin_set(std::initializer_list<value_type> init,
|
||||
size_type bucket_count,
|
||||
const Allocator& alloc):
|
||||
robin_set(init.begin(), init.end(), bucket_count, Hash(), KeyEqual(), alloc)
|
||||
{
|
||||
}
|
||||
|
||||
robin_set(std::initializer_list<value_type> init,
|
||||
size_type bucket_count,
|
||||
const Hash& hash,
|
||||
const Allocator& alloc):
|
||||
robin_set(init.begin(), init.end(), bucket_count, hash, KeyEqual(), alloc)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
robin_set& operator=(std::initializer_list<value_type> ilist) {
|
||||
m_ht.clear();
|
||||
|
||||
m_ht.reserve(ilist.size());
|
||||
m_ht.insert(ilist.begin(), ilist.end());
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
allocator_type get_allocator() const { return m_ht.get_allocator(); }
|
||||
|
||||
|
||||
/*
|
||||
* Iterators
|
||||
*/
|
||||
iterator begin() noexcept { return m_ht.begin(); }
|
||||
const_iterator begin() const noexcept { return m_ht.begin(); }
|
||||
const_iterator cbegin() const noexcept { return m_ht.cbegin(); }
|
||||
|
||||
iterator end() noexcept { return m_ht.end(); }
|
||||
const_iterator end() const noexcept { return m_ht.end(); }
|
||||
const_iterator cend() const noexcept { return m_ht.cend(); }
|
||||
|
||||
|
||||
/*
|
||||
* Capacity
|
||||
*/
|
||||
bool empty() const noexcept { return m_ht.empty(); }
|
||||
size_type size() const noexcept { return m_ht.size(); }
|
||||
size_type max_size() const noexcept { return m_ht.max_size(); }
|
||||
|
||||
/*
|
||||
* Modifiers
|
||||
*/
|
||||
void clear() noexcept { m_ht.clear(); }
|
||||
|
||||
|
||||
|
||||
|
||||
std::pair<iterator, bool> insert(const value_type& value) {
|
||||
return m_ht.insert(value);
|
||||
}
|
||||
|
||||
std::pair<iterator, bool> insert(value_type&& value) {
|
||||
return m_ht.insert(std::move(value));
|
||||
}
|
||||
|
||||
iterator insert(const_iterator hint, const value_type& value) {
|
||||
return m_ht.insert_hint(hint, value);
|
||||
}
|
||||
|
||||
iterator insert(const_iterator hint, value_type&& value) {
|
||||
return m_ht.insert_hint(hint, std::move(value));
|
||||
}
|
||||
|
||||
template<class InputIt>
|
||||
void insert(InputIt first, InputIt last) {
|
||||
m_ht.insert(first, last);
|
||||
}
|
||||
|
||||
void insert(std::initializer_list<value_type> ilist) {
|
||||
m_ht.insert(ilist.begin(), ilist.end());
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Due to the way elements are stored, emplace will need to move or copy the key-value once.
|
||||
* The method is equivalent to insert(value_type(std::forward<Args>(args)...));
|
||||
*
|
||||
* Mainly here for compatibility with the std::unordered_map interface.
|
||||
*/
|
||||
template<class... Args>
|
||||
std::pair<iterator, bool> emplace(Args&&... args) {
|
||||
return m_ht.emplace(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Due to the way elements are stored, emplace_hint will need to move or copy the key-value once.
|
||||
* The method is equivalent to insert(hint, value_type(std::forward<Args>(args)...));
|
||||
*
|
||||
* Mainly here for compatibility with the std::unordered_map interface.
|
||||
*/
|
||||
template<class... Args>
|
||||
iterator emplace_hint(const_iterator hint, Args&&... args) {
|
||||
return m_ht.emplace_hint(hint, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
|
||||
|
||||
iterator erase(iterator pos) { return m_ht.erase(pos); }
|
||||
iterator erase(const_iterator pos) { return m_ht.erase(pos); }
|
||||
iterator erase(const_iterator first, const_iterator last) { return m_ht.erase(first, last); }
|
||||
size_type erase(const key_type& key) { return m_ht.erase(key); }
|
||||
|
||||
/**
|
||||
* Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same
|
||||
* as hash_function()(key). Usefull to speed-up the lookup to the value if you already have the hash.
|
||||
*/
|
||||
size_type erase(const key_type& key, std::size_t precalculated_hash) {
|
||||
return m_ht.erase(key, precalculated_hash);
|
||||
}
|
||||
|
||||
/**
|
||||
* This overload only participates in the overload resolution if the typedef KeyEqual::is_transparent exists.
|
||||
* If so, K must be hashable and comparable to Key.
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
|
||||
size_type erase(const K& key) { return m_ht.erase(key); }
|
||||
|
||||
/**
|
||||
* @copydoc erase(const K& key)
|
||||
*
|
||||
* Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same
|
||||
* as hash_function()(key). Usefull to speed-up the lookup to the value if you already have the hash.
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
|
||||
size_type erase(const K& key, std::size_t precalculated_hash) {
|
||||
return m_ht.erase(key, precalculated_hash);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void swap(robin_set& other) { other.m_ht.swap(m_ht); }
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Lookup
|
||||
*/
|
||||
size_type count(const Key& key) const { return m_ht.count(key); }
|
||||
|
||||
/**
|
||||
* Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same
|
||||
* as hash_function()(key). Usefull to speed-up the lookup if you already have the hash.
|
||||
*/
|
||||
size_type count(const Key& key, std::size_t precalculated_hash) const { return m_ht.count(key, precalculated_hash); }
|
||||
|
||||
/**
|
||||
* This overload only participates in the overload resolution if the typedef KeyEqual::is_transparent exists.
|
||||
* If so, K must be hashable and comparable to Key.
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
|
||||
size_type count(const K& key) const { return m_ht.count(key); }
|
||||
|
||||
/**
|
||||
* @copydoc count(const K& key) const
|
||||
*
|
||||
* Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same
|
||||
* as hash_function()(key). Usefull to speed-up the lookup if you already have the hash.
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
|
||||
size_type count(const K& key, std::size_t precalculated_hash) const { return m_ht.count(key, precalculated_hash); }
|
||||
|
||||
|
||||
|
||||
|
||||
iterator find(const Key& key) { return m_ht.find(key); }
|
||||
|
||||
/**
|
||||
* Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same
|
||||
* as hash_function()(key). Usefull to speed-up the lookup if you already have the hash.
|
||||
*/
|
||||
iterator find(const Key& key, std::size_t precalculated_hash) { return m_ht.find(key, precalculated_hash); }
|
||||
|
||||
const_iterator find(const Key& key) const { return m_ht.find(key); }
|
||||
|
||||
/**
|
||||
* @copydoc find(const Key& key, std::size_t precalculated_hash)
|
||||
*/
|
||||
const_iterator find(const Key& key, std::size_t precalculated_hash) const { return m_ht.find(key, precalculated_hash); }
|
||||
|
||||
/**
|
||||
* This overload only participates in the overload resolution if the typedef KeyEqual::is_transparent exists.
|
||||
* If so, K must be hashable and comparable to Key.
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
|
||||
iterator find(const K& key) { return m_ht.find(key); }
|
||||
|
||||
/**
|
||||
* @copydoc find(const K& key)
|
||||
*
|
||||
* Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same
|
||||
* as hash_function()(key). Usefull to speed-up the lookup if you already have the hash.
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
|
||||
iterator find(const K& key, std::size_t precalculated_hash) { return m_ht.find(key, precalculated_hash); }
|
||||
|
||||
/**
|
||||
* @copydoc find(const K& key)
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
|
||||
const_iterator find(const K& key) const { return m_ht.find(key); }
|
||||
|
||||
/**
|
||||
* @copydoc find(const K& key)
|
||||
*
|
||||
* Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same
|
||||
* as hash_function()(key). Usefull to speed-up the lookup if you already have the hash.
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
|
||||
const_iterator find(const K& key, std::size_t precalculated_hash) const { return m_ht.find(key, precalculated_hash); }
|
||||
|
||||
|
||||
|
||||
|
||||
bool contains(const Key& key) const { return m_ht.contains(key); }
|
||||
|
||||
/**
|
||||
* Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same
|
||||
* as hash_function()(key). Usefull to speed-up the lookup if you already have the hash.
|
||||
*/
|
||||
bool contains(const Key& key, std::size_t precalculated_hash) const {
|
||||
return m_ht.contains(key, precalculated_hash);
|
||||
}
|
||||
|
||||
/**
|
||||
* This overload only participates in the overload resolution if the typedef KeyEqual::is_transparent exists.
|
||||
* If so, K must be hashable and comparable to Key.
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
|
||||
bool contains(const K& key) const { return m_ht.contains(key); }
|
||||
|
||||
/**
|
||||
* @copydoc contains(const K& key) const
|
||||
*
|
||||
* Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same
|
||||
* as hash_function()(key). Usefull to speed-up the lookup if you already have the hash.
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
|
||||
bool contains(const K& key, std::size_t precalculated_hash) const {
|
||||
return m_ht.contains(key, precalculated_hash);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
std::pair<iterator, iterator> equal_range(const Key& key) { return m_ht.equal_range(key); }
|
||||
|
||||
/**
|
||||
* Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same
|
||||
* as hash_function()(key). Usefull to speed-up the lookup if you already have the hash.
|
||||
*/
|
||||
std::pair<iterator, iterator> equal_range(const Key& key, std::size_t precalculated_hash) {
|
||||
return m_ht.equal_range(key, precalculated_hash);
|
||||
}
|
||||
|
||||
std::pair<const_iterator, const_iterator> equal_range(const Key& key) const { return m_ht.equal_range(key); }
|
||||
|
||||
/**
|
||||
* @copydoc equal_range(const Key& key, std::size_t precalculated_hash)
|
||||
*/
|
||||
std::pair<const_iterator, const_iterator> equal_range(const Key& key, std::size_t precalculated_hash) const {
|
||||
return m_ht.equal_range(key, precalculated_hash);
|
||||
}
|
||||
|
||||
/**
|
||||
* This overload only participates in the overload resolution if the typedef KeyEqual::is_transparent exists.
|
||||
* If so, K must be hashable and comparable to Key.
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
|
||||
std::pair<iterator, iterator> equal_range(const K& key) { return m_ht.equal_range(key); }
|
||||
|
||||
/**
|
||||
* @copydoc equal_range(const K& key)
|
||||
*
|
||||
* Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same
|
||||
* as hash_function()(key). Usefull to speed-up the lookup if you already have the hash.
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
|
||||
std::pair<iterator, iterator> equal_range(const K& key, std::size_t precalculated_hash) {
|
||||
return m_ht.equal_range(key, precalculated_hash);
|
||||
}
|
||||
|
||||
/**
|
||||
* @copydoc equal_range(const K& key)
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
|
||||
std::pair<const_iterator, const_iterator> equal_range(const K& key) const { return m_ht.equal_range(key); }
|
||||
|
||||
/**
|
||||
* @copydoc equal_range(const K& key, std::size_t precalculated_hash)
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
|
||||
std::pair<const_iterator, const_iterator> equal_range(const K& key, std::size_t precalculated_hash) const {
|
||||
return m_ht.equal_range(key, precalculated_hash);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Bucket interface
|
||||
*/
|
||||
size_type bucket_count() const { return m_ht.bucket_count(); }
|
||||
size_type max_bucket_count() const { return m_ht.max_bucket_count(); }
|
||||
|
||||
|
||||
/*
|
||||
* Hash policy
|
||||
*/
|
||||
float load_factor() const { return m_ht.load_factor(); }
|
||||
|
||||
float min_load_factor() const { return m_ht.min_load_factor(); }
|
||||
float max_load_factor() const { return m_ht.max_load_factor(); }
|
||||
|
||||
/**
|
||||
* Set the `min_load_factor` to `ml`. When the `load_factor` of the set goes
|
||||
* below `min_load_factor` after some erase operations, the set will be
|
||||
* shrunk when an insertion occurs. The erase method itself never shrinks
|
||||
* the set.
|
||||
*
|
||||
* The default value of `min_load_factor` is 0.0f, the set never shrinks by default.
|
||||
*/
|
||||
void min_load_factor(float ml) { m_ht.min_load_factor(ml); }
|
||||
void max_load_factor(float ml) { m_ht.max_load_factor(ml); }
|
||||
|
||||
void rehash(size_type count) { m_ht.rehash(count); }
|
||||
void reserve(size_type count) { m_ht.reserve(count); }
|
||||
|
||||
|
||||
/*
|
||||
* Observers
|
||||
*/
|
||||
hasher hash_function() const { return m_ht.hash_function(); }
|
||||
key_equal key_eq() const { return m_ht.key_eq(); }
|
||||
|
||||
|
||||
/*
|
||||
* Other
|
||||
*/
|
||||
|
||||
/**
|
||||
* Convert a const_iterator to an iterator.
|
||||
*/
|
||||
iterator mutable_iterator(const_iterator pos) {
|
||||
return m_ht.mutable_iterator(pos);
|
||||
}
|
||||
|
||||
friend bool operator==(const robin_set& lhs, const robin_set& rhs) {
|
||||
if(lhs.size() != rhs.size()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for(const auto& element_lhs: lhs) {
|
||||
const auto it_element_rhs = rhs.find(element_lhs);
|
||||
if(it_element_rhs == rhs.cend()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
friend bool operator!=(const robin_set& lhs, const robin_set& rhs) {
|
||||
return !operator==(lhs, rhs);
|
||||
}
|
||||
|
||||
friend void swap(robin_set& lhs, robin_set& rhs) {
|
||||
lhs.swap(rhs);
|
||||
}
|
||||
|
||||
private:
|
||||
ht m_ht;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Same as `tsl::robin_set<Key, Hash, KeyEqual, Allocator, StoreHash, tsl::rh::prime_growth_policy>`.
|
||||
*/
|
||||
template<class Key,
|
||||
class Hash = std::hash<Key>,
|
||||
class KeyEqual = std::equal_to<Key>,
|
||||
class Allocator = std::allocator<Key>,
|
||||
bool StoreHash = false>
|
||||
using robin_pg_set = robin_set<Key, Hash, KeyEqual, Allocator, StoreHash, tsl::rh::prime_growth_policy>;
|
||||
|
||||
} // end namespace tsl
|
||||
|
||||
#endif
|
||||
|
295
src/includes/3thparty/tsl/sparse_growth_policy.h
Normal file
295
src/includes/3thparty/tsl/sparse_growth_policy.h
Normal file
@ -0,0 +1,295 @@
|
||||
/**
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2017 Tessil
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
#ifndef TSL_SPARSE_GROWTH_POLICY_H
|
||||
#define TSL_SPARSE_GROWTH_POLICY_H
|
||||
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <climits>
|
||||
#include <cmath>
|
||||
#include <cstddef>
|
||||
#include <iterator>
|
||||
#include <limits>
|
||||
#include <ratio>
|
||||
#include <stdexcept>
|
||||
|
||||
|
||||
namespace tsl {
|
||||
namespace sh {
|
||||
|
||||
/**
|
||||
* Grow the hash table by a factor of GrowthFactor keeping the bucket count to a power of two. It allows
|
||||
* the table to use a mask operation instead of a modulo operation to map a hash to a bucket.
|
||||
*
|
||||
* GrowthFactor must be a power of two >= 2.
|
||||
*/
|
||||
template<std::size_t GrowthFactor>
|
||||
class power_of_two_growth_policy {
|
||||
public:
|
||||
/**
|
||||
* Called on the hash table creation and on rehash. The number of buckets for the table is passed in parameter.
|
||||
* This number is a minimum, the policy may update this value with a higher value if needed (but not lower).
|
||||
*
|
||||
* If 0 is given, min_bucket_count_in_out must still be 0 after the policy creation and
|
||||
* bucket_for_hash must always return 0 in this case.
|
||||
*/
|
||||
explicit power_of_two_growth_policy(std::size_t& min_bucket_count_in_out) {
|
||||
if(min_bucket_count_in_out > max_bucket_count()) {
|
||||
throw std::length_error("The hash table exceeds its maxmimum size.");
|
||||
}
|
||||
|
||||
if(min_bucket_count_in_out > 0) {
|
||||
min_bucket_count_in_out = round_up_to_power_of_two(min_bucket_count_in_out);
|
||||
m_mask = min_bucket_count_in_out - 1;
|
||||
}
|
||||
else {
|
||||
m_mask = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the bucket [0, bucket_count()) to which the hash belongs.
|
||||
* If bucket_count() is 0, it must always return 0.
|
||||
*/
|
||||
std::size_t bucket_for_hash(std::size_t hash) const noexcept {
|
||||
return hash & m_mask;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the number of buckets that should be used on next growth.
|
||||
*/
|
||||
std::size_t next_bucket_count() const {
|
||||
if((m_mask + 1) > max_bucket_count() / GrowthFactor) {
|
||||
throw std::length_error("The hash table exceeds its maxmimum size.");
|
||||
}
|
||||
|
||||
return (m_mask + 1) * GrowthFactor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the maximum number of buckets supported by the policy.
|
||||
*/
|
||||
std::size_t max_bucket_count() const {
|
||||
// Largest power of two.
|
||||
return (std::numeric_limits<std::size_t>::max() / 2) + 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the growth policy as if it was created with a bucket count of 0.
|
||||
* After a clear, the policy must always return 0 when bucket_for_hash is called.
|
||||
*/
|
||||
void clear() noexcept {
|
||||
m_mask = 0;
|
||||
}
|
||||
|
||||
private:
|
||||
static std::size_t round_up_to_power_of_two(std::size_t value) {
|
||||
if(is_power_of_two(value)) {
|
||||
return value;
|
||||
}
|
||||
|
||||
if(value == 0) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
--value;
|
||||
for(std::size_t i = 1; i < sizeof(std::size_t) * CHAR_BIT; i *= 2) {
|
||||
value |= value >> i;
|
||||
}
|
||||
|
||||
return value + 1;
|
||||
}
|
||||
|
||||
static constexpr bool is_power_of_two(std::size_t value) {
|
||||
return value != 0 && (value & (value - 1)) == 0;
|
||||
}
|
||||
|
||||
protected:
|
||||
static_assert(is_power_of_two(GrowthFactor) && GrowthFactor >= 2, "GrowthFactor must be a power of two >= 2.");
|
||||
|
||||
std::size_t m_mask;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Grow the hash table by GrowthFactor::num / GrowthFactor::den and use a modulo to map a hash
|
||||
* to a bucket. Slower but it can be useful if you want a slower growth.
|
||||
*/
|
||||
template<class GrowthFactor = std::ratio<3, 2>>
|
||||
class mod_growth_policy {
|
||||
public:
|
||||
explicit mod_growth_policy(std::size_t& min_bucket_count_in_out) {
|
||||
if(min_bucket_count_in_out > max_bucket_count()) {
|
||||
throw std::length_error("The hash table exceeds its maxmimum size.");
|
||||
}
|
||||
|
||||
if(min_bucket_count_in_out > 0) {
|
||||
m_mod = min_bucket_count_in_out;
|
||||
}
|
||||
else {
|
||||
m_mod = 1;
|
||||
}
|
||||
}
|
||||
|
||||
std::size_t bucket_for_hash(std::size_t hash) const noexcept {
|
||||
return hash % m_mod;
|
||||
}
|
||||
|
||||
std::size_t next_bucket_count() const {
|
||||
if(m_mod == max_bucket_count()) {
|
||||
throw std::length_error("The hash table exceeds its maxmimum size.");
|
||||
}
|
||||
|
||||
const double next_bucket_count = std::ceil(double(m_mod) * REHASH_SIZE_MULTIPLICATION_FACTOR);
|
||||
if(!std::isnormal(next_bucket_count)) {
|
||||
throw std::length_error("The hash table exceeds its maxmimum size.");
|
||||
}
|
||||
|
||||
if(next_bucket_count > double(max_bucket_count())) {
|
||||
return max_bucket_count();
|
||||
}
|
||||
else {
|
||||
return std::size_t(next_bucket_count);
|
||||
}
|
||||
}
|
||||
|
||||
std::size_t max_bucket_count() const {
|
||||
return MAX_BUCKET_COUNT;
|
||||
}
|
||||
|
||||
void clear() noexcept {
|
||||
m_mod = 1;
|
||||
}
|
||||
|
||||
private:
|
||||
static constexpr double REHASH_SIZE_MULTIPLICATION_FACTOR = 1.0 * GrowthFactor::num / GrowthFactor::den;
|
||||
static const std::size_t MAX_BUCKET_COUNT =
|
||||
std::size_t(double(
|
||||
std::numeric_limits<std::size_t>::max() / REHASH_SIZE_MULTIPLICATION_FACTOR
|
||||
));
|
||||
|
||||
static_assert(REHASH_SIZE_MULTIPLICATION_FACTOR >= 1.1, "Growth factor should be >= 1.1.");
|
||||
|
||||
std::size_t m_mod;
|
||||
};
|
||||
|
||||
|
||||
|
||||
namespace detail {
|
||||
|
||||
static constexpr const std::array<std::size_t, 40> PRIMES = {{
|
||||
1ul, 5ul, 17ul, 29ul, 37ul, 53ul, 67ul, 79ul, 97ul, 131ul, 193ul, 257ul, 389ul, 521ul, 769ul, 1031ul,
|
||||
1543ul, 2053ul, 3079ul, 6151ul, 12289ul, 24593ul, 49157ul, 98317ul, 196613ul, 393241ul, 786433ul,
|
||||
1572869ul, 3145739ul, 6291469ul, 12582917ul, 25165843ul, 50331653ul, 100663319ul, 201326611ul,
|
||||
402653189ul, 805306457ul, 1610612741ul, 3221225473ul, 4294967291ul
|
||||
}};
|
||||
|
||||
template<unsigned int IPrime>
|
||||
static constexpr std::size_t mod(std::size_t hash) { return hash % PRIMES[IPrime]; }
|
||||
|
||||
// MOD_PRIME[iprime](hash) returns hash % PRIMES[iprime]. This table allows for faster modulo as the
|
||||
// compiler can optimize the modulo code better with a constant known at the compilation.
|
||||
static constexpr const std::array<std::size_t(*)(std::size_t), 40> MOD_PRIME = {{
|
||||
&mod<0>, &mod<1>, &mod<2>, &mod<3>, &mod<4>, &mod<5>, &mod<6>, &mod<7>, &mod<8>, &mod<9>, &mod<10>,
|
||||
&mod<11>, &mod<12>, &mod<13>, &mod<14>, &mod<15>, &mod<16>, &mod<17>, &mod<18>, &mod<19>, &mod<20>,
|
||||
&mod<21>, &mod<22>, &mod<23>, &mod<24>, &mod<25>, &mod<26>, &mod<27>, &mod<28>, &mod<29>, &mod<30>,
|
||||
&mod<31>, &mod<32>, &mod<33>, &mod<34>, &mod<35>, &mod<36>, &mod<37> , &mod<38>, &mod<39>
|
||||
}};
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Grow the hash table by using prime numbers as bucket count. Slower than tsl::sh::power_of_two_growth_policy in
|
||||
* general but will probably distribute the values around better in the buckets with a poor hash function.
|
||||
*
|
||||
* To allow the compiler to optimize the modulo operation, a lookup table is used with constant primes numbers.
|
||||
*
|
||||
* With a switch the code would look like:
|
||||
* \code
|
||||
* switch(iprime) { // iprime is the current prime of the hash table
|
||||
* case 0: hash % 5ul;
|
||||
* break;
|
||||
* case 1: hash % 17ul;
|
||||
* break;
|
||||
* case 2: hash % 29ul;
|
||||
* break;
|
||||
* ...
|
||||
* }
|
||||
* \endcode
|
||||
*
|
||||
* Due to the constant variable in the modulo the compiler is able to optimize the operation
|
||||
* by a series of multiplications, substractions and shifts.
|
||||
*
|
||||
* The 'hash % 5' could become something like 'hash - (hash * 0xCCCCCCCD) >> 34) * 5' in a 64 bits environement.
|
||||
*/
|
||||
class prime_growth_policy {
|
||||
public:
|
||||
explicit prime_growth_policy(std::size_t& min_bucket_count_in_out) {
|
||||
auto it_prime = std::lower_bound(detail::PRIMES.begin(),
|
||||
detail::PRIMES.end(), min_bucket_count_in_out);
|
||||
if(it_prime == detail::PRIMES.end()) {
|
||||
throw std::length_error("The hash table exceeds its maxmimum size.");
|
||||
}
|
||||
|
||||
m_iprime = static_cast<unsigned int>(std::distance(detail::PRIMES.begin(), it_prime));
|
||||
if(min_bucket_count_in_out > 0) {
|
||||
min_bucket_count_in_out = *it_prime;
|
||||
}
|
||||
else {
|
||||
min_bucket_count_in_out = 0;
|
||||
}
|
||||
}
|
||||
|
||||
std::size_t bucket_for_hash(std::size_t hash) const noexcept {
|
||||
return detail::MOD_PRIME[m_iprime](hash);
|
||||
}
|
||||
|
||||
std::size_t next_bucket_count() const {
|
||||
if(m_iprime + 1 >= detail::PRIMES.size()) {
|
||||
throw std::length_error("The hash table exceeds its maxmimum size.");
|
||||
}
|
||||
|
||||
return detail::PRIMES[m_iprime + 1];
|
||||
}
|
||||
|
||||
std::size_t max_bucket_count() const {
|
||||
return detail::PRIMES.back();
|
||||
}
|
||||
|
||||
void clear() noexcept {
|
||||
m_iprime = 0;
|
||||
}
|
||||
|
||||
private:
|
||||
unsigned int m_iprime;
|
||||
|
||||
static_assert(std::numeric_limits<decltype(m_iprime)>::max() >= detail::PRIMES.size(),
|
||||
"The type of m_iprime is not big enough.");
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
2127
src/includes/3thparty/tsl/sparse_hash.h
Normal file
2127
src/includes/3thparty/tsl/sparse_hash.h
Normal file
File diff suppressed because it is too large
Load Diff
749
src/includes/3thparty/tsl/sparse_map.h
Normal file
749
src/includes/3thparty/tsl/sparse_map.h
Normal file
@ -0,0 +1,749 @@
|
||||
/**
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2017 Tessil
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
#ifndef TSL_SPARSE_MAP_H
|
||||
#define TSL_SPARSE_MAP_H
|
||||
|
||||
|
||||
#include <cstddef>
|
||||
#include <functional>
|
||||
#include <initializer_list>
|
||||
#include <memory>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include "sparse_hash.h"
|
||||
|
||||
|
||||
namespace tsl {
|
||||
|
||||
|
||||
/**
|
||||
* Implementation of a sparse hash map using open-addressing with quadratic probing.
|
||||
* The goal on the hash map is to be the most memory efficient possible, even at low load factor,
|
||||
* while keeping reasonable performances.
|
||||
*
|
||||
* `GrowthPolicy` defines how the map grows and consequently how a hash value is mapped to a bucket.
|
||||
* By default the map uses `tsl::sh::power_of_two_growth_policy`. This policy keeps the number of buckets
|
||||
* to a power of two and uses a mask to map the hash to a bucket instead of the slow modulo.
|
||||
* Other growth policies are available and you may define your own growth policy,
|
||||
* check `tsl::sh::power_of_two_growth_policy` for the interface.
|
||||
*
|
||||
* `ExceptionSafety` defines the exception guarantee provided by the class. By default only the basic
|
||||
* exception safety is guaranteed which mean that all resources used by the hash map will be freed (no memory leaks)
|
||||
* but the hash map may end-up in an undefined state if an exception is thrown (undefined here means that some elements
|
||||
* may be missing). This can ONLY happen on rehash (either on insert or if `rehash` is called explicitly) and will
|
||||
* occur if the Allocator can't allocate memory (`std::bad_alloc`) or if the copy constructor (when a nothrow
|
||||
* move constructor is not available) throws an exception. This can be avoided by calling `reserve` beforehand.
|
||||
* This basic guarantee is similar to the one of `google::sparse_hash_map` and `spp::sparse_hash_map`.
|
||||
* It is possible to ask for the strong exception guarantee with `tsl::sh::exception_safety::strong`, the drawback
|
||||
* is that the map will be slower on rehashes and will also need more memory on rehashes.
|
||||
*
|
||||
* `Sparsity` defines how much the hash set will compromise between insertion speed and memory usage. A high
|
||||
* sparsity means less memory usage but longer insertion times, and vice-versa for low sparsity. The default
|
||||
* `tsl::sh::sparsity::medium` sparsity offers a good compromise. It doesn't change the lookup speed.
|
||||
*
|
||||
* `Key` and `T` must be nothrow move constructible and/or copy constructible.
|
||||
*
|
||||
* If the destructor of `Key` or `T` throws an exception, the behaviour of the class is undefined.
|
||||
*
|
||||
* Iterators invalidation:
|
||||
* - clear, operator=, reserve, rehash: always invalidate the iterators.
|
||||
* - insert, emplace, emplace_hint, operator[]: if there is an effective insert, invalidate the iterators.
|
||||
* - erase: always invalidate the iterators.
|
||||
*/
|
||||
template<class Key,
|
||||
class T,
|
||||
class Hash = std::hash<Key>,
|
||||
class KeyEqual = std::equal_to<Key>,
|
||||
class Allocator = std::allocator<std::pair<Key, T>>,
|
||||
class GrowthPolicy = tsl::sh::power_of_two_growth_policy<2>,
|
||||
tsl::sh::exception_safety ExceptionSafety = tsl::sh::exception_safety::basic,
|
||||
tsl::sh::sparsity Sparsity = tsl::sh::sparsity::medium>
|
||||
class sparse_map {
|
||||
private:
|
||||
template<typename U>
|
||||
using has_is_transparent = tsl::detail_sparse_hash::has_is_transparent<U>;
|
||||
|
||||
class KeySelect {
|
||||
public:
|
||||
using key_type = Key;
|
||||
|
||||
const key_type& operator()(const std::pair<Key, T>& key_value) const noexcept {
|
||||
return key_value.first;
|
||||
}
|
||||
|
||||
key_type& operator()(std::pair<Key, T>& key_value) noexcept {
|
||||
return key_value.first;
|
||||
}
|
||||
};
|
||||
|
||||
class ValueSelect {
|
||||
public:
|
||||
using value_type = T;
|
||||
|
||||
const value_type& operator()(const std::pair<Key, T>& key_value) const noexcept {
|
||||
return key_value.second;
|
||||
}
|
||||
|
||||
value_type& operator()(std::pair<Key, T>& key_value) noexcept {
|
||||
return key_value.second;
|
||||
}
|
||||
};
|
||||
|
||||
using ht = detail_sparse_hash::sparse_hash<std::pair<Key, T>, KeySelect, ValueSelect,
|
||||
Hash, KeyEqual, Allocator, GrowthPolicy,
|
||||
ExceptionSafety, Sparsity,
|
||||
tsl::sh::probing::quadratic>;
|
||||
|
||||
public:
|
||||
using key_type = typename ht::key_type;
|
||||
using mapped_type = T;
|
||||
using value_type = typename ht::value_type;
|
||||
using size_type = typename ht::size_type;
|
||||
using difference_type = typename ht::difference_type;
|
||||
using hasher = typename ht::hasher;
|
||||
using key_equal = typename ht::key_equal;
|
||||
using allocator_type = typename ht::allocator_type;
|
||||
using reference = typename ht::reference;
|
||||
using const_reference = typename ht::const_reference;
|
||||
using pointer = typename ht::pointer;
|
||||
using const_pointer = typename ht::const_pointer;
|
||||
using iterator = typename ht::iterator;
|
||||
using const_iterator = typename ht::const_iterator;
|
||||
|
||||
|
||||
public:
|
||||
/*
|
||||
* Constructors
|
||||
*/
|
||||
sparse_map(): sparse_map(ht::DEFAULT_INIT_BUCKET_COUNT) {
|
||||
}
|
||||
|
||||
explicit sparse_map(size_type bucket_count,
|
||||
const Hash& hash = Hash(),
|
||||
const KeyEqual& equal = KeyEqual(),
|
||||
const Allocator& alloc = Allocator()):
|
||||
m_ht(bucket_count, hash, equal, alloc, ht::DEFAULT_MAX_LOAD_FACTOR)
|
||||
{
|
||||
}
|
||||
|
||||
sparse_map(size_type bucket_count,
|
||||
const Allocator& alloc): sparse_map(bucket_count, Hash(), KeyEqual(), alloc)
|
||||
{
|
||||
}
|
||||
|
||||
sparse_map(size_type bucket_count,
|
||||
const Hash& hash,
|
||||
const Allocator& alloc): sparse_map(bucket_count, hash, KeyEqual(), alloc)
|
||||
{
|
||||
}
|
||||
|
||||
explicit sparse_map(const Allocator& alloc): sparse_map(ht::DEFAULT_INIT_BUCKET_COUNT, alloc) {
|
||||
}
|
||||
|
||||
template<class InputIt>
|
||||
sparse_map(InputIt first, InputIt last,
|
||||
size_type bucket_count = ht::DEFAULT_INIT_BUCKET_COUNT,
|
||||
const Hash& hash = Hash(),
|
||||
const KeyEqual& equal = KeyEqual(),
|
||||
const Allocator& alloc = Allocator()): sparse_map(bucket_count, hash, equal, alloc)
|
||||
{
|
||||
insert(first, last);
|
||||
}
|
||||
|
||||
template<class InputIt>
|
||||
sparse_map(InputIt first, InputIt last,
|
||||
size_type bucket_count,
|
||||
const Allocator& alloc): sparse_map(first, last, bucket_count, Hash(), KeyEqual(), alloc)
|
||||
{
|
||||
}
|
||||
|
||||
template<class InputIt>
|
||||
sparse_map(InputIt first, InputIt last,
|
||||
size_type bucket_count,
|
||||
const Hash& hash,
|
||||
const Allocator& alloc): sparse_map(first, last, bucket_count, hash, KeyEqual(), alloc)
|
||||
{
|
||||
}
|
||||
|
||||
sparse_map(std::initializer_list<value_type> init,
|
||||
size_type bucket_count = ht::DEFAULT_INIT_BUCKET_COUNT,
|
||||
const Hash& hash = Hash(),
|
||||
const KeyEqual& equal = KeyEqual(),
|
||||
const Allocator& alloc = Allocator()):
|
||||
sparse_map(init.begin(), init.end(), bucket_count, hash, equal, alloc)
|
||||
{
|
||||
}
|
||||
|
||||
sparse_map(std::initializer_list<value_type> init,
|
||||
size_type bucket_count,
|
||||
const Allocator& alloc):
|
||||
sparse_map(init.begin(), init.end(), bucket_count, Hash(), KeyEqual(), alloc)
|
||||
{
|
||||
}
|
||||
|
||||
sparse_map(std::initializer_list<value_type> init,
|
||||
size_type bucket_count,
|
||||
const Hash& hash,
|
||||
const Allocator& alloc):
|
||||
sparse_map(init.begin(), init.end(), bucket_count, hash, KeyEqual(), alloc)
|
||||
{
|
||||
}
|
||||
|
||||
sparse_map& operator=(std::initializer_list<value_type> ilist) {
|
||||
m_ht.clear();
|
||||
|
||||
m_ht.reserve(ilist.size());
|
||||
m_ht.insert(ilist.begin(), ilist.end());
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
allocator_type get_allocator() const { return m_ht.get_allocator(); }
|
||||
|
||||
|
||||
/*
|
||||
* Iterators
|
||||
*/
|
||||
iterator begin() noexcept { return m_ht.begin(); }
|
||||
const_iterator begin() const noexcept { return m_ht.begin(); }
|
||||
const_iterator cbegin() const noexcept { return m_ht.cbegin(); }
|
||||
|
||||
iterator end() noexcept { return m_ht.end(); }
|
||||
const_iterator end() const noexcept { return m_ht.end(); }
|
||||
const_iterator cend() const noexcept { return m_ht.cend(); }
|
||||
|
||||
|
||||
/*
|
||||
* Capacity
|
||||
*/
|
||||
bool empty() const noexcept { return m_ht.empty(); }
|
||||
size_type size() const noexcept { return m_ht.size(); }
|
||||
size_type max_size() const noexcept { return m_ht.max_size(); }
|
||||
|
||||
/*
|
||||
* Modifiers
|
||||
*/
|
||||
void clear() noexcept { m_ht.clear(); }
|
||||
|
||||
|
||||
|
||||
std::pair<iterator, bool> insert(const value_type& value) {
|
||||
return m_ht.insert(value);
|
||||
}
|
||||
|
||||
template<class P, typename std::enable_if<std::is_constructible<value_type, P&&>::value>::type* = nullptr>
|
||||
std::pair<iterator, bool> insert(P&& value) {
|
||||
return m_ht.emplace(std::forward<P>(value));
|
||||
}
|
||||
|
||||
std::pair<iterator, bool> insert(value_type&& value) {
|
||||
return m_ht.insert(std::move(value));
|
||||
}
|
||||
|
||||
|
||||
iterator insert(const_iterator hint, const value_type& value) {
|
||||
return m_ht.insert_hint(hint, value);
|
||||
}
|
||||
|
||||
template<class P, typename std::enable_if<std::is_constructible<value_type, P&&>::value>::type* = nullptr>
|
||||
iterator insert(const_iterator hint, P&& value) {
|
||||
return m_ht.emplace_hint(hint, std::forward<P>(value));
|
||||
}
|
||||
|
||||
iterator insert(const_iterator hint, value_type&& value) {
|
||||
return m_ht.insert_hint(hint, std::move(value));
|
||||
}
|
||||
|
||||
|
||||
template<class InputIt>
|
||||
void insert(InputIt first, InputIt last) {
|
||||
m_ht.insert(first, last);
|
||||
}
|
||||
|
||||
void insert(std::initializer_list<value_type> ilist) {
|
||||
m_ht.insert(ilist.begin(), ilist.end());
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
template<class M>
|
||||
std::pair<iterator, bool> insert_or_assign(const key_type& k, M&& obj) {
|
||||
return m_ht.insert_or_assign(k, std::forward<M>(obj));
|
||||
}
|
||||
|
||||
template<class M>
|
||||
std::pair<iterator, bool> insert_or_assign(key_type&& k, M&& obj) {
|
||||
return m_ht.insert_or_assign(std::move(k), std::forward<M>(obj));
|
||||
}
|
||||
|
||||
template<class M>
|
||||
iterator insert_or_assign(const_iterator hint, const key_type& k, M&& obj) {
|
||||
return m_ht.insert_or_assign(hint, k, std::forward<M>(obj));
|
||||
}
|
||||
|
||||
template<class M>
|
||||
iterator insert_or_assign(const_iterator hint, key_type&& k, M&& obj) {
|
||||
return m_ht.insert_or_assign(hint, std::move(k), std::forward<M>(obj));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Due to the way elements are stored, emplace will need to move or copy the key-value once.
|
||||
* The method is equivalent to `insert(value_type(std::forward<Args>(args)...));`.
|
||||
*
|
||||
* Mainly here for compatibility with the `std::unordered_map` interface.
|
||||
*/
|
||||
template<class... Args>
|
||||
std::pair<iterator, bool> emplace(Args&&... args) {
|
||||
return m_ht.emplace(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Due to the way elements are stored, emplace_hint will need to move or copy the key-value once.
|
||||
* The method is equivalent to `insert(hint, value_type(std::forward<Args>(args)...));`.
|
||||
*
|
||||
* Mainly here for compatibility with the `std::unordered_map` interface.
|
||||
*/
|
||||
template<class... Args>
|
||||
iterator emplace_hint(const_iterator hint, Args&&... args) {
|
||||
return m_ht.emplace_hint(hint, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
template<class... Args>
|
||||
std::pair<iterator, bool> try_emplace(const key_type& k, Args&&... args) {
|
||||
return m_ht.try_emplace(k, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template<class... Args>
|
||||
std::pair<iterator, bool> try_emplace(key_type&& k, Args&&... args) {
|
||||
return m_ht.try_emplace(std::move(k), std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template<class... Args>
|
||||
iterator try_emplace(const_iterator hint, const key_type& k, Args&&... args) {
|
||||
return m_ht.try_emplace_hint(hint, k, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template<class... Args>
|
||||
iterator try_emplace(const_iterator hint, key_type&& k, Args&&... args) {
|
||||
return m_ht.try_emplace_hint(hint, std::move(k), std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
iterator erase(iterator pos) { return m_ht.erase(pos); }
|
||||
iterator erase(const_iterator pos) { return m_ht.erase(pos); }
|
||||
iterator erase(const_iterator first, const_iterator last) { return m_ht.erase(first, last); }
|
||||
size_type erase(const key_type& key) { return m_ht.erase(key); }
|
||||
|
||||
/**
|
||||
* Use the hash value `precalculated_hash` instead of hashing the key. The hash value should be the same
|
||||
* as `hash_function()(key)`, otherwise the behaviour is undefined. Useful to speed-up the lookup
|
||||
* if you already have the hash.
|
||||
*/
|
||||
size_type erase(const key_type& key, std::size_t precalculated_hash) {
|
||||
return m_ht.erase(key, precalculated_hash);
|
||||
}
|
||||
|
||||
/**
|
||||
* This overload only participates in the overload resolution if the typedef `KeyEqual::is_transparent` exists.
|
||||
* If so, `K` must be hashable and comparable to `Key`.
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
|
||||
size_type erase(const K& key) { return m_ht.erase(key); }
|
||||
|
||||
/**
|
||||
* @copydoc erase(const K& key)
|
||||
*
|
||||
* Use the hash value `precalculated_hash` instead of hashing the key. The hash value should be the same
|
||||
* as `hash_function()(key)`, otherwise the behaviour is undefined. Useful to speed-up the lookup
|
||||
* if you already have the hash.
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
|
||||
size_type erase(const K& key, std::size_t precalculated_hash) {
|
||||
return m_ht.erase(key, precalculated_hash);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void swap(sparse_map& other) { other.m_ht.swap(m_ht); }
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Lookup
|
||||
*/
|
||||
T& at(const Key& key) { return m_ht.at(key); }
|
||||
|
||||
/**
|
||||
* Use the hash value `precalculated_hash` instead of hashing the key. The hash value should be the same
|
||||
* as `hash_function()(key)`, otherwise the behaviour is undefined. Useful to speed-up the lookup
|
||||
* if you already have the hash.
|
||||
*/
|
||||
T& at(const Key& key, std::size_t precalculated_hash) { return m_ht.at(key, precalculated_hash); }
|
||||
|
||||
|
||||
const T& at(const Key& key) const { return m_ht.at(key); }
|
||||
|
||||
/**
|
||||
* @copydoc at(const Key& key, std::size_t precalculated_hash)
|
||||
*/
|
||||
const T& at(const Key& key, std::size_t precalculated_hash) const { return m_ht.at(key, precalculated_hash); }
|
||||
|
||||
|
||||
/**
|
||||
* This overload only participates in the overload resolution if the typedef `KeyEqual::is_transparent` exists.
|
||||
* If so, `K` must be hashable and comparable to `Key`.
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
|
||||
T& at(const K& key) { return m_ht.at(key); }
|
||||
|
||||
/**
|
||||
* @copydoc at(const K& key)
|
||||
*
|
||||
* Use the hash value `precalculated_hash` instead of hashing the key. The hash value should be the same
|
||||
* as `hash_function()(key)`, otherwise the behaviour is undefined. Useful to speed-up the lookup
|
||||
* if you already have the hash.
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
|
||||
T& at(const K& key, std::size_t precalculated_hash) { return m_ht.at(key, precalculated_hash); }
|
||||
|
||||
|
||||
/**
|
||||
* @copydoc at(const K& key)
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
|
||||
const T& at(const K& key) const { return m_ht.at(key); }
|
||||
|
||||
/**
|
||||
* @copydoc at(const K& key, std::size_t precalculated_hash)
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
|
||||
const T& at(const K& key, std::size_t precalculated_hash) const { return m_ht.at(key, precalculated_hash); }
|
||||
|
||||
|
||||
|
||||
|
||||
T& operator[](const Key& key) { return m_ht[key]; }
|
||||
T& operator[](Key&& key) { return m_ht[std::move(key)]; }
|
||||
|
||||
|
||||
|
||||
|
||||
size_type count(const Key& key) const { return m_ht.count(key); }
|
||||
|
||||
/**
|
||||
* Use the hash value `precalculated_hash` instead of hashing the key. The hash value should be the same
|
||||
* as `hash_function()(key)`, otherwise the behaviour is undefined. Useful to speed-up the lookup
|
||||
* if you already have the hash.
|
||||
*/
|
||||
size_type count(const Key& key, std::size_t precalculated_hash) const {
|
||||
return m_ht.count(key, precalculated_hash);
|
||||
}
|
||||
|
||||
/**
|
||||
* This overload only participates in the overload resolution if the typedef `KeyEqual::is_transparent` exists.
|
||||
* If so, `K` must be hashable and comparable to `Key`.
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
|
||||
size_type count(const K& key) const { return m_ht.count(key); }
|
||||
|
||||
/**
|
||||
* @copydoc count(const K& key) const
|
||||
*
|
||||
* Use the hash value `precalculated_hash` instead of hashing the key. The hash value should be the same
|
||||
* as `hash_function()(key)`, otherwise the behaviour is undefined. Useful to speed-up the lookup
|
||||
* if you already have the hash.
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
|
||||
size_type count(const K& key, std::size_t precalculated_hash) const { return m_ht.count(key, precalculated_hash); }
|
||||
|
||||
|
||||
|
||||
|
||||
iterator find(const Key& key) { return m_ht.find(key); }
|
||||
|
||||
/**
|
||||
* Use the hash value `precalculated_hash` instead of hashing the key. The hash value should be the same
|
||||
* as `hash_function()(key)`, otherwise the behaviour is undefined. Useful to speed-up the lookup
|
||||
* if you already have the hash.
|
||||
*/
|
||||
iterator find(const Key& key, std::size_t precalculated_hash) { return m_ht.find(key, precalculated_hash); }
|
||||
|
||||
const_iterator find(const Key& key) const { return m_ht.find(key); }
|
||||
|
||||
/**
|
||||
* @copydoc find(const Key& key, std::size_t precalculated_hash)
|
||||
*/
|
||||
const_iterator find(const Key& key, std::size_t precalculated_hash) const {
|
||||
return m_ht.find(key, precalculated_hash);
|
||||
}
|
||||
|
||||
/**
|
||||
* This overload only participates in the overload resolution if the typedef `KeyEqual::is_transparent` exists.
|
||||
* If so, `K` must be hashable and comparable to `Key`.
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
|
||||
iterator find(const K& key) { return m_ht.find(key); }
|
||||
|
||||
/**
|
||||
* @copydoc find(const K& key)
|
||||
*
|
||||
* Use the hash value `precalculated_hash` instead of hashing the key. The hash value should be the same
|
||||
* as `hash_function()(key)`, otherwise the behaviour is undefined. Useful to speed-up the lookup
|
||||
* if you already have the hash.
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
|
||||
iterator find(const K& key, std::size_t precalculated_hash) { return m_ht.find(key, precalculated_hash); }
|
||||
|
||||
/**
|
||||
* @copydoc find(const K& key)
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
|
||||
const_iterator find(const K& key) const { return m_ht.find(key); }
|
||||
|
||||
/**
|
||||
* @copydoc find(const K& key)
|
||||
*
|
||||
* Use the hash value `precalculated_hash` instead of hashing the key. The hash value should be the same
|
||||
* as `hash_function()(key)`, otherwise the behaviour is undefined. Useful to speed-up the lookup
|
||||
* if you already have the hash.
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
|
||||
const_iterator find(const K& key, std::size_t precalculated_hash) const {
|
||||
return m_ht.find(key, precalculated_hash);
|
||||
}
|
||||
|
||||
|
||||
|
||||
bool contains(const Key& key) const { return m_ht.contains(key); }
|
||||
|
||||
/**
|
||||
* Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same
|
||||
* as hash_function()(key). Usefull to speed-up the lookup if you already have the hash.
|
||||
*/
|
||||
bool contains(const Key& key, std::size_t precalculated_hash) const {
|
||||
return m_ht.contains(key, precalculated_hash);
|
||||
}
|
||||
|
||||
/**
|
||||
* This overload only participates in the overload resolution if the typedef KeyEqual::is_transparent exists.
|
||||
* If so, K must be hashable and comparable to Key.
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
|
||||
bool contains(const K& key) const { return m_ht.contains(key); }
|
||||
|
||||
/**
|
||||
* @copydoc contains(const K& key) const
|
||||
*
|
||||
* Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same
|
||||
* as hash_function()(key). Usefull to speed-up the lookup if you already have the hash.
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
|
||||
bool contains(const K& key, std::size_t precalculated_hash) const {
|
||||
return m_ht.contains(key, precalculated_hash);
|
||||
}
|
||||
|
||||
|
||||
|
||||
std::pair<iterator, iterator> equal_range(const Key& key) { return m_ht.equal_range(key); }
|
||||
|
||||
/**
|
||||
* Use the hash value `precalculated_hash` instead of hashing the key. The hash value should be the same
|
||||
* as `hash_function()(key)`, otherwise the behaviour is undefined. Useful to speed-up the lookup
|
||||
* if you already have the hash.
|
||||
*/
|
||||
std::pair<iterator, iterator> equal_range(const Key& key, std::size_t precalculated_hash) {
|
||||
return m_ht.equal_range(key, precalculated_hash);
|
||||
}
|
||||
|
||||
std::pair<const_iterator, const_iterator> equal_range(const Key& key) const { return m_ht.equal_range(key); }
|
||||
|
||||
/**
|
||||
* @copydoc equal_range(const Key& key, std::size_t precalculated_hash)
|
||||
*/
|
||||
std::pair<const_iterator, const_iterator> equal_range(const Key& key, std::size_t precalculated_hash) const {
|
||||
return m_ht.equal_range(key, precalculated_hash);
|
||||
}
|
||||
|
||||
/**
|
||||
* This overload only participates in the overload resolution if the typedef `KeyEqual::is_transparent` exists.
|
||||
* If so, `K` must be hashable and comparable to `Key`.
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
|
||||
std::pair<iterator, iterator> equal_range(const K& key) { return m_ht.equal_range(key); }
|
||||
|
||||
|
||||
/**
|
||||
* @copydoc equal_range(const K& key)
|
||||
*
|
||||
* Use the hash value `precalculated_hash` instead of hashing the key. The hash value should be the same
|
||||
* as `hash_function()(key)`, otherwise the behaviour is undefined. Useful to speed-up the lookup
|
||||
* if you already have the hash.
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
|
||||
std::pair<iterator, iterator> equal_range(const K& key, std::size_t precalculated_hash) {
|
||||
return m_ht.equal_range(key, precalculated_hash);
|
||||
}
|
||||
|
||||
/**
|
||||
* @copydoc equal_range(const K& key)
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
|
||||
std::pair<const_iterator, const_iterator> equal_range(const K& key) const { return m_ht.equal_range(key); }
|
||||
|
||||
/**
|
||||
* @copydoc equal_range(const K& key, std::size_t precalculated_hash)
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
|
||||
std::pair<const_iterator, const_iterator> equal_range(const K& key, std::size_t precalculated_hash) const {
|
||||
return m_ht.equal_range(key, precalculated_hash);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Bucket interface
|
||||
*/
|
||||
size_type bucket_count() const { return m_ht.bucket_count(); }
|
||||
size_type max_bucket_count() const { return m_ht.max_bucket_count(); }
|
||||
|
||||
|
||||
/*
|
||||
* Hash policy
|
||||
*/
|
||||
float load_factor() const { return m_ht.load_factor(); }
|
||||
float max_load_factor() const { return m_ht.max_load_factor(); }
|
||||
void max_load_factor(float ml) { m_ht.max_load_factor(ml); }
|
||||
|
||||
void rehash(size_type count) { m_ht.rehash(count); }
|
||||
void reserve(size_type count) { m_ht.reserve(count); }
|
||||
|
||||
|
||||
/*
|
||||
* Observers
|
||||
*/
|
||||
hasher hash_function() const { return m_ht.hash_function(); }
|
||||
key_equal key_eq() const { return m_ht.key_eq(); }
|
||||
|
||||
/*
|
||||
* Other
|
||||
*/
|
||||
|
||||
/**
|
||||
* Convert a `const_iterator` to an `iterator`.
|
||||
*/
|
||||
iterator mutable_iterator(const_iterator pos) {
|
||||
return m_ht.mutable_iterator(pos);
|
||||
}
|
||||
|
||||
/**
|
||||
* Serialize the map through the `serializer` parameter.
|
||||
*
|
||||
* The `serializer` parameter must be a function object that supports the following call:
|
||||
* - `template<typename U> void operator()(const U& value);` where the types `std::uint64_t`, `float` and `std::pair<Key, T>` must be supported for U.
|
||||
*
|
||||
* The implementation leaves binary compatibilty (endianness, IEEE 754 for floats, ...) of the types it serializes
|
||||
* in the hands of the `Serializer` function object if compatibilty is required.
|
||||
*/
|
||||
template<class Serializer>
|
||||
void serialize(Serializer& serializer) const {
|
||||
m_ht.serialize(serializer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deserialize a previouly serialized map through the `deserializer` parameter.
|
||||
*
|
||||
* The `deserializer` parameter must be a function object that supports the following calls:
|
||||
* - `template<typename U> U operator()();` where the types `std::uint64_t`, `float` and `std::pair<Key, T>` must be supported for U.
|
||||
*
|
||||
* If the deserialized hash map type is hash compatible with the serialized map, the deserialization process can be
|
||||
* sped up by setting `hash_compatible` to true. To be hash compatible, the Hash, KeyEqual and GrowthPolicy must behave the
|
||||
* same way than the ones used on the serialized map. The `std::size_t` must also be of the same size as the one on the platform used
|
||||
* to serialize the map. If these criteria are not met, the behaviour is undefined with `hash_compatible` sets to true.
|
||||
*
|
||||
* The behaviour is undefined if the type `Key` and `T` of the `sparse_map` are not the same as the
|
||||
* types used during serialization.
|
||||
*
|
||||
* The implementation leaves binary compatibilty (endianness, IEEE 754 for floats, size of int, ...) of the types it
|
||||
* deserializes in the hands of the `Deserializer` function object if compatibilty is required.
|
||||
*/
|
||||
template<class Deserializer>
|
||||
static sparse_map deserialize(Deserializer& deserializer, bool hash_compatible = false) {
|
||||
sparse_map map(0);
|
||||
map.m_ht.deserialize(deserializer, hash_compatible);
|
||||
|
||||
return map;
|
||||
}
|
||||
|
||||
friend bool operator==(const sparse_map& lhs, const sparse_map& rhs) {
|
||||
if(lhs.size() != rhs.size()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for(const auto& element_lhs: lhs) {
|
||||
const auto it_element_rhs = rhs.find(element_lhs.first);
|
||||
if(it_element_rhs == rhs.cend() || element_lhs.second != it_element_rhs->second) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
friend bool operator!=(const sparse_map& lhs, const sparse_map& rhs) {
|
||||
return !operator==(lhs, rhs);
|
||||
}
|
||||
|
||||
friend void swap(sparse_map& lhs, sparse_map& rhs) {
|
||||
lhs.swap(rhs);
|
||||
}
|
||||
|
||||
private:
|
||||
ht m_ht;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Same as `tsl::sparse_map<Key, T, Hash, KeyEqual, Allocator, tsl::sh::prime_growth_policy>`.
|
||||
*/
|
||||
template<class Key,
|
||||
class T,
|
||||
class Hash = std::hash<Key>,
|
||||
class KeyEqual = std::equal_to<Key>,
|
||||
class Allocator = std::allocator<std::pair<Key, T>>>
|
||||
using sparse_pg_map = sparse_map<Key, T, Hash, KeyEqual, Allocator, tsl::sh::prime_growth_policy>;
|
||||
|
||||
} // end namespace tsl
|
||||
|
||||
#endif
|
612
src/includes/3thparty/tsl/sparse_set.h
Normal file
612
src/includes/3thparty/tsl/sparse_set.h
Normal file
@ -0,0 +1,612 @@
|
||||
/**
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2017 Tessil
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
#ifndef TSL_SPARSE_SET_H
|
||||
#define TSL_SPARSE_SET_H
|
||||
|
||||
|
||||
#include <cstddef>
|
||||
#include <functional>
|
||||
#include <initializer_list>
|
||||
#include <memory>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include "sparse_hash.h"
|
||||
|
||||
|
||||
namespace tsl {
|
||||
|
||||
|
||||
/**
|
||||
* Implementation of a sparse hash set using open-addressing with quadratic probing.
|
||||
* The goal on the hash set is to be the most memory efficient possible, even at low load factor,
|
||||
* while keeping reasonable performances.
|
||||
*
|
||||
* `GrowthPolicy` defines how the set grows and consequently how a hash value is mapped to a bucket.
|
||||
* By default the set uses `tsl::sh::power_of_two_growth_policy`. This policy keeps the number of buckets
|
||||
* to a power of two and uses a mask to map the hash to a bucket instead of the slow modulo.
|
||||
* Other growth policies are available and you may define your own growth policy,
|
||||
* check `tsl::sh::power_of_two_growth_policy` for the interface.
|
||||
*
|
||||
* `ExceptionSafety` defines the exception guarantee provided by the class. By default only the basic
|
||||
* exception safety is guaranteed which mean that all resources used by the hash set will be freed (no memory leaks)
|
||||
* but the hash set may end-up in an undefined state if an exception is thrown (undefined here means that some elements
|
||||
* may be missing). This can ONLY happen on rehash (either on insert or if `rehash` is called explicitly) and will
|
||||
* occur if the Allocator can't allocate memory (`std::bad_alloc`) or if the copy constructor (when a nothrow
|
||||
* move constructor is not available) throws an exception. This can be avoided by calling `reserve` beforehand.
|
||||
* This basic guarantee is similar to the one of `google::sparse_hash_map` and `spp::sparse_hash_map`.
|
||||
* It is possible to ask for the strong exception guarantee with `tsl::sh::exception_safety::strong`, the drawback
|
||||
* is that the set will be slower on rehashes and will also need more memory on rehashes.
|
||||
*
|
||||
* `Sparsity` defines how much the hash set will compromise between insertion speed and memory usage. A high
|
||||
* sparsity means less memory usage but longer insertion times, and vice-versa for low sparsity. The default
|
||||
* `tsl::sh::sparsity::medium` sparsity offers a good compromise. It doesn't change the lookup speed.
|
||||
*
|
||||
* `Key` must be nothrow move constructible and/or copy constructible.
|
||||
*
|
||||
* If the destructor of `Key` throws an exception, the behaviour of the class is undefined.
|
||||
*
|
||||
* Iterators invalidation:
|
||||
* - clear, operator=, reserve, rehash: always invalidate the iterators.
|
||||
* - insert, emplace, emplace_hint: if there is an effective insert, invalidate the iterators.
|
||||
* - erase: always invalidate the iterators.
|
||||
*/
|
||||
template<class Key,
|
||||
class Hash = std::hash<Key>,
|
||||
class KeyEqual = std::equal_to<Key>,
|
||||
class Allocator = std::allocator<Key>,
|
||||
class GrowthPolicy = tsl::sh::power_of_two_growth_policy<2>,
|
||||
tsl::sh::exception_safety ExceptionSafety = tsl::sh::exception_safety::basic,
|
||||
tsl::sh::sparsity Sparsity = tsl::sh::sparsity::medium>
|
||||
class sparse_set {
|
||||
private:
|
||||
template<typename U>
|
||||
using has_is_transparent = tsl::detail_sparse_hash::has_is_transparent<U>;
|
||||
|
||||
class KeySelect {
|
||||
public:
|
||||
using key_type = Key;
|
||||
|
||||
const key_type& operator()(const Key& key) const noexcept {
|
||||
return key;
|
||||
}
|
||||
|
||||
key_type& operator()(Key& key) noexcept {
|
||||
return key;
|
||||
}
|
||||
};
|
||||
|
||||
using ht = detail_sparse_hash::sparse_hash<Key, KeySelect, void, Hash, KeyEqual, Allocator, GrowthPolicy,
|
||||
ExceptionSafety, Sparsity,
|
||||
tsl::sh::probing::quadratic>;
|
||||
|
||||
public:
|
||||
using key_type = typename ht::key_type;
|
||||
using value_type = typename ht::value_type;
|
||||
using size_type = typename ht::size_type;
|
||||
using difference_type = typename ht::difference_type;
|
||||
using hasher = typename ht::hasher;
|
||||
using key_equal = typename ht::key_equal;
|
||||
using allocator_type = typename ht::allocator_type;
|
||||
using reference = typename ht::reference;
|
||||
using const_reference = typename ht::const_reference;
|
||||
using pointer = typename ht::pointer;
|
||||
using const_pointer = typename ht::const_pointer;
|
||||
using iterator = typename ht::iterator;
|
||||
using const_iterator = typename ht::const_iterator;
|
||||
|
||||
|
||||
/*
|
||||
* Constructors
|
||||
*/
|
||||
sparse_set(): sparse_set(ht::DEFAULT_INIT_BUCKET_COUNT) {
|
||||
}
|
||||
|
||||
explicit sparse_set(size_type bucket_count,
|
||||
const Hash& hash = Hash(),
|
||||
const KeyEqual& equal = KeyEqual(),
|
||||
const Allocator& alloc = Allocator()):
|
||||
m_ht(bucket_count, hash, equal, alloc, ht::DEFAULT_MAX_LOAD_FACTOR)
|
||||
{
|
||||
}
|
||||
|
||||
sparse_set(size_type bucket_count,
|
||||
const Allocator& alloc): sparse_set(bucket_count, Hash(), KeyEqual(), alloc)
|
||||
{
|
||||
}
|
||||
|
||||
sparse_set(size_type bucket_count,
|
||||
const Hash& hash,
|
||||
const Allocator& alloc): sparse_set(bucket_count, hash, KeyEqual(), alloc)
|
||||
{
|
||||
}
|
||||
|
||||
explicit sparse_set(const Allocator& alloc): sparse_set(ht::DEFAULT_INIT_BUCKET_COUNT, alloc) {
|
||||
}
|
||||
|
||||
template<class InputIt>
|
||||
sparse_set(InputIt first, InputIt last,
|
||||
size_type bucket_count = ht::DEFAULT_INIT_BUCKET_COUNT,
|
||||
const Hash& hash = Hash(),
|
||||
const KeyEqual& equal = KeyEqual(),
|
||||
const Allocator& alloc = Allocator()): sparse_set(bucket_count, hash, equal, alloc)
|
||||
{
|
||||
insert(first, last);
|
||||
}
|
||||
|
||||
template<class InputIt>
|
||||
sparse_set(InputIt first, InputIt last,
|
||||
size_type bucket_count,
|
||||
const Allocator& alloc): sparse_set(first, last, bucket_count, Hash(), KeyEqual(), alloc)
|
||||
{
|
||||
}
|
||||
|
||||
template<class InputIt>
|
||||
sparse_set(InputIt first, InputIt last,
|
||||
size_type bucket_count,
|
||||
const Hash& hash,
|
||||
const Allocator& alloc): sparse_set(first, last, bucket_count, hash, KeyEqual(), alloc)
|
||||
{
|
||||
}
|
||||
|
||||
sparse_set(std::initializer_list<value_type> init,
|
||||
size_type bucket_count = ht::DEFAULT_INIT_BUCKET_COUNT,
|
||||
const Hash& hash = Hash(),
|
||||
const KeyEqual& equal = KeyEqual(),
|
||||
const Allocator& alloc = Allocator()):
|
||||
sparse_set(init.begin(), init.end(), bucket_count, hash, equal, alloc)
|
||||
{
|
||||
}
|
||||
|
||||
sparse_set(std::initializer_list<value_type> init,
|
||||
size_type bucket_count,
|
||||
const Allocator& alloc):
|
||||
sparse_set(init.begin(), init.end(), bucket_count, Hash(), KeyEqual(), alloc)
|
||||
{
|
||||
}
|
||||
|
||||
sparse_set(std::initializer_list<value_type> init,
|
||||
size_type bucket_count,
|
||||
const Hash& hash,
|
||||
const Allocator& alloc):
|
||||
sparse_set(init.begin(), init.end(), bucket_count, hash, KeyEqual(), alloc)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
sparse_set& operator=(std::initializer_list<value_type> ilist) {
|
||||
m_ht.clear();
|
||||
|
||||
m_ht.reserve(ilist.size());
|
||||
m_ht.insert(ilist.begin(), ilist.end());
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
allocator_type get_allocator() const { return m_ht.get_allocator(); }
|
||||
|
||||
|
||||
/*
|
||||
* Iterators
|
||||
*/
|
||||
iterator begin() noexcept { return m_ht.begin(); }
|
||||
const_iterator begin() const noexcept { return m_ht.begin(); }
|
||||
const_iterator cbegin() const noexcept { return m_ht.cbegin(); }
|
||||
|
||||
iterator end() noexcept { return m_ht.end(); }
|
||||
const_iterator end() const noexcept { return m_ht.end(); }
|
||||
const_iterator cend() const noexcept { return m_ht.cend(); }
|
||||
|
||||
|
||||
/*
|
||||
* Capacity
|
||||
*/
|
||||
bool empty() const noexcept { return m_ht.empty(); }
|
||||
size_type size() const noexcept { return m_ht.size(); }
|
||||
size_type max_size() const noexcept { return m_ht.max_size(); }
|
||||
|
||||
/*
|
||||
* Modifiers
|
||||
*/
|
||||
void clear() noexcept { m_ht.clear(); }
|
||||
|
||||
|
||||
|
||||
|
||||
std::pair<iterator, bool> insert(const value_type& value) {
|
||||
return m_ht.insert(value);
|
||||
}
|
||||
|
||||
std::pair<iterator, bool> insert(value_type&& value) {
|
||||
return m_ht.insert(std::move(value));
|
||||
}
|
||||
|
||||
iterator insert(const_iterator hint, const value_type& value) {
|
||||
return m_ht.insert_hint(hint, value);
|
||||
}
|
||||
|
||||
iterator insert(const_iterator hint, value_type&& value) {
|
||||
return m_ht.insert_hint(hint, std::move(value));
|
||||
}
|
||||
|
||||
template<class InputIt>
|
||||
void insert(InputIt first, InputIt last) {
|
||||
m_ht.insert(first, last);
|
||||
}
|
||||
|
||||
void insert(std::initializer_list<value_type> ilist) {
|
||||
m_ht.insert(ilist.begin(), ilist.end());
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Due to the way elements are stored, emplace will need to move or copy the key-value once.
|
||||
* The method is equivalent to `insert(value_type(std::forward<Args>(args)...));`.
|
||||
*
|
||||
* Mainly here for compatibility with the `std::unordered_map` interface.
|
||||
*/
|
||||
template<class... Args>
|
||||
std::pair<iterator, bool> emplace(Args&&... args) {
|
||||
return m_ht.emplace(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Due to the way elements are stored, emplace_hint will need to move or copy the key-value once.
|
||||
* The method is equivalent to `insert(hint, value_type(std::forward<Args>(args)...));`.
|
||||
*
|
||||
* Mainly here for compatibility with the `std::unordered_map` interface.
|
||||
*/
|
||||
template<class... Args>
|
||||
iterator emplace_hint(const_iterator hint, Args&&... args) {
|
||||
return m_ht.emplace_hint(hint, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
|
||||
|
||||
iterator erase(iterator pos) { return m_ht.erase(pos); }
|
||||
iterator erase(const_iterator pos) { return m_ht.erase(pos); }
|
||||
iterator erase(const_iterator first, const_iterator last) { return m_ht.erase(first, last); }
|
||||
size_type erase(const key_type& key) { return m_ht.erase(key); }
|
||||
|
||||
/**
|
||||
* Use the hash value `precalculated_hash` instead of hashing the key. The hash value should be the same
|
||||
* as `hash_function()(key)`, otherwise the behaviour is undefined. Useful to speed-up the lookup
|
||||
* if you already have the hash.
|
||||
*/
|
||||
size_type erase(const key_type& key, std::size_t precalculated_hash) {
|
||||
return m_ht.erase(key, precalculated_hash);
|
||||
}
|
||||
|
||||
/**
|
||||
* This overload only participates in the overload resolution if the typedef `KeyEqual::is_transparent` exists.
|
||||
* If so, `K` must be hashable and comparable to `Key`.
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
|
||||
size_type erase(const K& key) { return m_ht.erase(key); }
|
||||
|
||||
/**
|
||||
* @copydoc erase(const K& key)
|
||||
*
|
||||
* Use the hash value `precalculated_hash` instead of hashing the key. The hash value should be the same
|
||||
* as `hash_function()(key)`, otherwise the behaviour is undefined. Useful to speed-up the lookup
|
||||
* if you already have the hash.
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
|
||||
size_type erase(const K& key, std::size_t precalculated_hash) {
|
||||
return m_ht.erase(key, precalculated_hash);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void swap(sparse_set& other) { other.m_ht.swap(m_ht); }
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Lookup
|
||||
*/
|
||||
size_type count(const Key& key) const { return m_ht.count(key); }
|
||||
|
||||
/**
|
||||
* Use the hash value `precalculated_hash` instead of hashing the key. The hash value should be the same
|
||||
* as `hash_function()(key)`, otherwise the behaviour is undefined. Useful to speed-up the lookup
|
||||
* if you already have the hash.
|
||||
*/
|
||||
size_type count(const Key& key, std::size_t precalculated_hash) const { return m_ht.count(key, precalculated_hash); }
|
||||
|
||||
/**
|
||||
* This overload only participates in the overload resolution if the typedef `KeyEqual::is_transparent` exists.
|
||||
* If so, `K` must be hashable and comparable to `Key`.
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
|
||||
size_type count(const K& key) const { return m_ht.count(key); }
|
||||
|
||||
/**
|
||||
* @copydoc count(const K& key) const
|
||||
*
|
||||
* Use the hash value `precalculated_hash` instead of hashing the key. The hash value should be the same
|
||||
* as `hash_function()(key)`, otherwise the behaviour is undefined. Useful to speed-up the lookup
|
||||
* if you already have the hash.
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
|
||||
size_type count(const K& key, std::size_t precalculated_hash) const { return m_ht.count(key, precalculated_hash); }
|
||||
|
||||
|
||||
|
||||
|
||||
iterator find(const Key& key) { return m_ht.find(key); }
|
||||
|
||||
/**
|
||||
* Use the hash value `precalculated_hash` instead of hashing the key. The hash value should be the same
|
||||
* as `hash_function()(key)`, otherwise the behaviour is undefined. Useful to speed-up the lookup
|
||||
* if you already have the hash.
|
||||
*/
|
||||
iterator find(const Key& key, std::size_t precalculated_hash) { return m_ht.find(key, precalculated_hash); }
|
||||
|
||||
const_iterator find(const Key& key) const { return m_ht.find(key); }
|
||||
|
||||
/**
|
||||
* @copydoc find(const Key& key, std::size_t precalculated_hash)
|
||||
*/
|
||||
const_iterator find(const Key& key, std::size_t precalculated_hash) const { return m_ht.find(key, precalculated_hash); }
|
||||
|
||||
/**
|
||||
* This overload only participates in the overload resolution if the typedef `KeyEqual::is_transparent` exists.
|
||||
* If so, `K` must be hashable and comparable to `Key`.
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
|
||||
iterator find(const K& key) { return m_ht.find(key); }
|
||||
|
||||
/**
|
||||
* @copydoc find(const K& key)
|
||||
*
|
||||
* Use the hash value `precalculated_hash` instead of hashing the key. The hash value should be the same
|
||||
* as `hash_function()(key)`, otherwise the behaviour is undefined. Useful to speed-up the lookup
|
||||
* if you already have the hash.
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
|
||||
iterator find(const K& key, std::size_t precalculated_hash) { return m_ht.find(key, precalculated_hash); }
|
||||
|
||||
/**
|
||||
* @copydoc find(const K& key)
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
|
||||
const_iterator find(const K& key) const { return m_ht.find(key); }
|
||||
|
||||
/**
|
||||
* @copydoc find(const K& key)
|
||||
*
|
||||
* Use the hash value `precalculated_hash` instead of hashing the key. The hash value should be the same
|
||||
* as `hash_function()(key)`, otherwise the behaviour is undefined. Useful to speed-up the lookup
|
||||
* if you already have the hash.
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
|
||||
const_iterator find(const K& key, std::size_t precalculated_hash) const { return m_ht.find(key, precalculated_hash); }
|
||||
|
||||
|
||||
|
||||
bool contains(const Key& key) const { return m_ht.contains(key); }
|
||||
|
||||
/**
|
||||
* Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same
|
||||
* as hash_function()(key). Usefull to speed-up the lookup if you already have the hash.
|
||||
*/
|
||||
bool contains(const Key& key, std::size_t precalculated_hash) const {
|
||||
return m_ht.contains(key, precalculated_hash);
|
||||
}
|
||||
|
||||
/**
|
||||
* This overload only participates in the overload resolution if the typedef KeyEqual::is_transparent exists.
|
||||
* If so, K must be hashable and comparable to Key.
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
|
||||
bool contains(const K& key) const { return m_ht.contains(key); }
|
||||
|
||||
/**
|
||||
* @copydoc contains(const K& key) const
|
||||
*
|
||||
* Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same
|
||||
* as hash_function()(key). Usefull to speed-up the lookup if you already have the hash.
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
|
||||
bool contains(const K& key, std::size_t precalculated_hash) const {
|
||||
return m_ht.contains(key, precalculated_hash);
|
||||
}
|
||||
|
||||
|
||||
|
||||
std::pair<iterator, iterator> equal_range(const Key& key) { return m_ht.equal_range(key); }
|
||||
|
||||
/**
|
||||
* Use the hash value `precalculated_hash` instead of hashing the key. The hash value should be the same
|
||||
* as `hash_function()(key)`, otherwise the behaviour is undefined. Useful to speed-up the lookup
|
||||
* if you already have the hash.
|
||||
*/
|
||||
std::pair<iterator, iterator> equal_range(const Key& key, std::size_t precalculated_hash) {
|
||||
return m_ht.equal_range(key, precalculated_hash);
|
||||
}
|
||||
|
||||
std::pair<const_iterator, const_iterator> equal_range(const Key& key) const { return m_ht.equal_range(key); }
|
||||
|
||||
/**
|
||||
* @copydoc equal_range(const Key& key, std::size_t precalculated_hash)
|
||||
*/
|
||||
std::pair<const_iterator, const_iterator> equal_range(const Key& key, std::size_t precalculated_hash) const {
|
||||
return m_ht.equal_range(key, precalculated_hash);
|
||||
}
|
||||
|
||||
/**
|
||||
* This overload only participates in the overload resolution if the typedef `KeyEqual::is_transparent` exists.
|
||||
* If so, `K` must be hashable and comparable to `Key`.
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
|
||||
std::pair<iterator, iterator> equal_range(const K& key) { return m_ht.equal_range(key); }
|
||||
|
||||
/**
|
||||
* @copydoc equal_range(const K& key)
|
||||
*
|
||||
* Use the hash value `precalculated_hash` instead of hashing the key. The hash value should be the same
|
||||
* as `hash_function()(key)`, otherwise the behaviour is undefined. Useful to speed-up the lookup
|
||||
* if you already have the hash.
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
|
||||
std::pair<iterator, iterator> equal_range(const K& key, std::size_t precalculated_hash) {
|
||||
return m_ht.equal_range(key, precalculated_hash);
|
||||
}
|
||||
|
||||
/**
|
||||
* @copydoc equal_range(const K& key)
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
|
||||
std::pair<const_iterator, const_iterator> equal_range(const K& key) const { return m_ht.equal_range(key); }
|
||||
|
||||
/**
|
||||
* @copydoc equal_range(const K& key, std::size_t precalculated_hash)
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
|
||||
std::pair<const_iterator, const_iterator> equal_range(const K& key, std::size_t precalculated_hash) const {
|
||||
return m_ht.equal_range(key, precalculated_hash);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Bucket interface
|
||||
*/
|
||||
size_type bucket_count() const { return m_ht.bucket_count(); }
|
||||
size_type max_bucket_count() const { return m_ht.max_bucket_count(); }
|
||||
|
||||
|
||||
/*
|
||||
* Hash policy
|
||||
*/
|
||||
float load_factor() const { return m_ht.load_factor(); }
|
||||
float max_load_factor() const { return m_ht.max_load_factor(); }
|
||||
void max_load_factor(float ml) { m_ht.max_load_factor(ml); }
|
||||
|
||||
void rehash(size_type count) { m_ht.rehash(count); }
|
||||
void reserve(size_type count) { m_ht.reserve(count); }
|
||||
|
||||
|
||||
/*
|
||||
* Observers
|
||||
*/
|
||||
hasher hash_function() const { return m_ht.hash_function(); }
|
||||
key_equal key_eq() const { return m_ht.key_eq(); }
|
||||
|
||||
|
||||
/*
|
||||
* Other
|
||||
*/
|
||||
|
||||
/**
|
||||
* Convert a `const_iterator` to an `iterator`.
|
||||
*/
|
||||
iterator mutable_iterator(const_iterator pos) {
|
||||
return m_ht.mutable_iterator(pos);
|
||||
}
|
||||
|
||||
/**
|
||||
* Serialize the set through the `serializer` parameter.
|
||||
*
|
||||
* The `serializer` parameter must be a function object that supports the following call:
|
||||
* - `void operator()(const U& value);` where the types `std::uint64_t`, `float` and `Key` must be supported for U.
|
||||
*
|
||||
* The implementation leaves binary compatibilty (endianness, IEEE 754 for floats, ...) of the types it serializes
|
||||
* in the hands of the `Serializer` function object if compatibilty is required.
|
||||
*/
|
||||
template<class Serializer>
|
||||
void serialize(Serializer& serializer) const {
|
||||
m_ht.serialize(serializer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deserialize a previouly serialized set through the `deserializer` parameter.
|
||||
*
|
||||
* The `deserializer` parameter must be a function object that supports the following calls:
|
||||
* - `template<typename U> U operator()();` where the types `std::uint64_t`, `float` and `Key` must be supported for U.
|
||||
*
|
||||
* If the deserialized hash set type is hash compatible with the serialized set, the deserialization process can be
|
||||
* sped up by setting `hash_compatible` to true. To be hash compatible, the Hash, KeyEqual and GrowthPolicy must behave the
|
||||
* same way than the ones used on the serialized set. The `std::size_t` must also be of the same size as the one on the platform used
|
||||
* to serialize the set. If these criteria are not met, the behaviour is undefined with `hash_compatible` sets to true.
|
||||
*
|
||||
* The behaviour is undefined if the type `Key` of the `sparse_set` is not the same as the
|
||||
* type used during serialization.
|
||||
*
|
||||
* The implementation leaves binary compatibilty (endianness, IEEE 754 for floats, size of int, ...) of the types it
|
||||
* deserializes in the hands of the `Deserializer` function object if compatibilty is required.
|
||||
*/
|
||||
template<class Deserializer>
|
||||
static sparse_set deserialize(Deserializer& deserializer, bool hash_compatible = false) {
|
||||
sparse_set set(0);
|
||||
set.m_ht.deserialize(deserializer, hash_compatible);
|
||||
|
||||
return set;
|
||||
}
|
||||
|
||||
friend bool operator==(const sparse_set& lhs, const sparse_set& rhs) {
|
||||
if(lhs.size() != rhs.size()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for(const auto& element_lhs: lhs) {
|
||||
const auto it_element_rhs = rhs.find(element_lhs);
|
||||
if(it_element_rhs == rhs.cend()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
friend bool operator!=(const sparse_set& lhs, const sparse_set& rhs) {
|
||||
return !operator==(lhs, rhs);
|
||||
}
|
||||
|
||||
friend void swap(sparse_set& lhs, sparse_set& rhs) {
|
||||
lhs.swap(rhs);
|
||||
}
|
||||
|
||||
private:
|
||||
ht m_ht;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Same as `tsl::sparse_set<Key, Hash, KeyEqual, Allocator, tsl::sh::prime_growth_policy>`.
|
||||
*/
|
||||
template<class Key,
|
||||
class Hash = std::hash<Key>,
|
||||
class KeyEqual = std::equal_to<Key>,
|
||||
class Allocator = std::allocator<Key>>
|
||||
using sparse_pg_set = sparse_set<Key, Hash, KeyEqual, Allocator, tsl::sh::prime_growth_policy>;
|
||||
|
||||
} // end namespace tsl
|
||||
|
||||
#endif
|
@ -5,13 +5,14 @@
|
||||
#include <ostream>
|
||||
#include <iostream>
|
||||
#include <thread>
|
||||
#include <fstream>
|
||||
|
||||
|
||||
vector<int> sizes = {
|
||||
50000, 100000, 150000, 200000, 250000, 300000, 350000, 400000, 500000,
|
||||
600000, 700000, 800000, 900000, 1000000,
|
||||
2000000, 3000000, 4000000, 5000000, 6000000, 7000000, 8000000, 9000000, 10000000,
|
||||
15000000, 20000000, 25000000, 30000000, 35000000, 40000000, 45000000, 50000000
|
||||
// 2000000, 3000000, 4000000, 5000000, 6000000, 7000000, 8000000, 9000000, 10000000,
|
||||
// 15000000, 20000000, 25000000, 30000000, 35000000, 40000000, 45000000, 50000000
|
||||
};
|
||||
// to print type info
|
||||
|
||||
|
179
src/includes/prepare.h
Normal file
179
src/includes/prepare.h
Normal file
@ -0,0 +1,179 @@
|
||||
|
||||
#ifndef PREPARE_H
|
||||
#define PREPARE_H
|
||||
|
||||
#include <string>
|
||||
|
||||
|
||||
// hashmaps and hash
|
||||
#include <unordered_map>
|
||||
#include <sparsehash/sparse_hash_map>
|
||||
#include <sparsehash/dense_hash_map>
|
||||
#include "./3thparty/abseil-cpp/absl/container/node_hash_map.h"
|
||||
#include "./3thparty/abseil-cpp/absl/container/flat_hash_map.h"
|
||||
#include "./3thparty/abseil-cpp/absl/hash/hash.h"
|
||||
#include "./3thparty/tsl/sparse_map.h"
|
||||
#include "./3thparty/tsl/array_map.h"
|
||||
#include "./3thparty/tsl/ordered_map.h"
|
||||
#include "./3thparty/tsl/robin_map.h"
|
||||
#include "./3thparty/tsl/hopscotch_map.h"
|
||||
#include <boost/unordered_map.hpp>
|
||||
#include "./3thparty/skarupke/bytell_hash_map.hpp"
|
||||
#include "./3thparty/skarupke/flat_hash_map.hpp"
|
||||
#include "./3thparty/skarupke/unordered_map.hpp"
|
||||
#include "./3thparty/parallel_hashmap/phmap.h"
|
||||
#include "./3thparty/emilib/hash_map.hpp"
|
||||
#include "3thparty/robinhood/robin_hood.h"
|
||||
|
||||
using std::string;
|
||||
using absl::Hash;
|
||||
|
||||
|
||||
// since my testing is based on this hashmap, this one doesn't need no prep
|
||||
void prepare(std::unordered_map<int, int>& map,int size){
|
||||
map.reserve(size);
|
||||
}
|
||||
void prepare(std::unordered_map<string, string>& map,int size){
|
||||
map.reserve(size);
|
||||
}
|
||||
|
||||
|
||||
// goooooogle
|
||||
void prepare(google::sparse_hash_map<int, int>& map, int size){
|
||||
map.set_deleted_key(0);
|
||||
}
|
||||
void prepare(google::sparse_hash_map<string, string>& map, int size){
|
||||
map.set_deleted_key("");
|
||||
}
|
||||
|
||||
|
||||
void prepare(google::dense_hash_map<int, int>& map, int size){
|
||||
map.set_empty_key(0);
|
||||
map.set_deleted_key(-1);
|
||||
}
|
||||
void prepare(google::dense_hash_map<string, string>& map, int size){
|
||||
map.set_deleted_key("a");
|
||||
map.set_empty_key("");
|
||||
}
|
||||
|
||||
//absl
|
||||
void prepare(absl::node_hash_map<int, int>& map, int size){
|
||||
map.reserve(size);
|
||||
}
|
||||
void prepare(absl::node_hash_map<string, string>& map, int size){
|
||||
map.reserve(size);
|
||||
}
|
||||
|
||||
|
||||
void prepare(absl::flat_hash_map<int, int>& map, int size){
|
||||
map.reserve(size);
|
||||
}
|
||||
void prepare(absl::flat_hash_map<string, string>& map, int size){
|
||||
map.reserve(size);
|
||||
}
|
||||
|
||||
// tessil
|
||||
void prepare(tsl::sparse_map<int, int>& map, int size){
|
||||
map.reserve(size);
|
||||
}
|
||||
void prepare(tsl::sparse_map<string, string>& map, int size){
|
||||
map.reserve(size);
|
||||
}
|
||||
|
||||
void prepare(tsl::array_map<int, int>& map, int size){
|
||||
// map.reserve(size);
|
||||
}
|
||||
void prepare(tsl::array_map<string, string>& map, int size){
|
||||
// map.reserve(size);
|
||||
}
|
||||
|
||||
void prepare(tsl::ordered_map<int, int>& map, int size){
|
||||
map.reserve(size);
|
||||
}
|
||||
void prepare(tsl::ordered_map<string, string>& map, int size){
|
||||
map.reserve(size);
|
||||
}
|
||||
|
||||
void prepare(tsl::robin_map<int, int>& map, int size){
|
||||
map.reserve(size);
|
||||
}
|
||||
void prepare(tsl::robin_map<string, string>& map, int size){
|
||||
map.reserve(size);
|
||||
}
|
||||
|
||||
void prepare(tsl::hopscotch_map<int, int>& map, int size){
|
||||
map.reserve(size);
|
||||
}
|
||||
void prepare(tsl::hopscotch_map<string, string>& map, int size){
|
||||
map.reserve(size);
|
||||
}
|
||||
|
||||
// booooooost
|
||||
void prepare(boost::unordered_map<int, int>& map, int size){
|
||||
map.reserve(size);
|
||||
}
|
||||
void prepare(boost::unordered_map<string, string>& map, int size){
|
||||
map.reserve(size);
|
||||
}
|
||||
|
||||
// skarupke's maps
|
||||
void prepare(ska::bytell_hash_map<int, int>& map, int size){
|
||||
map.reserve(size);
|
||||
}
|
||||
void prepare(ska::bytell_hash_map<string, string>& map, int size){
|
||||
map.reserve(size);
|
||||
}
|
||||
|
||||
void prepare(ska::flat_hash_map<int, int>& map, int size){
|
||||
map.reserve(size);
|
||||
}
|
||||
void prepare(ska::flat_hash_map<string, string>& map, int size){
|
||||
map.reserve(size);
|
||||
}
|
||||
|
||||
void prepare(ska::unordered_map<int, int>& map, int size){
|
||||
map.reserve(size);
|
||||
}
|
||||
void prepare(ska::unordered_map<string, string>& map, int size){
|
||||
map.reserve(size);
|
||||
}
|
||||
// Gregory Popovitch' paralel hashmaps
|
||||
void prepare(phmap::parallel_flat_hash_map<int, int>& map, int size){
|
||||
map.reserve(size);
|
||||
}
|
||||
void prepare(phmap::parallel_flat_hash_map<string, string>& map, int size){
|
||||
map.reserve(size);
|
||||
}
|
||||
|
||||
void prepare(phmap::parallel_node_hash_map<int, int>& map, int size){
|
||||
map.reserve(size);
|
||||
}
|
||||
void prepare(phmap::parallel_node_hash_map<string, string>& map, int size){
|
||||
map.reserve(size);
|
||||
}
|
||||
|
||||
// emilib (Emil Ernerfeld library)
|
||||
void prepare(emilib::HashMap<int, int>& map, int size){
|
||||
map.reserve(size);
|
||||
}
|
||||
void prepare(emilib::HashMap<string, string>& map, int size){
|
||||
map.reserve(size);
|
||||
}
|
||||
// martin robinhood
|
||||
void prepare(robin_hood::unordered_flat_map<int, int>& map, int size){
|
||||
map.reserve(size);
|
||||
}
|
||||
void prepare(robin_hood::unordered_flat_map<string, string>& map, int size){
|
||||
map.reserve(size);
|
||||
}
|
||||
|
||||
void prepare(robin_hood::unordered_node_map<int, int>& map, int size){
|
||||
map.reserve(size);
|
||||
}
|
||||
void prepare(robin_hood::unordered_node_map<string, string>& map, int size){
|
||||
map.reserve(size);
|
||||
}
|
||||
|
||||
|
||||
|
||||
#endif /* TESTS_H */
|
@ -7,35 +7,20 @@
|
||||
#include <iterator>
|
||||
#include <chrono>
|
||||
|
||||
// maps
|
||||
// #include <sparsehash/sparse_hash_map>
|
||||
|
||||
// own
|
||||
#include "./generator.h"
|
||||
#include "./prepare.h"
|
||||
|
||||
|
||||
using namespace std::chrono;
|
||||
using std::vector;
|
||||
using std::string;
|
||||
using std::cout;
|
||||
|
||||
|
||||
// since my testing is based on this function, this one doesn't need no prep
|
||||
void prepare(std::unordered_map<int, int>& map,int size){
|
||||
map.reserve(size);
|
||||
return;
|
||||
}
|
||||
void prepare(std::unordered_map<string, string>& map,int size){
|
||||
map.reserve(size);
|
||||
return;
|
||||
}
|
||||
|
||||
// void prepare(google::sparse_hash_map<int, int>& map, int size){
|
||||
// map.set_deleted_key(0);
|
||||
// return;
|
||||
// }
|
||||
|
||||
template <class T>
|
||||
vector<int> int_test(T testmap, int size){
|
||||
vector<int> int_test(T map, int size){
|
||||
vector<int> results; // insert, lookup, unsuccesful lookup, delete times
|
||||
vector<int> sample_keys; // get a sample of keys to lookup and later delete
|
||||
|
||||
@ -47,7 +32,7 @@ vector<int> int_test(T testmap, int size){
|
||||
vector<int> insert_keys(10000);
|
||||
std::generate(insert_keys.begin(), insert_keys.end(), gen_int);
|
||||
|
||||
// T testmap {};
|
||||
T testmap{};
|
||||
prepare(testmap, size); // do special actions, such as setting the tombstone marker for other, more exotic hashmaps
|
||||
|
||||
{ // seperate scope, so all_keys gets destroyed. for good measure, empty it too
|
||||
@ -120,8 +105,6 @@ vector<int> int_test(T testmap, int size){
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
template <class T>
|
||||
vector<int> string_test(T map, int size){
|
||||
vector<int> results; // insert, lookup, unsuccesful lookup, delete times
|
||||
@ -135,7 +118,8 @@ vector<int> string_test(T map, int size){
|
||||
vector<string> insert_keys(10000);
|
||||
std::generate(insert_keys.begin(), insert_keys.end(), gen_string);
|
||||
|
||||
T testmap {};
|
||||
|
||||
T testmap{};
|
||||
prepare(testmap, size); // do special actions, such as setting the tombstone marker for other, more exotic hashmaps
|
||||
|
||||
{ // seperate scope, so all_keys gets destroyed. for good measure, empty it too
|
||||
|
Loading…
Reference in New Issue
Block a user