#[cfg(not(feature = "std"))]
use alloc::vec::Vec;
#[cfg(feature = "std")]
use std::vec::Vec;
use crate::{
position::Position,
ring::BlockRing,
};
#[derive(Debug)]
pub(crate) struct CheckpointRing<F> {
slots: Vec<Option<Slot<F>>>,
next: usize,
}
#[derive(Debug)]
pub(crate) struct Slot<F> {
pub(crate) fold: F,
pub(crate) cursor: Option<Position>,
}
fn live<F>(slot: &Slot<F>, ring: &BlockRing) -> bool {
slot.cursor.is_none_or(|cursor| ring.observes(cursor.block))
}
impl<F> CheckpointRing<F> {
pub(crate) fn new(slots: usize) -> Self {
Self {
slots: (0..slots).map(|_| None).collect(),
next: 0,
}
}
fn oldest_first<'a>(
&'a self,
ring: &'a BlockRing,
) -> impl DoubleEndedIterator<Item = &'a Slot<F>> {
let (newest, oldest) = self.slots.split_at(self.next);
oldest
.iter()
.chain(newest)
.flatten()
.filter(move |slot| live(slot, ring))
}
pub(crate) fn count(&self, ring: &BlockRing) -> usize {
self.oldest_first(ring).count()
}
pub(crate) fn store(&mut self, slot: Slot<F>) {
let len = self.slots.len();
if len == 0 {
return;
}
self.slots[self.next] = Some(slot);
self.next = (self.next + 1) % len;
}
pub(crate) fn oldest<'a>(&'a self, ring: &'a BlockRing) -> Option<&'a Slot<F>> {
self.oldest_first(ring).next()
}
#[cold]
pub(crate) fn best_at_or_below<'a>(
&'a self,
block: u64,
ring: &'a BlockRing,
) -> Option<&'a Slot<F>> {
self.oldest_first(ring)
.rev()
.find(|slot| slot.cursor.is_none_or(|cursor| cursor.block <= block))
}
#[cold]
pub(crate) fn drop_above(&mut self, block: u64) {
for slot in &mut self.slots {
slot.take_if(|slot| slot.cursor.is_some_and(|cursor| cursor.block > block));
}
}
pub(crate) fn clear(&mut self) {
self.slots.fill_with(|| None);
}
}