use crate::{buffer::Buffer, input_buffer::InputBuffer};
use std::cmp::min;
pub(crate) const WINDOW_SIZE: usize = 131072;
const WINDOW_MASK: usize = 131071;
#[derive(Debug)]
pub(crate) struct OutputWindow {
window: [u8; WINDOW_SIZE],
end: usize,
bytes_used: usize,
}
impl OutputWindow {
pub fn new() -> Self {
Self {
window: [0; WINDOW_SIZE],
end: 0,
bytes_used: 0,
}
}
pub(crate) fn clear_bytes_used(&mut self) {
self.bytes_used = 0;
}
#[inline(always)]
pub fn write(&mut self, b: u8) {
debug_assert!(
self.bytes_used < WINDOW_SIZE,
"Can't add byte when window is full!"
);
self.window[self.end] = b;
self.end += 1;
self.end &= WINDOW_MASK;
self.bytes_used += 1;
}
#[inline(always)]
pub fn write_length_distance(&mut self, length: usize, distance: usize) {
debug_assert!((self.bytes_used + length) <= WINDOW_SIZE, "No Enough space");
self.bytes_used += length;
let mut from = self.end.wrapping_sub(distance) & WINDOW_MASK;
let mut to = self.end;
for _ in 0..length {
self.window[to] = self.window[from];
to = (to + 1) & WINDOW_MASK;
from = (from + 1) & WINDOW_MASK;
}
self.end = to;
}
pub fn copy_from(&mut self, input: &mut InputBuffer<'_>, mut length: usize) -> usize {
length = min(
min(length, WINDOW_SIZE - self.bytes_used),
input.available_bytes(),
);
let mut copied: usize;
let tail_len = WINDOW_SIZE - self.end;
if length > tail_len {
copied = input.copy_to(&mut self.window[self.end..][..tail_len]);
if copied == tail_len {
copied += input.copy_to(&mut self.window[..length - tail_len]);
}
} else {
copied = input.copy_to(&mut self.window[self.end..][..length]);
}
self.end = (self.end + copied) & WINDOW_MASK;
self.bytes_used += copied;
copied
}
pub fn free_bytes(&self) -> usize {
WINDOW_SIZE - self.bytes_used
}
pub fn available_bytes(&self) -> usize {
self.bytes_used
}
pub fn copy_to(&mut self, output: Buffer<'_>) -> usize {
let (copy_end, mut output) = if output.len() > self.bytes_used {
(self.end, output.index_mut(..self.bytes_used))
} else {
(
(self
.end
.overflowing_sub(self.bytes_used)
.0
.overflowing_add(output.len())
.0)
& WINDOW_MASK,
output,
)
};
let copied = output.len();
let mut output = if output.len() > copy_end {
let tail_len = output.len() - copy_end;
output
.reborrow()
.index_mut(..tail_len)
.copy_from_slice(&self.window[WINDOW_SIZE - tail_len..][..tail_len]);
output.index_mut(tail_len..).index_mut(..copy_end)
} else {
output
};
output.copy_from_slice(&self.window[copy_end - output.len()..][..output.len()]);
self.bytes_used -= copied;
copied
}
#[cfg(feature = "checkpoint")]
pub(crate) fn get_checkpoint_data(&self, total_output_written: u64) -> (&[u8], &[u8]) {
use crate::inflater_managed::TABLE_LOOKUP_DISTANCE_MAX;
let history_needed = min(TABLE_LOOKUP_DISTANCE_MAX, total_output_written as usize);
let data_len = history_needed.max(self.bytes_used);
let start = (self.end + WINDOW_SIZE - data_len) & WINDOW_MASK;
if data_len <= WINDOW_SIZE - start {
(&self.window[start..start + data_len], &[])
} else {
(&self.window[start..], &self.window[..self.end])
}
}
#[cfg(feature = "checkpoint")]
pub(crate) fn restore_from_checkpoint(&mut self, data: &[u8], bytes_used: usize) {
self.window[..data.len()].copy_from_slice(data);
self.end = data.len();
self.bytes_used = bytes_used;
}
}