| 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 | |