use ::core::fmt;
use ::core::mem::MaybeUninit;
use alloc::vec::Vec;
use super::copy::WILDCOPY_OVERLENGTH;
use crate::decompressor::DecodeError;
use crate::shared::format::BLOCK_SIZE_MAX;
#[derive(Default)]
pub(crate) struct History {
buffer: Vec<u8>,
filled: usize,
delivered: usize,
}
impl fmt::Debug for History {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter
.debug_struct("History")
.field("len", &self.filled)
.field("delivered", &self.delivered)
.finish()
}
}
pub(crate) fn grow_zeroed(buffer: &mut Vec<u8>, len: usize) -> Result<(), DecodeError> {
if buffer.len() >= len {
return Ok(());
}
if buffer.capacity() == 0 {
let mut fresh = Vec::new();
fresh
.try_reserve_exact(len)
.map_err(|_| DecodeError::AllocationFailed)?;
fresh.resize(len, 0);
*buffer = fresh;
return Ok(());
}
buffer
.try_reserve(len - buffer.len())
.map_err(|_| DecodeError::AllocationFailed)?;
buffer.resize(len, 0);
Ok(())
}
pub(crate) trait Sink {
fn capacity(&self) -> usize;
fn put(&mut self, at: usize, bytes: &[u8]);
}
impl Sink for [u8] {
#[inline]
fn capacity(&self) -> usize {
self.len()
}
#[inline]
fn put(&mut self, at: usize, bytes: &[u8]) {
self[at..at + bytes.len()].copy_from_slice(bytes);
}
}
impl Sink for [MaybeUninit<u8>] {
#[inline]
fn capacity(&self) -> usize {
self.len()
}
#[inline]
fn put(&mut self, at: usize, bytes: &[u8]) {
for (slot, &byte) in self[at..at + bytes.len()].iter_mut().zip(bytes) {
slot.write(byte);
}
}
}
impl History {
pub(crate) fn reset(&mut self) {
self.filled = 0;
self.delivered = 0;
}
pub(crate) const fn pending(&self) -> usize {
self.filled - self.delivered
}
pub(crate) fn deliver<O: Sink + ?Sized>(&mut self, output: &mut O, at: usize) -> usize {
let pending = &self.buffer[self.delivered..self.filled];
let count = pending.len().min(output.capacity() - at);
output.put(at, &pending[..count]);
self.delivered += count;
count
}
pub(crate) fn prepare(
&mut self,
window: u64,
block_max: usize,
) -> Result<(&mut [u8], usize), DecodeError> {
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.filled > keep.saturating_add(slack) {
let drop = self.filled - keep;
self.buffer.copy_within(drop..self.filled, 0);
self.filled = keep;
self.delivered = keep;
}
grow_zeroed(
&mut self.buffer,
self.filled + block_max + WILDCOPY_OVERLENGTH,
)?;
Ok((&mut self.buffer, self.filled))
}
pub(crate) fn commit(&mut self, end: usize) {
debug_assert!(end >= self.filled && end <= self.buffer.len());
self.filled = end;
}
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.filled
}
}
#[cfg(test)]
mod tests {
use super::*;
fn append(history: &mut History, window: u64, bytes: &[u8]) {
let (buffer, start) = history.prepare(window, BLOCK_SIZE_MAX).expect("room");
buffer[start..start + bytes.len()].copy_from_slice(bytes);
history.commit(start + bytes.len());
}
#[test]
fn delivery_drains_the_pending_tail() {
let mut history = History::default();
append(&mut history, 1024, b"hello");
assert_eq!(history.pending(), 5);
let mut out = [0u8; 3];
assert_eq!(history.deliver(&mut out[..], 0), 3);
assert_eq!(&out, b"hel");
assert_eq!(history.deliver(&mut out[..], 1), 2);
assert_eq!(&out, b"hlo");
assert_eq!(history.deliver(&mut out[..], 0), 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];
append(&mut history, window, &block);
history.deliver(&mut alloc::vec![0; BLOCK_SIZE_MAX][..], 0);
append(&mut history, window, &block);
history.deliver(&mut alloc::vec![0; BLOCK_SIZE_MAX][..], 0);
history.prepare(window, BLOCK_SIZE_MAX).expect("room");
assert_eq!(history.len(), 2 * BLOCK_SIZE_MAX);
append(&mut history, window, &[1]);
history.deliver(&mut [0][..], 0);
history.prepare(window, BLOCK_SIZE_MAX).expect("room");
assert_eq!(history.len(), BLOCK_SIZE_MAX);
assert_eq!(history.pending(), 0);
history.reset();
assert_eq!(history.len(), 0);
}
#[test]
fn uninitialized_delivery_writes_only_the_delivered_range() {
let mut history = History::default();
append(&mut history, 1024, b"hello");
let mut out = [MaybeUninit::new(0xAAu8); 4];
assert_eq!(history.deliver(&mut out[..], 1), 3);
assert_eq!(history.pending(), 2);
let bytes = out.map(|slot| unsafe { slot.assume_init() });
assert_eq!(bytes, [0xAA, b'h', b'e', b'l']);
let sink: &mut [MaybeUninit<u8>] = &mut out;
assert_eq!(sink.capacity(), 4);
assert_eq!(history.deliver(sink, 4), 0);
}
}