| 1 | use crate::deflate::core::deflate_flags::{ |
| 2 | TDEFL_FORCE_ALL_RAW_BLOCKS, TDEFL_GREEDY_PARSING_FLAG, TDEFL_RLE_MATCHES, |
| 3 | }; |
| 4 | |
| 5 | const DEFAULT_CM: u8 = 8; |
| 6 | const DEFAULT_CINFO: u8 = 7 << 4; |
| 7 | const _DEFAULT_FDICT: u8 = 0; |
| 8 | const DEFAULT_CMF: u8 = DEFAULT_CM | DEFAULT_CINFO; |
| 9 | // CMF used for RLE (technically it uses a window size of 0 but the lowest that can |
| 10 | // be specified in the header corresponds to a window size of 1 << (0 + 8) aka 256. |
| 11 | const MIN_CMF: u8 = DEFAULT_CM; // | 0 |
| 12 | /// The 16-bit value consisting of CMF and FLG must be divisible by this to be valid. |
| 13 | const FCHECK_DIVISOR: u8 = 31; |
| 14 | |
| 15 | /// Generate FCHECK from CMF and FLG (without FCKECH )so that they are correct according to the |
| 16 | /// specification, i.e (CMF*256 + FCHK) % 31 = 0. |
| 17 | /// Returns flg with the FCHKECK bits added (any existing FCHECK bits are ignored). |
| 18 | #[inline ] |
| 19 | fn add_fcheck(cmf: u8, flg: u8) -> u8 { |
| 20 | let rem: usize = ((usize::from(cmf) * 256) + usize::from(flg)) % usize::from(FCHECK_DIVISOR); |
| 21 | |
| 22 | // Clear existing FCHECK if any |
| 23 | let flg: u8 = flg & 0b11100000; |
| 24 | |
| 25 | // Casting is safe as rem can't overflow since it is a value mod 31 |
| 26 | // We can simply add the value to flg as (31 - rem) will never be above 2^5 |
| 27 | flg + (FCHECK_DIVISOR - rem as u8) |
| 28 | } |
| 29 | |
| 30 | #[inline ] |
| 31 | const fn zlib_level_from_flags(flags: u32) -> u8 { |
| 32 | use crate::deflate::core::NUM_PROBES; |
| 33 | |
| 34 | let num_probes: u32 = flags & super::MAX_PROBES_MASK; |
| 35 | if (flags & TDEFL_GREEDY_PARSING_FLAG != 0) || (flags & TDEFL_RLE_MATCHES != 0) { |
| 36 | if num_probes <= 1 { |
| 37 | 0 |
| 38 | } else { |
| 39 | 1 |
| 40 | } |
| 41 | } else if num_probes >= NUM_PROBES[9] as u32 { |
| 42 | 3 |
| 43 | } else { |
| 44 | 2 |
| 45 | } |
| 46 | } |
| 47 | |
| 48 | #[inline ] |
| 49 | const fn cmf_from_flags(flags: u32) -> u8 { |
| 50 | if (flags & TDEFL_RLE_MATCHES == 0) && (flags & TDEFL_FORCE_ALL_RAW_BLOCKS == 0) { |
| 51 | DEFAULT_CMF |
| 52 | // If we are using RLE encoding or no compression the window bits can be set as the |
| 53 | // minimum. |
| 54 | } else { |
| 55 | MIN_CMF |
| 56 | } |
| 57 | } |
| 58 | |
| 59 | /// Get the zlib header for the level using the default window size and no |
| 60 | /// dictionary. |
| 61 | #[inline ] |
| 62 | fn header_from_level(level: u8, flags: u32) -> [u8; 2] { |
| 63 | let cmf: u8 = cmf_from_flags(flags); |
| 64 | [cmf, add_fcheck(cmf, flg:level << 6)] |
| 65 | } |
| 66 | |
| 67 | /// Create a zlib header from the given compression flags. |
| 68 | /// Only level is considered. |
| 69 | #[inline ] |
| 70 | pub fn header_from_flags(flags: u32) -> [u8; 2] { |
| 71 | let level: u8 = zlib_level_from_flags(flags); |
| 72 | header_from_level(level, flags) |
| 73 | } |
| 74 | |
| 75 | #[cfg (test)] |
| 76 | mod test { |
| 77 | use crate::shared::MZ_DEFAULT_WINDOW_BITS; |
| 78 | #[test ] |
| 79 | fn zlib() { |
| 80 | use super::super::*; |
| 81 | use super::*; |
| 82 | |
| 83 | let test_level = |level, expected| { |
| 84 | let flags = create_comp_flags_from_zip_params( |
| 85 | level, |
| 86 | MZ_DEFAULT_WINDOW_BITS, |
| 87 | CompressionStrategy::Default as i32, |
| 88 | ); |
| 89 | assert_eq!(zlib_level_from_flags(flags), expected); |
| 90 | }; |
| 91 | |
| 92 | assert_eq!(zlib_level_from_flags(DEFAULT_FLAGS), 2); |
| 93 | test_level(0, 0); |
| 94 | test_level(1, 0); |
| 95 | test_level(2, 1); |
| 96 | test_level(3, 1); |
| 97 | for i in 4..=8 { |
| 98 | test_level(i, 2) |
| 99 | } |
| 100 | test_level(9, 3); |
| 101 | test_level(10, 3); |
| 102 | } |
| 103 | |
| 104 | #[test ] |
| 105 | fn test_header() { |
| 106 | let header = super::header_from_level(3, 0); |
| 107 | assert_eq!( |
| 108 | ((usize::from(header[0]) * 256) + usize::from(header[1])) % 31, |
| 109 | 0 |
| 110 | ); |
| 111 | } |
| 112 | } |
| 113 | |