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 pub mod stream;
9 use 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)]
16 pub 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 /*
33 fn 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 /*
70 fn 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 /*
91 fn 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 /*
101 fn 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).
compress_to_vec(input: &[u8], level: u8) -> Vec<u8>110 pub fn compress_to_vec(input: &[u8], level: u8) -> Vec<u8> {
111     compress_to_vec_inner(input, level, 0, 0)
112 }
113 
114 /// Compress the input data to a vector, using the specified compression level (0-10), and with a
115 /// zlib wrapper.
compress_to_vec_zlib(input: &[u8], level: u8) -> Vec<u8>116 pub fn compress_to_vec_zlib(input: &[u8], level: u8) -> Vec<u8> {
117     compress_to_vec_inner(input, level, 1, 0)
118 }
119 
120 /// Simple function to compress data to a vec.
compress_to_vec_inner(input: &[u8], level: u8, window_bits: i32, strategy: i32) -> Vec<u8>121 fn 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)]
160 mod test {
161     use super::{compress_to_vec, compress_to_vec_inner, CompressionStrategy};
162     use crate::inflate::decompress_to_vec;
163     use std::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]
compress_small()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]
compress_huff_only()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]
compress_raw()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]
short()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