1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
//! A fast deflate implementation.
//!
//! This crate contains an optimized implementation of the deflate algorithm tuned to compress PNG
//! images. It is compatible with standard zlib, but make a bunch of simplifying assumptions that
//! drastically improve encoding performance:
//!
//! - Exactly one block per deflate stream.
//! - No distance codes except for run length encoding of zeros.
//! - A single fixed huffman tree trained on a large corpus of PNG images.
//! - All huffman codes are 12 bits or less.
//!
//! It also contains a fast decompressor that supports arbitrary zlib streams but does especially
//! well on streams that meet the above assumptions.
//!
//! # Inspiration
//!
//! The algorithms in this crate take inspiration from multiple sources:
//! * [fpnge](https://github.com/veluca93/fpnge)
//! * [zune-inflate](https://github.com/etemesi254/zune-image/tree/main/zune-inflate)
//! * [RealTime Data Compression blog](https://fastcompression.blogspot.com/2015/10/huffman-revisited-part-4-multi-bytes.html)
#![forbid(unsafe_code)]
#![warn(missing_docs)]

mod compress;
mod decompress;
mod tables;

pub use compress::{compress_to_vec, Compressor, StoredOnlyCompressor};
pub use decompress::{decompress_to_vec, DecompressionError, Decompressor};

/// Build a length limited huffman tree.
///
/// Dynamic programming algorithm from fpnge.
#[doc(hidden)]
pub fn compute_code_lengths(
    freqs: &[u64],
    min_limit: &[u8],
    max_limit: &[u8],
    calculated_nbits: &mut [u8],
) {
    debug_assert_eq!(freqs.len(), min_limit.len());
    debug_assert_eq!(freqs.len(), max_limit.len());
    debug_assert_eq!(freqs.len(), calculated_nbits.len());
    let len = freqs.len();

    for i in 0..len {
        debug_assert!(min_limit[i] >= 1);
        debug_assert!(min_limit[i] <= max_limit[i]);
    }

    let precision = *max_limit.iter().max().unwrap();
    let num_patterns = 1 << precision;

    let mut dynp = vec![u64::MAX; (num_patterns + 1) * (len + 1)];
    let index = |sym: usize, off: usize| sym * (num_patterns + 1) + off;

    dynp[index(0, 0)] = 0;
    for sym in 0..len {
        for bits in min_limit[sym]..=max_limit[sym] {
            let off_delta = 1 << (precision - bits);
            for off in 0..=num_patterns.saturating_sub(off_delta) {
                dynp[index(sym + 1, off + off_delta)] = dynp[index(sym, off)]
                    .saturating_add(freqs[sym] * u64::from(bits))
                    .min(dynp[index(sym + 1, off + off_delta)]);
            }
        }
    }

    let mut sym = len;
    let mut off = num_patterns;

    while sym > 0 {
        sym -= 1;
        assert!(off > 0);

        for bits in min_limit[sym]..=max_limit[sym] {
            let off_delta = 1 << (precision - bits);
            if off_delta <= off
                && dynp[index(sym + 1, off)]
                    == dynp[index(sym, off - off_delta)]
                        .saturating_add(freqs[sym] * u64::from(bits))
            {
                off -= off_delta;
                calculated_nbits[sym] = bits;
                break;
            }
        }
    }

    for i in 0..len {
        debug_assert!(calculated_nbits[i] >= min_limit[i]);
        debug_assert!(calculated_nbits[i] <= max_limit[i]);
    }
}

const fn compute_codes<const NSYMS: usize>(lengths: &[u8; NSYMS]) -> Option<[u16; NSYMS]> {
    let mut codes = [0u16; NSYMS];

    let mut code = 0u32;

    let mut len = 1;
    while len <= 16 {
        let mut i = 0;
        while i < lengths.len() {
            if lengths[i] == len {
                codes[i] = (code as u16).reverse_bits() >> (16 - len);
                code += 1;
            }
            i += 1;
        }
        code <<= 1;
        len += 1;
    }

    if code == 2 << 16 {
        Some(codes)
    } else {
        None
    }
}