use super::super::path::DecisionPath;
use super::super::trace::DecisionTrace;
use super::traits::TraceCollector;
pub struct RingCollector<P: DecisionPath, const N: usize> {
buffer: Vec<DecisionTrace<P>>,
head: usize,
}
impl<P: DecisionPath, const N: usize> RingCollector<P, N> {
pub fn new() -> Self {
Self { buffer: Vec::with_capacity(N), head: 0 }
}
pub fn recent(&self, n: usize) -> Vec<&DecisionTrace<P>> {
let take = n.min(self.buffer.len());
let mut result = Vec::with_capacity(take);
for i in 0..take {
let idx = if self.buffer.len() < N {
self.buffer.len() - 1 - i
} else {
(self.head + N - 1 - i) % N
};
result.push(&self.buffer[idx]);
}
result
}
pub fn all(&self) -> Vec<&DecisionTrace<P>> {
let mut result = Vec::with_capacity(self.buffer.len());
if self.buffer.is_empty() {
return result;
}
if self.buffer.len() < N {
for trace in &self.buffer {
result.push(trace);
}
} else {
for i in 0..N {
let idx = (self.head + i) % N;
result.push(&self.buffer[idx]);
}
}
result
}
pub fn last(&self) -> Option<&DecisionTrace<P>> {
if self.buffer.is_empty() {
return None;
}
if self.buffer.len() < N {
self.buffer.last()
} else {
let idx = (self.head + N - 1) % N;
Some(&self.buffer[idx])
}
}
pub fn clear(&mut self) {
self.buffer.clear();
self.head = 0;
}
pub const fn capacity(&self) -> usize {
N
}
}
impl<P: DecisionPath, const N: usize> TraceCollector<P> for RingCollector<P, N> {
fn record(&mut self, trace: DecisionTrace<P>) {
if self.buffer.len() < N {
self.buffer.push(trace);
} else {
self.buffer[self.head] = trace;
self.head = (self.head + 1) % N;
}
}
fn flush(&mut self) -> std::io::Result<()> {
Ok(())
}
fn len(&self) -> usize {
self.buffer.len()
}
}
impl<P: DecisionPath, const N: usize> Default for RingCollector<P, N> {
fn default() -> Self {
Self::new()
}
}