deflate/
stored_block.rs

1use crate::bitstream::LsbWriter;
2use std::io;
3use std::io::Write;
4use std::u16;
5
6#[cfg(test)]
7const BLOCK_SIZE: u16 = 32000;
8
9const STORED_FIRST_BYTE: u8 = 0b0000_0000;
10pub const STORED_FIRST_BYTE_FINAL: u8 = 0b0000_0001;
11pub const MAX_STORED_BLOCK_LENGTH: usize = (u16::MAX as usize) / 2;
12
13pub fn write_stored_header(writer: &mut LsbWriter, final_block: bool) {
14    let header = if final_block {
15        STORED_FIRST_BYTE_FINAL
16    } else {
17        STORED_FIRST_BYTE
18    };
19    // Write the block header
20    writer.write_bits(header.into(), 3);
21    // Flush the writer to make sure we are aligned to the byte boundary.
22    writer.flush_raw();
23}
24
25// Compress one stored block (excluding the header)
26pub fn compress_block_stored<W: Write>(input: &[u8], writer: &mut W) -> io::Result<usize> {
27    if input.len() > u16::max_value() as usize {
28        return Err(io::Error::new(
29            io::ErrorKind::InvalidInput,
30            "Stored block too long!",
31        ));
32    };
33    // The header is written before this function.
34    // The next two bytes indicates the length
35    writer.write_all(&(input.len() as u16).to_le_bytes())?;
36    // the next two after the length is the ones complement of the length
37    writer.write_all(&(!input.len() as u16).to_le_bytes())?;
38    // After this the data is written directly with no compression
39    writer.write(input)
40}
41
42#[cfg(test)]
43pub fn compress_data_stored(input: &[u8]) -> Vec<u8> {
44    let block_length = BLOCK_SIZE as usize;
45
46    let mut output = Vec::with_capacity(input.len() + 2);
47    let mut i = input.chunks(block_length).peekable();
48    while let Some(chunk) = i.next() {
49        let last_chunk = i.peek().is_none();
50        // First bit tells us if this is the final chunk
51        // the next two details compression type (none in this case)
52        let first_byte = if last_chunk {
53            STORED_FIRST_BYTE_FINAL
54        } else {
55            STORED_FIRST_BYTE
56        };
57        output.write(&[first_byte]).unwrap();
58
59        compress_block_stored(chunk, &mut output).unwrap();
60    }
61    output
62}
63
64#[cfg(test)]
65mod test {
66    use super::*;
67    use crate::test_utils::decompress_to_end;
68
69    #[test]
70    fn no_compression_one_chunk() {
71        let test_data = vec![1u8, 2, 3, 4, 5, 6, 7, 8];
72        let compressed = compress_data_stored(&test_data);
73        let result = decompress_to_end(&compressed);
74        assert_eq!(test_data, result);
75    }
76
77    #[test]
78    fn no_compression_multiple_chunks() {
79        let test_data = vec![32u8; 40000];
80        let compressed = compress_data_stored(&test_data);
81        let result = decompress_to_end(&compressed);
82        assert_eq!(test_data, result);
83    }
84
85    #[test]
86    fn no_compression_string() {
87        let test_data = String::from(
88            "This is some text, this is some more text, this is even \
89             more text, lots of text here.",
90        )
91        .into_bytes();
92        let compressed = compress_data_stored(&test_data);
93        let result = decompress_to_end(&compressed);
94        assert_eq!(test_data, result);
95    }
96}