Skip to content

Commit aa25806

Browse files
author
Julian LALU
committed
Improve hashmap
1 parent 5d6e94f commit aa25806

108 files changed

Lines changed: 389 additions & 259 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

core.vscode.code-workspace

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
"cmake.options.statusBarVisibility": "compact",
2727
"cmake.outputLogEncoding": "utf8",
2828
"debug.showVariableTypes": true,
29+
"rust-analyzer.checkOnSave": false,
2930
},
3031
"extensions": {
3132
"recommendations": [

interface/core/atomics/atomics_gcc.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -210,13 +210,13 @@ namespace hud::gcc
210210
private:
211211

212212
private:
213-
static constexpr i32 to_gcc_order(memory_order_e order)
213+
static inline constexpr i32 to_gcc_order(memory_order_e order)
214214
{
215215
// Avoid switch statement to make this a constexpr.
216216
return order == memory_order_e::relaxed ? __ATOMIC_RELAXED : (order == memory_order_e::acquire ? __ATOMIC_ACQUIRE : (order == memory_order_e::release ? __ATOMIC_RELEASE : (order == memory_order_e::seq_cst ? __ATOMIC_SEQ_CST : (order == memory_order_e::acq_rel ? __ATOMIC_ACQ_REL : __ATOMIC_CONSUME))));
217217
}
218218

219-
static constexpr i32 to_gcc_failure_order(memory_order_e order)
219+
static inline constexpr i32 to_gcc_failure_order(memory_order_e order)
220220
{
221221
// Avoid switch statement to make this a constexpr.
222222
// This memory order cannot be __ATOMIC_RELEASE nor __ATOMIC_ACQ_REL. It also cannot be a stronger order than that specified by success_memorder.

interface/core/containers/hashset.h

Lines changed: 136 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -859,7 +859,7 @@ namespace hud
859859
* Copy assign another hashset_impl.
860860
* The copy assignement only grow allocation and never shrink allocation.
861861
* No new allocation is done if the hashset_impl contains enough memory to copy all elements, in other words we don't copy the capacity of the copied hashset_impl.
862-
* @param other The other array to copy
862+
* @param other The other hashset_impl to copy
863863
* @return *this
864864
*/
865865
constexpr hashset_impl &operator=(const hashset_impl &other) noexcept
@@ -869,7 +869,6 @@ namespace hud
869869
{
870870
copy_assign(other);
871871
}
872-
873872
return *this;
874873
}
875874

@@ -881,6 +880,30 @@ namespace hud
881880
return *this;
882881
}
883882

883+
/**
884+
* Move assign another hashset.
885+
* Never assume that the move assignement will keep the capacity of the moved hashset_impl.
886+
* Depending of the Type and the allocator the move operation can reallocate or not, this is by design and allow some move optimization
887+
* @param other The other hashset_impl to move
888+
*/
889+
constexpr hashset_impl &operator=(hashset_impl &&other) noexcept
890+
requires(hud::is_move_constructible_v<slot_type>)
891+
{
892+
if (this != &other)
893+
{
894+
move_assign(other);
895+
}
896+
return *this;
897+
}
898+
899+
template<typename u_storage_t, typename u_hasher_t, typename u_key_equal_t, typename u_allocator_t>
900+
requires(hud::is_move_constructible_v<slot_type>)
901+
constexpr hashset_impl &operator=(hashset_impl<u_storage_t, u_hasher_t, u_key_equal_t, u_allocator_t> &&other) noexcept
902+
{
903+
move_assign(other);
904+
return *this;
905+
}
906+
884907
/** Reserve memory for at least `count` element and regenerates the hash table. */
885908
constexpr void reserve(usize count) noexcept
886909
{
@@ -1081,7 +1104,7 @@ namespace hud
10811104
// In a non constant evaluated context
10821105
// If type is trivially move constructible, just memcpy control and slot
10831106
// else do like grow_capacity
1084-
if (extra_max_count > 0 || hud::is_constant_evaluated() || !hud::is_bitwise_move_constructible_v<slot_type>)
1107+
if (extra_max_count > 0 || hud::is_constant_evaluated() || !hud::is_bitwise_copy_constructible_v<slot_type, typename hashset_impl<u_storage_t, u_hasher_t, u_key_equal_t, u_allocator_t>::slot_type>)
10851108
{
10861109
// Set control to empty ending with sentinel
10871110
hud::memory::set_memory(control_ptr_, control_size, empty_byte);
@@ -1130,9 +1153,9 @@ namespace hud
11301153
// If constant evaluated context or when slot_type is not bitwise move constructible or when we allocate more memory than the copied set
11311154
// loop through all slot and construct them regardless of the trivially constructible ( Maybe only for control_ptr_ ) like like grow_capacity
11321155
// In a non constant evaluated context
1133-
// If type is trivially move constructible, just memcpy control and slot
1156+
// If type is trivially move constructible, just take ownership of control and slot
11341157
// else do like grow_capacity
1135-
if (extra_max_count > 0 || hud::is_constant_evaluated() || !hud::is_bitwise_move_constructible_v<slot_type>)
1158+
if (extra_max_count > 0 || hud::is_constant_evaluated() || !hud::is_bitwise_move_constructible_v<slot_type, typename hashset_impl<u_storage_t, u_hasher_t, u_key_equal_t, u_allocator_t>::slot_type>)
11361159
{
11371160
// Set control to empty ending with sentinel
11381161
hud::memory::set_memory(control_ptr_, control_size, empty_byte);
@@ -1156,17 +1179,18 @@ namespace hud
11561179
};
11571180
iterate_over_full_slots(other.control_ptr_, other.slot_ptr_, other.count_, other.max_slot_count_, insert_slot_by_copy);
11581181
}
1182+
other.reset_control_and_slot();
11591183
}
11601184
else
11611185
{
1162-
hud::memory::fast_move_or_copy_construct_object_array_then_destroy(control_ptr_, other.control_ptr_, control_size);
1163-
if (other.count() > 0)
1164-
{
1165-
hud::memory::fast_move_or_copy_construct_object_array_then_destroy(slot_ptr_, other.slot_ptr_, other.max_count());
1166-
}
1186+
control_ptr_ = other.control_ptr_;
1187+
other.control_ptr_ = const_cast<control_type *>(&INIT_GROUP[16]);
1188+
slot_ptr_ = reinterpret_cast<slot_type *>(other.slot_ptr_);
1189+
other.slot_ptr_ = nullptr;
1190+
other.max_slot_count_ = 0;
1191+
other.count_ = 0;
1192+
other.free_slot_before_grow_ = 0;
11671193
}
1168-
1169-
other.reset_control_and_slot();
11701194
}
11711195

11721196
template<typename u_storage_t, typename u_hasher_t, typename u_key_equal_t, typename u_allocator_t>
@@ -1225,7 +1249,6 @@ namespace hud
12251249
// If we have elements to copy, copy them
12261250
if (count_ > 0)
12271251
{
1228-
12291252
auto insert_slot_by_copy = [this](control_type *control_ptr, auto *slot_ptr)
12301253
{
12311254
u64 hash {hud::is_same_v<key_type, typename u_storage_t::key_type> ? hasher_type {}(slot_ptr->key()) : hasher_type {}(key_type {slot_ptr->key()})};
@@ -1241,6 +1264,106 @@ namespace hud
12411264
}
12421265
}
12431266

1267+
template<typename u_storage_t, typename u_hasher_t, typename u_key_equal_t, typename u_allocator_t>
1268+
constexpr void move_assign(hashset_impl<u_storage_t, u_hasher_t, u_key_equal_t, u_allocator_t> &&other) noexcept
1269+
{
1270+
// Destroy all slots but don't touch the allocated memory now
1271+
// If we can just move control and slot pointers we do it
1272+
// If we can't just move control and slot pointers, we deal with 2 scenarios
1273+
// First, we have enough memory and just add elements by moving them one by one
1274+
// Second, we don't have enough memory and we reallacte memory and add elements by moving them one by one
1275+
destroy_all_slots();
1276+
1277+
if (hud::is_constant_evaluated() || !hud::is_bitwise_move_constructible_v<slot_type, typename hashset_impl<u_storage_t, u_hasher_t, u_key_equal_t, u_allocator_t>::slot_type>)
1278+
{
1279+
// We don't keep the max count of the copied array
1280+
// The requested memory is the number of element in the copied array, not the max slot count.
1281+
const usize max_count_requested = other.count_;
1282+
// If we have enough allocated memory
1283+
if (max_count_requested <= max_slot_count_)
1284+
{
1285+
// Reset the control to empty if we have allocated memory ( Do nothing if we have nothing because control_ptr_ points to static INIT_GROUP)
1286+
if (max_slot_count_ > 0)
1287+
{
1288+
hud::memory::set_memory(control_ptr_, control_size_for_max_count(max_slot_count_), empty_byte);
1289+
control_ptr_[max_slot_count_] = sentinel_byte;
1290+
}
1291+
// Copy the allocator if copy_when_container_copy_assigned is true
1292+
if constexpr (hud::is_not_same_v<u_allocator_t, allocator_type> || hud::allocator_traits<allocator_t>::copy_when_container_copy_assigned::value)
1293+
{
1294+
allocator_ = hud::move(other.allocator_);
1295+
}
1296+
// Copy the number of element
1297+
count_ = other.count_;
1298+
// Compute the free slot count before growing
1299+
free_slot_before_grow_ = max_slot_before_grow(max_slot_count_) - count_;
1300+
}
1301+
else // If we don't have enough memory
1302+
{
1303+
// Free the control and slot allocation
1304+
free_control_and_slot(control_ptr_, slot_ptr_, max_slot_count_);
1305+
1306+
// Copy the allocator if copy_when_container_copy_assigned is true
1307+
if constexpr (hud::is_not_same_v<u_allocator_t, allocator_type> || hud::allocator_traits<allocator_t>::copy_when_container_copy_assigned::value)
1308+
{
1309+
allocator_ = hud::move(other.allocator_);
1310+
}
1311+
// Copy the number of element
1312+
count_ = other.count_;
1313+
// Copy the max number of element
1314+
max_slot_count_ = compute_max_count(count_);
1315+
// Compute the free slot count before growing
1316+
free_slot_before_grow_ = max_slot_before_grow(max_slot_count_) - count_;
1317+
// Allocate the control and slot
1318+
usize control_size {allocate_control_and_slot(max_slot_count_)};
1319+
1320+
// Set control to empty ending with sentinel
1321+
hud::memory::set_memory(control_ptr_, control_size, empty_byte);
1322+
control_ptr_[max_slot_count_] = sentinel_byte;
1323+
}
1324+
// If we have elements to move, move them
1325+
if (count_ > 0)
1326+
{
1327+
auto insert_slot_by_copy = [this](control_type *control_ptr, auto *slot_ptr)
1328+
{
1329+
u64 hash {hud::is_same_v<key_type, typename u_storage_t::key_type> ? hasher_type {}(slot_ptr->key()) : hasher_type {}(key_type {slot_ptr->key()})};
1330+
// Find H1 slot index
1331+
u64 h1 {H1(hash)};
1332+
usize slot_index {find_first_empty_or_deleted(control_ptr_, max_slot_count_, h1)};
1333+
// Save h2 in control h1 index
1334+
control::set_h2(control_ptr_, slot_index, H2(hash), max_slot_count_);
1335+
// Copy slot
1336+
hud::memory::construct_object_at(slot_ptr_ + slot_index, *slot_ptr);
1337+
};
1338+
iterate_over_full_slots(other.control_ptr_, other.slot_ptr_, count_, other.max_slot_count_, insert_slot_by_copy);
1339+
}
1340+
1341+
other.reset_control_and_slot();
1342+
}
1343+
// If we are not in constant expression and the slot_type are bitwise moveable, move it.
1344+
else
1345+
{
1346+
// Free the allocated buffers and take ownership of other buffers
1347+
free_control_and_slot(control_ptr_, slot_ptr_, max_slot_count_);
1348+
1349+
// Move allocator and move members
1350+
if constexpr (hud::is_not_same_v<u_allocator_t, allocator_type> || hud::allocator_traits<allocator_t>::move_when_container_move_assigned::value)
1351+
{
1352+
allocator_ = hud::move(other.allocator_);
1353+
}
1354+
control_ptr_ = other.control_ptr_;
1355+
other.control_ptr_ = const_cast<control_type *>(&INIT_GROUP[16]);
1356+
slot_ptr_ = reinterpret_cast<slot_type *>(other.slot_ptr_);
1357+
other.slot_ptr_ = nullptr;
1358+
count_ = other.count_;
1359+
other.count_ = 0;
1360+
max_slot_count_ = other.max_slot_count_;
1361+
other.max_slot_count_ = 0;
1362+
free_slot_before_grow_ = other.free_slot_before_grow_ = 0;
1363+
other.free_slot_before_grow_ = 0;
1364+
}
1365+
}
1366+
12441367
/**
12451368
* Find the key and add the H2 hash in the control
12461369
* If the key is found, return the iterator

interface/core/containers/optional.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -431,7 +431,7 @@ namespace hud
431431
};
432432

433433
/** Constant used to indicate optional type with uninitialized state. */
434-
constexpr nullopt_t nullopt {nullopt_t::tag {}};
434+
inline constexpr nullopt_t nullopt {nullopt_t::tag {}};
435435

436436
/**
437437
* Manages an optional contained value, i.e. a value that may or may not be present.

interface/core/containers/tuple.h

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -192,7 +192,7 @@ namespace hud
192192
};
193193

194194
template<typename pair_t, typename tuple_t>
195-
constexpr bool is_pair_copy_constructible_to_tuple_v = is_pair_copy_constructible_to_tuple<pair_t, tuple_t>::value;
195+
inline constexpr bool is_pair_copy_constructible_to_tuple_v = is_pair_copy_constructible_to_tuple<pair_t, tuple_t>::value;
196196

197197
/**
198198
* Check if a tuple<T0,T1> is explicitly copy constructible from pair<first_type,second_type>.
@@ -211,7 +211,7 @@ namespace hud
211211
};
212212

213213
template<typename pair_t, typename tuple_t>
214-
constexpr bool is_pair_explicitly_copy_constructible_to_tuple_v = is_pair_explicitly_copy_constructible_to_tuple<pair_t, tuple_t>::value;
214+
inline constexpr bool is_pair_explicitly_copy_constructible_to_tuple_v = is_pair_explicitly_copy_constructible_to_tuple<pair_t, tuple_t>::value;
215215

216216
/**
217217
* @brief Check if a tuple<T0,T1> is move constructible from pair<first_type,second_type>.
@@ -233,7 +233,7 @@ namespace hud
233233
};
234234

235235
template<typename pair_t, typename tuple_t>
236-
constexpr bool is_pair_move_constructible_to_tuple_v = is_pair_move_constructible_to_tuple<pair_t, tuple_t>::value;
236+
inline constexpr bool is_pair_move_constructible_to_tuple_v = is_pair_move_constructible_to_tuple<pair_t, tuple_t>::value;
237237

238238
/**
239239
* Check if a tuple<T0,T1> is explicitly move constructible from pair<first_type,second_type>.
@@ -252,7 +252,7 @@ namespace hud
252252
};
253253

254254
template<typename pair_t, typename tuple_t>
255-
constexpr bool is_pair_explicitly_move_constructible_to_tuple_v = is_pair_explicitly_move_constructible_to_tuple<pair_t, tuple_t>::value;
255+
inline constexpr bool is_pair_explicitly_move_constructible_to_tuple_v = is_pair_explicitly_move_constructible_to_tuple<pair_t, tuple_t>::value;
256256

257257
/**
258258
* Recursively assign a tuple to another.

interface/core/containers/tuple_size.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ namespace hud
7272

7373
/** Equalivent of tuple_size<tuple_like>::value. */
7474
template<typename tuple_like>
75-
constexpr usize tuple_size_v = tuple_size<tuple_like>::value;
75+
inline constexpr usize tuple_size_v = tuple_size<tuple_like>::value;
7676

7777
} // namespace hud
7878

interface/core/i128.h

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -149,11 +149,11 @@ namespace hud
149149
{
150150
}
151151

152-
static constexpr i128 i128_max = i128 {i64_max, u64_max};
153-
static constexpr i128 i128_min = i128 {i64_min, 0u};
152+
static inline constexpr i128 i128_max = i128 {i64_max, u64_max};
153+
static inline constexpr i128 i128_min = i128 {i64_min, 0u};
154154

155-
static constexpr u128 u128_max = u128 {u64_max, u64_max};
156-
static constexpr u128 u128_min = u128 {u64_min, 0u};
155+
static inline constexpr u128 u128_max = u128 {u64_max, u64_max};
156+
static inline constexpr u128 u128_min = u128 {u64_min, 0u};
157157

158158
template<> struct limits<i128>
159159
{

interface/core/i128/i128_portable.h

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1223,11 +1223,11 @@ namespace hud
12231223

12241224
} // namespace details::i128
12251225

1226-
// static constexpr details::i128::i128_impl i128_max = details::i128::i128_impl {i64_max, u64_max};
1227-
// static constexpr details::i128::i128_impl i128_min = details::i128::i128_impl {i64_min, 0u};
1226+
// static inline constexpr details::i128::i128_impl i128_max = details::i128::i128_impl {i64_max, u64_max};
1227+
// static inline constexpr details::i128::i128_impl i128_min = details::i128::i128_impl {i64_min, 0u};
12281228

1229-
// static constexpr details::i128::u128_impl u128_max = details::i128::u128_impl {u64_max, u64_max};
1230-
// static constexpr details::i128::u128_impl u128_min = details::i128::u128_impl {u64_min, 0u};
1229+
// static inline constexpr details::i128::u128_impl u128_max = details::i128::u128_impl {u64_max, u64_max};
1230+
// static inline constexpr details::i128::u128_impl u128_min = details::i128::u128_impl {u64_min, 0u};
12311231

12321232
// template<> struct limits<details::i128::i128_impl>
12331233
// {

interface/core/tag_in_place.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ namespace hud
1111
};
1212

1313
/** Constant used to indicate to construct an object in-place. */
14-
constexpr tag_in_place in_place {};
14+
inline constexpr tag_in_place in_place {};
1515
} // namespace hud
1616

1717
#endif // HD_INC_CORE_INPLACE_H

interface/core/tag_init.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ namespace hud
1717
};
1818

1919
/** Constant used to indicate to initialize an object. */
20-
constexpr tag_init taginit {tag_init::tag {}};
20+
inline constexpr tag_init taginit {tag_init::tag {}};
2121

2222
} // namespace hud
2323

0 commit comments

Comments
 (0)