miniz_oxide/deflate/
mod.rs

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