@@ -663,7 +663,7 @@ impl<T, A: Allocator + Clone> RawTable<T, A> {
663
663
/// without reallocation.
664
664
#[ cfg_attr( feature = "inline-more" , inline) ]
665
665
pub fn reserve ( & mut self , additional : usize , hasher : impl Fn ( & T ) -> u64 ) {
666
- if additional > self . table . growth_left {
666
+ if unlikely ( additional > self . table . growth_left ) {
667
667
// Avoid `Result::unwrap_or_else` because it bloats LLVM IR.
668
668
if self
669
669
. reserve_rehash ( additional, hasher, Fallibility :: Infallible )
@@ -832,6 +832,22 @@ impl<T, A: Allocator + Clone> RawTable<T, A> {
832
832
}
833
833
}
834
834
835
+ /// Searches for an element in the table,
836
+ /// or a potential slot where that element could be inserted.
837
+ #[ inline]
838
+ pub fn find_potential ( & self , hash : u64 , mut eq : impl FnMut ( & T ) -> bool ) -> ( usize , bool ) {
839
+ self . table . find_potential_inner ( hash, & mut |index| unsafe {
840
+ eq ( self . bucket ( index) . as_ref ( ) )
841
+ } )
842
+ }
843
+
844
+ /// Marks an element in the table as inserted.
845
+ #[ inline]
846
+ pub unsafe fn mark_inserted ( & mut self , index : usize , hash : u64 ) {
847
+ let old_ctrl = * self . table . ctrl ( index) ;
848
+ self . table . record_item_insert_at ( index, old_ctrl, hash) ;
849
+ }
850
+
835
851
/// Searches for an element in the table.
836
852
#[ inline]
837
853
pub fn find ( & self , hash : u64 , mut eq : impl FnMut ( & T ) -> bool ) -> Option < Bucket < T > > {
@@ -1138,6 +1154,89 @@ impl<A: Allocator + Clone> RawTableInner<A> {
1138
1154
}
1139
1155
}
1140
1156
1157
+ /// Finds the position to insert something in a group.
1158
+ #[ inline]
1159
+ fn find_insert_slot_in_group ( & self , group : & Group , probe_seq : & ProbeSeq ) -> Option < usize > {
1160
+ let bit = group. match_empty_or_deleted ( ) . lowest_set_bit ( ) ;
1161
+
1162
+ if likely ( bit. is_some ( ) ) {
1163
+ let mut index = ( probe_seq. pos + bit. unwrap ( ) ) & self . bucket_mask ;
1164
+
1165
+ // In tables smaller than the group width, trailing control
1166
+ // bytes outside the range of the table are filled with
1167
+ // EMPTY entries. These will unfortunately trigger a
1168
+ // match, but once masked may point to a full bucket that
1169
+ // is already occupied. We detect this situation here and
1170
+ // perform a second scan starting at the beginning of the
1171
+ // table. This second scan is guaranteed to find an empty
1172
+ // slot (due to the load factor) before hitting the trailing
1173
+ // control bytes (containing EMPTY).
1174
+ unsafe {
1175
+ if unlikely ( self . is_bucket_full ( index) ) {
1176
+ debug_assert ! ( self . bucket_mask < Group :: WIDTH ) ;
1177
+ debug_assert_ne ! ( probe_seq. pos, 0 ) ;
1178
+ index = Group :: load_aligned ( self . ctrl ( 0 ) )
1179
+ . match_empty_or_deleted ( )
1180
+ . lowest_set_bit_nonzero ( ) ;
1181
+ }
1182
+ }
1183
+
1184
+ Some ( index)
1185
+ } else {
1186
+ None
1187
+ }
1188
+ }
1189
+
1190
+ /// Searches for an element in the table, or a potential slot where that element could be
1191
+ /// inserted.
1192
+ ///
1193
+ /// This uses dynamic dispatch to reduce the amount of code generated, but that is
1194
+ /// eliminated by LLVM optimizations.
1195
+ #[ inline]
1196
+ pub fn find_potential_inner (
1197
+ & self ,
1198
+ hash : u64 ,
1199
+ eq : & mut dyn FnMut ( usize ) -> bool ,
1200
+ ) -> ( usize , bool ) {
1201
+ let mut insert_slot = None ;
1202
+
1203
+ let h2_hash = h2 ( hash) ;
1204
+ let mut probe_seq = self . probe_seq ( hash) ;
1205
+
1206
+ loop {
1207
+ let group = unsafe { Group :: load ( self . ctrl ( probe_seq. pos ) ) } ;
1208
+
1209
+ for bit in group. match_byte ( h2_hash) {
1210
+ let index = ( probe_seq. pos + bit) & self . bucket_mask ;
1211
+
1212
+ if likely ( eq ( index) ) {
1213
+ return ( index, true ) ;
1214
+ }
1215
+ }
1216
+
1217
+ // We didn't find the element we were looking for in the group, try to get an
1218
+ // insertion slot from the group if we don't have one yet.
1219
+ if likely ( insert_slot. is_none ( ) ) {
1220
+ insert_slot = self . find_insert_slot_in_group ( & group, & probe_seq) ;
1221
+ }
1222
+
1223
+ // Only stop the search if the group contains at least one empty element.
1224
+ // Otherwise, the element that we are looking for might be in a following group.
1225
+ if likely ( group. match_empty ( ) . any_bit_set ( ) ) {
1226
+ // We must have found a insert slot by now, since the current group contains at
1227
+ // least one. For tables smaller than the group width, there will still be an
1228
+ // empty element in the current (and only) group due to the load factor.
1229
+ debug_assert ! ( insert_slot. is_some( ) ) ;
1230
+ match insert_slot {
1231
+ Some ( insert_slot) => return ( insert_slot, false ) ,
1232
+ None => unsafe { hint:: unreachable_unchecked ( ) } ,
1233
+ }
1234
+ }
1235
+
1236
+ probe_seq. move_next ( self . bucket_mask ) ;
1237
+ }
1238
+ }
1239
+
1141
1240
/// Searches for an empty or deleted bucket which is suitable for inserting
1142
1241
/// a new element and sets the hash for that slot.
1143
1242
///
@@ -1160,27 +1259,10 @@ impl<A: Allocator + Clone> RawTableInner<A> {
1160
1259
loop {
1161
1260
unsafe {
1162
1261
let group = Group :: load ( self . ctrl ( probe_seq. pos ) ) ;
1163
- if let Some ( bit) = group. match_empty_or_deleted ( ) . lowest_set_bit ( ) {
1164
- let result = ( probe_seq. pos + bit) & self . bucket_mask ;
1165
-
1166
- // In tables smaller than the group width, trailing control
1167
- // bytes outside the range of the table are filled with
1168
- // EMPTY entries. These will unfortunately trigger a
1169
- // match, but once masked may point to a full bucket that
1170
- // is already occupied. We detect this situation here and
1171
- // perform a second scan starting at the beginning of the
1172
- // table. This second scan is guaranteed to find an empty
1173
- // slot (due to the load factor) before hitting the trailing
1174
- // control bytes (containing EMPTY).
1175
- if unlikely ( self . is_bucket_full ( result) ) {
1176
- debug_assert ! ( self . bucket_mask < Group :: WIDTH ) ;
1177
- debug_assert_ne ! ( probe_seq. pos, 0 ) ;
1178
- return Group :: load_aligned ( self . ctrl ( 0 ) )
1179
- . match_empty_or_deleted ( )
1180
- . lowest_set_bit_nonzero ( ) ;
1181
- }
1262
+ let index = self . find_insert_slot_in_group ( & group, & probe_seq) ;
1182
1263
1183
- return result;
1264
+ if likely ( index. is_some ( ) ) {
1265
+ return index. unwrap ( ) ;
1184
1266
}
1185
1267
}
1186
1268
probe_seq. move_next ( self . bucket_mask ) ;
0 commit comments