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