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