blob: 4cf765703c269b70f9daac55d92f39aa795c8f67 [file] [log] [blame]
use std::io::Write;
use std::io;
use std::u16;
use bitstream::LsbWriter;
use byteorder::{LittleEndian, WriteBytesExt};
#[cfg(test)]
const BLOCK_SIZE: u16 = 32000;
const STORED_FIRST_BYTE: u8 = 0b0000_0000;
pub const STORED_FIRST_BYTE_FINAL: u8 = 0b0000_0001;
pub const MAX_STORED_BLOCK_LENGTH: usize = (u16::MAX as usize) / 2;
pub fn write_stored_header(writer: &mut LsbWriter, final_block: bool) {
let header = if final_block {
STORED_FIRST_BYTE_FINAL
} else {
STORED_FIRST_BYTE
};
// Write the block header
writer.write_bits(header.into(), 3);
// Flush the writer to make sure we are aligned to the byte boundary.
writer.flush_raw();
}
// Compress one stored block (excluding the header)
pub fn compress_block_stored<W: Write>(input: &[u8], writer: &mut W) -> io::Result<usize> {
if input.len() > u16::max_value() as usize {
return Err(io::Error::new(
io::ErrorKind::InvalidInput,
"Stored block too long!",
));
};
// The header is written before this function.
// The next two bytes indicates the length
writer.write_u16::<LittleEndian>(input.len() as u16)?;
// the next two after the length is the ones complement of the length
writer.write_u16::<LittleEndian>(!input.len() as u16)?;
// After this the data is written directly with no compression
writer.write(input)
}
#[cfg(test)]
pub fn compress_data_stored(input: &[u8]) -> Vec<u8> {
let block_length = BLOCK_SIZE as usize;
let mut output = Vec::with_capacity(input.len() + 2);
let mut i = input.chunks(block_length).peekable();
while let Some(chunk) = i.next() {
let last_chunk = i.peek().is_none();
// First bit tells us if this is the final chunk
// the next two details compression type (none in this case)
let first_byte = if last_chunk {
STORED_FIRST_BYTE_FINAL
} else {
STORED_FIRST_BYTE
};
output.write(&[first_byte]).unwrap();
compress_block_stored(chunk, &mut output).unwrap();
}
output
}
#[cfg(test)]
mod test {
use super::*;
use test_utils::decompress_to_end;
#[test]
fn no_compression_one_chunk() {
let test_data = vec![1u8, 2, 3, 4, 5, 6, 7, 8];
let compressed = compress_data_stored(&test_data);
let result = decompress_to_end(&compressed);
assert_eq!(test_data, result);
}
#[test]
fn no_compression_multiple_chunks() {
let test_data = vec![32u8; 40000];
let compressed = compress_data_stored(&test_data);
let result = decompress_to_end(&compressed);
assert_eq!(test_data, result);
}
#[test]
fn no_compression_string() {
let test_data = String::from(
"This is some text, this is some more text, this is even \
more text, lots of text here.",
).into_bytes();
let compressed = compress_data_stored(&test_data);
let result = decompress_to_end(&compressed);
assert_eq!(test_data, result);
}
}