#[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>,
pub(crate) ring: BlockRing,
}
impl<F> CheckpointRing<F> {
pub(crate) fn new(slots: usize) -> Self {
Self {
slots: (0..slots).map(|_| None).collect(),
next: 0,
}
}
fn oldest_first(&self) -> impl DoubleEndedIterator<Item = &Slot<F>> {
let (newest, oldest) = self.slots.split_at(self.next);
oldest.iter().chain(newest).flatten()
}
pub(crate) fn count(&self) -> usize {
self.oldest_first().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(&self) -> Option<&Slot<F>> {
self.oldest_first().next()
}
#[cold]
pub(crate) fn best_at_or_below(&self, block: u64) -> Option<&Slot<F>> {
self.oldest_first()
.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);
}
}