use std::cmp::Ordering;
use std::sync::atomic::Ordering as AtomicOrdering;
use super::node::{MAX_HEIGHT, SkipNode};
use super::{SkipList, strip_mark};
pub(crate) struct Finger<N: SkipNode> {
preds: [*mut N; MAX_HEIGHT],
}
impl<N: SkipNode> SkipList<N> {
pub(crate) fn finger(&self) -> Finger<N> {
Finger {
preds: [self.head; MAX_HEIGHT],
}
}
#[inline]
fn next_unmarked(&self, current: *mut N, level: usize) -> *mut N {
let mut next = strip_mark(unsafe { (*current).tower(level).load(AtomicOrdering::Acquire) });
while !next.is_null() && unsafe { &*next }.is_marked() {
next = strip_mark(unsafe { (*next).tower(level).load(AtomicOrdering::Acquire) });
}
next
}
#[inline]
fn at_or_after(&self, current: *mut N, candidate: *mut N) -> bool {
if candidate == current || current == self.head {
return true;
}
if candidate == self.head {
return false;
}
self.key_cmp(
unsafe { &*candidate }.key_bytes(),
unsafe { &*current }.key_bytes(),
) != Ordering::Less
}
pub(crate) fn finger_seek<'g>(
&self,
finger: &mut Finger<N>,
key: &[u8],
guard: &'g seize::LocalGuard<'_>,
) -> Option<&'g N> {
let _ = guard; let h = self.height.load(AtomicOrdering::Relaxed);
let mut current = self.head;
for level in (0..h).rev() {
let saved = finger.preds[level];
if self.at_or_after(current, saved) {
current = saved;
}
loop {
let next = self.next_unmarked(current, level);
if next.is_null() {
break;
}
if self.key_cmp(unsafe { &*next }.key_bytes(), key) == Ordering::Less {
current = next;
} else {
break;
}
}
finger.preds[level] = current;
}
let result = self.next_unmarked(current, 0);
if result.is_null() {
None
} else {
Some(unsafe { &*result })
}
}
}
#[cfg(test)]
mod tests {
use crate::DiskLoc;
use crate::skiplist::SkipList;
use crate::skiplist::node::ConstNode;
type TestNode = ConstNode<[u8; 4], 8>;
type TestList = SkipList<TestNode>;
fn disk() -> DiskLoc {
DiskLoc::new(0, 0, 0)
}
fn n(key: [u8; 4], h: u8) -> *mut TestNode {
TestNode::alloc(key, [0u8; 8], disk(), h)
}
fn key(b: u8) -> [u8; 4] {
[0, 0, 0, b]
}
fn build(reversed: bool, items: &[(u8, u8)]) -> TestList {
let list = TestList::new(reversed);
{
let guard = list.collector().enter();
for &(b, h) in items {
list.insert(n(key(b), h), &guard);
}
}
list
}
#[test]
fn finger_matches_find_first_ge_asc() {
let list = build(false, &[(2, 1), (4, 2), (6, 2), (8, 1)]);
let guard = list.collector().enter();
for probe in 0u8..=10 {
let mut finger = list.finger();
let got = list.finger_seek(&mut finger, &key(probe), &guard);
let oracle = list.find_first_ge(&key(probe), &guard);
let got_ptr = got.map_or(std::ptr::null(), |r| r as *const TestNode);
assert_eq!(got_ptr, oracle as *const TestNode, "probe={probe}");
}
}
#[test]
fn finger_forward_sweep_matches_oracle_asc() {
let list = build(false, &[(1, 1), (3, 2), (5, 1), (7, 3), (9, 1)]);
let guard = list.collector().enter();
let probes: Vec<u8> = vec![0, 1, 3, 4, 5, 9, 10]; let mut finger = list.finger();
for &p in &probes {
let got = list.finger_seek(&mut finger, &key(p), &guard);
let oracle = list.find_first_ge(&key(p), &guard);
let got_ptr = got.map_or(std::ptr::null(), |r| r as *const TestNode);
assert_eq!(got_ptr, oracle as *const TestNode, "p={p}");
}
}
#[test]
fn finger_duplicates_and_no_backward() {
let list = build(false, &[(2, 2), (4, 1), (6, 2)]);
let guard = list.collector().enter();
let mut finger = list.finger();
let a = list.finger_seek(&mut finger, &key(4), &guard).unwrap().key;
let b = list.finger_seek(&mut finger, &key(4), &guard).unwrap().key;
assert_eq!(a, key(4));
assert_eq!(b, key(4));
let c = list.finger_seek(&mut finger, &key(6), &guard).unwrap().key;
assert_eq!(c, key(6));
}
#[test]
fn finger_matches_find_first_ge_desc() {
let list = build(true, &[(2, 1), (4, 2), (6, 2), (8, 1)]);
let guard = list.collector().enter();
for probe in (0u8..=10).rev() {
let mut finger = list.finger();
let got = list.finger_seek(&mut finger, &key(probe), &guard);
let oracle = list.find_first_ge(&key(probe), &guard);
let got_ptr = got.map_or(std::ptr::null(), |r| r as *const TestNode);
assert_eq!(got_ptr, oracle as *const TestNode, "probe={probe}");
}
}
#[test]
fn finger_edge_empty_and_single() {
let empty = build(false, &[]);
let guard = empty.collector().enter();
let mut f = empty.finger();
assert!(empty.finger_seek(&mut f, &key(5), &guard).is_none());
let single = build(false, &[(5, 1)]);
let guard2 = single.collector().enter();
let mut f2 = single.finger();
assert_eq!(
single.finger_seek(&mut f2, &key(1), &guard2).unwrap().key,
key(5)
);
let mut f3 = single.finger();
assert!(single.finger_seek(&mut f3, &key(9), &guard2).is_none());
}
#[test]
fn finger_skips_marked_nodes() {
let list = build(false, &[(2, 2), (4, 1), (6, 2), (8, 1)]);
let guard = list.collector().enter();
list.remove(&key(4), &guard);
let mut finger = list.finger();
let got = list.finger_seek(&mut finger, &key(3), &guard);
let oracle = list.find_first_ge(&key(3), &guard);
let got_ptr = got.map_or(std::ptr::null(), |r| r as *const TestNode);
assert_eq!(got_ptr, oracle as *const TestNode);
}
}