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}*/