Revision control
Copy as Markdown
Other Tools
use crate::deflate::buffer::{update_hash, LZ_HASH_SHIFT, LZ_HASH_SIZE};
use crate::deflate::core::{
flush_block, CallbackOxide, CompressorOxide, TDEFLFlush, TDEFLStatus, LZ_DICT_SIZE,
LZ_DICT_SIZE_MASK, MAX_MATCH_LEN, MIN_MATCH_LEN,
};
use core::cmp;
pub(crate) fn compress_stored(d: &mut CompressorOxide, callback: &mut CallbackOxide) -> bool {
let in_buf = match callback.buf() {
None => return true,
Some(in_buf) => in_buf,
};
// Make sure this is cleared in case compression level is switched later.
// TODO: It's possible we don't need this or could do this elsewhere later
// but just do this here to avoid causing issues for now.
d.params.saved_match_len = 0;
let mut bytes_written = d.lz.total_bytes;
let mut src_pos = d.params.src_pos;
let mut lookahead_size = d.dict.lookahead_size;
let mut lookahead_pos = d.dict.lookahead_pos;
while src_pos < in_buf.len() || (d.params.flush != TDEFLFlush::None && lookahead_size != 0) {
let src_buf_left = in_buf.len() - src_pos;
let num_bytes_to_process = cmp::min(src_buf_left, MAX_MATCH_LEN - lookahead_size);
if lookahead_size + d.dict.size >= usize::from(MIN_MATCH_LEN) - 1
&& num_bytes_to_process > 0
{
let dictb = &mut d.dict.b;
let mut dst_pos = (lookahead_pos + lookahead_size) & LZ_DICT_SIZE_MASK;
let mut ins_pos = lookahead_pos + lookahead_size - 2;
// Start the hash value from the first two bytes
let mut hash = update_hash(
u16::from(dictb.dict[ins_pos & LZ_DICT_SIZE_MASK]),
dictb.dict[(ins_pos + 1) & LZ_DICT_SIZE_MASK],
);
lookahead_size += num_bytes_to_process;
for &c in &in_buf[src_pos..src_pos + num_bytes_to_process] {
// Add byte to input buffer.
dictb.dict[dst_pos] = c;
if dst_pos < MAX_MATCH_LEN - 1 {
dictb.dict[LZ_DICT_SIZE + dst_pos] = c;
}
// Generate hash from the current byte,
hash = update_hash(hash, c);
dictb.next[ins_pos & LZ_DICT_SIZE_MASK] = dictb.hash[hash as usize];
// and insert it into the hash chain.
dictb.hash[hash as usize] = ins_pos as u16;
dst_pos = (dst_pos + 1) & LZ_DICT_SIZE_MASK;
ins_pos += 1;
}
src_pos += num_bytes_to_process;
} else {
let dictb = &mut d.dict.b;
for &c in &in_buf[src_pos..src_pos + num_bytes_to_process] {
let dst_pos = (lookahead_pos + lookahead_size) & LZ_DICT_SIZE_MASK;
dictb.dict[dst_pos] = c;
if dst_pos < MAX_MATCH_LEN - 1 {
dictb.dict[LZ_DICT_SIZE + dst_pos] = c;
}
lookahead_size += 1;
if lookahead_size + d.dict.size >= MIN_MATCH_LEN.into() {
let ins_pos = lookahead_pos + lookahead_size - 3;
let hash = ((u32::from(dictb.dict[ins_pos & LZ_DICT_SIZE_MASK])
<< (LZ_HASH_SHIFT * 2))
^ ((u32::from(dictb.dict[(ins_pos + 1) & LZ_DICT_SIZE_MASK])
<< LZ_HASH_SHIFT)
^ u32::from(c)))
& (LZ_HASH_SIZE as u32 - 1);
dictb.next[ins_pos & LZ_DICT_SIZE_MASK] = dictb.hash[hash as usize];
dictb.hash[hash as usize] = ins_pos as u16;
}
}
src_pos += num_bytes_to_process;
}
d.dict.size = cmp::min(LZ_DICT_SIZE - lookahead_size, d.dict.size);
if d.params.flush == TDEFLFlush::None && lookahead_size < MAX_MATCH_LEN {
break;
}
let len_to_move = 1;
bytes_written += 1;
lookahead_pos += len_to_move;
assert!(lookahead_size >= len_to_move);
lookahead_size -= len_to_move;
d.dict.size = cmp::min(d.dict.size + len_to_move, LZ_DICT_SIZE);
if bytes_written > 31 * 1024 {
d.lz.total_bytes = bytes_written;
d.params.src_pos = src_pos;
// These values are used in flush_block, so we need to write them back here.
d.dict.lookahead_size = lookahead_size;
d.dict.lookahead_pos = lookahead_pos;
let n = flush_block(d, callback, TDEFLFlush::None)
.unwrap_or(TDEFLStatus::PutBufFailed as i32);
if n != 0 {
return n > 0;
}
bytes_written = d.lz.total_bytes;
}
}
d.lz.total_bytes = bytes_written;
d.params.src_pos = src_pos;
d.dict.lookahead_size = lookahead_size;
d.dict.lookahead_pos = lookahead_pos;
true
}
/*
fn compress_rle(d: &mut CompressorOxide, callback: &mut CallbackOxide) -> bool {
let mut src_pos = d.params.src_pos;
let in_buf = match callback.in_buf {
None => return true,
Some(in_buf) => in_buf,
};
let mut lookahead_size = d.dict.lookahead_size;
let mut lookahead_pos = d.dict.lookahead_pos;
let mut saved_lit = d.params.saved_lit;
let mut saved_match_dist = d.params.saved_match_dist;
let mut saved_match_len = d.params.saved_match_len;
while src_pos < in_buf.len() || (d.params.flush != TDEFLFlush::None && lookahead_size != 0) {
let src_buf_left = in_buf.len() - src_pos;
let num_bytes_to_process = cmp::min(src_buf_left, MAX_MATCH_LEN - lookahead_size);
if lookahead_size + d.dict.size >= usize::from(MIN_MATCH_LEN) - 1
&& num_bytes_to_process > 0
{
let dictb = &mut d.dict.b;
let mut dst_pos = (lookahead_pos + lookahead_size) & LZ_DICT_SIZE_MASK;
let mut ins_pos = lookahead_pos + lookahead_size - 2;
// Start the hash value from the first two bytes
let mut hash = update_hash(
u16::from(dictb.dict[ins_pos & LZ_DICT_SIZE_MASK]),
dictb.dict[(ins_pos + 1) & LZ_DICT_SIZE_MASK],
);
lookahead_size += num_bytes_to_process;
for &c in &in_buf[src_pos..src_pos + num_bytes_to_process] {
// Add byte to input buffer.
dictb.dict[dst_pos] = c;
if dst_pos < MAX_MATCH_LEN - 1 {
dictb.dict[LZ_DICT_SIZE + dst_pos] = c;
}
// Generate hash from the current byte,
hash = update_hash(hash, c);
dictb.next[ins_pos & LZ_DICT_SIZE_MASK] = dictb.hash[hash as usize];
// and insert it into the hash chain.
dictb.hash[hash as usize] = ins_pos as u16;
dst_pos = (dst_pos + 1) & LZ_DICT_SIZE_MASK;
ins_pos += 1;
}
src_pos += num_bytes_to_process;
} else {
let dictb = &mut d.dict.b;
for &c in &in_buf[src_pos..src_pos + num_bytes_to_process] {
let dst_pos = (lookahead_pos + lookahead_size) & LZ_DICT_SIZE_MASK;
dictb.dict[dst_pos] = c;
if dst_pos < MAX_MATCH_LEN - 1 {
dictb.dict[LZ_DICT_SIZE + dst_pos] = c;
}
lookahead_size += 1;
if lookahead_size + d.dict.size >= MIN_MATCH_LEN.into() {
let ins_pos = lookahead_pos + lookahead_size - 3;
let hash = ((u32::from(dictb.dict[ins_pos & LZ_DICT_SIZE_MASK])
<< (LZ_HASH_SHIFT * 2))
^ ((u32::from(dictb.dict[(ins_pos + 1) & LZ_DICT_SIZE_MASK])
<< LZ_HASH_SHIFT)
^ u32::from(c)))
& (LZ_HASH_SIZE as u32 - 1);
dictb.next[ins_pos & LZ_DICT_SIZE_MASK] = dictb.hash[hash as usize];
dictb.hash[hash as usize] = ins_pos as u16;
}
}
src_pos += num_bytes_to_process;
}
d.dict.size = cmp::min(LZ_DICT_SIZE - lookahead_size, d.dict.size);
if d.params.flush == TDEFLFlush::None && lookahead_size < MAX_MATCH_LEN {
break;
}
let mut len_to_move = 1;
let mut cur_match_dist = 0;
let mut cur_match_len = if saved_match_len != 0 {
saved_match_len
} else {
u32::from(MIN_MATCH_LEN) - 1
};
let cur_pos = lookahead_pos & LZ_DICT_SIZE_MASK;
// If TDEFL_RLE_MATCHES is set, we only look for repeating sequences of the current byte.
if d.dict.size != 0 && d.params.flags & TDEFL_FORCE_ALL_RAW_BLOCKS == 0 {
let c = d.dict.b.dict[(cur_pos.wrapping_sub(1)) & LZ_DICT_SIZE_MASK];
cur_match_len = d.dict.b.dict[cur_pos..(cur_pos + lookahead_size)]
.iter()
.take_while(|&x| *x == c)
.count() as u32;
if cur_match_len < MIN_MATCH_LEN.into() {
cur_match_len = 0
} else {
cur_match_dist = 1
}
}
let far_and_small = cur_match_len == MIN_MATCH_LEN.into() && cur_match_dist >= 8 * 1024;
let filter_small = d.params.flags & TDEFL_FILTER_MATCHES != 0 && cur_match_len <= 5;
if far_and_small || filter_small || cur_pos == cur_match_dist as usize {
cur_match_dist = 0;
cur_match_len = 0;
}
if saved_match_len != 0 {
if cur_match_len > saved_match_len {
record_literal(&mut d.huff, &mut d.lz, saved_lit);
if cur_match_len >= 128 {
record_match(&mut d.huff, &mut d.lz, cur_match_len, cur_match_dist);
saved_match_len = 0;
len_to_move = cur_match_len as usize;
} else {
saved_lit = d.dict.b.dict[cur_pos];
saved_match_dist = cur_match_dist;
saved_match_len = cur_match_len;
}
} else {
record_match(&mut d.huff, &mut d.lz, saved_match_len, saved_match_dist);
len_to_move = (saved_match_len - 1) as usize;
saved_match_len = 0;
}
} else if cur_match_dist == 0 {
record_literal(
&mut d.huff,
&mut d.lz,
d.dict.b.dict[cmp::min(cur_pos, d.dict.b.dict.len() - 1)],
);
} else if d.params.greedy_parsing
|| (d.params.flags & TDEFL_RLE_MATCHES != 0)
|| cur_match_len >= 128
{
// If we are using lazy matching, check for matches at the next byte if the current
// match was shorter than 128 bytes.
record_match(&mut d.huff, &mut d.lz, cur_match_len, cur_match_dist);
len_to_move = cur_match_len as usize;
} else {
saved_lit = d.dict.b.dict[cmp::min(cur_pos, d.dict.b.dict.len() - 1)];
saved_match_dist = cur_match_dist;
saved_match_len = cur_match_len;
}
lookahead_pos += len_to_move;
assert!(lookahead_size >= len_to_move);
lookahead_size -= len_to_move;
d.dict.size = cmp::min(d.dict.size + len_to_move, LZ_DICT_SIZE);
let lz_buf_tight = d.lz.code_position > LZ_CODE_BUF_SIZE - 8;
let raw = d.params.flags & TDEFL_FORCE_ALL_RAW_BLOCKS != 0;
let fat = ((d.lz.code_position * 115) >> 7) >= d.lz.total_bytes as usize;
let fat_or_raw = (d.lz.total_bytes > 31 * 1024) && (fat || raw);
if lz_buf_tight || fat_or_raw {
d.params.src_pos = src_pos;
// These values are used in flush_block, so we need to write them back here.
d.dict.lookahead_size = lookahead_size;
d.dict.lookahead_pos = lookahead_pos;
let n = flush_block(d, callback, TDEFLFlush::None)
.unwrap_or(TDEFLStatus::PutBufFailed as i32);
if n != 0 {
d.params.saved_lit = saved_lit;
d.params.saved_match_dist = saved_match_dist;
d.params.saved_match_len = saved_match_len;
return n > 0;
}
}
}
d.params.src_pos = src_pos;
d.dict.lookahead_size = lookahead_size;
d.dict.lookahead_pos = lookahead_pos;
d.params.saved_lit = saved_lit;
d.params.saved_match_dist = saved_match_dist;
d.params.saved_match_len = saved_match_len;
true
}*/