@@ -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
0 commit comments