miniz_oxide/deflate/
zlib.rs

1use crate::deflate::core::deflate_flags::{
2    TDEFL_FORCE_ALL_RAW_BLOCKS, TDEFL_GREEDY_PARSING_FLAG, TDEFL_RLE_MATCHES,
3};
4
5const DEFAULT_CM: u8 = 8;
6const DEFAULT_CINFO: u8 = 7 << 4;
7const _DEFAULT_FDICT: u8 = 0;
8const 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.
11const MIN_CMF: u8 = DEFAULT_CM; // | 0
12/// The 16-bit value consisting of CMF and FLG must be divisible by this to be valid.
13const 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]
19fn add_fcheck(cmf: u8, flg: u8) -> u8 {
20    let rem = ((usize::from(cmf) * 256) + usize::from(flg)) % usize::from(FCHECK_DIVISOR);
21
22    // Clear existing FCHECK if any
23    let flg = 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]
31const fn zlib_level_from_flags(flags: u32) -> u8 {
32    use crate::deflate::core::NUM_PROBES;
33
34    let num_probes = 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]
49const 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]
62fn header_from_level(level: u8, flags: u32) -> [u8; 2] {
63    let cmf = cmf_from_flags(flags);
64    [cmf, add_fcheck(cmf, level << 6)]
65}
66
67/// Create a zlib header from the given compression flags.
68/// Only level is considered.
69#[inline]
70pub fn header_from_flags(flags: u32) -> [u8; 2] {
71    let level = zlib_level_from_flags(flags);
72    header_from_level(level, flags)
73}
74
75#[cfg(test)]
76mod 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}