1//! Streaming compression functionality.
2
3use alloc::boxed::Box;
4use core::convert::TryInto;
5use core::{cmp, mem};
6
7use super::super::*;
8use super::deflate_flags::*;
9use super::CompressionLevel;
10use crate::deflate::buffer::{
11 update_hash, HashBuffers, LocalBuf, LZ_CODE_BUF_SIZE, LZ_DICT_FULL_SIZE, LZ_HASH_BITS,
12 LZ_HASH_SHIFT, LZ_HASH_SIZE, OUT_BUF_SIZE,
13};
14use crate::deflate::stored::compress_stored;
15use crate::deflate::zlib;
16use crate::shared::{update_adler32, HUFFMAN_LENGTH_ORDER, MZ_ADLER32_INIT};
17use crate::DataFormat;
18
19// Currently not bubbled up outside this module, so can fill in with more
20// context eventually if needed.
21type Result<T, E = Error> = core::result::Result<T, E>;
22pub(crate) struct Error {}
23
24pub(crate) const MAX_PROBES_MASK: i32 = 0xFFF;
25
26const MAX_SUPPORTED_HUFF_CODESIZE: usize = 32;
27
28/// Length code for length values.
29#[rustfmt::skip]
30const LEN_SYM: [u16; 256] = [
31 257, 258, 259, 260, 261, 262, 263, 264, 265, 265, 266, 266, 267, 267, 268, 268,
32 269, 269, 269, 269, 270, 270, 270, 270, 271, 271, 271, 271, 272, 272, 272, 272,
33 273, 273, 273, 273, 273, 273, 273, 273, 274, 274, 274, 274, 274, 274, 274, 274,
34 275, 275, 275, 275, 275, 275, 275, 275, 276, 276, 276, 276, 276, 276, 276, 276,
35 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277,
36 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278,
37 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279,
38 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280,
39 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281,
40 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281,
41 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282,
42 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282,
43 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283,
44 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283,
45 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284,
46 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 285
47];
48
49/// Number of extra bits for length values.
50#[rustfmt::skip]
51const LEN_EXTRA: [u8; 256] = [
52 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1,
53 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
54 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
55 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
56 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
57 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
58 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
59 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
60 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
61 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
62 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
63 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
64 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
65 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
66 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
67 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 0
68];
69
70/// Distance codes for distances smaller than 512.
71#[rustfmt::skip]
72const SMALL_DIST_SYM: [u8; 512] = [
73 0, 1, 2, 3, 4, 4, 5, 5, 6, 6, 6, 6, 7, 7, 7, 7,
74 8, 8, 8, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9,
75 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
76 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
77 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12,
78 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12,
79 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13,
80 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13,
81 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
82 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
83 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
84 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
85 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
86 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
87 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
88 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
89 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
90 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
91 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
92 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
93 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
94 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
95 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
96 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
97 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17,
98 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17,
99 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17,
100 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17,
101 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17,
102 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17,
103 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17,
104 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17
105];
106
107/// Number of extra bits for distances smaller than 512.
108#[rustfmt::skip]
109const SMALL_DIST_EXTRA: [u8; 512] = [
110 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
111 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
112 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
113 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
114 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
115 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
116 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
117 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
118 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
119 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
120 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
121 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
122 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
123 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
124 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
125 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7
126];
127
128/// Base values to calculate distances above 512.
129#[rustfmt::skip]
130const LARGE_DIST_SYM: [u8; 128] = [
131 0, 0, 18, 19, 20, 20, 21, 21, 22, 22, 22, 22, 23, 23, 23, 23,
132 24, 24, 24, 24, 24, 24, 24, 24, 25, 25, 25, 25, 25, 25, 25, 25,
133 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26,
134 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27,
135 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28,
136 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28,
137 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29,
138 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29
139];
140
141/// Number of extra bits distances above 512.
142#[rustfmt::skip]
143const LARGE_DIST_EXTRA: [u8; 128] = [
144 0, 0, 8, 8, 9, 9, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10,
145 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
146 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12,
147 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12,
148 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13,
149 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13,
150 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13,
151 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13
152];
153
154#[rustfmt::skip]
155const BITMASKS: [u32; 17] = [
156 0x0000, 0x0001, 0x0003, 0x0007, 0x000F, 0x001F, 0x003F, 0x007F, 0x00FF,
157 0x01FF, 0x03FF, 0x07FF, 0x0FFF, 0x1FFF, 0x3FFF, 0x7FFF, 0xFFFF
158];
159
160/// The maximum number of checks for matches in the hash table the compressor will make for each
161/// compression level.
162pub(crate) const NUM_PROBES: [u32; 11] = [0, 1, 6, 32, 16, 32, 128, 256, 512, 768, 1500];
163
164#[derive(Copy, Clone)]
165struct SymFreq {
166 key: u16,
167 sym_index: u16,
168}
169
170pub mod deflate_flags {
171 /// Whether to use a zlib wrapper.
172 pub const TDEFL_WRITE_ZLIB_HEADER: u32 = 0x0000_1000;
173 /// Should we compute the adler32 checksum.
174 pub const TDEFL_COMPUTE_ADLER32: u32 = 0x0000_2000;
175 /// Should we use greedy parsing (as opposed to lazy parsing where look ahead one or more
176 /// bytes to check for better matches.)
177 pub const TDEFL_GREEDY_PARSING_FLAG: u32 = 0x0000_4000;
178 /// Used in miniz to skip zero-initializing hash and dict. We don't do this here, so
179 /// this flag is ignored.
180 pub const TDEFL_NONDETERMINISTIC_PARSING_FLAG: u32 = 0x0000_8000;
181 /// Only look for matches with a distance of 0.
182 pub const TDEFL_RLE_MATCHES: u32 = 0x0001_0000;
183 /// Only use matches that are at least 6 bytes long.
184 pub const TDEFL_FILTER_MATCHES: u32 = 0x0002_0000;
185 /// Force the compressor to only output static blocks. (Blocks using the default huffman codes
186 /// specified in the deflate specification.)
187 pub const TDEFL_FORCE_ALL_STATIC_BLOCKS: u32 = 0x0004_0000;
188 /// Force the compressor to only output raw/uncompressed blocks.
189 pub const TDEFL_FORCE_ALL_RAW_BLOCKS: u32 = 0x0008_0000;
190}
191
192/// Strategy setting for compression.
193///
194/// The non-default settings offer some special-case compression variants.
195#[repr(i32)]
196#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
197pub enum CompressionStrategy {
198 /// Don't use any of the special strategies.
199 Default = 0,
200 /// Only use matches that are at least 5 bytes long.
201 Filtered = 1,
202 /// Don't look for matches, only huffman encode the literals.
203 HuffmanOnly = 2,
204 /// Only look for matches with a distance of 1, i.e do run-length encoding only.
205 RLE = 3,
206 /// Only use static/fixed blocks. (Blocks using the default huffman codes
207 /// specified in the deflate specification.)
208 Fixed = 4,
209}
210
211impl From<CompressionStrategy> for i32 {
212 #[inline(always)]
213 fn from(value: CompressionStrategy) -> Self {
214 value as i32
215 }
216}
217
218/// A list of deflate flush types.
219#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
220pub enum TDEFLFlush {
221 /// Normal operation.
222 ///
223 /// Compress as much as there is space for, and then return waiting for more input.
224 None = 0,
225
226 /// Try to flush all the current data and output an empty raw block.
227 Sync = 2,
228
229 /// Same as [`Sync`][Self::Sync], but reset the dictionary so that the following data does not
230 /// depend on previous data.
231 Full = 3,
232
233 /// Try to flush everything and end the deflate stream.
234 ///
235 /// On success this will yield a [`TDEFLStatus::Done`] return status.
236 Finish = 4,
237}
238
239impl From<MZFlush> for TDEFLFlush {
240 fn from(flush: MZFlush) -> Self {
241 match flush {
242 MZFlush::None => TDEFLFlush::None,
243 MZFlush::Sync => TDEFLFlush::Sync,
244 MZFlush::Full => TDEFLFlush::Full,
245 MZFlush::Finish => TDEFLFlush::Finish,
246 _ => TDEFLFlush::None, // TODO: ??? What to do ???
247 }
248 }
249}
250
251impl TDEFLFlush {
252 pub const fn new(flush: i32) -> Result<Self, MZError> {
253 match flush {
254 0 => Ok(TDEFLFlush::None),
255 2 => Ok(TDEFLFlush::Sync),
256 3 => Ok(TDEFLFlush::Full),
257 4 => Ok(TDEFLFlush::Finish),
258 _ => Err(MZError::Param),
259 }
260 }
261}
262
263/// Return status of compression.
264#[repr(i32)]
265#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
266pub enum TDEFLStatus {
267 /// Usage error.
268 ///
269 /// This indicates that either the [`CompressorOxide`] experienced a previous error, or the
270 /// stream has already been [`TDEFLFlush::Finish`]'d.
271 BadParam = -2,
272
273 /// Error putting data into output buffer.
274 ///
275 /// This usually indicates a too-small buffer.
276 PutBufFailed = -1,
277
278 /// Compression succeeded normally.
279 Okay = 0,
280
281 /// Compression succeeded and the deflate stream was ended.
282 ///
283 /// This is the result of calling compression with [`TDEFLFlush::Finish`].
284 Done = 1,
285}
286
287const MAX_HUFF_SYMBOLS: usize = 288;
288/// Size of hash chain for fast compression mode.
289const LEVEL1_HASH_SIZE_MASK: u32 = 4095;
290/// The number of huffman tables used by the compressor.
291/// Literal/length, Distances and Length of the huffman codes for the other two tables.
292const MAX_HUFF_TABLES: usize = 3;
293/// Literal/length codes
294const MAX_HUFF_SYMBOLS_0: usize = 288;
295/// Distance codes.
296const MAX_HUFF_SYMBOLS_1: usize = 32;
297/// Huffman length values.
298const MAX_HUFF_SYMBOLS_2: usize = 19;
299/// Size of the chained hash table.
300pub(crate) const LZ_DICT_SIZE: usize = 32_768;
301/// Mask used when stepping through the hash chains.
302pub(crate) const LZ_DICT_SIZE_MASK: usize = (LZ_DICT_SIZE as u32 - 1) as usize;
303/// The minimum length of a match.
304pub(crate) const MIN_MATCH_LEN: u8 = 3;
305/// The maximum length of a match.
306pub(crate) const MAX_MATCH_LEN: usize = 258;
307
308pub(crate) const DEFAULT_FLAGS: u32 = NUM_PROBES[4] | TDEFL_WRITE_ZLIB_HEADER;
309
310#[cfg(test)]
311#[inline]
312fn write_u16_le(val: u16, slice: &mut [u8], pos: usize) {
313 slice[pos] = val as u8;
314 slice[pos + 1] = (val >> 8) as u8;
315}
316
317// Read the two bytes starting at pos and interpret them as an u16.
318#[inline]
319const fn read_u16_le(slice: &[u8], pos: usize) -> u16 {
320 // The compiler is smart enough to optimize this into an unaligned load.
321 slice[pos] as u16 | ((slice[pos + 1] as u16) << 8)
322}
323
324/// Main compression struct.
325pub struct CompressorOxide {
326 pub(crate) lz: LZOxide,
327 pub(crate) params: ParamsOxide,
328 /// Put HuffmanOxide on the heap with default trick to avoid
329 /// excessive stack copies.
330 pub(crate) huff: Box<HuffmanOxide>,
331 pub(crate) dict: DictOxide,
332}
333
334impl CompressorOxide {
335 /// Create a new `CompressorOxide` with the given flags.
336 ///
337 /// # Notes
338 /// This function may be changed to take different parameters in the future.
339 pub fn new(flags: u32) -> Self {
340 CompressorOxide {
341 lz: LZOxide::new(),
342 params: ParamsOxide::new(flags),
343 huff: Box::default(),
344 dict: DictOxide::new(flags),
345 }
346 }
347
348 /// Get the adler32 checksum of the currently encoded data.
349 pub const fn adler32(&self) -> u32 {
350 self.params.adler32
351 }
352
353 /// Get the return status of the previous [`compress`](fn.compress.html)
354 /// call with this compressor.
355 pub const fn prev_return_status(&self) -> TDEFLStatus {
356 self.params.prev_return_status
357 }
358
359 /// Get the raw compressor flags.
360 ///
361 /// # Notes
362 /// This function may be deprecated or changed in the future to use more rust-style flags.
363 pub const fn flags(&self) -> i32 {
364 self.params.flags as i32
365 }
366
367 /// Returns whether the compressor is wrapping the data in a zlib format or not.
368 pub const fn data_format(&self) -> DataFormat {
369 if (self.params.flags & TDEFL_WRITE_ZLIB_HEADER) != 0 {
370 DataFormat::Zlib
371 } else {
372 DataFormat::Raw
373 }
374 }
375
376 /// Reset the state of the compressor, keeping the same parameters.
377 ///
378 /// This avoids re-allocating data.
379 pub fn reset(&mut self) {
380 // LZ buf and huffman has no settings or dynamic memory
381 // that needs to be saved, so we simply replace them.
382 self.lz = LZOxide::new();
383 self.params.reset();
384 *self.huff = HuffmanOxide::default();
385 self.dict.reset();
386 }
387
388 /// Set the compression level of the compressor.
389 ///
390 /// Using this to change level after compression has started is supported.
391 /// # Notes
392 /// The compression strategy will be reset to the default one when this is called.
393 pub fn set_compression_level(&mut self, level: CompressionLevel) {
394 let format = self.data_format();
395 self.set_format_and_level(format, level as u8);
396 }
397
398 /// Set the compression level of the compressor using an integer value.
399 ///
400 /// Using this to change level after compression has started is supported.
401 /// # Notes
402 /// The compression strategy will be reset to the default one when this is called.
403 pub fn set_compression_level_raw(&mut self, level: u8) {
404 let format = self.data_format();
405 self.set_format_and_level(format, level);
406 }
407
408 /// Update the compression settings of the compressor.
409 ///
410 /// Changing the `DataFormat` after compression has started will result in
411 /// a corrupted stream.
412 ///
413 /// # Notes
414 /// This function mainly intended for setting the initial settings after e.g creating with
415 /// `default` or after calling `CompressorOxide::reset()`, and behaviour may be changed
416 /// to disallow calling it after starting compression in the future.
417 pub fn set_format_and_level(&mut self, data_format: DataFormat, level: u8) {
418 let flags = create_comp_flags_from_zip_params(
419 level.into(),
420 data_format.to_window_bits(),
421 CompressionStrategy::Default as i32,
422 );
423 self.params.update_flags(flags);
424 self.dict.update_flags(flags);
425 }
426}
427
428impl Default for CompressorOxide {
429 /// Initialize the compressor with a level of 4, zlib wrapper and
430 /// the default strategy.
431 fn default() -> Self {
432 CompressorOxide {
433 lz: LZOxide::new(),
434 params: ParamsOxide::new(DEFAULT_FLAGS),
435 huff: Box::default(),
436 dict: DictOxide::new(DEFAULT_FLAGS),
437 }
438 }
439}
440
441/// Callback function and user used in `compress_to_output`.
442pub struct CallbackFunc<'a> {
443 pub put_buf_func: &'a mut dyn FnMut(&[u8]) -> bool,
444}
445
446impl CallbackFunc<'_> {
447 fn flush_output(
448 &mut self,
449 saved_output: SavedOutputBufferOxide,
450 params: &mut ParamsOxide,
451 ) -> i32 {
452 // TODO: As this could be unsafe since
453 // we can't verify the function pointer
454 // this whole function should maybe be unsafe as well.
455 let call_success: bool = (self.put_buf_func)(&params.local_buf.b[0..saved_output.pos]);
456
457 if !call_success {
458 params.prev_return_status = TDEFLStatus::PutBufFailed;
459 return params.prev_return_status as i32;
460 }
461
462 params.flush_remaining as i32
463 }
464}
465
466struct CallbackBuf<'a> {
467 pub out_buf: &'a mut [u8],
468}
469
470impl CallbackBuf<'_> {
471 fn flush_output(
472 &mut self,
473 saved_output: SavedOutputBufferOxide,
474 params: &mut ParamsOxide,
475 ) -> i32 {
476 if saved_output.local {
477 let n: usize = cmp::min(v1:saved_output.pos, self.out_buf.len() - params.out_buf_ofs);
478 (self.out_buf[params.out_buf_ofs..params.out_buf_ofs + n])
479 .copy_from_slice(&params.local_buf.b[..n]);
480
481 params.out_buf_ofs += n;
482 if saved_output.pos != n {
483 params.flush_ofs = n as u32;
484 params.flush_remaining = (saved_output.pos - n) as u32;
485 }
486 } else {
487 params.out_buf_ofs += saved_output.pos;
488 }
489
490 params.flush_remaining as i32
491 }
492}
493
494enum CallbackOut<'a> {
495 Func(CallbackFunc<'a>),
496 Buf(CallbackBuf<'a>),
497}
498
499impl CallbackOut<'_> {
500 fn new_output_buffer<'b>(
501 &'b mut self,
502 local_buf: &'b mut [u8],
503 out_buf_ofs: usize,
504 ) -> OutputBufferOxide<'b> {
505 let is_local;
506 let buf_len = OUT_BUF_SIZE - 16;
507 let chosen_buffer = match *self {
508 CallbackOut::Buf(ref mut cb) if cb.out_buf.len() - out_buf_ofs >= OUT_BUF_SIZE => {
509 is_local = false;
510 &mut cb.out_buf[out_buf_ofs..out_buf_ofs + buf_len]
511 }
512 _ => {
513 is_local = true;
514 &mut local_buf[..buf_len]
515 }
516 };
517
518 OutputBufferOxide {
519 inner: chosen_buffer,
520 inner_pos: 0,
521 local: is_local,
522 bit_buffer: 0,
523 bits_in: 0,
524 }
525 }
526}
527
528pub(crate) struct CallbackOxide<'a> {
529 in_buf: Option<&'a [u8]>,
530 in_buf_size: Option<&'a mut usize>,
531 out_buf_size: Option<&'a mut usize>,
532 out: CallbackOut<'a>,
533}
534
535impl<'a> CallbackOxide<'a> {
536 fn new_callback_buf(in_buf: &'a [u8], out_buf: &'a mut [u8]) -> Self {
537 CallbackOxide {
538 in_buf: Some(in_buf),
539 in_buf_size: None,
540 out_buf_size: None,
541 out: CallbackOut::Buf(CallbackBuf { out_buf }),
542 }
543 }
544
545 fn new_callback_func(in_buf: &'a [u8], callback_func: CallbackFunc<'a>) -> Self {
546 CallbackOxide {
547 in_buf: Some(in_buf),
548 in_buf_size: None,
549 out_buf_size: None,
550 out: CallbackOut::Func(callback_func),
551 }
552 }
553
554 fn update_size(&mut self, in_size: Option<usize>, out_size: Option<usize>) {
555 if let (Some(in_size), Some(size)) = (in_size, self.in_buf_size.as_mut()) {
556 **size = in_size;
557 }
558
559 if let (Some(out_size), Some(size)) = (out_size, self.out_buf_size.as_mut()) {
560 **size = out_size
561 }
562 }
563
564 fn flush_output(
565 &mut self,
566 saved_output: SavedOutputBufferOxide,
567 params: &mut ParamsOxide,
568 ) -> i32 {
569 if saved_output.pos == 0 {
570 return params.flush_remaining as i32;
571 }
572
573 self.update_size(Some(params.src_pos), None);
574 match self.out {
575 CallbackOut::Func(ref mut cf) => cf.flush_output(saved_output, params),
576 CallbackOut::Buf(ref mut cb) => cb.flush_output(saved_output, params),
577 }
578 }
579
580 pub(crate) fn buf(&mut self) -> Option<&'a [u8]> {
581 self.in_buf
582 }
583}
584
585struct OutputBufferOxide<'a> {
586 pub inner: &'a mut [u8],
587 pub inner_pos: usize,
588 pub local: bool,
589
590 pub bit_buffer: u32,
591 pub bits_in: u32,
592}
593
594impl OutputBufferOxide<'_> {
595 /// Write bits to the bit buffer and flushes
596 /// the bit buffer so any whole bytes are output
597 /// to the underlying buffer.
598 fn put_bits(&mut self, bits: u32, len: u32) {
599 // TODO: Removing this assertion worsens performance
600 // Need to figure out why
601 assert!(bits <= ((1u32 << len) - 1u32));
602 self.bit_buffer |= bits << self.bits_in;
603 self.bits_in += len;
604
605 while self.bits_in >= 8 {
606 self.inner[self.inner_pos] = self.bit_buffer as u8;
607 self.inner_pos += 1;
608 self.bit_buffer >>= 8;
609 self.bits_in -= 8;
610 }
611 }
612
613 #[inline]
614 /// Write the provided bits to the bit buffer without flushing
615 /// anything. Does not check if there is actually space for it.
616 fn put_bits_no_flush(&mut self, bits: u32, len: u32) {
617 self.bit_buffer |= bits << self.bits_in;
618 self.bits_in += len;
619 }
620
621 const fn save(&self) -> SavedOutputBufferOxide {
622 SavedOutputBufferOxide {
623 pos: self.inner_pos,
624 bit_buffer: self.bit_buffer,
625 bits_in: self.bits_in,
626 local: self.local,
627 }
628 }
629
630 fn load(&mut self, saved: SavedOutputBufferOxide) {
631 self.inner_pos = saved.pos;
632 self.bit_buffer = saved.bit_buffer;
633 self.bits_in = saved.bits_in;
634 self.local = saved.local;
635 }
636
637 #[inline]
638 /// Pad the bit buffer to a whole byte with
639 /// zeroes and write that byte to the output buffer.
640 fn pad_to_bytes(&mut self) {
641 if self.bits_in != 0 {
642 let len = 8 - self.bits_in;
643 self.put_bits(0, len);
644 }
645 }
646
647 #[inline]
648 fn write_bytes(&mut self, bytes: &[u8]) {
649 debug_assert_eq!(self.bits_in, 0);
650 self.inner[self.inner_pos..self.inner_pos + bytes.len()].copy_from_slice(bytes);
651 self.inner_pos += bytes.len();
652 }
653}
654
655struct SavedOutputBufferOxide {
656 pub pos: usize,
657 pub bit_buffer: u32,
658 pub bits_in: u32,
659 pub local: bool,
660}
661
662struct BitBuffer {
663 pub bit_buffer: u64,
664 pub bits_in: u32,
665}
666
667impl BitBuffer {
668 fn put_fast(&mut self, bits: u64, len: u32) {
669 self.bit_buffer |= bits << self.bits_in;
670 self.bits_in += len;
671 }
672
673 fn flush(&mut self, output: &mut OutputBufferOxide) -> Result<()> {
674 let pos: usize = output.inner_pos;
675 {
676 // isolation to please borrow checker
677 let inner: &mut [u8] = &mut output.inner[pos..pos + 8];
678 let bytes: [u8; _] = u64::to_le_bytes(self.bit_buffer);
679 inner.copy_from_slice(&bytes);
680 }
681 match output.inner_pos.checked_add((self.bits_in >> 3) as usize) {
682 Some(n: usize) if n <= output.inner.len() => output.inner_pos = n,
683 _ => return Err(Error {}),
684 }
685 self.bit_buffer >>= self.bits_in & !7;
686 self.bits_in &= 7;
687 Ok(())
688 }
689}
690
691/// A struct containing data about huffman codes and symbol frequencies.
692///
693/// NOTE: Only the literal/lengths have enough symbols to actually use
694/// the full array. It's unclear why it's defined like this in miniz,
695/// it could be for cache/alignment reasons.
696pub(crate) struct HuffmanOxide {
697 /// Number of occurrences of each symbol.
698 pub count: [[u16; MAX_HUFF_SYMBOLS]; MAX_HUFF_TABLES],
699 /// The bits of the huffman code assigned to the symbol
700 pub codes: [[u16; MAX_HUFF_SYMBOLS]; MAX_HUFF_TABLES],
701 /// The length of the huffman code assigned to the symbol.
702 pub code_sizes: [[u8; MAX_HUFF_SYMBOLS]; MAX_HUFF_TABLES],
703}
704
705/// Tables used for literal/lengths in `HuffmanOxide`.
706const LITLEN_TABLE: usize = 0;
707/// Tables for distances.
708const DIST_TABLE: usize = 1;
709/// Tables for the run-length encoded huffman lengths for literals/lengths/distances.
710const HUFF_CODES_TABLE: usize = 2;
711
712/// Status of RLE encoding of huffman code lengths.
713struct Rle {
714 pub z_count: u32,
715 pub repeat_count: u16,
716 pub prev_code_size: u8,
717}
718
719impl Rle {
720 fn prev_code_size(
721 &mut self,
722 packed_code_sizes: &mut [u8],
723 packed_pos: &mut usize,
724 h: &mut HuffmanOxide,
725 ) -> Result<()> {
726 let mut write = |buf| write(buf, packed_code_sizes, packed_pos);
727 let counts = &mut h.count[HUFF_CODES_TABLE];
728 if self.repeat_count != 0 {
729 if self.repeat_count < 3 {
730 counts[self.prev_code_size as usize] =
731 counts[self.prev_code_size as usize].wrapping_add(self.repeat_count);
732 let code = self.prev_code_size;
733 write(&[code, code, code][..self.repeat_count as usize])?;
734 } else {
735 counts[16] = counts[16].wrapping_add(1);
736 write(&[16, (self.repeat_count - 3) as u8][..])?;
737 }
738 self.repeat_count = 0;
739 }
740
741 Ok(())
742 }
743
744 fn zero_code_size(
745 &mut self,
746 packed_code_sizes: &mut [u8],
747 packed_pos: &mut usize,
748 h: &mut HuffmanOxide,
749 ) -> Result<()> {
750 let mut write = |buf| write(buf, packed_code_sizes, packed_pos);
751 let counts = &mut h.count[HUFF_CODES_TABLE];
752 if self.z_count != 0 {
753 if self.z_count < 3 {
754 counts[0] = counts[0].wrapping_add(self.z_count as u16);
755 write(&[0, 0, 0][..self.z_count as usize])?;
756 } else if self.z_count <= 10 {
757 counts[17] = counts[17].wrapping_add(1);
758 write(&[17, (self.z_count - 3) as u8][..])?;
759 } else {
760 counts[18] = counts[18].wrapping_add(1);
761 write(&[18, (self.z_count - 11) as u8][..])?;
762 }
763 self.z_count = 0;
764 }
765
766 Ok(())
767 }
768}
769
770fn write(src: &[u8], dst: &mut [u8], dst_pos: &mut usize) -> Result<()> {
771 match dst.get_mut(*dst_pos..*dst_pos + src.len()) {
772 Some(s: &mut [u8]) => s.copy_from_slice(src),
773 None => return Err(Error {}),
774 }
775 *dst_pos += src.len();
776 Ok(())
777}
778
779impl Default for HuffmanOxide {
780 fn default() -> Self {
781 HuffmanOxide {
782 count: [[0; MAX_HUFF_SYMBOLS]; MAX_HUFF_TABLES],
783 codes: [[0; MAX_HUFF_SYMBOLS]; MAX_HUFF_TABLES],
784 code_sizes: [[0; MAX_HUFF_SYMBOLS]; MAX_HUFF_TABLES],
785 }
786 }
787}
788
789impl HuffmanOxide {
790 fn radix_sort_symbols<'a>(
791 symbols0: &'a mut [SymFreq],
792 symbols1: &'a mut [SymFreq],
793 ) -> &'a mut [SymFreq] {
794 let mut hist = [[0; 256]; 2];
795
796 for freq in symbols0.iter() {
797 hist[0][(freq.key & 0xFF) as usize] += 1;
798 hist[1][((freq.key >> 8) & 0xFF) as usize] += 1;
799 }
800
801 let mut n_passes = 2;
802 if symbols0.len() == hist[1][0] {
803 n_passes -= 1;
804 }
805
806 let mut current_symbols = symbols0;
807 let mut new_symbols = symbols1;
808
809 for (pass, hist_item) in hist.iter().enumerate().take(n_passes) {
810 let mut offsets = [0; 256];
811 let mut offset = 0;
812 for i in 0..256 {
813 offsets[i] = offset;
814 offset += hist_item[i];
815 }
816
817 for sym in current_symbols.iter() {
818 let j = ((sym.key >> (pass * 8)) & 0xFF) as usize;
819 new_symbols[offsets[j]] = *sym;
820 offsets[j] += 1;
821 }
822
823 mem::swap(&mut current_symbols, &mut new_symbols);
824 }
825
826 current_symbols
827 }
828
829 fn calculate_minimum_redundancy(symbols: &mut [SymFreq]) {
830 match symbols.len() {
831 0 => (),
832 1 => symbols[0].key = 1,
833 n => {
834 symbols[0].key += symbols[1].key;
835 let mut root = 0;
836 let mut leaf = 2;
837 for next in 1..n - 1 {
838 if (leaf >= n) || (symbols[root].key < symbols[leaf].key) {
839 symbols[next].key = symbols[root].key;
840 symbols[root].key = next as u16;
841 root += 1;
842 } else {
843 symbols[next].key = symbols[leaf].key;
844 leaf += 1;
845 }
846
847 if (leaf >= n) || (root < next && symbols[root].key < symbols[leaf].key) {
848 symbols[next].key = symbols[next].key.wrapping_add(symbols[root].key);
849 symbols[root].key = next as u16;
850 root += 1;
851 } else {
852 symbols[next].key = symbols[next].key.wrapping_add(symbols[leaf].key);
853 leaf += 1;
854 }
855 }
856
857 symbols[n - 2].key = 0;
858 for next in (0..n - 2).rev() {
859 symbols[next].key = symbols[symbols[next].key as usize].key + 1;
860 }
861
862 let mut avbl = 1;
863 let mut used = 0;
864 let mut dpth = 0;
865 let mut root = (n - 2) as i32;
866 let mut next = (n - 1) as i32;
867 while avbl > 0 {
868 while (root >= 0) && (symbols[root as usize].key == dpth) {
869 used += 1;
870 root -= 1;
871 }
872 while avbl > used {
873 symbols[next as usize].key = dpth;
874 next -= 1;
875 avbl -= 1;
876 }
877 avbl = 2 * used;
878 dpth += 1;
879 used = 0;
880 }
881 }
882 }
883 }
884
885 fn enforce_max_code_size(num_codes: &mut [i32], code_list_len: usize, max_code_size: usize) {
886 if code_list_len <= 1 {
887 return;
888 }
889
890 num_codes[max_code_size] += num_codes[max_code_size + 1..].iter().sum::<i32>();
891 let total = num_codes[1..=max_code_size]
892 .iter()
893 .rev()
894 .enumerate()
895 .fold(0u32, |total, (i, &x)| total + ((x as u32) << i));
896
897 for _ in (1 << max_code_size)..total {
898 num_codes[max_code_size] -= 1;
899 for i in (1..max_code_size).rev() {
900 if num_codes[i] != 0 {
901 num_codes[i] -= 1;
902 num_codes[i + 1] += 2;
903 break;
904 }
905 }
906 }
907 }
908
909 fn optimize_table(
910 &mut self,
911 table_num: usize,
912 table_len: usize,
913 code_size_limit: usize,
914 static_table: bool,
915 ) {
916 let mut num_codes = [0i32; MAX_SUPPORTED_HUFF_CODESIZE + 1];
917 let mut next_code = [0u32; MAX_SUPPORTED_HUFF_CODESIZE + 1];
918
919 if static_table {
920 for &code_size in &self.code_sizes[table_num][..table_len] {
921 num_codes[code_size as usize] += 1;
922 }
923 } else {
924 let mut symbols0 = [SymFreq {
925 key: 0,
926 sym_index: 0,
927 }; MAX_HUFF_SYMBOLS];
928 let mut symbols1 = [SymFreq {
929 key: 0,
930 sym_index: 0,
931 }; MAX_HUFF_SYMBOLS];
932
933 let mut num_used_symbols = 0;
934 for i in 0..table_len {
935 if self.count[table_num][i] != 0 {
936 symbols0[num_used_symbols] = SymFreq {
937 key: self.count[table_num][i],
938 sym_index: i as u16,
939 };
940 num_used_symbols += 1;
941 }
942 }
943
944 let symbols = Self::radix_sort_symbols(
945 &mut symbols0[..num_used_symbols],
946 &mut symbols1[..num_used_symbols],
947 );
948 Self::calculate_minimum_redundancy(symbols);
949
950 for symbol in symbols.iter() {
951 num_codes[symbol.key as usize] += 1;
952 }
953
954 Self::enforce_max_code_size(&mut num_codes, num_used_symbols, code_size_limit);
955
956 self.code_sizes[table_num].fill(0);
957 self.codes[table_num].fill(0);
958
959 let mut last = num_used_symbols;
960 for (i, &num_item) in num_codes
961 .iter()
962 .enumerate()
963 .take(code_size_limit + 1)
964 .skip(1)
965 {
966 let first = last - num_item as usize;
967 for symbol in &symbols[first..last] {
968 self.code_sizes[table_num][symbol.sym_index as usize] = i as u8;
969 }
970 last = first;
971 }
972 }
973
974 let mut j = 0;
975 next_code[1] = 0;
976 for i in 2..=code_size_limit {
977 j = (j + num_codes[i - 1]) << 1;
978 next_code[i] = j as u32;
979 }
980
981 for (&code_size, huff_code) in self.code_sizes[table_num]
982 .iter()
983 .take(table_len)
984 .zip(self.codes[table_num].iter_mut().take(table_len))
985 {
986 if code_size == 0 {
987 continue;
988 }
989
990 let mut code = next_code[code_size as usize];
991 next_code[code_size as usize] += 1;
992
993 let mut rev_code = 0;
994 for _ in 0..code_size {
995 rev_code = (rev_code << 1) | (code & 1);
996 code >>= 1;
997 }
998 *huff_code = rev_code as u16;
999 }
1000 }
1001
1002 fn start_static_block(&mut self, output: &mut OutputBufferOxide) {
1003 self.code_sizes[LITLEN_TABLE][0..144].fill(8);
1004 self.code_sizes[LITLEN_TABLE][144..256].fill(9);
1005 self.code_sizes[LITLEN_TABLE][256..280].fill(7);
1006 self.code_sizes[LITLEN_TABLE][280..288].fill(8);
1007
1008 self.code_sizes[DIST_TABLE][..32].fill(5);
1009
1010 self.optimize_table(LITLEN_TABLE, 288, 15, true);
1011 self.optimize_table(DIST_TABLE, 32, 15, true);
1012
1013 output.put_bits(0b01, 2)
1014 }
1015
1016 fn start_dynamic_block(&mut self, output: &mut OutputBufferOxide) -> Result<()> {
1017 // There will always be one, and only one end of block code.
1018 self.count[0][256] = 1;
1019
1020 self.optimize_table(0, MAX_HUFF_SYMBOLS_0, 15, false);
1021 self.optimize_table(1, MAX_HUFF_SYMBOLS_1, 15, false);
1022
1023 let num_lit_codes = 286
1024 - &self.code_sizes[0][257..286]
1025 .iter()
1026 .rev()
1027 .take_while(|&x| *x == 0)
1028 .count();
1029
1030 let num_dist_codes = 30
1031 - &self.code_sizes[1][1..30]
1032 .iter()
1033 .rev()
1034 .take_while(|&x| *x == 0)
1035 .count();
1036
1037 let mut code_sizes_to_pack = [0u8; MAX_HUFF_SYMBOLS_0 + MAX_HUFF_SYMBOLS_1];
1038 let mut packed_code_sizes = [0u8; MAX_HUFF_SYMBOLS_0 + MAX_HUFF_SYMBOLS_1];
1039
1040 let total_code_sizes_to_pack = num_lit_codes + num_dist_codes;
1041
1042 code_sizes_to_pack[..num_lit_codes].copy_from_slice(&self.code_sizes[0][..num_lit_codes]);
1043
1044 code_sizes_to_pack[num_lit_codes..total_code_sizes_to_pack]
1045 .copy_from_slice(&self.code_sizes[1][..num_dist_codes]);
1046
1047 let mut rle = Rle {
1048 z_count: 0,
1049 repeat_count: 0,
1050 prev_code_size: 0xFF,
1051 };
1052
1053 self.count[HUFF_CODES_TABLE][..MAX_HUFF_SYMBOLS_2].fill(0);
1054
1055 let mut packed_pos = 0;
1056 for &code_size in &code_sizes_to_pack[..total_code_sizes_to_pack] {
1057 if code_size == 0 {
1058 rle.prev_code_size(&mut packed_code_sizes, &mut packed_pos, self)?;
1059 rle.z_count += 1;
1060 if rle.z_count == 138 {
1061 rle.zero_code_size(&mut packed_code_sizes, &mut packed_pos, self)?;
1062 }
1063 } else {
1064 rle.zero_code_size(&mut packed_code_sizes, &mut packed_pos, self)?;
1065 if code_size != rle.prev_code_size {
1066 rle.prev_code_size(&mut packed_code_sizes, &mut packed_pos, self)?;
1067 self.count[HUFF_CODES_TABLE][code_size as usize] =
1068 self.count[HUFF_CODES_TABLE][code_size as usize].wrapping_add(1);
1069 write(&[code_size], &mut packed_code_sizes, &mut packed_pos)?;
1070 } else {
1071 rle.repeat_count += 1;
1072 if rle.repeat_count == 6 {
1073 rle.prev_code_size(&mut packed_code_sizes, &mut packed_pos, self)?;
1074 }
1075 }
1076 }
1077 rle.prev_code_size = code_size;
1078 }
1079
1080 if rle.repeat_count != 0 {
1081 rle.prev_code_size(&mut packed_code_sizes, &mut packed_pos, self)?;
1082 } else {
1083 rle.zero_code_size(&mut packed_code_sizes, &mut packed_pos, self)?;
1084 }
1085
1086 self.optimize_table(2, MAX_HUFF_SYMBOLS_2, 7, false);
1087
1088 output.put_bits_no_flush(2, 2);
1089
1090 output.put_bits_no_flush((num_lit_codes - 257) as u32, 5);
1091 output.put_bits_no_flush((num_dist_codes - 1) as u32, 5);
1092
1093 let mut num_bit_lengths = 18
1094 - HUFFMAN_LENGTH_ORDER
1095 .iter()
1096 .rev()
1097 .take_while(|&swizzle| self.code_sizes[HUFF_CODES_TABLE][*swizzle as usize] == 0)
1098 .count();
1099
1100 num_bit_lengths = cmp::max(4, num_bit_lengths + 1);
1101 output.put_bits(num_bit_lengths as u32 - 4, 4);
1102 for &swizzle in &HUFFMAN_LENGTH_ORDER[..num_bit_lengths] {
1103 output.put_bits(
1104 u32::from(self.code_sizes[HUFF_CODES_TABLE][swizzle as usize]),
1105 3,
1106 );
1107 }
1108
1109 let mut packed_code_size_index = 0;
1110 while packed_code_size_index < packed_pos {
1111 let code = packed_code_sizes[packed_code_size_index] as usize;
1112 packed_code_size_index += 1;
1113 assert!(code < MAX_HUFF_SYMBOLS_2);
1114 output.put_bits(
1115 u32::from(self.codes[HUFF_CODES_TABLE][code]),
1116 u32::from(self.code_sizes[HUFF_CODES_TABLE][code]),
1117 );
1118 if code >= 16 {
1119 output.put_bits(
1120 u32::from(packed_code_sizes[packed_code_size_index]),
1121 [2, 3, 7][code - 16],
1122 );
1123 packed_code_size_index += 1;
1124 }
1125 }
1126
1127 Ok(())
1128 }
1129}
1130
1131pub(crate) struct DictOxide {
1132 /// The maximum number of checks in the hash chain, for the initial,
1133 /// and the lazy match respectively.
1134 pub max_probes: [u32; 2],
1135 /// Buffer of input data.
1136 /// Padded with 1 byte to simplify matching code in `compress_fast`.
1137 pub b: Box<HashBuffers>,
1138
1139 pub code_buf_dict_pos: usize,
1140 pub lookahead_size: usize,
1141 pub lookahead_pos: usize,
1142 pub size: usize,
1143}
1144
1145const fn probes_from_flags(flags: u32) -> [u32; 2] {
1146 [
1147 1 + ((flags & 0xFFF) + 2) / 3,
1148 1 + (((flags & 0xFFF) >> 2) + 2) / 3,
1149 ]
1150}
1151
1152impl DictOxide {
1153 fn new(flags: u32) -> Self {
1154 DictOxide {
1155 max_probes: probes_from_flags(flags),
1156 b: Box::default(),
1157 code_buf_dict_pos: 0,
1158 lookahead_size: 0,
1159 lookahead_pos: 0,
1160 size: 0,
1161 }
1162 }
1163
1164 fn update_flags(&mut self, flags: u32) {
1165 self.max_probes = probes_from_flags(flags);
1166 }
1167
1168 fn reset(&mut self) {
1169 self.b.reset();
1170 self.code_buf_dict_pos = 0;
1171 self.lookahead_size = 0;
1172 self.lookahead_pos = 0;
1173 self.size = 0;
1174 }
1175
1176 /// Do an unaligned read of the data at `pos` in the dictionary and treat it as if it was of
1177 /// type T.
1178 #[inline]
1179 fn read_unaligned_u32(&self, pos: usize) -> u32 {
1180 // Masking the value here helps avoid bounds checks.
1181 let pos = pos & LZ_DICT_SIZE_MASK;
1182 let end = pos + 4;
1183 // Somehow this assertion makes things faster.
1184 // TODO: as of may 2024 this does not seem to make any difference
1185 // so consider removing.
1186 assert!(end < LZ_DICT_FULL_SIZE);
1187
1188 let bytes: [u8; 4] = self.b.dict[pos..end].try_into().unwrap();
1189 u32::from_le_bytes(bytes)
1190 }
1191
1192 /// Do an unaligned read of the data at `pos` in the dictionary and treat it as if it was of
1193 /// type T.
1194 #[inline]
1195 fn read_unaligned_u64(&self, pos: usize) -> u64 {
1196 // Help evade bounds/panic code check by masking the position value
1197 // This provides a small speedup at the cost of an instruction or two instead of
1198 // having to use unsafe.
1199 let pos = pos & LZ_DICT_SIZE_MASK;
1200 let bytes: [u8; 8] = self.b.dict[pos..pos + 8].try_into().unwrap();
1201 u64::from_le_bytes(bytes)
1202 }
1203
1204 /// Do an unaligned read of the data at `pos` in the dictionary and treat it as if it was of
1205 /// type T.
1206 #[inline]
1207 fn read_as_u16(&self, pos: usize) -> u16 {
1208 read_u16_le(&self.b.dict[..], pos)
1209 }
1210
1211 /// Try to find a match for the data at lookahead_pos in the dictionary that is
1212 /// longer than `match_len`.
1213 /// Returns a tuple containing (match_distance, match_length). Will be equal to the input
1214 /// values if no better matches were found.
1215 fn find_match(
1216 &self,
1217 lookahead_pos: usize,
1218 max_dist: usize,
1219 max_match_len: u32,
1220 mut match_dist: u32,
1221 mut match_len: u32,
1222 ) -> (u32, u32) {
1223 // Clamp the match len and max_match_len to be valid. (It should be when this is called, but
1224 // do it for now just in case for safety reasons.)
1225 // This should normally end up as at worst conditional moves,
1226 // so it shouldn't slow us down much.
1227 // TODO: Statically verify these so we don't need to do this.
1228 let max_match_len = cmp::min(MAX_MATCH_LEN as u32, max_match_len);
1229 match_len = cmp::max(match_len, 1);
1230
1231 let pos = lookahead_pos & LZ_DICT_SIZE_MASK;
1232 let mut probe_pos = pos;
1233 // Number of probes into the hash chains.
1234 let mut num_probes_left = self.max_probes[(match_len >= 32) as usize];
1235
1236 // If we already have a match of the full length don't bother searching for another one.
1237 if max_match_len <= match_len {
1238 return (match_dist, match_len);
1239 }
1240
1241 // Read the last byte of the current match, and the next one, used to compare matches.
1242 let mut c01: u16 = self.read_as_u16(pos + match_len as usize - 1);
1243 // Read the two bytes at the end position of the current match.
1244 let s01: u16 = self.read_as_u16(pos);
1245
1246 'outer: loop {
1247 let mut dist;
1248 'found: loop {
1249 num_probes_left -= 1;
1250 if num_probes_left == 0 {
1251 // We have done as many probes in the hash chain as the current compression
1252 // settings allow, so return the best match we found, if any.
1253 return (match_dist, match_len);
1254 }
1255
1256 for _ in 0..3 {
1257 let next_probe_pos = self.b.next[probe_pos] as usize;
1258
1259 dist = (lookahead_pos - next_probe_pos) & 0xFFFF;
1260 if next_probe_pos == 0 || dist > max_dist {
1261 // We reached the end of the hash chain, or the next value is further away
1262 // than the maximum allowed distance, so return the best match we found, if
1263 // any.
1264 return (match_dist, match_len);
1265 }
1266
1267 // Mask the position value to get the position in the hash chain of the next
1268 // position to match against.
1269 probe_pos = next_probe_pos & LZ_DICT_SIZE_MASK;
1270
1271 // TODO: This bounds check does not get optimized out
1272 if self.read_as_u16(probe_pos + match_len as usize - 1) == c01 {
1273 break 'found;
1274 }
1275 }
1276 }
1277
1278 if dist == 0 {
1279 // We've looked through the whole match range, so return the best match we
1280 // found.
1281 return (match_dist, match_len);
1282 }
1283
1284 // Check if the two first bytes match.
1285 if self.read_as_u16(probe_pos) != s01 {
1286 continue;
1287 }
1288
1289 let mut p = pos + 2;
1290 let mut q = probe_pos + 2;
1291 // The first two bytes matched, so check the full length of the match.
1292 for _ in 0..32 {
1293 let p_data: u64 = self.read_unaligned_u64(p);
1294 let q_data: u64 = self.read_unaligned_u64(q);
1295 // Compare of 8 bytes at a time by using unaligned loads of 64-bit integers.
1296 let xor_data = p_data ^ q_data;
1297 if xor_data == 0 {
1298 p += 8;
1299 q += 8;
1300 } else {
1301 // If not all of the last 8 bytes matched, check how may of them did.
1302 let trailing = xor_data.trailing_zeros();
1303
1304 let probe_len = p - pos + (trailing as usize >> 3);
1305 if probe_len > match_len as usize {
1306 match_dist = dist as u32;
1307 match_len = cmp::min(max_match_len, probe_len as u32);
1308 if match_len == max_match_len {
1309 // We found a match that had the maximum allowed length,
1310 // so there is now point searching further.
1311 return (match_dist, match_len);
1312 }
1313 // We found a better match, so save the last two bytes for further match
1314 // comparisons.
1315 c01 = self.read_as_u16(pos + match_len as usize - 1)
1316 }
1317 continue 'outer;
1318 }
1319 }
1320
1321 return (dist as u32, cmp::min(max_match_len, MAX_MATCH_LEN as u32));
1322 }
1323 }
1324}
1325
1326pub(crate) struct ParamsOxide {
1327 pub flags: u32,
1328 pub greedy_parsing: bool,
1329 pub block_index: u32,
1330
1331 pub saved_match_dist: u32,
1332 pub saved_match_len: u32,
1333 pub saved_lit: u8,
1334
1335 pub flush: TDEFLFlush,
1336 pub flush_ofs: u32,
1337 pub flush_remaining: u32,
1338 pub finished: bool,
1339
1340 pub adler32: u32,
1341
1342 pub src_pos: usize,
1343
1344 pub out_buf_ofs: usize,
1345 pub prev_return_status: TDEFLStatus,
1346
1347 pub saved_bit_buffer: u32,
1348 pub saved_bits_in: u32,
1349
1350 pub local_buf: Box<LocalBuf>,
1351}
1352
1353impl ParamsOxide {
1354 fn new(flags: u32) -> Self {
1355 ParamsOxide {
1356 flags,
1357 greedy_parsing: flags & TDEFL_GREEDY_PARSING_FLAG != 0,
1358 block_index: 0,
1359 saved_match_dist: 0,
1360 saved_match_len: 0,
1361 saved_lit: 0,
1362 flush: TDEFLFlush::None,
1363 flush_ofs: 0,
1364 flush_remaining: 0,
1365 finished: false,
1366 adler32: MZ_ADLER32_INIT,
1367 src_pos: 0,
1368 out_buf_ofs: 0,
1369 prev_return_status: TDEFLStatus::Okay,
1370 saved_bit_buffer: 0,
1371 saved_bits_in: 0,
1372 local_buf: Box::default(),
1373 }
1374 }
1375
1376 fn update_flags(&mut self, flags: u32) {
1377 self.flags = flags;
1378 self.greedy_parsing = self.flags & TDEFL_GREEDY_PARSING_FLAG != 0;
1379 }
1380
1381 /// Reset state, saving settings.
1382 fn reset(&mut self) {
1383 self.block_index = 0;
1384 self.saved_match_len = 0;
1385 self.saved_match_dist = 0;
1386 self.saved_lit = 0;
1387 self.flush = TDEFLFlush::None;
1388 self.flush_ofs = 0;
1389 self.flush_remaining = 0;
1390 self.finished = false;
1391 self.adler32 = MZ_ADLER32_INIT;
1392 self.src_pos = 0;
1393 self.out_buf_ofs = 0;
1394 self.prev_return_status = TDEFLStatus::Okay;
1395 self.saved_bit_buffer = 0;
1396 self.saved_bits_in = 0;
1397 self.local_buf.b = [0; OUT_BUF_SIZE];
1398 }
1399}
1400
1401pub(crate) struct LZOxide {
1402 pub codes: [u8; LZ_CODE_BUF_SIZE],
1403 pub code_position: usize,
1404 pub flag_position: usize,
1405
1406 // The total number of bytes in the current block.
1407 pub total_bytes: u32,
1408 pub num_flags_left: u32,
1409}
1410
1411impl LZOxide {
1412 const fn new() -> Self {
1413 LZOxide {
1414 codes: [0; LZ_CODE_BUF_SIZE],
1415 code_position: 1,
1416 flag_position: 0,
1417 total_bytes: 0,
1418 num_flags_left: 8,
1419 }
1420 }
1421
1422 fn write_code(&mut self, val: u8) {
1423 // Perf - go via u16 to help evade bounds check
1424 // TODO: see if we can use u16 for flag_position in general.
1425 self.codes[usize::from(self.code_position as u16)] = val;
1426 self.code_position += 1;
1427 }
1428
1429 fn init_flag(&mut self) {
1430 if self.num_flags_left == 8 {
1431 *self.get_flag() = 0;
1432 self.code_position -= 1;
1433 } else {
1434 *self.get_flag() >>= self.num_flags_left;
1435 }
1436 }
1437
1438 fn get_flag(&mut self) -> &mut u8 {
1439 // Perf - go via u16 to help evade bounds check
1440 // TODO: see if we can use u16 for flag_position in general.
1441 &mut self.codes[usize::from(self.flag_position as u16)]
1442 }
1443
1444 fn plant_flag(&mut self) {
1445 self.flag_position = self.code_position;
1446 self.code_position += 1;
1447 }
1448
1449 fn consume_flag(&mut self) {
1450 self.num_flags_left -= 1;
1451 if self.num_flags_left == 0 {
1452 self.num_flags_left = 8;
1453 self.plant_flag();
1454 }
1455 }
1456}
1457
1458fn compress_lz_codes(
1459 huff: &HuffmanOxide,
1460 output: &mut OutputBufferOxide,
1461 lz_code_buf: &[u8],
1462) -> Result<bool> {
1463 let mut flags = 1;
1464 let mut bb = BitBuffer {
1465 bit_buffer: u64::from(output.bit_buffer),
1466 bits_in: output.bits_in,
1467 };
1468
1469 let mut i: usize = 0;
1470 while i < lz_code_buf.len() {
1471 if flags == 1 {
1472 flags = u32::from(lz_code_buf[i]) | 0x100;
1473 i += 1;
1474 }
1475
1476 // The lz code was a length code
1477 if flags & 1 == 1 {
1478 flags >>= 1;
1479
1480 let sym;
1481 let num_extra_bits;
1482
1483 let match_len = lz_code_buf[i] as usize;
1484
1485 let match_dist = read_u16_le(lz_code_buf, i + 1);
1486
1487 i += 3;
1488
1489 debug_assert!(huff.code_sizes[0][LEN_SYM[match_len] as usize] != 0);
1490 bb.put_fast(
1491 u64::from(huff.codes[0][LEN_SYM[match_len] as usize]),
1492 u32::from(huff.code_sizes[0][LEN_SYM[match_len] as usize]),
1493 );
1494 bb.put_fast(
1495 match_len as u64 & u64::from(BITMASKS[LEN_EXTRA[match_len] as usize]),
1496 u32::from(LEN_EXTRA[match_len]),
1497 );
1498
1499 if match_dist < 512 {
1500 sym = SMALL_DIST_SYM[match_dist as usize] as usize;
1501 num_extra_bits = SMALL_DIST_EXTRA[match_dist as usize] as usize;
1502 } else {
1503 sym = LARGE_DIST_SYM[(match_dist >> 8) as usize] as usize;
1504 num_extra_bits = LARGE_DIST_EXTRA[(match_dist >> 8) as usize] as usize;
1505 }
1506
1507 debug_assert!(huff.code_sizes[1][sym] != 0);
1508 bb.put_fast(
1509 u64::from(huff.codes[1][sym]),
1510 u32::from(huff.code_sizes[1][sym]),
1511 );
1512 bb.put_fast(
1513 u64::from(match_dist) & u64::from(BITMASKS[num_extra_bits]),
1514 num_extra_bits as u32,
1515 );
1516 } else {
1517 // The lz code was a literal
1518 for _ in 0..3 {
1519 flags >>= 1;
1520 let lit = lz_code_buf[i];
1521 i += 1;
1522
1523 debug_assert!(huff.code_sizes[0][lit as usize] != 0);
1524 bb.put_fast(
1525 u64::from(huff.codes[0][lit as usize]),
1526 u32::from(huff.code_sizes[0][lit as usize]),
1527 );
1528
1529 if flags & 1 == 1 || i >= lz_code_buf.len() {
1530 break;
1531 }
1532 }
1533 }
1534
1535 bb.flush(output)?;
1536 }
1537
1538 output.bits_in = 0;
1539 output.bit_buffer = 0;
1540 while bb.bits_in != 0 {
1541 let n = cmp::min(bb.bits_in, 16);
1542 output.put_bits(bb.bit_buffer as u32 & BITMASKS[n as usize], n);
1543 bb.bit_buffer >>= n;
1544 bb.bits_in -= n;
1545 }
1546
1547 // Output the end of block symbol.
1548 output.put_bits(
1549 u32::from(huff.codes[0][256]),
1550 u32::from(huff.code_sizes[0][256]),
1551 );
1552
1553 Ok(true)
1554}
1555
1556fn compress_block(
1557 huff: &mut HuffmanOxide,
1558 output: &mut OutputBufferOxide,
1559 lz: &LZOxide,
1560 static_block: bool,
1561) -> Result<bool> {
1562 if static_block {
1563 huff.start_static_block(output);
1564 } else {
1565 huff.start_dynamic_block(output)?;
1566 }
1567
1568 compress_lz_codes(huff, output, &lz.codes[..lz.code_position])
1569}
1570
1571pub(crate) fn flush_block(
1572 d: &mut CompressorOxide,
1573 callback: &mut CallbackOxide,
1574 flush: TDEFLFlush,
1575) -> Result<i32> {
1576 let mut saved_buffer;
1577 {
1578 let mut output = callback
1579 .out
1580 .new_output_buffer(&mut d.params.local_buf.b, d.params.out_buf_ofs);
1581 output.bit_buffer = d.params.saved_bit_buffer;
1582 output.bits_in = d.params.saved_bits_in;
1583
1584 // TODO: Don't think this second condition should be here but need to verify.
1585 let use_raw_block = (d.params.flags & TDEFL_FORCE_ALL_RAW_BLOCKS != 0)
1586 && (d.dict.lookahead_pos - d.dict.code_buf_dict_pos) <= d.dict.size;
1587 debug_assert_eq!(
1588 use_raw_block,
1589 d.params.flags & TDEFL_FORCE_ALL_RAW_BLOCKS != 0
1590 );
1591
1592 assert!(d.params.flush_remaining == 0);
1593 d.params.flush_ofs = 0;
1594 d.params.flush_remaining = 0;
1595
1596 d.lz.init_flag();
1597
1598 // If we are at the start of the stream, write the zlib header if requested.
1599 if d.params.flags & TDEFL_WRITE_ZLIB_HEADER != 0 && d.params.block_index == 0 {
1600 let header = zlib::header_from_flags(d.params.flags);
1601 output.put_bits_no_flush(header[0].into(), 8);
1602 output.put_bits(header[1].into(), 8);
1603 }
1604
1605 // Output the block header.
1606 output.put_bits((flush == TDEFLFlush::Finish) as u32, 1);
1607
1608 saved_buffer = output.save();
1609
1610 let comp_success = if !use_raw_block {
1611 let use_static =
1612 (d.params.flags & TDEFL_FORCE_ALL_STATIC_BLOCKS != 0) || (d.lz.total_bytes < 48);
1613 compress_block(&mut d.huff, &mut output, &d.lz, use_static)?
1614 } else {
1615 false
1616 };
1617
1618 // If we failed to compress anything and the output would take up more space than the output
1619 // data, output a stored block instead, which has at most 5 bytes of overhead.
1620 // We only use some simple heuristics for now.
1621 // A stored block will have an overhead of at least 4 bytes containing the block length
1622 // but usually more due to the length parameters having to start at a byte boundary and thus
1623 // requiring up to 5 bytes of padding.
1624 // As a static block will have an overhead of at most 1 bit per byte
1625 // (as literals are either 8 or 9 bytes), a raw block will
1626 // never take up less space if the number of input bytes are less than 32.
1627 let expanded = (d.lz.total_bytes > 32)
1628 && (output.inner_pos - saved_buffer.pos + 1 >= (d.lz.total_bytes as usize))
1629 && (d.dict.lookahead_pos - d.dict.code_buf_dict_pos <= d.dict.size);
1630
1631 if use_raw_block || expanded {
1632 output.load(saved_buffer);
1633
1634 // Block header.
1635 output.put_bits(0, 2);
1636
1637 // Block length has to start on a byte boundary, s opad.
1638 output.pad_to_bytes();
1639
1640 // Block length and ones complement of block length.
1641 output.put_bits(d.lz.total_bytes & 0xFFFF, 16);
1642 output.put_bits(!d.lz.total_bytes & 0xFFFF, 16);
1643
1644 // Write the actual bytes.
1645 let start = d.dict.code_buf_dict_pos & LZ_DICT_SIZE_MASK;
1646 let end = (d.dict.code_buf_dict_pos + d.lz.total_bytes as usize) & LZ_DICT_SIZE_MASK;
1647 let dict = &mut d.dict.b.dict;
1648 if start < end {
1649 output.write_bytes(&dict[start..end]);
1650 } else {
1651 output.write_bytes(&dict[start..LZ_DICT_SIZE]);
1652 output.write_bytes(&dict[..end]);
1653 }
1654 } else if !comp_success {
1655 output.load(saved_buffer);
1656 compress_block(&mut d.huff, &mut output, &d.lz, true)?;
1657 }
1658
1659 if flush != TDEFLFlush::None {
1660 if flush == TDEFLFlush::Finish {
1661 output.pad_to_bytes();
1662 if d.params.flags & TDEFL_WRITE_ZLIB_HEADER != 0 {
1663 let mut adler = d.params.adler32;
1664 for _ in 0..4 {
1665 output.put_bits((adler >> 24) & 0xFF, 8);
1666 adler <<= 8;
1667 }
1668 }
1669 } else {
1670 // Sync or Full flush.
1671 // Output an empty raw block.
1672 output.put_bits(0, 3);
1673 output.pad_to_bytes();
1674 output.put_bits(0, 16);
1675 output.put_bits(0xFFFF, 16);
1676 }
1677 }
1678
1679 d.huff.count[0][..MAX_HUFF_SYMBOLS_0].fill(0);
1680 d.huff.count[1][..MAX_HUFF_SYMBOLS_1].fill(0);
1681
1682 // Clear LZ buffer for the next block.
1683 d.lz.code_position = 1;
1684 d.lz.flag_position = 0;
1685 d.lz.num_flags_left = 8;
1686 d.dict.code_buf_dict_pos += d.lz.total_bytes as usize;
1687 d.lz.total_bytes = 0;
1688 d.params.block_index += 1;
1689
1690 saved_buffer = output.save();
1691
1692 d.params.saved_bit_buffer = saved_buffer.bit_buffer;
1693 d.params.saved_bits_in = saved_buffer.bits_in;
1694 }
1695
1696 Ok(callback.flush_output(saved_buffer, &mut d.params))
1697}
1698
1699pub(crate) fn record_literal(h: &mut HuffmanOxide, lz: &mut LZOxide, lit: u8) {
1700 lz.total_bytes += 1;
1701 lz.write_code(val:lit);
1702
1703 *lz.get_flag() >>= 1;
1704 lz.consume_flag();
1705
1706 h.count[0][lit as usize] += 1;
1707}
1708
1709fn record_match(h: &mut HuffmanOxide, lz: &mut LZOxide, mut match_len: u32, mut match_dist: u32) {
1710 debug_assert!(match_len >= MIN_MATCH_LEN.into());
1711 debug_assert!(match_dist >= 1);
1712 debug_assert!(match_dist as usize <= LZ_DICT_SIZE);
1713
1714 lz.total_bytes += match_len;
1715 match_dist -= 1;
1716 match_len -= u32::from(MIN_MATCH_LEN);
1717 lz.write_code(match_len as u8);
1718 lz.write_code(match_dist as u8);
1719 lz.write_code((match_dist >> 8) as u8);
1720
1721 *lz.get_flag() >>= 1;
1722 *lz.get_flag() |= 0x80;
1723 lz.consume_flag();
1724
1725 let symbol = if match_dist < 512 {
1726 SMALL_DIST_SYM[match_dist as usize]
1727 } else {
1728 LARGE_DIST_SYM[((match_dist >> 8) & 127) as usize]
1729 } as usize;
1730 h.count[1][symbol] += 1;
1731 // Perf - go via u8 to help optimize out bounds check.
1732 h.count[0][LEN_SYM[usize::from(match_len as u8)] as usize] += 1;
1733}
1734
1735fn compress_normal(d: &mut CompressorOxide, callback: &mut CallbackOxide) -> bool {
1736 let mut src_pos = d.params.src_pos;
1737 let in_buf = match callback.in_buf {
1738 None => return true,
1739 Some(in_buf) => in_buf,
1740 };
1741
1742 let mut lookahead_size = d.dict.lookahead_size;
1743 let mut lookahead_pos = d.dict.lookahead_pos;
1744 let mut saved_lit = d.params.saved_lit;
1745 let mut saved_match_dist = d.params.saved_match_dist;
1746 let mut saved_match_len = d.params.saved_match_len;
1747
1748 while src_pos < in_buf.len() || (d.params.flush != TDEFLFlush::None && lookahead_size != 0) {
1749 let src_buf_left = in_buf.len() - src_pos;
1750 let num_bytes_to_process = cmp::min(src_buf_left, MAX_MATCH_LEN - lookahead_size);
1751
1752 if lookahead_size + d.dict.size >= usize::from(MIN_MATCH_LEN) - 1
1753 && num_bytes_to_process > 0
1754 {
1755 let dictb = &mut d.dict.b;
1756
1757 let mut dst_pos = (lookahead_pos + lookahead_size) & LZ_DICT_SIZE_MASK;
1758 let mut ins_pos = lookahead_pos + lookahead_size - 2;
1759 // Start the hash value from the first two bytes
1760 let mut hash = update_hash(
1761 u16::from(dictb.dict[ins_pos & LZ_DICT_SIZE_MASK]),
1762 dictb.dict[(ins_pos + 1) & LZ_DICT_SIZE_MASK],
1763 );
1764
1765 lookahead_size += num_bytes_to_process;
1766
1767 for &c in &in_buf[src_pos..src_pos + num_bytes_to_process] {
1768 // Add byte to input buffer.
1769 dictb.dict[dst_pos] = c;
1770 if dst_pos < MAX_MATCH_LEN - 1 {
1771 dictb.dict[LZ_DICT_SIZE + dst_pos] = c;
1772 }
1773
1774 // Generate hash from the current byte,
1775 hash = update_hash(hash, c);
1776 dictb.next[ins_pos & LZ_DICT_SIZE_MASK] = dictb.hash[hash as usize];
1777 // and insert it into the hash chain.
1778 dictb.hash[hash as usize] = ins_pos as u16;
1779 dst_pos = (dst_pos + 1) & LZ_DICT_SIZE_MASK;
1780 ins_pos += 1;
1781 }
1782 src_pos += num_bytes_to_process;
1783 } else {
1784 let dictb = &mut d.dict.b;
1785 for &c in &in_buf[src_pos..src_pos + num_bytes_to_process] {
1786 let dst_pos = (lookahead_pos + lookahead_size) & LZ_DICT_SIZE_MASK;
1787 dictb.dict[dst_pos] = c;
1788 if dst_pos < MAX_MATCH_LEN - 1 {
1789 dictb.dict[LZ_DICT_SIZE + dst_pos] = c;
1790 }
1791
1792 lookahead_size += 1;
1793 if lookahead_size + d.dict.size >= MIN_MATCH_LEN.into() {
1794 let ins_pos = lookahead_pos + lookahead_size - 3;
1795 let hash = ((u32::from(dictb.dict[ins_pos & LZ_DICT_SIZE_MASK])
1796 << (LZ_HASH_SHIFT * 2))
1797 ^ ((u32::from(dictb.dict[(ins_pos + 1) & LZ_DICT_SIZE_MASK])
1798 << LZ_HASH_SHIFT)
1799 ^ u32::from(c)))
1800 & (LZ_HASH_SIZE as u32 - 1);
1801
1802 dictb.next[ins_pos & LZ_DICT_SIZE_MASK] = dictb.hash[hash as usize];
1803 dictb.hash[hash as usize] = ins_pos as u16;
1804 }
1805 }
1806
1807 src_pos += num_bytes_to_process;
1808 }
1809
1810 d.dict.size = cmp::min(LZ_DICT_SIZE - lookahead_size, d.dict.size);
1811 if d.params.flush == TDEFLFlush::None && lookahead_size < MAX_MATCH_LEN {
1812 break;
1813 }
1814
1815 let mut len_to_move = 1;
1816 let mut cur_match_dist = 0;
1817 let mut cur_match_len = if saved_match_len != 0 {
1818 saved_match_len
1819 } else {
1820 u32::from(MIN_MATCH_LEN) - 1
1821 };
1822 let cur_pos = lookahead_pos & LZ_DICT_SIZE_MASK;
1823 if d.params.flags & TDEFL_RLE_MATCHES != 0 {
1824 // If TDEFL_RLE_MATCHES is set, we only look for repeating sequences of the current byte.
1825 if d.dict.size != 0 {
1826 let c = d.dict.b.dict[(cur_pos.wrapping_sub(1)) & LZ_DICT_SIZE_MASK];
1827 cur_match_len = d.dict.b.dict[cur_pos..(cur_pos + lookahead_size)]
1828 .iter()
1829 .take_while(|&x| *x == c)
1830 .count() as u32;
1831 if cur_match_len < MIN_MATCH_LEN.into() {
1832 cur_match_len = 0
1833 } else {
1834 cur_match_dist = 1
1835 }
1836 }
1837 } else {
1838 // Try to find a match for the bytes at the current position.
1839 let dist_len = d.dict.find_match(
1840 lookahead_pos,
1841 d.dict.size,
1842 lookahead_size as u32,
1843 cur_match_dist,
1844 cur_match_len,
1845 );
1846 cur_match_dist = dist_len.0;
1847 cur_match_len = dist_len.1;
1848 }
1849
1850 let far_and_small = cur_match_len == MIN_MATCH_LEN.into() && cur_match_dist >= 8 * 1024;
1851 let filter_small = d.params.flags & TDEFL_FILTER_MATCHES != 0 && cur_match_len <= 5;
1852 if far_and_small || filter_small || cur_pos == cur_match_dist as usize {
1853 cur_match_dist = 0;
1854 cur_match_len = 0;
1855 }
1856
1857 if saved_match_len != 0 {
1858 if cur_match_len > saved_match_len {
1859 record_literal(&mut d.huff, &mut d.lz, saved_lit);
1860 if cur_match_len >= 128 {
1861 record_match(&mut d.huff, &mut d.lz, cur_match_len, cur_match_dist);
1862 saved_match_len = 0;
1863 len_to_move = cur_match_len as usize;
1864 } else {
1865 saved_lit = d.dict.b.dict[cur_pos];
1866 saved_match_dist = cur_match_dist;
1867 saved_match_len = cur_match_len;
1868 }
1869 } else {
1870 record_match(&mut d.huff, &mut d.lz, saved_match_len, saved_match_dist);
1871 len_to_move = (saved_match_len - 1) as usize;
1872 saved_match_len = 0;
1873 }
1874 } else if cur_match_dist == 0 {
1875 record_literal(
1876 &mut d.huff,
1877 &mut d.lz,
1878 d.dict.b.dict[cmp::min(cur_pos, d.dict.b.dict.len() - 1)],
1879 );
1880 } else if d.params.greedy_parsing
1881 || (d.params.flags & TDEFL_RLE_MATCHES != 0)
1882 || cur_match_len >= 128
1883 {
1884 // If we are using lazy matching, check for matches at the next byte if the current
1885 // match was shorter than 128 bytes.
1886 record_match(&mut d.huff, &mut d.lz, cur_match_len, cur_match_dist);
1887 len_to_move = cur_match_len as usize;
1888 } else {
1889 saved_lit = d.dict.b.dict[cmp::min(cur_pos, d.dict.b.dict.len() - 1)];
1890 saved_match_dist = cur_match_dist;
1891 saved_match_len = cur_match_len;
1892 }
1893
1894 lookahead_pos += len_to_move;
1895 assert!(lookahead_size >= len_to_move);
1896 lookahead_size -= len_to_move;
1897 d.dict.size = cmp::min(d.dict.size + len_to_move, LZ_DICT_SIZE);
1898
1899 let lz_buf_tight = d.lz.code_position > LZ_CODE_BUF_SIZE - 8;
1900 let fat = ((d.lz.code_position * 115) >> 7) >= d.lz.total_bytes as usize;
1901 let buf_fat = (d.lz.total_bytes > 31 * 1024) && fat;
1902
1903 if lz_buf_tight || buf_fat {
1904 d.params.src_pos = src_pos;
1905 // These values are used in flush_block, so we need to write them back here.
1906 d.dict.lookahead_size = lookahead_size;
1907 d.dict.lookahead_pos = lookahead_pos;
1908
1909 let n = flush_block(d, callback, TDEFLFlush::None)
1910 .unwrap_or(TDEFLStatus::PutBufFailed as i32);
1911 if n != 0 {
1912 d.params.saved_lit = saved_lit;
1913 d.params.saved_match_dist = saved_match_dist;
1914 d.params.saved_match_len = saved_match_len;
1915 return n > 0;
1916 }
1917 }
1918 }
1919
1920 d.params.src_pos = src_pos;
1921 d.dict.lookahead_size = lookahead_size;
1922 d.dict.lookahead_pos = lookahead_pos;
1923 d.params.saved_lit = saved_lit;
1924 d.params.saved_match_dist = saved_match_dist;
1925 d.params.saved_match_len = saved_match_len;
1926 true
1927}
1928
1929const COMP_FAST_LOOKAHEAD_SIZE: usize = 4096;
1930
1931fn compress_fast(d: &mut CompressorOxide, callback: &mut CallbackOxide) -> bool {
1932 let mut src_pos = d.params.src_pos;
1933 let mut lookahead_size = d.dict.lookahead_size;
1934 let mut lookahead_pos = d.dict.lookahead_pos;
1935
1936 let mut cur_pos = lookahead_pos & LZ_DICT_SIZE_MASK;
1937 let in_buf = match callback.in_buf {
1938 None => return true,
1939 Some(in_buf) => in_buf,
1940 };
1941
1942 debug_assert!(d.lz.code_position < LZ_CODE_BUF_SIZE - 2);
1943
1944 while src_pos < in_buf.len() || (d.params.flush != TDEFLFlush::None && lookahead_size > 0) {
1945 let mut dst_pos = (lookahead_pos + lookahead_size) & LZ_DICT_SIZE_MASK;
1946 let mut num_bytes_to_process = cmp::min(
1947 in_buf.len() - src_pos,
1948 COMP_FAST_LOOKAHEAD_SIZE - lookahead_size,
1949 );
1950 lookahead_size += num_bytes_to_process;
1951
1952 while num_bytes_to_process != 0 {
1953 let n = cmp::min(LZ_DICT_SIZE - dst_pos, num_bytes_to_process);
1954 d.dict.b.dict[dst_pos..dst_pos + n].copy_from_slice(&in_buf[src_pos..src_pos + n]);
1955
1956 if dst_pos < MAX_MATCH_LEN - 1 {
1957 let m = cmp::min(n, MAX_MATCH_LEN - 1 - dst_pos);
1958 d.dict.b.dict[dst_pos + LZ_DICT_SIZE..dst_pos + LZ_DICT_SIZE + m]
1959 .copy_from_slice(&in_buf[src_pos..src_pos + m]);
1960 }
1961
1962 src_pos += n;
1963 dst_pos = (dst_pos + n) & LZ_DICT_SIZE_MASK;
1964 num_bytes_to_process -= n;
1965 }
1966
1967 d.dict.size = cmp::min(LZ_DICT_SIZE - lookahead_size, d.dict.size);
1968 if d.params.flush == TDEFLFlush::None && lookahead_size < COMP_FAST_LOOKAHEAD_SIZE {
1969 break;
1970 }
1971
1972 while lookahead_size >= 4 {
1973 let mut cur_match_len = 1;
1974
1975 let first_trigram = d.dict.read_unaligned_u32(cur_pos) & 0xFF_FFFF;
1976
1977 let hash = (first_trigram ^ (first_trigram >> (24 - (LZ_HASH_BITS - 8))))
1978 & LEVEL1_HASH_SIZE_MASK;
1979
1980 let mut probe_pos = usize::from(d.dict.b.hash[hash as usize]);
1981 d.dict.b.hash[hash as usize] = lookahead_pos as u16;
1982
1983 let mut cur_match_dist = (lookahead_pos - probe_pos) as u16;
1984 if cur_match_dist as usize <= d.dict.size {
1985 probe_pos &= LZ_DICT_SIZE_MASK;
1986
1987 let trigram = d.dict.read_unaligned_u32(probe_pos) & 0xFF_FFFF;
1988
1989 if first_trigram == trigram {
1990 // Trigram was tested, so we can start with "+ 3" displacement.
1991 let mut p = cur_pos + 3;
1992 let mut q = probe_pos + 3;
1993 cur_match_len = (|| {
1994 for _ in 0..32 {
1995 let p_data: u64 = d.dict.read_unaligned_u64(p);
1996 let q_data: u64 = d.dict.read_unaligned_u64(q);
1997 let xor_data = p_data ^ q_data;
1998 if xor_data == 0 {
1999 p += 8;
2000 q += 8;
2001 } else {
2002 let trailing = xor_data.trailing_zeros();
2003 return p as u32 - cur_pos as u32 + (trailing >> 3);
2004 }
2005 }
2006
2007 if cur_match_dist == 0 {
2008 0
2009 } else {
2010 MAX_MATCH_LEN as u32
2011 }
2012 })();
2013
2014 if cur_match_len < MIN_MATCH_LEN.into()
2015 || (cur_match_len == MIN_MATCH_LEN.into() && cur_match_dist >= 8 * 1024)
2016 {
2017 let lit = first_trigram as u8;
2018 cur_match_len = 1;
2019 d.lz.write_code(lit);
2020 *d.lz.get_flag() >>= 1;
2021 d.huff.count[0][lit as usize] += 1;
2022 } else {
2023 // Limit the match to the length of the lookahead so we don't create a match
2024 // that ends after the end of the input data.
2025 cur_match_len = cmp::min(cur_match_len, lookahead_size as u32);
2026 debug_assert!(cur_match_len >= MIN_MATCH_LEN.into());
2027 debug_assert!(cur_match_dist >= 1);
2028 debug_assert!(cur_match_dist as usize <= LZ_DICT_SIZE);
2029 cur_match_dist -= 1;
2030
2031 d.lz.write_code((cur_match_len - u32::from(MIN_MATCH_LEN)) as u8);
2032 d.lz.write_code(cur_match_dist as u8);
2033 d.lz.write_code((cur_match_dist >> 8) as u8);
2034
2035 *d.lz.get_flag() >>= 1;
2036 *d.lz.get_flag() |= 0x80;
2037 if cur_match_dist < 512 {
2038 d.huff.count[1][SMALL_DIST_SYM[cur_match_dist as usize] as usize] += 1;
2039 } else {
2040 d.huff.count[1]
2041 [LARGE_DIST_SYM[(cur_match_dist >> 8) as usize] as usize] += 1;
2042 }
2043
2044 d.huff.count[0][LEN_SYM[(cur_match_len - u32::from(MIN_MATCH_LEN)) as usize]
2045 as usize] += 1;
2046 }
2047 } else {
2048 d.lz.write_code(first_trigram as u8);
2049 *d.lz.get_flag() >>= 1;
2050 d.huff.count[0][first_trigram as u8 as usize] += 1;
2051 }
2052
2053 d.lz.consume_flag();
2054 d.lz.total_bytes += cur_match_len;
2055 lookahead_pos += cur_match_len as usize;
2056 d.dict.size = cmp::min(d.dict.size + cur_match_len as usize, LZ_DICT_SIZE);
2057 cur_pos = (cur_pos + cur_match_len as usize) & LZ_DICT_SIZE_MASK;
2058 lookahead_size -= cur_match_len as usize;
2059
2060 if d.lz.code_position > LZ_CODE_BUF_SIZE - 8 {
2061 // These values are used in flush_block, so we need to write them back here.
2062 d.dict.lookahead_size = lookahead_size;
2063 d.dict.lookahead_pos = lookahead_pos;
2064
2065 let n = match flush_block(d, callback, TDEFLFlush::None) {
2066 Err(_) => {
2067 d.params.src_pos = src_pos;
2068 d.params.prev_return_status = TDEFLStatus::PutBufFailed;
2069 return false;
2070 }
2071 Ok(status) => status,
2072 };
2073 if n != 0 {
2074 d.params.src_pos = src_pos;
2075 return n > 0;
2076 }
2077 debug_assert!(d.lz.code_position < LZ_CODE_BUF_SIZE - 2);
2078
2079 lookahead_size = d.dict.lookahead_size;
2080 lookahead_pos = d.dict.lookahead_pos;
2081 }
2082 }
2083 }
2084
2085 while lookahead_size != 0 {
2086 let lit = d.dict.b.dict[cur_pos];
2087 d.lz.total_bytes += 1;
2088 d.lz.write_code(lit);
2089 *d.lz.get_flag() >>= 1;
2090 d.lz.consume_flag();
2091
2092 d.huff.count[0][lit as usize] += 1;
2093 lookahead_pos += 1;
2094 d.dict.size = cmp::min(d.dict.size + 1, LZ_DICT_SIZE);
2095 cur_pos = (cur_pos + 1) & LZ_DICT_SIZE_MASK;
2096 lookahead_size -= 1;
2097
2098 if d.lz.code_position > LZ_CODE_BUF_SIZE - 8 {
2099 // These values are used in flush_block, so we need to write them back here.
2100 d.dict.lookahead_size = lookahead_size;
2101 d.dict.lookahead_pos = lookahead_pos;
2102
2103 let n = match flush_block(d, callback, TDEFLFlush::None) {
2104 Err(_) => {
2105 d.params.prev_return_status = TDEFLStatus::PutBufFailed;
2106 d.params.src_pos = src_pos;
2107 return false;
2108 }
2109 Ok(status) => status,
2110 };
2111 if n != 0 {
2112 d.params.src_pos = src_pos;
2113 return n > 0;
2114 }
2115
2116 lookahead_size = d.dict.lookahead_size;
2117 lookahead_pos = d.dict.lookahead_pos;
2118 }
2119 }
2120 }
2121
2122 d.params.src_pos = src_pos;
2123 d.dict.lookahead_size = lookahead_size;
2124 d.dict.lookahead_pos = lookahead_pos;
2125 true
2126}
2127
2128fn flush_output_buffer(c: &mut CallbackOxide, p: &mut ParamsOxide) -> (TDEFLStatus, usize, usize) {
2129 let mut res: (TDEFLStatus, usize, usize) = (TDEFLStatus::Okay, p.src_pos, 0);
2130 if let CallbackOut::Buf(ref mut cb: &mut CallbackBuf<'_>) = c.out {
2131 let n: usize = cmp::min(v1:cb.out_buf.len() - p.out_buf_ofs, v2:p.flush_remaining as usize);
2132 if n != 0 {
2133 cb.out_buf[p.out_buf_ofs..p.out_buf_ofs + n]
2134 .copy_from_slice(&p.local_buf.b[p.flush_ofs as usize..p.flush_ofs as usize + n]);
2135 }
2136 p.flush_ofs += n as u32;
2137 p.flush_remaining -= n as u32;
2138 p.out_buf_ofs += n;
2139 res.2 = p.out_buf_ofs;
2140 }
2141
2142 if p.finished && p.flush_remaining == 0 {
2143 res.0 = TDEFLStatus::Done
2144 }
2145 res
2146}
2147
2148/// Main compression function. Tries to compress as much as possible from `in_buf` and
2149/// puts compressed output into `out_buf`.
2150///
2151/// The value of `flush` determines if the compressor should attempt to flush all output
2152/// and alternatively try to finish the stream.
2153///
2154/// Use [`TDEFLFlush::Finish`] on the final call to signal that the stream is finishing.
2155///
2156/// Note that this function does not keep track of whether a flush marker has been output, so
2157/// if called using [`TDEFLFlush::Sync`], the caller needs to ensure there is enough space in the
2158/// output buffer if they want to avoid repeated flush markers.
2159/// See #105 for details.
2160///
2161/// # Returns
2162/// Returns a tuple containing the current status of the compressor, the current position
2163/// in the input buffer and the current position in the output buffer.
2164pub fn compress(
2165 d: &mut CompressorOxide,
2166 in_buf: &[u8],
2167 out_buf: &mut [u8],
2168 flush: TDEFLFlush,
2169) -> (TDEFLStatus, usize, usize) {
2170 compress_inner(
2171 d,
2172 &mut CallbackOxide::new_callback_buf(in_buf, out_buf),
2173 flush,
2174 )
2175}
2176
2177/// Main compression function. Callbacks output.
2178///
2179/// # Returns
2180/// Returns a tuple containing the current status of the compressor, the current position
2181/// in the input buffer.
2182///
2183/// The caller is responsible for ensuring the `CallbackFunc` struct will not cause undefined
2184/// behaviour.
2185pub fn compress_to_output(
2186 d: &mut CompressorOxide,
2187 in_buf: &[u8],
2188 flush: TDEFLFlush,
2189 mut callback_func: impl FnMut(&[u8]) -> bool,
2190) -> (TDEFLStatus, usize) {
2191 let res: (TDEFLStatus, usize, usize) = compress_inner(
2192 d,
2193 &mut CallbackOxide::new_callback_func(
2194 in_buf,
2195 CallbackFunc {
2196 put_buf_func: &mut callback_func,
2197 },
2198 ),
2199 flush,
2200 );
2201
2202 (res.0, res.1)
2203}
2204
2205fn compress_inner(
2206 d: &mut CompressorOxide,
2207 callback: &mut CallbackOxide,
2208 flush: TDEFLFlush,
2209) -> (TDEFLStatus, usize, usize) {
2210 d.params.out_buf_ofs = 0;
2211 d.params.src_pos = 0;
2212
2213 let prev_ok = d.params.prev_return_status == TDEFLStatus::Okay;
2214 let flush_finish_once = d.params.flush != TDEFLFlush::Finish || flush == TDEFLFlush::Finish;
2215
2216 d.params.flush = flush;
2217 if !prev_ok || !flush_finish_once {
2218 d.params.prev_return_status = TDEFLStatus::BadParam;
2219 return (d.params.prev_return_status, 0, 0);
2220 }
2221
2222 if d.params.flush_remaining != 0 || d.params.finished {
2223 let res = flush_output_buffer(callback, &mut d.params);
2224 d.params.prev_return_status = res.0;
2225 return res;
2226 }
2227
2228 let one_probe = d.params.flags & MAX_PROBES_MASK as u32 == 1;
2229 let greedy = d.params.flags & TDEFL_GREEDY_PARSING_FLAG != 0;
2230 let filter_or_rle = d.params.flags & (TDEFL_FILTER_MATCHES | TDEFL_FORCE_ALL_RAW_BLOCKS) != 0;
2231
2232 let raw = d.params.flags & TDEFL_FORCE_ALL_RAW_BLOCKS != 0;
2233
2234 let compress_success = if raw {
2235 compress_stored(d, callback)
2236 } else if one_probe && greedy && !filter_or_rle {
2237 compress_fast(d, callback)
2238 } else {
2239 compress_normal(d, callback)
2240 };
2241
2242 if !compress_success {
2243 return (
2244 d.params.prev_return_status,
2245 d.params.src_pos,
2246 d.params.out_buf_ofs,
2247 );
2248 }
2249
2250 if let Some(in_buf) = callback.in_buf {
2251 if d.params.flags & (TDEFL_WRITE_ZLIB_HEADER | TDEFL_COMPUTE_ADLER32) != 0 {
2252 d.params.adler32 = update_adler32(d.params.adler32, &in_buf[..d.params.src_pos]);
2253 }
2254 }
2255
2256 let flush_none = d.params.flush == TDEFLFlush::None;
2257 let in_left = callback.in_buf.map_or(0, |buf| buf.len()) - d.params.src_pos;
2258 let remaining = in_left != 0 || d.params.flush_remaining != 0;
2259 if !flush_none && d.dict.lookahead_size == 0 && !remaining {
2260 let flush = d.params.flush;
2261 match flush_block(d, callback, flush) {
2262 Err(_) => {
2263 d.params.prev_return_status = TDEFLStatus::PutBufFailed;
2264 return (
2265 d.params.prev_return_status,
2266 d.params.src_pos,
2267 d.params.out_buf_ofs,
2268 );
2269 }
2270 Ok(x) if x < 0 => {
2271 return (
2272 d.params.prev_return_status,
2273 d.params.src_pos,
2274 d.params.out_buf_ofs,
2275 )
2276 }
2277 _ => {
2278 d.params.finished = d.params.flush == TDEFLFlush::Finish;
2279 if d.params.flush == TDEFLFlush::Full {
2280 d.dict.b.hash.fill(0);
2281 d.dict.b.next.fill(0);
2282 d.dict.size = 0;
2283 }
2284 }
2285 }
2286 }
2287
2288 let res = flush_output_buffer(callback, &mut d.params);
2289 d.params.prev_return_status = res.0;
2290
2291 res
2292}
2293
2294/// Create a set of compression flags using parameters used by zlib and other compressors.
2295/// Mainly intended for use with transition from c libraries as it deals with raw integers.
2296///
2297/// # Parameters
2298/// `level` determines compression level. Clamped to maximum of 10. Negative values result in
2299/// `CompressionLevel::DefaultLevel`.
2300/// `window_bits`: Above 0, wraps the stream in a zlib wrapper, 0 or negative for a raw deflate
2301/// stream.
2302/// `strategy`: Sets the strategy if this conforms to any of the values in `CompressionStrategy`.
2303///
2304/// # Notes
2305/// This function may be removed or moved to the `miniz_oxide_c_api` in the future.
2306pub fn create_comp_flags_from_zip_params(level: i32, window_bits: i32, strategy: i32) -> u32 {
2307 let num_probes = (if level >= 0 {
2308 cmp::min(10, level)
2309 } else {
2310 CompressionLevel::DefaultLevel as i32
2311 }) as usize;
2312 let greedy = if level <= 3 {
2313 TDEFL_GREEDY_PARSING_FLAG
2314 } else {
2315 0
2316 };
2317 let mut comp_flags = NUM_PROBES[num_probes] | greedy;
2318
2319 if window_bits > 0 {
2320 comp_flags |= TDEFL_WRITE_ZLIB_HEADER;
2321 }
2322
2323 if level == 0 {
2324 comp_flags |= TDEFL_FORCE_ALL_RAW_BLOCKS;
2325 } else if strategy == CompressionStrategy::Filtered as i32 {
2326 comp_flags |= TDEFL_FILTER_MATCHES;
2327 } else if strategy == CompressionStrategy::HuffmanOnly as i32 {
2328 comp_flags &= !MAX_PROBES_MASK as u32;
2329 } else if strategy == CompressionStrategy::Fixed as i32 {
2330 comp_flags |= TDEFL_FORCE_ALL_STATIC_BLOCKS;
2331 } else if strategy == CompressionStrategy::RLE as i32 {
2332 comp_flags |= TDEFL_RLE_MATCHES;
2333 }
2334
2335 comp_flags
2336}
2337
2338#[cfg(test)]
2339mod test {
2340 use super::{
2341 compress_to_output, create_comp_flags_from_zip_params, read_u16_le, write_u16_le,
2342 CompressionStrategy, CompressorOxide, TDEFLFlush, TDEFLStatus, DEFAULT_FLAGS,
2343 MZ_DEFAULT_WINDOW_BITS,
2344 };
2345 use crate::inflate::decompress_to_vec;
2346 use alloc::vec;
2347
2348 #[test]
2349 fn u16_to_slice() {
2350 let mut slice = [0, 0];
2351 write_u16_le(2000, &mut slice, 0);
2352 assert_eq!(slice, [208, 7]);
2353 }
2354
2355 #[test]
2356 fn u16_from_slice() {
2357 let slice = [208, 7];
2358 assert_eq!(read_u16_le(&slice, 0), 2000);
2359 }
2360
2361 #[test]
2362 fn compress_output() {
2363 assert_eq!(
2364 DEFAULT_FLAGS,
2365 create_comp_flags_from_zip_params(
2366 4,
2367 MZ_DEFAULT_WINDOW_BITS,
2368 CompressionStrategy::Default as i32
2369 )
2370 );
2371
2372 let slice = [
2373 1, 2, 3, 4, 1, 2, 3, 1, 2, 3, 1, 2, 6, 1, 2, 3, 1, 2, 3, 2, 3, 1, 2, 3,
2374 ];
2375 let mut encoded = vec![];
2376 let flags = create_comp_flags_from_zip_params(6, 0, 0);
2377 let mut d = CompressorOxide::new(flags);
2378 let (status, in_consumed) =
2379 compress_to_output(&mut d, &slice, TDEFLFlush::Finish, |out: &[u8]| {
2380 encoded.extend_from_slice(out);
2381 true
2382 });
2383
2384 assert_eq!(status, TDEFLStatus::Done);
2385 assert_eq!(in_consumed, slice.len());
2386
2387 let decoded = decompress_to_vec(&encoded[..]).unwrap();
2388 assert_eq!(&decoded[..], &slice[..]);
2389 }
2390
2391 #[test]
2392 /// Check fast compress mode
2393 fn compress_fast() {
2394 let slice = [
2395 1, 2, 3, 4, 1, 2, 3, 1, 2, 3, 1, 2, 6, 1, 2, 3, 1, 2, 3, 2, 3, 1, 2, 3,
2396 ];
2397 let mut encoded = vec![];
2398 let flags = create_comp_flags_from_zip_params(1, 0, 0);
2399 let mut d = CompressorOxide::new(flags);
2400 let (status, in_consumed) =
2401 compress_to_output(&mut d, &slice, TDEFLFlush::Finish, |out: &[u8]| {
2402 encoded.extend_from_slice(out);
2403 true
2404 });
2405
2406 assert_eq!(status, TDEFLStatus::Done);
2407 assert_eq!(in_consumed, slice.len());
2408
2409 // Needs to be altered if algorithm improves.
2410 assert_eq!(
2411 &encoded[..],
2412 [99, 100, 98, 102, 1, 98, 48, 98, 3, 147, 204, 76, 204, 140, 76, 204, 0]
2413 );
2414
2415 let decoded = decompress_to_vec(&encoded[..]).unwrap();
2416 assert_eq!(&decoded[..], &slice[..]);
2417 }
2418
2419 #[test]
2420 fn zlib_window_bits() {
2421 use crate::inflate::stream::{inflate, InflateState};
2422 use crate::DataFormat;
2423 use alloc::boxed::Box;
2424 let slice = [
2425 1, 2, 3, 4, 1, 2, 3, 1, 2, 3, 1, 2, 6, 1, 2, 3, 1, 2, 3, 2, 3, 1, 2, 3, 35, 22, 22, 2,
2426 6, 2, 6,
2427 ];
2428 let mut encoded = vec![];
2429 let flags = create_comp_flags_from_zip_params(2, 1, CompressionStrategy::RLE.into());
2430 let mut d = CompressorOxide::new(flags);
2431 let (status, in_consumed) =
2432 compress_to_output(&mut d, &slice, TDEFLFlush::Finish, |out: &[u8]| {
2433 encoded.extend_from_slice(out);
2434 true
2435 });
2436
2437 assert_eq!(status, TDEFLStatus::Done);
2438 assert_eq!(in_consumed, slice.len());
2439
2440 let mut output = vec![0; slice.len()];
2441
2442 let mut decompressor = Box::new(InflateState::new(DataFormat::Zlib));
2443
2444 let mut out_slice = output.as_mut_slice();
2445 // Feed 1 byte at a time and no back buffer to test that RLE encoding has been used.
2446 for i in 0..encoded.len() {
2447 let result = inflate(
2448 &mut decompressor,
2449 &encoded[i..i + 1],
2450 out_slice,
2451 crate::MZFlush::None,
2452 );
2453 out_slice = &mut out_slice[result.bytes_written..];
2454 }
2455 let cmf = decompressor.decompressor().zlib_header().0;
2456 assert_eq!(cmf, 8);
2457 assert_eq!(output, slice)
2458 }
2459}
2460