use std::cmp;
use chained_hash_table::WINDOW_SIZE;
use huffman_table;
const MAX_MATCH: usize = huffman_table::MAX_MATCH as usize;
pub const BUFFER_SIZE: usize = (WINDOW_SIZE * 2) + MAX_MATCH;
pub struct InputBuffer {
buffer: Vec<u8>,
}
impl InputBuffer {
#[cfg(test)]
pub fn new<'a>(data: &'a [u8]) -> (InputBuffer, Option<&[u8]>) {
let mut b = InputBuffer::empty();
let rem = b.add_data(data);
(b, rem)
}
pub fn empty() -> InputBuffer {
InputBuffer {
buffer: Vec::with_capacity(BUFFER_SIZE),
}
}
pub fn add_data<'a>(&mut self, data: &'a [u8]) -> Option<&'a [u8]> {
debug_assert!(self.current_end() <= BUFFER_SIZE);
if self.current_end() + data.len() > BUFFER_SIZE {
let consumed = {
let space_left = BUFFER_SIZE - self.buffer.len();
self.buffer.extend_from_slice(&data[..space_left]);
space_left
};
Some(&data[consumed..])
} else {
self.buffer.extend_from_slice(data);
None
}
}
pub fn current_end(&self) -> usize {
self.buffer.len()
}
pub fn slide<'a>(&mut self, data: &'a [u8]) -> Option<&'a [u8]> {
assert!(self.buffer.len() > WINDOW_SIZE * 2);
let (final_len, upper_len, end) = {
let (lower, upper) = self.buffer.split_at_mut(WINDOW_SIZE);
lower.copy_from_slice(&upper[..WINDOW_SIZE]);
let lookahead_len = {
let (upper_2, lookahead) = upper.split_at_mut(WINDOW_SIZE);
let lookahead_len = lookahead.len();
debug_assert!(lookahead_len <= MAX_MATCH);
upper_2[..lookahead_len].copy_from_slice(lookahead);
lookahead_len
};
let upper_len = upper.len() - lookahead_len;
let end = cmp::min(data.len(), upper_len);
upper[lookahead_len..lookahead_len + end].copy_from_slice(&data[..end]);
(lower.len() + lookahead_len + end, upper_len, end)
};
self.buffer.truncate(final_len);
if data.len() > upper_len {
Some(&data[end..])
} else {
None
}
}
pub fn get_buffer(&mut self) -> &mut [u8] {
&mut self.buffer
}
}
#[cfg(test)]
mod test {
use super::MAX_MATCH;
use chained_hash_table::WINDOW_SIZE;
use super::*;
#[test]
pub fn buffer_add_full() {
let data = [10u8; BUFFER_SIZE + 10];
let (mut buf, extra) = InputBuffer::new(&data[..]);
assert!(extra.unwrap() == &[10; 10]);
let to_add = [2, 5, 3];
let not_added = buf.add_data(&to_add);
assert_eq!(not_added.unwrap(), to_add);
}
#[test]
pub fn buffer_add_not_full() {
let data = [10u8; BUFFER_SIZE - 5];
let (mut buf, extra) = InputBuffer::new(&data[..]);
assert_eq!(buf.current_end(), data.len());
assert_eq!(extra, None);
let to_add = [2, 5, 3];
{
let not_added = buf.add_data(&to_add);
assert!(not_added.is_none());
}
let not_added = buf.add_data(&to_add);
assert_eq!(not_added.unwrap()[0], 3);
}
#[test]
fn slide() {
let data = [10u8; BUFFER_SIZE];
let (mut buf, extra) = InputBuffer::new(&data[..]);
assert_eq!(extra, None);
let to_add = [5; 5];
let rem = buf.slide(&to_add);
assert!(rem.is_none());
{
let slice = buf.get_buffer();
assert!(slice[..WINDOW_SIZE + MAX_MATCH] == data[WINDOW_SIZE..]);
assert_eq!(
slice[WINDOW_SIZE + MAX_MATCH..WINDOW_SIZE + MAX_MATCH + 5],
to_add
);
}
assert_eq!(buf.current_end(), WINDOW_SIZE + MAX_MATCH + to_add.len());
}
}