Skip to content

Commit ba2db9a

Browse files
committed
Make find_potential and search less generic by using dynamic dispatch
1 parent 406ebb7 commit ba2db9a

File tree

1 file changed

+97
-77
lines changed

1 file changed

+97
-77
lines changed

src/raw/mod.rs

Lines changed: 97 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -912,92 +912,25 @@ impl<T, A: Allocator + Clone> RawTable<T, A> {
912912
}
913913
}
914914

915-
/// Searches for an element in the table, stopping at the group where `stop` returns `Some` and
916-
/// no elements matched. Returns the bucket that matches or the result of `stop`.
917-
#[inline]
918-
unsafe fn search<R>(
919-
&self,
920-
hash: u64,
921-
mut eq: impl FnMut(&T) -> bool,
922-
mut stop: impl FnMut(&Group, &ProbeSeq) -> Option<R>,
923-
) -> Result<Bucket<T>, R> {
924-
let h2_hash = h2(hash);
925-
let mut probe_seq = self.table.probe_seq(hash);
926-
927-
loop {
928-
let group = Group::load(self.table.ctrl(probe_seq.pos));
929-
930-
for bit in group.match_byte(h2_hash).into_iter() {
931-
let index = (probe_seq.pos + bit) & self.table.bucket_mask;
932-
933-
let bucket = self.bucket(index);
934-
let elm = self.bucket(index).as_ref();
935-
if likely(eq(elm)) {
936-
return Ok(bucket);
937-
}
938-
}
939-
940-
if let Some(stop) = stop(&group, &probe_seq) {
941-
return Err(stop);
942-
}
943-
944-
probe_seq.move_next(self.table.bucket_mask);
945-
}
946-
}
947-
948915
/// Searches for an element in the table,
949916
/// or a potential slot where that element could be inserted.
950917
#[inline]
951918
pub fn find_potential(
952919
&self,
953920
hash: u64,
954-
eq: impl FnMut(&T) -> bool,
921+
mut eq: impl FnMut(&T) -> bool,
955922
) -> Result<Bucket<T>, usize> {
956923
unsafe {
957-
let mut tombstone = None;
958-
self.search(hash, eq, |group, probe_seq| {
959-
let bit = group.match_empty_or_deleted().lowest_set_bit();
960-
961-
if likely(bit.is_some()) {
962-
let mut index = (probe_seq.pos + bit.unwrap()) & self.table.bucket_mask;
963-
964-
// In tables smaller than the group width, trailing control
965-
// bytes outside the range of the table are filled with
966-
// EMPTY entries. These will unfortunately trigger a
967-
// match, but once masked may point to a full bucket that
968-
// is already occupied. We detect this situation here and
969-
// perform a second scan starting at the begining of the
970-
// table. This second scan is guaranteed to find an empty
971-
// slot (due to the load factor) before hitting the trailing
972-
// control bytes (containing EMPTY).
973-
if unlikely(is_full(*self.table.ctrl(index))) {
974-
debug_assert!(self.table.bucket_mask < Group::WIDTH);
975-
debug_assert_ne!(probe_seq.pos, 0);
976-
977-
index = Group::load_aligned(self.table.ctrl(0))
978-
.match_empty_or_deleted()
979-
.lowest_set_bit_nonzero()
980-
}
924+
let result = self.table.find_potential_inner(hash, &mut |index| {
925+
let bucket = self.bucket(index);
926+
let elm = bucket.as_ref();
927+
eq(elm)
928+
});
981929

982-
// Only stop the search if the group is empty. The element might be
983-
// in a following group.
984-
if likely(group.match_empty().any_bit_set()) {
985-
// Use a tombstone if we found one
986-
if unlikely(tombstone.is_some()) {
987-
tombstone
988-
} else {
989-
Some(index)
990-
}
991-
} else {
992-
// We found a tombstone, record it so we can return it as a potential
993-
// insertion location.
994-
tombstone = Some(index);
995-
None
996-
}
997-
} else {
998-
None
999-
}
1000-
})
930+
match result {
931+
Ok(index) => Ok(self.bucket(index)),
932+
Err(index) => Err(index),
933+
}
1001934
}
1002935
}
1003936

@@ -1288,6 +1221,93 @@ impl<A: Allocator + Clone> RawTableInner<A> {
12881221
}
12891222
}
12901223

1224+
/// Searches for an element in the table, stopping at the group where `stop` returns `Some` and
1225+
/// no elements matched. Returns the bucket that matches or the result of `stop`.
1226+
#[inline]
1227+
unsafe fn search<R>(
1228+
&self,
1229+
hash: u64,
1230+
eq: &mut dyn FnMut(usize) -> bool,
1231+
mut stop: impl FnMut(&Group, &ProbeSeq) -> Option<R>,
1232+
) -> Result<usize, R> {
1233+
let h2_hash = h2(hash);
1234+
let mut probe_seq = self.probe_seq(hash);
1235+
1236+
loop {
1237+
let group = Group::load(self.ctrl(probe_seq.pos));
1238+
1239+
for bit in group.match_byte(h2_hash).into_iter() {
1240+
let index = (probe_seq.pos + bit) & self.bucket_mask;
1241+
1242+
if likely(eq(index)) {
1243+
return Ok(index);
1244+
}
1245+
}
1246+
1247+
if let Some(stop) = stop(&group, &probe_seq) {
1248+
return Err(stop);
1249+
}
1250+
1251+
probe_seq.move_next(self.bucket_mask);
1252+
}
1253+
}
1254+
1255+
/// Searches for an element in the table,
1256+
/// or a potential slot where that element could be inserted.
1257+
#[inline]
1258+
pub fn find_potential_inner(
1259+
&self,
1260+
hash: u64,
1261+
eq: &mut dyn FnMut(usize) -> bool,
1262+
) -> Result<usize, usize> {
1263+
unsafe {
1264+
let mut tombstone = None;
1265+
self.search(hash, eq, |group, probe_seq| {
1266+
let bit = group.match_empty_or_deleted().lowest_set_bit();
1267+
1268+
if likely(bit.is_some()) {
1269+
let mut index = (probe_seq.pos + bit.unwrap()) & self.bucket_mask;
1270+
1271+
// In tables smaller than the group width, trailing control
1272+
// bytes outside the range of the table are filled with
1273+
// EMPTY entries. These will unfortunately trigger a
1274+
// match, but once masked may point to a full bucket that
1275+
// is already occupied. We detect this situation here and
1276+
// perform a second scan starting at the begining of the
1277+
// table. This second scan is guaranteed to find an empty
1278+
// slot (due to the load factor) before hitting the trailing
1279+
// control bytes (containing EMPTY).
1280+
if unlikely(is_full(*self.ctrl(index))) {
1281+
debug_assert!(self.bucket_mask < Group::WIDTH);
1282+
debug_assert_ne!(probe_seq.pos, 0);
1283+
1284+
index = Group::load_aligned(self.ctrl(0))
1285+
.match_empty_or_deleted()
1286+
.lowest_set_bit_nonzero()
1287+
}
1288+
1289+
// Only stop the search if the group is empty. The element might be
1290+
// in a following group.
1291+
if likely(group.match_empty().any_bit_set()) {
1292+
// Use a tombstone if we found one
1293+
if unlikely(tombstone.is_some()) {
1294+
tombstone
1295+
} else {
1296+
Some(index)
1297+
}
1298+
} else {
1299+
// We found a tombstone, record it so we can return it as a potential
1300+
// insertion location.
1301+
tombstone = Some(index);
1302+
None
1303+
}
1304+
} else {
1305+
None
1306+
}
1307+
})
1308+
}
1309+
}
1310+
12911311
/// Searches for an empty or deleted bucket which is suitable for inserting
12921312
/// a new element and sets the hash for that slot.
12931313
///

0 commit comments

Comments
 (0)