miniz_oxide/deflate/
zlib.rs

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