| 1 | //! A fast deflate implementation. |
| 2 | //! |
| 3 | //! This crate contains an optimized implementation of the deflate algorithm tuned to compress PNG |
| 4 | //! images. It is compatible with standard zlib, but make a bunch of simplifying assumptions that |
| 5 | //! drastically improve encoding performance: |
| 6 | //! |
| 7 | //! - Exactly one block per deflate stream. |
| 8 | //! - No distance codes except for run length encoding of zeros. |
| 9 | //! - A single fixed huffman tree trained on a large corpus of PNG images. |
| 10 | //! - All huffman codes are 12 bits or less. |
| 11 | //! |
| 12 | //! It also contains a fast decompressor that supports arbitrary zlib streams but does especially |
| 13 | //! well on streams that meet the above assumptions. |
| 14 | //! |
| 15 | //! # Inspiration |
| 16 | //! |
| 17 | //! The algorithms in this crate take inspiration from multiple sources: |
| 18 | //! * [fpnge](https://github.com/veluca93/fpnge) |
| 19 | //! * [zune-inflate](https://github.com/etemesi254/zune-image/tree/main/zune-inflate) |
| 20 | //! * [RealTime Data Compression blog](https://fastcompression.blogspot.com/2015/10/huffman-revisited-part-4-multi-bytes.html) |
| 21 | #![forbid (unsafe_code)] |
| 22 | #![warn (missing_docs)] |
| 23 | |
| 24 | mod compress; |
| 25 | mod decompress; |
| 26 | mod huffman; |
| 27 | mod tables; |
| 28 | |
| 29 | pub use compress::{compress_to_vec, Compressor, StoredOnlyCompressor}; |
| 30 | pub use decompress::{ |
| 31 | decompress_to_vec, decompress_to_vec_bounded, BoundedDecompressionError, DecompressionError, |
| 32 | Decompressor, |
| 33 | }; |
| 34 | |
| 35 | /// Build a length limited huffman tree. |
| 36 | /// |
| 37 | /// Dynamic programming algorithm from fpnge. |
| 38 | #[doc (hidden)] |
| 39 | pub fn compute_code_lengths( |
| 40 | freqs: &[u64], |
| 41 | min_limit: &[u8], |
| 42 | max_limit: &[u8], |
| 43 | calculated_nbits: &mut [u8], |
| 44 | ) { |
| 45 | debug_assert_eq!(freqs.len(), min_limit.len()); |
| 46 | debug_assert_eq!(freqs.len(), max_limit.len()); |
| 47 | debug_assert_eq!(freqs.len(), calculated_nbits.len()); |
| 48 | let len = freqs.len(); |
| 49 | |
| 50 | for i in 0..len { |
| 51 | debug_assert!(min_limit[i] >= 1); |
| 52 | debug_assert!(min_limit[i] <= max_limit[i]); |
| 53 | } |
| 54 | |
| 55 | let precision = *max_limit.iter().max().unwrap(); |
| 56 | let num_patterns = 1 << precision; |
| 57 | |
| 58 | let mut dynp = vec![u64::MAX; (num_patterns + 1) * (len + 1)]; |
| 59 | let index = |sym: usize, off: usize| sym * (num_patterns + 1) + off; |
| 60 | |
| 61 | dynp[index(0, 0)] = 0; |
| 62 | for sym in 0..len { |
| 63 | for bits in min_limit[sym]..=max_limit[sym] { |
| 64 | let off_delta = 1 << (precision - bits); |
| 65 | for off in 0..=num_patterns.saturating_sub(off_delta) { |
| 66 | dynp[index(sym + 1, off + off_delta)] = dynp[index(sym, off)] |
| 67 | .saturating_add(freqs[sym] * u64::from(bits)) |
| 68 | .min(dynp[index(sym + 1, off + off_delta)]); |
| 69 | } |
| 70 | } |
| 71 | } |
| 72 | |
| 73 | let mut sym = len; |
| 74 | let mut off = num_patterns; |
| 75 | |
| 76 | while sym > 0 { |
| 77 | sym -= 1; |
| 78 | assert!(off > 0); |
| 79 | |
| 80 | for bits in min_limit[sym]..=max_limit[sym] { |
| 81 | let off_delta = 1 << (precision - bits); |
| 82 | if off_delta <= off |
| 83 | && dynp[index(sym + 1, off)] |
| 84 | == dynp[index(sym, off - off_delta)] |
| 85 | .saturating_add(freqs[sym] * u64::from(bits)) |
| 86 | { |
| 87 | off -= off_delta; |
| 88 | calculated_nbits[sym] = bits; |
| 89 | break; |
| 90 | } |
| 91 | } |
| 92 | } |
| 93 | |
| 94 | for i in 0..len { |
| 95 | debug_assert!(calculated_nbits[i] >= min_limit[i]); |
| 96 | debug_assert!(calculated_nbits[i] <= max_limit[i]); |
| 97 | } |
| 98 | } |
| 99 | |
| 100 | const fn compute_codes<const NSYMS: usize>(lengths: &[u8; NSYMS]) -> Option<[u16; NSYMS]> { |
| 101 | let mut codes = [0u16; NSYMS]; |
| 102 | |
| 103 | let mut code = 0u32; |
| 104 | |
| 105 | let mut len = 1; |
| 106 | while len <= 16 { |
| 107 | let mut i = 0; |
| 108 | while i < lengths.len() { |
| 109 | if lengths[i] == len { |
| 110 | codes[i] = (code as u16).reverse_bits() >> (16 - len); |
| 111 | code += 1; |
| 112 | } |
| 113 | i += 1; |
| 114 | } |
| 115 | code <<= 1; |
| 116 | len += 1; |
| 117 | } |
| 118 | |
| 119 | if code == 2 << 16 { |
| 120 | Some(codes) |
| 121 | } else { |
| 122 | None |
| 123 | } |
| 124 | } |
| 125 | |