miniz_oxide/deflate/stored.rs
1use crate::deflate::buffer::{update_hash, LZ_HASH_SHIFT, LZ_HASH_SIZE};
2use crate::deflate::core::{
3 flush_block, CallbackOxide, CompressorOxide, TDEFLFlush, TDEFLStatus, LZ_DICT_SIZE,
4 LZ_DICT_SIZE_MASK, MAX_MATCH_LEN, MIN_MATCH_LEN,
5};
6use core::cmp;
7
8pub(crate) fn compress_stored(d: &mut CompressorOxide, callback: &mut CallbackOxide) -> bool {
9 let in_buf = match callback.buf() {
10 None => return true,
11 Some(in_buf) => in_buf,
12 };
13
14 // Make sure this is cleared in case compression level is switched later.
15 // TODO: It's possible we don't need this or could do this elsewhere later
16 // but just do this here to avoid causing issues for now.
17 d.params.saved_match_len = 0;
18 let mut bytes_written = d.lz.total_bytes;
19 let mut src_pos = d.params.src_pos;
20 let mut lookahead_size = d.dict.lookahead_size;
21 let mut lookahead_pos = d.dict.lookahead_pos;
22
23 while src_pos < in_buf.len() || (d.params.flush != TDEFLFlush::None && lookahead_size != 0) {
24 let src_buf_left = in_buf.len() - src_pos;
25 let num_bytes_to_process = cmp::min(src_buf_left, MAX_MATCH_LEN - lookahead_size);
26
27 if lookahead_size + d.dict.size >= usize::from(MIN_MATCH_LEN) - 1
28 && num_bytes_to_process > 0
29 {
30 let dictb = &mut d.dict.b;
31
32 let mut dst_pos = (lookahead_pos + lookahead_size) & LZ_DICT_SIZE_MASK;
33 let mut ins_pos = lookahead_pos + lookahead_size - 2;
34 // Start the hash value from the first two bytes
35 let mut hash = update_hash(
36 u16::from(dictb.dict[ins_pos & LZ_DICT_SIZE_MASK]),
37 dictb.dict[(ins_pos + 1) & LZ_DICT_SIZE_MASK],
38 );
39
40 lookahead_size += num_bytes_to_process;
41
42 for &c in &in_buf[src_pos..src_pos + num_bytes_to_process] {
43 // Add byte to input buffer.
44 dictb.dict[dst_pos] = c;
45 if dst_pos < MAX_MATCH_LEN - 1 {
46 dictb.dict[LZ_DICT_SIZE + dst_pos] = c;
47 }
48
49 // Generate hash from the current byte,
50 hash = update_hash(hash, c);
51 dictb.next[ins_pos & LZ_DICT_SIZE_MASK] = dictb.hash[hash as usize];
52 // and insert it into the hash chain.
53 dictb.hash[hash as usize] = ins_pos as u16;
54 dst_pos = (dst_pos + 1) & LZ_DICT_SIZE_MASK;
55 ins_pos += 1;
56 }
57 src_pos += num_bytes_to_process;
58 } else {
59 let dictb = &mut d.dict.b;
60 for &c in &in_buf[src_pos..src_pos + num_bytes_to_process] {
61 let dst_pos = (lookahead_pos + lookahead_size) & LZ_DICT_SIZE_MASK;
62 dictb.dict[dst_pos] = c;
63 if dst_pos < MAX_MATCH_LEN - 1 {
64 dictb.dict[LZ_DICT_SIZE + dst_pos] = c;
65 }
66
67 lookahead_size += 1;
68 if lookahead_size + d.dict.size >= MIN_MATCH_LEN.into() {
69 let ins_pos = lookahead_pos + lookahead_size - 3;
70 let hash = ((u32::from(dictb.dict[ins_pos & LZ_DICT_SIZE_MASK])
71 << (LZ_HASH_SHIFT * 2))
72 ^ ((u32::from(dictb.dict[(ins_pos + 1) & LZ_DICT_SIZE_MASK])
73 << LZ_HASH_SHIFT)
74 ^ u32::from(c)))
75 & (LZ_HASH_SIZE as u32 - 1);
76
77 dictb.next[ins_pos & LZ_DICT_SIZE_MASK] = dictb.hash[hash as usize];
78 dictb.hash[hash as usize] = ins_pos as u16;
79 }
80 }
81
82 src_pos += num_bytes_to_process;
83 }
84
85 d.dict.size = cmp::min(LZ_DICT_SIZE - lookahead_size, d.dict.size);
86 if d.params.flush == TDEFLFlush::None && lookahead_size < MAX_MATCH_LEN {
87 break;
88 }
89
90 let len_to_move = 1;
91
92 bytes_written += 1;
93
94 lookahead_pos += len_to_move;
95 assert!(lookahead_size >= len_to_move);
96 lookahead_size -= len_to_move;
97 d.dict.size = cmp::min(d.dict.size + len_to_move, LZ_DICT_SIZE);
98
99 if bytes_written > 31 * 1024 {
100 d.lz.total_bytes = bytes_written;
101
102 d.params.src_pos = src_pos;
103 // These values are used in flush_block, so we need to write them back here.
104 d.dict.lookahead_size = lookahead_size;
105 d.dict.lookahead_pos = lookahead_pos;
106
107 let n = flush_block(d, callback, TDEFLFlush::None)
108 .unwrap_or(TDEFLStatus::PutBufFailed as i32);
109 if n != 0 {
110 return n > 0;
111 }
112 bytes_written = d.lz.total_bytes;
113 }
114 }
115
116 d.lz.total_bytes = bytes_written;
117 d.params.src_pos = src_pos;
118 d.dict.lookahead_size = lookahead_size;
119 d.dict.lookahead_pos = lookahead_pos;
120 true
121}
122
123/*
124fn compress_rle(d: &mut CompressorOxide, callback: &mut CallbackOxide) -> bool {
125 let mut src_pos = d.params.src_pos;
126 let in_buf = match callback.in_buf {
127 None => return true,
128 Some(in_buf) => in_buf,
129 };
130
131 let mut lookahead_size = d.dict.lookahead_size;
132 let mut lookahead_pos = d.dict.lookahead_pos;
133 let mut saved_lit = d.params.saved_lit;
134 let mut saved_match_dist = d.params.saved_match_dist;
135 let mut saved_match_len = d.params.saved_match_len;
136
137 while src_pos < in_buf.len() || (d.params.flush != TDEFLFlush::None && lookahead_size != 0) {
138 let src_buf_left = in_buf.len() - src_pos;
139 let num_bytes_to_process = cmp::min(src_buf_left, MAX_MATCH_LEN - lookahead_size);
140
141 if lookahead_size + d.dict.size >= usize::from(MIN_MATCH_LEN) - 1
142 && num_bytes_to_process > 0
143 {
144 let dictb = &mut d.dict.b;
145
146 let mut dst_pos = (lookahead_pos + lookahead_size) & LZ_DICT_SIZE_MASK;
147 let mut ins_pos = lookahead_pos + lookahead_size - 2;
148 // Start the hash value from the first two bytes
149 let mut hash = update_hash(
150 u16::from(dictb.dict[ins_pos & LZ_DICT_SIZE_MASK]),
151 dictb.dict[(ins_pos + 1) & LZ_DICT_SIZE_MASK],
152 );
153
154 lookahead_size += num_bytes_to_process;
155
156 for &c in &in_buf[src_pos..src_pos + num_bytes_to_process] {
157 // Add byte to input buffer.
158 dictb.dict[dst_pos] = c;
159 if dst_pos < MAX_MATCH_LEN - 1 {
160 dictb.dict[LZ_DICT_SIZE + dst_pos] = c;
161 }
162
163 // Generate hash from the current byte,
164 hash = update_hash(hash, c);
165 dictb.next[ins_pos & LZ_DICT_SIZE_MASK] = dictb.hash[hash as usize];
166 // and insert it into the hash chain.
167 dictb.hash[hash as usize] = ins_pos as u16;
168 dst_pos = (dst_pos + 1) & LZ_DICT_SIZE_MASK;
169 ins_pos += 1;
170 }
171 src_pos += num_bytes_to_process;
172 } else {
173 let dictb = &mut d.dict.b;
174 for &c in &in_buf[src_pos..src_pos + num_bytes_to_process] {
175 let dst_pos = (lookahead_pos + lookahead_size) & LZ_DICT_SIZE_MASK;
176 dictb.dict[dst_pos] = c;
177 if dst_pos < MAX_MATCH_LEN - 1 {
178 dictb.dict[LZ_DICT_SIZE + dst_pos] = c;
179 }
180
181 lookahead_size += 1;
182 if lookahead_size + d.dict.size >= MIN_MATCH_LEN.into() {
183 let ins_pos = lookahead_pos + lookahead_size - 3;
184 let hash = ((u32::from(dictb.dict[ins_pos & LZ_DICT_SIZE_MASK])
185 << (LZ_HASH_SHIFT * 2))
186 ^ ((u32::from(dictb.dict[(ins_pos + 1) & LZ_DICT_SIZE_MASK])
187 << LZ_HASH_SHIFT)
188 ^ u32::from(c)))
189 & (LZ_HASH_SIZE as u32 - 1);
190
191 dictb.next[ins_pos & LZ_DICT_SIZE_MASK] = dictb.hash[hash as usize];
192 dictb.hash[hash as usize] = ins_pos as u16;
193 }
194 }
195
196 src_pos += num_bytes_to_process;
197 }
198
199 d.dict.size = cmp::min(LZ_DICT_SIZE - lookahead_size, d.dict.size);
200 if d.params.flush == TDEFLFlush::None && lookahead_size < MAX_MATCH_LEN {
201 break;
202 }
203
204 let mut len_to_move = 1;
205 let mut cur_match_dist = 0;
206 let mut cur_match_len = if saved_match_len != 0 {
207 saved_match_len
208 } else {
209 u32::from(MIN_MATCH_LEN) - 1
210 };
211 let cur_pos = lookahead_pos & LZ_DICT_SIZE_MASK;
212 // If TDEFL_RLE_MATCHES is set, we only look for repeating sequences of the current byte.
213 if d.dict.size != 0 && d.params.flags & TDEFL_FORCE_ALL_RAW_BLOCKS == 0 {
214 let c = d.dict.b.dict[(cur_pos.wrapping_sub(1)) & LZ_DICT_SIZE_MASK];
215 cur_match_len = d.dict.b.dict[cur_pos..(cur_pos + lookahead_size)]
216 .iter()
217 .take_while(|&x| *x == c)
218 .count() as u32;
219 if cur_match_len < MIN_MATCH_LEN.into() {
220 cur_match_len = 0
221 } else {
222 cur_match_dist = 1
223 }
224 }
225
226
227 let far_and_small = cur_match_len == MIN_MATCH_LEN.into() && cur_match_dist >= 8 * 1024;
228 let filter_small = d.params.flags & TDEFL_FILTER_MATCHES != 0 && cur_match_len <= 5;
229 if far_and_small || filter_small || cur_pos == cur_match_dist as usize {
230 cur_match_dist = 0;
231 cur_match_len = 0;
232 }
233
234 if saved_match_len != 0 {
235 if cur_match_len > saved_match_len {
236 record_literal(&mut d.huff, &mut d.lz, saved_lit);
237 if cur_match_len >= 128 {
238 record_match(&mut d.huff, &mut d.lz, cur_match_len, cur_match_dist);
239 saved_match_len = 0;
240 len_to_move = cur_match_len as usize;
241 } else {
242 saved_lit = d.dict.b.dict[cur_pos];
243 saved_match_dist = cur_match_dist;
244 saved_match_len = cur_match_len;
245 }
246 } else {
247 record_match(&mut d.huff, &mut d.lz, saved_match_len, saved_match_dist);
248 len_to_move = (saved_match_len - 1) as usize;
249 saved_match_len = 0;
250 }
251 } else if cur_match_dist == 0 {
252 record_literal(
253 &mut d.huff,
254 &mut d.lz,
255 d.dict.b.dict[cmp::min(cur_pos, d.dict.b.dict.len() - 1)],
256 );
257 } else if d.params.greedy_parsing
258 || (d.params.flags & TDEFL_RLE_MATCHES != 0)
259 || cur_match_len >= 128
260 {
261 // If we are using lazy matching, check for matches at the next byte if the current
262 // match was shorter than 128 bytes.
263 record_match(&mut d.huff, &mut d.lz, cur_match_len, cur_match_dist);
264 len_to_move = cur_match_len as usize;
265 } else {
266 saved_lit = d.dict.b.dict[cmp::min(cur_pos, d.dict.b.dict.len() - 1)];
267 saved_match_dist = cur_match_dist;
268 saved_match_len = cur_match_len;
269 }
270
271 lookahead_pos += len_to_move;
272 assert!(lookahead_size >= len_to_move);
273 lookahead_size -= len_to_move;
274 d.dict.size = cmp::min(d.dict.size + len_to_move, LZ_DICT_SIZE);
275
276 let lz_buf_tight = d.lz.code_position > LZ_CODE_BUF_SIZE - 8;
277 let raw = d.params.flags & TDEFL_FORCE_ALL_RAW_BLOCKS != 0;
278 let fat = ((d.lz.code_position * 115) >> 7) >= d.lz.total_bytes as usize;
279 let fat_or_raw = (d.lz.total_bytes > 31 * 1024) && (fat || raw);
280
281 if lz_buf_tight || fat_or_raw {
282 d.params.src_pos = src_pos;
283 // These values are used in flush_block, so we need to write them back here.
284 d.dict.lookahead_size = lookahead_size;
285 d.dict.lookahead_pos = lookahead_pos;
286
287 let n = flush_block(d, callback, TDEFLFlush::None)
288 .unwrap_or(TDEFLStatus::PutBufFailed as i32);
289 if n != 0 {
290 d.params.saved_lit = saved_lit;
291 d.params.saved_match_dist = saved_match_dist;
292 d.params.saved_match_len = saved_match_len;
293 return n > 0;
294 }
295 }
296 }
297
298 d.params.src_pos = src_pos;
299 d.dict.lookahead_size = lookahead_size;
300 d.dict.lookahead_pos = lookahead_pos;
301 d.params.saved_lit = saved_lit;
302 d.params.saved_match_dist = saved_match_dist;
303 d.params.saved_match_len = saved_match_len;
304 true
305}*/