use crate::{Result, bucket::HashBucket, error::Error, overflow_pool::OverflowPool};
pub(crate) enum ChainStep {
Next,
End,
Cycle,
}
pub(crate) const MAX_CHAIN_STEPS: usize = 1 << 22;
pub(crate) enum SlotScan {
Hit(u64),
Free,
Occupied,
}
pub(crate) struct ChainWalker<'a> {
pub(crate) curr: &'a HashBucket,
pub(crate) step: usize,
}
impl<'a> ChainWalker<'a> {
#[inline]
pub(crate) fn new(start: &'a HashBucket) -> Self {
Self {
curr: start,
step: 0,
}
}
#[inline]
pub(crate) fn advance(&mut self, pool: &'a OverflowPool) -> ChainStep {
let overflow_idx = self.curr.overflow_index();
if overflow_idx == 0 {
return ChainStep::End;
}
self.curr = unsafe { pool.get_unchecked(overflow_idx) };
self.step += 1;
if self.step >= MAX_CHAIN_STEPS {
ChainStep::Cycle
} else {
ChainStep::Next
}
}
#[inline]
pub(crate) fn advance_or_extend(&mut self, pool: &'a OverflowPool) -> Result<()> {
#[inline(always)]
fn step_result(step: ChainStep) -> Result<()> {
match step {
ChainStep::Next => Ok(()),
ChainStep::Cycle => Err(Error::OverflowCycleDetected),
ChainStep::End => Err(Error::OverflowPoolExhausted),
}
}
let step = self.advance(pool);
if !matches!(step, ChainStep::End) {
return step_result(step);
}
if self.curr.overflow_index() == 0 {
let new_idx = pool.allocate()?;
if !self.curr.set_overflow_index(new_idx) {
pool.free(new_idx);
}
}
step_result(self.advance(pool))
}
}