| 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). |
| 110 | pub 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. |
| 116 | pub 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. |
| 121 | fn 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)] |
| 159 | mod 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 | |