miniz_oxide/deflate/
mod.rs

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