11// /////////////////////// ankerl::unordered_dense::{map, set} /////////////////////////
22
33// A fast & densely stored hashmap and hashset based on robin-hood backward shift deletion.
4- // Version 2 .0.0
4+ // Version 3 .0.2
55// https://github.com/martinus/unordered_dense
66//
77// Licensed under the MIT License <http://opensource.org/licenses/MIT>.
3030#define ANKERL_UNORDERED_DENSE_H
3131
3232// see https://semver.org/spec/v2.0.0.html
33- #define ANKERL_UNORDERED_DENSE_VERSION_MAJOR 2 // NOLINT(cppcoreguidelines-macro-usage) incompatible API changes
33+ #define ANKERL_UNORDERED_DENSE_VERSION_MAJOR 3 // NOLINT(cppcoreguidelines-macro-usage) incompatible API changes
3434#define ANKERL_UNORDERED_DENSE_VERSION_MINOR 0 // NOLINT(cppcoreguidelines-macro-usage) backwards compatible functionality
35- #define ANKERL_UNORDERED_DENSE_VERSION_PATCH 0 // NOLINT(cppcoreguidelines-macro-usage) backwards compatible bug fixes
35+ #define ANKERL_UNORDERED_DENSE_VERSION_PATCH 2 // NOLINT(cppcoreguidelines-macro-usage) backwards compatible bug fixes
3636
3737// API versioning with inline namespace, see https://www.foonathan.net/2018/11/inline-namespaces/
3838#define ANKERL_UNORDERED_DENSE_VERSION_CONCAT1 (major, minor, patch ) v##major##_##minor##_##patch
7979# if __has_include(<memory_resource>)
8080# undef ANKERL_UNORDERED_DENSE_PMR
8181# define ANKERL_UNORDERED_DENSE_PMR 1 // NOLINT(cppcoreguidelines-macro-usage)
82- # include < memory_resource> // for polymorphic_allocator
82+ # define ANKERL_UNORDERED_DENSE_PMR_ALLOCATOR \
83+ std::pmr::polymorphic_allocator // NOLINT(cppcoreguidelines-macro-usage)
84+ # include < memory_resource> // for polymorphic_allocator
85+ # elif __has_include(<experimental/memory_resource>)
86+ # undef ANKERL_UNORDERED_DENSE_PMR
87+ # define ANKERL_UNORDERED_DENSE_PMR 1 // NOLINT(cppcoreguidelines-macro-usage)
88+ # define ANKERL_UNORDERED_DENSE_PMR_ALLOCATOR \
89+ std::experimental::pmr::polymorphic_allocator // NOLINT(cppcoreguidelines-macro-usage)
90+ # include < experimental/memory_resource> // for polymorphic_allocator
8391# endif
8492# endif
8593
@@ -364,30 +372,48 @@ namespace ankerl::unordered_dense {
364372 template <typename T>
365373 using detect_iterator = typename T::iterator;
366374
375+ template <typename T>
376+ using detect_reserve = decltype (std::declval<T&>().reserve(size_t {}));
377+
367378 // enable_if helpers
368379
369380 template <typename Mapped>
370381 constexpr bool is_map_v = !std::is_void_v<Mapped>;
371382
383+ // clang-format off
372384 template <typename Hash, typename KeyEqual>
373385 constexpr bool is_transparent_v = is_detected_v<detect_is_transparent, Hash> && is_detected_v<detect_is_transparent, KeyEqual>;
386+ // clang-format on
374387
375388 template <typename From, typename To1, typename To2>
376389 constexpr bool is_neither_convertible_v = !std::is_convertible_v<From, To1> && !std::is_convertible_v<From, To2>;
377390
391+ template <typename T>
392+ constexpr bool has_reserve = is_detected_v<detect_reserve, T>;
393+
394+ // base type for map has mapped_type
395+ template <class T >
396+ struct base_table_type_map {
397+ using mapped_type = T;
398+ };
399+
400+ // base type for set doesn't have mapped_type
401+ struct base_table_type_set {
402+ };
403+
378404 // This is it, the table. Doubles as map and set, and uses `void` for T when its used as a set.
379405 template <class Key ,
380406 class T , // when void, treat it as a set.
381407 class Hash ,
382408 class KeyEqual ,
383409 class AllocatorOrContainer ,
384410 class Bucket >
385- class table {
411+ class table : public std :: conditional_t <is_map_v<T>, base_table_type_map<T>, base_table_type_set> {
386412 public:
387413 using value_container_type = std::conditional_t <
388414 is_detected_v<detect_iterator, AllocatorOrContainer>,
389415 AllocatorOrContainer,
390- typename std::vector<typename std::conditional_t <std::is_void_v <T>, Key, std::pair<Key, T>>, AllocatorOrContainer>>;
416+ typename std::vector<typename std::conditional_t <is_map_v <T>, std::pair<Key, T>, Key >, AllocatorOrContainer>>;
391417
392418 private:
393419 using bucket_alloc =
@@ -399,7 +425,6 @@ namespace ankerl::unordered_dense {
399425
400426 public:
401427 using key_type = Key;
402- using mapped_type = T;
403428 using value_type = typename value_container_type::value_type;
404429 using size_type = typename value_container_type::size_type;
405430 using difference_type = typename value_container_type::difference_type;
@@ -410,8 +435,8 @@ namespace ankerl::unordered_dense {
410435 using const_reference = typename value_container_type::const_reference;
411436 using pointer = typename value_container_type::pointer;
412437 using const_pointer = typename value_container_type::const_pointer;
413- using iterator = typename value_container_type::iterator;
414438 using const_iterator = typename value_container_type::const_iterator;
439+ using iterator = std::conditional_t <is_map_v<T>, typename value_container_type::iterator, const_iterator>;
415440 using bucket_type = Bucket;
416441
417442 private:
@@ -478,10 +503,10 @@ namespace ankerl::unordered_dense {
478503 }
479504
480505 [[nodiscard]] static constexpr auto get_key (value_type const & vt) -> key_type const & {
481- if constexpr (std::is_void_v<T>) {
482- return vt;
483- } else {
506+ if constexpr (is_map_v<T>) {
484507 return vt.first ;
508+ } else {
509+ return vt;
485510 }
486511 }
487512
@@ -748,13 +773,16 @@ namespace ankerl::unordered_dense {
748773 : table(0 ) {
749774 }
750775
751- explicit table (size_t /* bucket_count*/ ,
776+ explicit table (size_t bucket_count,
752777 Hash const & hash = Hash(),
753778 KeyEqual const & equal = KeyEqual(),
754779 allocator_type const & alloc_or_container = allocator_type())
755780 : m_values(alloc_or_container)
756781 , m_hash(hash)
757782 , m_equal(equal) {
783+ if (0 != bucket_count) {
784+ reserve (bucket_count);
785+ }
758786 }
759787
760788 table (size_t bucket_count, allocator_type const & alloc)
@@ -836,8 +864,10 @@ namespace ankerl::unordered_dense {
836864 }
837865
838866 ~table () {
839- auto ba = bucket_alloc (m_values.get_allocator ());
840- bucket_alloc_traits::deallocate (ba, m_buckets, bucket_count ());
867+ if (nullptr != m_buckets) {
868+ auto ba = bucket_alloc (m_values.get_allocator ());
869+ bucket_alloc_traits::deallocate (ba, m_buckets, bucket_count ());
870+ }
841871 }
842872
843873 auto operator =(table const & other) -> table& {
@@ -1196,6 +1226,7 @@ namespace ankerl::unordered_dense {
11961226 return begin () + static_cast <difference_type>(value_idx_to_remove);
11971227 }
11981228
1229+ template <typename Q = T, std::enable_if_t <is_map_v<Q>, bool > = true >
11991230 auto erase (const_iterator it) -> iterator {
12001231 return erase (begin () + (it - cbegin ()));
12011232 }
@@ -1387,7 +1418,10 @@ namespace ankerl::unordered_dense {
13871418
13881419 void reserve (size_t capa) {
13891420 capa = std::min (capa, max_size ());
1390- m_values.reserve (capa);
1421+ if constexpr (has_reserve<value_container_type>) {
1422+ // std::deque doesn't have reserve(). Make sure we only call when available
1423+ m_values.reserve (capa);
1424+ }
13911425 auto shifts = calc_shifts_for_size (std::max (capa, size ()));
13921426 if (0 == m_num_buckets || shifts < m_shifts) {
13931427 m_shifts = shifts;
@@ -1423,14 +1457,14 @@ namespace ankerl::unordered_dense {
14231457 }
14241458 for (auto const & b_entry : b) {
14251459 auto it = a.find (get_key (b_entry));
1426- if constexpr (std::is_void_v <T>) {
1427- // set: only check that the key is here
1428- if (a.end () == it) {
1460+ if constexpr (is_map_v <T>) {
1461+ // map: check that key is here, then also check that value is the same
1462+ if (a.end () == it || !(b_entry. second == it-> second ) ) {
14291463 return false ;
14301464 }
14311465 } else {
1432- // map: check that key is here, then also check that value is the same
1433- if (a.end () == it || !(b_entry. second == it-> second ) ) {
1466+ // set: only check that the key is here
1467+ if (a.end () == it) {
14341468 return false ;
14351469 }
14361470 }
@@ -1469,10 +1503,10 @@ namespace ankerl::unordered_dense {
14691503 class Hash = hash<Key>,
14701504 class KeyEqual = std::equal_to<Key>,
14711505 class Bucket = bucket_type::standard>
1472- using map = detail::table<Key, T, Hash, KeyEqual, std::pmr::polymorphic_allocator <std::pair<Key, T>>, Bucket>;
1506+ using map = detail::table<Key, T, Hash, KeyEqual, ANKERL_UNORDERED_DENSE_PMR_ALLOCATOR <std::pair<Key, T>>, Bucket>;
14731507
14741508 template <class Key , class Hash = hash<Key>, class KeyEqual = std::equal_to<Key>, class Bucket = bucket_type::standard>
1475- using set = detail::table<Key, void , Hash, KeyEqual, std::pmr::polymorphic_allocator <Key>, Bucket>;
1509+ using set = detail::table<Key, void , Hash, KeyEqual, ANKERL_UNORDERED_DENSE_PMR_ALLOCATOR <Key>, Bucket>;
14761510
14771511 } // namespace pmr
14781512
0 commit comments