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: [u8; BUFFER_SIZE],
current_end: usize,
}
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: [0; BUFFER_SIZE],
current_end: 0,
}
}
pub fn add_data<'a>(&mut self, data: &'a [u8]) -> Option<&'a [u8]> {
if self.current_end + data.len() > self.buffer.len() {
let len = {
let mut remaining_buffer = &mut self.buffer[self.current_end..];
let len = remaining_buffer.len();
remaining_buffer.copy_from_slice(&data[..len]);
len
};
self.current_end = BUFFER_SIZE;
Some(&data[len..])
} else {
self.buffer[self.current_end..self.current_end + data.len()].copy_from_slice(data);
self.current_end += data.len();
None
}
}
pub fn current_end(&self) -> usize {
self.current_end
}
pub fn slide<'a>(&mut self, data: &'a [u8]) -> Option<&'a [u8]> {
assert!(self.current_end == BUFFER_SIZE);
let (lower, upper) = self.buffer[..].split_at_mut(WINDOW_SIZE);
lower.copy_from_slice(&upper[..WINDOW_SIZE]);
{
let (upper_2, lookahead) = upper.split_at_mut(WINDOW_SIZE);
upper_2[..MAX_MATCH].copy_from_slice(lookahead);
}
let upper_len = upper.len() - MAX_MATCH;
let end = cmp::min(data.len(), upper_len);
upper[MAX_MATCH..MAX_MATCH + end].copy_from_slice(&data[..end]);
self.current_end = lower.len() + MAX_MATCH + end;
if data.len() > upper_len {
Some(&data[end..])
} else {
None
}
}
pub fn move_down(&mut self) -> usize {
assert!(self.current_end >= WINDOW_SIZE);
if self.current_end == WINDOW_SIZE {
return 0;
}
for i in 0..WINDOW_SIZE {
self.buffer[i] = self.buffer[self.current_end - WINDOW_SIZE + i];
}
let ret = self.current_end - WINDOW_SIZE;
self.current_end = WINDOW_SIZE;
ret
}
pub fn get_buffer(&mut self) -> &mut [u8] {
&mut self.buffer[..self.current_end]
}
}
#[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_eq!(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());
}
#[test]
fn move_down() {
let mut data = [10u8; BUFFER_SIZE - 300];
*(data.last_mut().unwrap()) = 5;
let (mut buf, extra) = InputBuffer::new(&data[..]);
assert_eq!(extra, None);
buf.move_down();
assert_eq!(*buf.get_buffer().last().unwrap(), 5);
}
}