use ::core::fmt;
use alloc::vec::Vec;
use crate::shared::format::BLOCK_SIZE_MAX;
#[derive(Default)]
pub(crate) struct History {
buffer: Vec<u8>,
delivered: usize,
}
impl fmt::Debug for History {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter
.debug_struct("History")
.field("len", &self.buffer.len())
.field("delivered", &self.delivered)
.finish()
}
}
impl History {
pub(crate) fn reset(&mut self) {
self.buffer.clear();
self.delivered = 0;
}
pub(crate) const fn pending(&self) -> usize {
self.buffer.len() - self.delivered
}
pub(crate) fn deliver(&mut self, output: &mut [u8]) -> usize {
let pending = &self.buffer[self.delivered..];
let count = pending.len().min(output.len());
output[..count].copy_from_slice(&pending[..count]);
self.delivered += count;
count
}
pub(crate) fn prepare(&mut self, window: u64) -> &mut Vec<u8> {
debug_assert_eq!(self.pending(), 0);
let keep = usize::try_from(window).unwrap_or(usize::MAX);
let slack = keep.max(BLOCK_SIZE_MAX);
if self.buffer.len() > keep.saturating_add(slack) {
let drop = self.buffer.len() - keep;
self.buffer.drain(..drop);
self.delivered = self.buffer.len();
}
&mut self.buffer
}
pub(crate) fn release(&mut self) {
*self = Self::default();
}
pub(crate) fn retained_bytes(&self) -> usize {
self.buffer.capacity()
}
#[cfg(test)]
pub(crate) fn len(&self) -> usize {
self.buffer.len()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn delivery_drains_the_pending_tail() {
let mut history = History::default();
history.prepare(1024).extend_from_slice(b"hello");
assert_eq!(history.pending(), 5);
let mut out = [0u8; 3];
assert_eq!(history.deliver(&mut out), 3);
assert_eq!(&out, b"hel");
assert_eq!(history.deliver(&mut out), 2);
assert_eq!(&out[..2], b"lo");
assert_eq!(history.deliver(&mut out), 0);
assert!(alloc::format!("{history:?}").contains("len: 5"));
}
#[test]
fn history_is_compacted_only_past_a_window_of_slack() {
let mut history = History::default();
let window = BLOCK_SIZE_MAX as u64;
let block = alloc::vec![7u8; BLOCK_SIZE_MAX];
history.prepare(window).extend_from_slice(&block);
history.deliver(&mut alloc::vec![0; BLOCK_SIZE_MAX]);
history.prepare(window).extend_from_slice(&block);
history.deliver(&mut alloc::vec![0; BLOCK_SIZE_MAX]);
history.prepare(window);
assert_eq!(history.len(), 2 * BLOCK_SIZE_MAX);
history.prepare(window).push(1);
history.deliver(&mut [0]);
history.prepare(window);
assert_eq!(history.len(), BLOCK_SIZE_MAX);
assert_eq!(history.pending(), 0);
history.reset();
assert_eq!(history.len(), 0);
}
}