use std::collections::VecDeque;
use std::rc::Rc;
use gnitz_zset::repr::Batch;
fn held(delta: &Batch) -> usize {
delta.allocated_bytes() + std::mem::size_of::<Batch>() + 2 * std::mem::size_of::<usize>() + ENTRY
}
const ENTRY: usize = std::mem::size_of::<(u64, Rc<Batch>)>();
pub(crate) struct Feed {
rounds: VecDeque<(u64, Rc<Batch>)>,
bytes: usize,
budget: usize,
dropped_through: u64,
}
impl Feed {
pub(super) fn new(budget: u64) -> Self {
Feed {
rounds: VecDeque::new(),
bytes: 0,
budget: usize::try_from(budget).unwrap_or(usize::MAX),
dropped_through: 0,
}
}
pub(super) fn record(&mut self, round: u64, delta: Rc<Batch>) {
debug_assert!(delta.is_consolidated() && !delta.is_empty());
debug_assert!(self.rounds.back().is_none_or(|&(last, _)| last <= round));
self.bytes += held(&delta);
self.rounds.push_back((round, delta));
while self.bytes > self.budget {
let oldest = self.rounds[0].0;
if oldest == round {
break;
}
self.dropped_through = oldest;
while let Some((_, gone)) = self.rounds.pop_front_if(|&mut (r, _)| r == oldest) {
self.bytes -= held(&gone);
}
}
}
pub(crate) fn dropped_through(&self) -> u64 {
self.dropped_through
}
pub(crate) fn rounds(&self, after: u64) -> impl Iterator<Item = &Rc<Batch>> + Clone {
let first = self.rounds.partition_point(|&(r, _)| r <= after);
self.rounds.range(first..).map(|(_, delta)| delta)
}
#[cfg(test)]
pub(crate) fn retained_bytes(&self) -> usize {
self.bytes
}
}
#[cfg(test)]
#[path = "tests/feed.rs"]
mod tests;