1 //! This module contains functionality for compression.
2 
3 mod buffer;
4 pub mod core;
5 pub mod stream;
6 use 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)]
13 pub 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 /*
30 fn 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 /*
67 fn 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 /*
88 fn 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 /*
98 fn 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).
107 pub 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.
113 pub 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.
118 fn 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)]
157 mod 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 }
224