miniz_oxide/deflate/
mod.rs

1//! This module contains functionality for compression.
2
3mod buffer;
4pub mod core;
5pub mod stream;
6use self::core::*;
7
8/// How much processing the compressor should do to compress the data.
9/// `NoCompression` and `Bestspeed` have special meanings, the other levels determine the number
10/// of checks for matches in the hash chains and whether to use lazy or greedy parsing.
11#[repr(i32)]
12#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
13pub enum CompressionLevel {
14    /// Don't do any compression, only output uncompressed blocks.
15    NoCompression = 0,
16    /// Fast compression. Uses a special compression routine that is optimized for speed.
17    BestSpeed = 1,
18    /// Slow/high compression. Do a lot of checks to try to find good matches.
19    BestCompression = 9,
20    /// Even more checks, can be very slow.
21    UberCompression = 10,
22    /// Default compromise between speed and compression.
23    DefaultLevel = 6,
24    /// Use the default compression level.
25    DefaultCompression = -1,
26}
27
28// Missing safe rust analogue (this and mem-to-mem are quite similar)
29/*
30fn tdefl_compress(
31    d: Option<&mut CompressorOxide>,
32    in_buf: *const c_void,
33    in_size: Option<&mut usize>,
34    out_buf: *mut c_void,
35    out_size: Option<&mut usize>,
36    flush: TDEFLFlush,
37) -> TDEFLStatus {
38    let res = match d {
39        None => {
40            in_size.map(|size| *size = 0);
41            out_size.map(|size| *size = 0);
42            (TDEFLStatus::BadParam, 0, 0)
43        },
44        Some(compressor) => {
45            let callback_res = CallbackOxide::new(
46                compressor.callback_func.clone(),
47                in_buf,
48                in_size,
49                out_buf,
50                out_size,
51            );
52
53            if let Ok(mut callback) = callback_res {
54                let res = compress(compressor, &mut callback, flush);
55                callback.update_size(Some(res.1), Some(res.2));
56                res
57            } else {
58                (TDEFLStatus::BadParam, 0, 0)
59            }
60        }
61    };
62    res.0
63}*/
64
65// Missing safe rust analogue
66/*
67fn tdefl_init(
68    d: Option<&mut CompressorOxide>,
69    put_buf_func: PutBufFuncPtr,
70    put_buf_user: *mut c_void,
71    flags: c_int,
72) -> TDEFLStatus {
73    if let Some(d) = d {
74        *d = CompressorOxide::new(
75            put_buf_func.map(|func|
76                CallbackFunc { put_buf_func: func, put_buf_user: put_buf_user }
77            ),
78            flags as u32,
79        );
80        TDEFLStatus::Okay
81    } else {
82        TDEFLStatus::BadParam
83    }
84}*/
85
86// Missing safe rust analogue (though maybe best served by flate2 front-end instead)
87/*
88fn tdefl_compress_mem_to_output(
89    buf: *const c_void,
90    buf_len: usize,
91    put_buf_func: PutBufFuncPtr,
92    put_buf_user: *mut c_void,
93    flags: c_int,
94) -> bool*/
95
96// Missing safe Rust analogue
97/*
98fn tdefl_compress_mem_to_mem(
99    out_buf: *mut c_void,
100    out_buf_len: usize,
101    src_buf: *const c_void,
102    src_buf_len: usize,
103    flags: c_int,
104) -> usize*/
105
106/// Compress the input data to a vector, using the specified compression level (0-10).
107pub fn compress_to_vec(input: &[u8], level: u8) -> Vec<u8> {
108    compress_to_vec_inner(input, level, 0, 0)
109}
110
111/// Compress the input data to a vector, using the specified compression level (0-10), and with a
112/// zlib wrapper.
113pub fn compress_to_vec_zlib(input: &[u8], level: u8) -> Vec<u8> {
114    compress_to_vec_inner(input, level, 1, 0)
115}
116
117/// Simple function to compress data to a vec.
118fn compress_to_vec_inner(input: &[u8], level: u8, window_bits: i32, strategy: i32) -> Vec<u8> {
119    // The comp flags function sets the zlib flag if the window_bits parameter is > 0.
120    let flags = create_comp_flags_from_zip_params(level.into(), window_bits, strategy);
121    let mut compressor = CompressorOxide::new(flags);
122    let mut output = vec![0; input.len() / 2];
123
124    let mut in_pos = 0;
125    let mut out_pos = 0;
126    loop {
127        let (status, bytes_in, bytes_out) = compress(
128            &mut compressor,
129            &input[in_pos..],
130            &mut output[out_pos..],
131            TDEFLFlush::Finish,
132        );
133
134        out_pos += bytes_out;
135        in_pos += bytes_in;
136
137        match status {
138            TDEFLStatus::Done => {
139                output.truncate(out_pos);
140                break;
141            }
142            TDEFLStatus::Okay => {
143                // We need more space, so resize the vector.
144                if output.len().saturating_sub(out_pos) < 30 {
145                    output.resize(output.len() * 2, 0)
146                }
147            }
148            // Not supposed to happen unless there is a bug.
149            _ => panic!("Bug! Unexpectedly failed to compress!"),
150        }
151    }
152
153    output
154}
155
156#[cfg(test)]
157mod test {
158    use super::{compress_to_vec, compress_to_vec_inner, CompressionStrategy};
159    use crate::inflate::decompress_to_vec;
160
161    /// Test deflate example.
162    ///
163    /// Check if the encoder produces the same code as the example given by Mark Adler here:
164    /// https://stackoverflow.com/questions/17398931/deflate-encoding-with-static-huffman-codes/17415203
165    #[test]
166    fn compress_small() {
167        let test_data = b"Deflate late";
168        let check = [
169            0x73, 0x49, 0x4d, 0xcb, 0x49, 0x2c, 0x49, 0x55, 0x00, 0x11, 0x00,
170        ];
171
172        let res = compress_to_vec(test_data, 1);
173        assert_eq!(&check[..], res.as_slice());
174
175        let res = compress_to_vec(test_data, 9);
176        assert_eq!(&check[..], res.as_slice());
177    }
178
179    #[test]
180    fn compress_huff_only() {
181        let test_data = b"Deflate late";
182
183        let res = compress_to_vec_inner(test_data, 1, 0, CompressionStrategy::HuffmanOnly as i32);
184        let d = decompress_to_vec(res.as_slice()).expect("Failed to decompress!");
185        assert_eq!(test_data, d.as_slice());
186    }
187
188    /// Test that a raw block compresses fine.
189    #[test]
190    fn compress_raw() {
191        let text = b"Hello, zlib!";
192        let encoded = {
193            let len = text.len();
194            let notlen = !len;
195            let mut encoded = vec![
196                1,
197                len as u8,
198                (len >> 8) as u8,
199                notlen as u8,
200                (notlen >> 8) as u8,
201            ];
202            encoded.extend_from_slice(&text[..]);
203            encoded
204        };
205
206        let res = compress_to_vec(text, 0);
207        assert_eq!(encoded, res.as_slice());
208    }
209
210    #[test]
211    fn short() {
212        let test_data = [10, 10, 10, 10, 10, 55];
213        let c = compress_to_vec(&test_data, 9);
214
215        let d = decompress_to_vec(c.as_slice()).expect("Failed to decompress!");
216        assert_eq!(&test_data, d.as_slice());
217        // Check that a static block is used here, rather than a raw block
218        // , so the data is actually compressed.
219        // (The optimal compressed length would be 5, but neither miniz nor zlib manages that either
220        // as neither checks matches against the byte at index 0.)
221        assert!(c.len() <= 6);
222    }
223}