use crate::node::{
Node,
visitor::{Step, Visitor},
};
pub(crate) struct IndexVisitor<'a, T, const N: usize> {
current: &'a Node<T, N>,
index: usize,
level: usize,
target: usize,
}
impl<'a, T, const N: usize> IndexVisitor<'a, T, N> {
pub(crate) fn new(node: &'a Node<T, N>, target: usize) -> Self {
Self {
current: node,
index: 0,
level: node.level(),
target,
}
}
}
impl<'a, T, const N: usize> Visitor for IndexVisitor<'a, T, N> {
type NodeRef = &'a Node<T, N>;
fn current(&self) -> Self::NodeRef {
self.current
}
fn level(&self) -> usize {
self.level
}
fn found(&self) -> bool {
self.index == self.target
}
fn step(&mut self) -> Step<Self::NodeRef> {
if self.found() {
return Step::FoundTarget;
}
for (level, maybe_link) in (0..self.level).zip(self.current.links()).rev() {
if let Some(link) = maybe_link
&& self.index.saturating_add(link.distance().get()) <= self.target
{
self.current = unsafe { link.node().as_ref() };
self.level = level.saturating_add(1);
self.index = self.index.saturating_add(link.distance().get());
return Step::Advanced(self.current);
}
}
self.level = 0;
if let Some(next) = self.current.next_as_ref() {
self.current = next;
self.index = self.index.saturating_add(1);
return Step::Advanced(self.current);
}
Step::Exhausted
}
}
#[expect(
clippy::undocumented_unsafe_blocks,
reason = "test code, safety guarantees can be relaxed"
)]
#[cfg(test)]
mod tests {
use core::ptr::NonNull;
use anyhow::Result;
use pretty_assertions::assert_eq;
use rstest::rstest;
use super::IndexVisitor;
use crate::node::{
Node,
tests::{MAX_LEVELS, skiplist},
visitor::{Step, Visitor},
};
#[rstest]
fn index_traverser_step(skiplist: Result<NonNull<Node<u8, MAX_LEVELS>>>) -> Result<()> {
let head = skiplist?;
let mut traverser = IndexVisitor::new(unsafe { head.as_ref() }, 2);
let mut last_node = None;
while let Step::Advanced(node) = traverser.step() {
last_node = Some(node);
}
assert!(traverser.found());
assert_eq!(last_node.and_then(|n| n.value()), Some(&20));
unsafe { drop(Box::from_raw(head.as_ptr())) };
Ok(())
}
#[rstest]
fn index_traverser_step_none(skiplist: Result<NonNull<Node<u8, MAX_LEVELS>>>) -> Result<()> {
let head = skiplist?;
let mut traverser = IndexVisitor::new(unsafe { head.as_ref() }, 5);
let mut last_node = None;
while let Step::Advanced(node) = traverser.step() {
last_node = Some(node);
}
assert!(!traverser.found());
assert_eq!(last_node.and_then(|n| n.value()), Some(&40));
unsafe { drop(Box::from_raw(head.as_ptr())) };
Ok(())
}
#[rstest]
fn index_traverser_traverse(skiplist: Result<NonNull<Node<u8, MAX_LEVELS>>>) -> Result<()> {
let head = skiplist?;
let mut traverser = IndexVisitor::new(unsafe { head.as_ref() }, 2);
let node = traverser.traverse();
assert!(traverser.found());
assert_eq!(node.and_then(|n| n.value()), Some(&20));
unsafe { drop(Box::from_raw(head.as_ptr())) };
Ok(())
}
#[rstest]
fn index_traverser_traverse_not_found(
skiplist: Result<NonNull<Node<u8, MAX_LEVELS>>>,
) -> Result<()> {
let head = skiplist?;
let mut traverser = IndexVisitor::new(unsafe { head.as_ref() }, 5);
let node = traverser.traverse();
assert!(!traverser.found());
assert!(node.is_none());
unsafe { drop(Box::from_raw(head.as_ptr())) };
Ok(())
}
}