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(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}
227