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, window_bits:0, strategy: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, window_bits:1, strategy:0)
118}
119
120/// Simple function to compress data to a vec.
121fn compress_to_vec_inner(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 in_pos = 0;
128 let mut out_pos = 0;
129 loop {
130 let (status, bytes_in, bytes_out) = compress(
131 &mut compressor,
132 &input[in_pos..],
133 &mut output[out_pos..],
134 TDEFLFlush::Finish,
135 );
136
137 out_pos += bytes_out;
138 in_pos += bytes_in;
139
140 match status {
141 TDEFLStatus::Done => {
142 output.truncate(out_pos);
143 break;
144 }
145 TDEFLStatus::Okay => {
146 // We need more space, so resize the vector.
147 if output.len().saturating_sub(out_pos) < 30 {
148 output.resize(output.len() * 2, 0)
149 }
150 }
151 // Not supposed to happen unless there is a bug.
152 _ => panic!("Bug! Unexpectedly failed to compress!"),
153 }
154 }
155
156 output
157}
158
159#[cfg(test)]
160mod test {
161 use super::{compress_to_vec, compress_to_vec_inner, CompressionStrategy};
162 use crate::inflate::decompress_to_vec;
163 use alloc::vec;
164
165 /// Test deflate example.
166 ///
167 /// Check if the encoder produces the same code as the example given by Mark Adler here:
168 /// https://stackoverflow.com/questions/17398931/deflate-encoding-with-static-huffman-codes/17415203
169 #[test]
170 fn compress_small() {
171 let test_data = b"Deflate late";
172 let check = [
173 0x73, 0x49, 0x4d, 0xcb, 0x49, 0x2c, 0x49, 0x55, 0x00, 0x11, 0x00,
174 ];
175
176 let res = compress_to_vec(test_data, 1);
177 assert_eq!(&check[..], res.as_slice());
178
179 let res = compress_to_vec(test_data, 9);
180 assert_eq!(&check[..], res.as_slice());
181 }
182
183 #[test]
184 fn compress_huff_only() {
185 let test_data = b"Deflate late";
186
187 let res = compress_to_vec_inner(test_data, 1, 0, CompressionStrategy::HuffmanOnly as i32);
188 let d = decompress_to_vec(res.as_slice()).expect("Failed to decompress!");
189 assert_eq!(test_data, d.as_slice());
190 }
191
192 /// Test that a raw block compresses fine.
193 #[test]
194 fn compress_raw() {
195 let text = b"Hello, zlib!";
196 let encoded = {
197 let len = text.len();
198 let notlen = !len;
199 let mut encoded = vec![
200 1,
201 len as u8,
202 (len >> 8) as u8,
203 notlen as u8,
204 (notlen >> 8) as u8,
205 ];
206 encoded.extend_from_slice(&text[..]);
207 encoded
208 };
209
210 let res = compress_to_vec(text, 0);
211 assert_eq!(encoded, res.as_slice());
212 }
213
214 #[test]
215 fn short() {
216 let test_data = [10, 10, 10, 10, 10, 55];
217 let c = compress_to_vec(&test_data, 9);
218
219 let d = decompress_to_vec(c.as_slice()).expect("Failed to decompress!");
220 assert_eq!(&test_data, d.as_slice());
221 // Check that a static block is used here, rather than a raw block
222 // , so the data is actually compressed.
223 // (The optimal compressed length would be 5, but neither miniz nor zlib manages that either
224 // as neither checks matches against the byte at index 0.)
225 assert!(c.len() <= 6);
226 }
227}
228