Skip to content

Commit eacaa3b

Browse files
author
Julian LALU
committed
Improve hashmap
1 parent 409c746 commit eacaa3b

5 files changed

Lines changed: 375 additions & 27 deletions

File tree

interface/core/containers/array.h

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -990,7 +990,6 @@ namespace hud
990990
* @param other The array to swap with
991991
*/
992992
constexpr void swap(array &other) noexcept
993-
requires(hud::is_swappable_v<type_t>)
994993
{
995994
static_assert(hud::is_nothrow_swappable_v<type_t>, "swap(array<type_t>&) is throwable. array is not designed to allow throwable swappable components");
996995
hud::swap(end_ptr, other.end_ptr);
@@ -1430,7 +1429,6 @@ namespace hud
14301429
* @param second The second array to swap
14311430
*/
14321431
template<typename type_t, typename allocator_t>
1433-
requires(hud::is_swappable_v<type_t>)
14341432
HD_FORCEINLINE void swap(array<type_t, allocator_t> &first, array<type_t, allocator_t> &second) noexcept
14351433
{
14361434
first.swap(second);

interface/core/containers/hashmap.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -415,6 +415,12 @@ namespace hud
415415
using type = hud::conditional_t<idx_to_reach == 0, const typename details::hashmap::hashmap_storage<key_t, value_t>::key_type, typename details::hashmap::hashmap_storage<key_t, value_t>::value_type>;
416416
};
417417

418+
template<typename key_t, typename value_t, typename hasher_t, typename key_equal_t, typename allocator_t>
419+
constexpr void swap(hashmap<key_t, value_t, hasher_t, key_equal_t, allocator_t> &first, hashmap<key_t, value_t, hasher_t, key_equal_t, allocator_t> &second) noexcept
420+
{
421+
first.swap(second);
422+
}
423+
418424
} // namespace hud
419425

420426
namespace std

interface/core/containers/hashset.h

Lines changed: 49 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,13 @@
1313
#include "../traits/is_trivially_copy_constructible.h"
1414
#include "tuple_size.h"
1515
#include "tuple_element.h"
16+
#include "compressed_pair.h"
17+
18+
// TODO:
19+
// Move common to a common class that contains max_slot_count_, count_, control_ptr_, slot_ptr_ and free_slot_before_grow_
20+
// Use compressed_pair with allocator and the common object
21+
// Add hasher and equal object added in compressed_pair like compressed_pair<allocator, compressed_pair<hasher, compressed_pair<equal, common>>>
22+
// Maybe create a compressed_tuple? ;)
1623

1724
namespace hud
1825
{
@@ -819,7 +826,7 @@ namespace hud
819826
template<typename u_storage_t, typename u_hasher_t, typename u_key_equal_t, typename u_allocator_t>
820827
constexpr explicit hashset_impl(const hashset_impl<u_storage_t, u_hasher_t, u_key_equal_t, u_allocator_t> &other, usize extra_max_count, const allocator_type &allocator) noexcept
821828
: allocator_ {allocator}
822-
, max_slot_count_ {compute_max_count(other.max_count() + extra_max_count)}
829+
, max_slot_count_ {normalize_max_count(other.max_count() + extra_max_count)}
823830
, count_ {other.count()}
824831
, free_slot_before_grow_(max_slot_before_grow(max_slot_count_) - count_)
825832
{
@@ -851,7 +858,7 @@ namespace hud
851858
template<typename u_storage_t, typename u_hasher_t, typename u_key_equal_t, typename u_allocator_t>
852859
constexpr explicit hashset_impl(hashset_impl<u_storage_t, u_hasher_t, u_key_equal_t, u_allocator_t> &&other, usize extra_max_count, const allocator_type &allocator) noexcept
853860
: allocator_ {allocator}
854-
, max_slot_count_ {compute_max_count(other.max_count() + extra_max_count)}
861+
, max_slot_count_ {normalize_max_count(other.max_count() + extra_max_count)}
855862
, count_ {other.count()}
856863
, free_slot_before_grow_(max_slot_before_grow(max_slot_count_) - count_)
857864
{
@@ -912,12 +919,13 @@ namespace hud
912919
return *this;
913920
}
914921

915-
/** Reserve memory for at least `count` element and regenerates the hash table. */
922+
/** Reserve memory for at least `count` element and regenerates the hash table if needed. */
916923
constexpr void reserve(usize count) noexcept
917924
{
918-
if (count > max_slot_count_)
925+
const usize max_count = normalize_max_count(min_capacity_for_count(count));
926+
if (max_count > max_slot_count_)
919927
{
920-
resize(compute_max_count(count));
928+
resize(max_count);
921929
}
922930
}
923931

@@ -1041,7 +1049,7 @@ namespace hud
10411049
// We request 0 or more and we have allocation and elements
10421050
// bitor is a faster way of doing `max` here. We will round up to the next
10431051
// power-of-2-minus-1, so bitor is good enough.
1044-
usize max_count = compute_max_count(count | min_capacity_for_count(count_));
1052+
usize max_count = normalize_max_count(count | min_capacity_for_count(count_));
10451053
if (count == 0 || max_count > max_slot_count_)
10461054
resize(max_count);
10471055
}
@@ -1069,6 +1077,23 @@ namespace hud
10691077
return find(key) != end();
10701078
}
10711079

1080+
constexpr void swap(hashset_impl &other) noexcept
1081+
requires(hud::is_swappable_v<slot_type>)
1082+
{
1083+
static_assert(hud::is_nothrow_swappable_v<allocator_type>, "swap(hashmap<type_t>&) is throwable. hashmap is not designed to allow throwable swappable components");
1084+
1085+
if constexpr (hud::allocator_traits<allocator_type>::swap_when_container_swap::value)
1086+
{
1087+
hud::swap(allocator_(), other.allocator_());
1088+
}
1089+
1090+
hud::swap(other.count_, count_);
1091+
hud::swap(other.control_ptr_, control_ptr_);
1092+
hud::swap(other.slot_ptr_, slot_ptr_);
1093+
hud::swap(other.max_slot_count_, max_slot_count_);
1094+
hud::swap(other.free_slot_before_grow_, free_slot_before_grow_);
1095+
}
1096+
10721097
constexpr void remove(const key_type &key) noexcept
10731098
{
10741099
iterator it = find(key);
@@ -1321,7 +1346,7 @@ namespace hud
13211346
// Copy the number of element
13221347
count_ = other.count_;
13231348
// Copy the max number of element
1324-
max_slot_count_ = compute_max_count(count_);
1349+
max_slot_count_ = normalize_max_count(count_);
13251350
// Compute the free slot count before growing
13261351
free_slot_before_grow_ = max_slot_before_grow(max_slot_count_) - count_;
13271352
// Allocate the control and slot
@@ -1397,7 +1422,7 @@ namespace hud
13971422
// Copy the number of element
13981423
count_ = other.count_;
13991424
// Copy the max number of element
1400-
max_slot_count_ = compute_max_count(count_);
1425+
max_slot_count_ = normalize_max_count(count_);
14011426
// Compute the free slot count before growing
14021427
free_slot_before_grow_ = max_slot_before_grow(max_slot_count_) - count_;
14031428
// Allocate the control and slot
@@ -1493,7 +1518,7 @@ namespace hud
14931518
[[nodiscard]] constexpr usize insert_no_construct(u64 h1, u8 h2) noexcept
14941519
{
14951520
// If we reach the load factor grow the table and retrieves the new slot, else use the given slot
1496-
// TODO : check rehash_and_grow_if_necessary and implement the SQuash DELETED branch
1521+
// TODO : check rehash_and_grow_if_necessary and implement the Squash DELETED branch
14971522
if (free_slot_before_grow() == 0)
14981523
{
14991524
resize(next_capacity());
@@ -1512,7 +1537,7 @@ namespace hud
15121537
constexpr void resize(usize new_max_slot_count) noexcept
15131538
{
15141539
// hud::check(new_max_slot_count > max_slot_count_ && "Grow need a bigger value");
1515-
hud::check(hud::bits::is_valid_power_of_two_mask(max_slot_count_) && "Not a mask");
1540+
hud::check(hud::bits::is_valid_power_of_two_mask(new_max_slot_count) && "Not a mask");
15161541

15171542
// Create the buffer with control and slots
15181543
// Slots are aligned based on alignof(slot_type)
@@ -1575,10 +1600,10 @@ namespace hud
15751600
return max_slot_count_ * 2 + 1;
15761601
}
15771602

1578-
/** Retrieves the max slot count for the given count. */
1579-
[[nodiscard]] constexpr usize compute_max_count(usize count) const noexcept
1603+
/** Retrieves a correct max slot count that is applicable. */
1604+
[[nodiscard]] constexpr usize normalize_max_count(usize max_slot_count) const noexcept
15801605
{
1581-
return count ? ~usize {} >> hud::bits::leading_zeros(count) : 0;
1606+
return max_slot_count ? ~usize {} >> hud::bits::leading_zeros(max_slot_count) : 0;
15821607
}
15831608

15841609
/** Compute the size of the allocation needed for the given slot count. */
@@ -1653,7 +1678,10 @@ namespace hud
16531678
capacity - capacity / 8;
16541679
}
16551680

1656-
/** Compute the minimum capacity needed for the given `count` element that respect the load factor */
1681+
/**
1682+
* Compute the minimum capacity needed for the given `count` element that respect the load factor.
1683+
* The capacity can be invalid, you should call
1684+
*/
16571685
[[nodiscard]] constexpr usize min_capacity_for_count(usize count) const noexcept
16581686
{
16591687
// `count*8/7`
@@ -1786,7 +1814,7 @@ namespace hud
17861814
}
17871815

17881816
private:
1789-
/** The allocator. */
1817+
// /** The allocator. */
17901818
allocator_type allocator_;
17911819

17921820
/** The control of the hashmap. Initialized to sentinel. */
@@ -1873,6 +1901,12 @@ namespace hud
18731901
using type = const typename details::hashset::slot<key_t>::key_type;
18741902
};
18751903

1904+
template<typename key_t, typename hasher_t, typename key_equal_t, typename allocator_t>
1905+
constexpr void swap(hashset<key_t, hasher_t, key_equal_t, allocator_t> &first, hashset<key_t, hasher_t, key_equal_t, allocator_t> &second) noexcept
1906+
{
1907+
first.swap(second);
1908+
}
1909+
18761910
} // namespace hud
18771911

18781912
namespace std

test/array/array_swap.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ GTEST_TEST(array, swap_non_trivial_type)
110110
hud_assert_eq(b.allocator().allocation_count(), 1u);
111111
hud_assert_eq(b.allocator().free_count(), 0u);
112112

113-
swap(a, b);
113+
hud::swap(a, b);
114114

115115
hud_assert_ne(a.data(), nullptr);
116116
hud_assert_eq(a.count(), 2u);
@@ -188,7 +188,7 @@ GTEST_TEST(array, swap_trivial_type)
188188
hud_assert_eq(b.allocator().allocation_count(), 1u);
189189
hud_assert_eq(b.allocator().free_count(), 0u);
190190

191-
swap(a, b);
191+
hud::swap(a, b);
192192

193193
hud_assert_ne(a.data(), nullptr);
194194
hud_assert_eq(a.count(), 2u);

0 commit comments

Comments
 (0)