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