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 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/*
125fn 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