use crate::bitwriter::BitWriter;
use crate::trees::{BL_CODES, CtData, D_CODES, HEAP_SIZE, MAX_BITS, MAX_MATCH, MIN_MATCH};
const W_BITS: usize = 15;
const W_SIZE: usize = 1 << W_BITS; const W_MASK: usize = W_SIZE - 1; const HASH_BITS: usize = 15; const HASH_SIZE: usize = 1 << HASH_BITS; const HASH_MASK: u32 = (HASH_SIZE - 1) as u32; const HASH_SHIFT: u32 = HASH_BITS.div_ceil(MIN_MATCH) as u32; const LIT_BUFSIZE: usize = 1 << (8 + 6); const WINDOW_SIZE: usize = 2 * W_SIZE;
const MIN_LOOKAHEAD: usize = MAX_MATCH + MIN_MATCH + 1; const WIN_INIT: usize = MAX_MATCH; const TOO_FAR: usize = 4096;
const GOOD_MATCH: usize = 32;
const MAX_LAZY_MATCH: usize = 258;
const NICE_MATCH: usize = 258;
const MAX_CHAIN_LENGTH: usize = 4096;
const LEVEL: i32 = 9;
pub(crate) struct DeflateState<'a> {
pub(crate) input: &'a [u8],
pub(crate) next_in: usize,
pub(crate) avail_in: usize,
pub(crate) bw: BitWriter,
pub(crate) window: Vec<u8>, pub(crate) window_size: usize,
pub(crate) w_size: usize,
pub(crate) w_mask: usize,
pub(crate) prev: Vec<u16>, pub(crate) head: Vec<u16>, pub(crate) ins_h: u32,
pub(crate) hash_mask: u32,
pub(crate) hash_shift: u32,
pub(crate) block_start: isize,
pub(crate) match_length: usize,
pub(crate) prev_match: usize,
pub(crate) match_available: bool,
pub(crate) strstart: usize,
pub(crate) match_start: usize,
pub(crate) lookahead: usize,
pub(crate) prev_length: usize,
pub(crate) max_chain_length: usize,
pub(crate) max_lazy_match: usize,
pub(crate) good_match: usize,
pub(crate) nice_match: usize,
pub(crate) level: i32,
pub(crate) insert: usize,
pub(crate) high_water: usize,
pub(crate) dyn_ltree: Vec<CtData>, pub(crate) dyn_dtree: Vec<CtData>, pub(crate) bl_tree: Vec<CtData>, pub(crate) l_max_code: i32,
pub(crate) d_max_code: i32,
pub(crate) bl_count: [u16; MAX_BITS + 1],
pub(crate) heap: [i32; HEAP_SIZE],
pub(crate) heap_len: usize,
pub(crate) heap_max: usize,
pub(crate) depth: [u8; HEAP_SIZE],
pub(crate) sym_buf: Vec<u8>, pub(crate) sym_next: usize,
pub(crate) sym_end: usize,
pub(crate) opt_len: u32, pub(crate) static_len: u32,
}
impl<'a> DeflateState<'a> {
pub(crate) fn new(input: &'a [u8]) -> Self {
Self {
input,
next_in: 0,
avail_in: input.len(),
bw: BitWriter::new(),
window: vec![0u8; WINDOW_SIZE],
window_size: WINDOW_SIZE,
w_size: W_SIZE,
w_mask: W_MASK,
prev: vec![0u16; W_SIZE],
head: vec![0u16; HASH_SIZE], ins_h: 0,
hash_mask: HASH_MASK,
hash_shift: HASH_SHIFT,
block_start: 0,
match_length: MIN_MATCH - 1,
prev_match: 0,
match_available: false,
strstart: 0,
match_start: 0,
lookahead: 0,
prev_length: MIN_MATCH - 1,
max_chain_length: MAX_CHAIN_LENGTH,
max_lazy_match: MAX_LAZY_MATCH,
good_match: GOOD_MATCH,
nice_match: NICE_MATCH,
level: LEVEL,
insert: 0,
high_water: 0,
dyn_ltree: vec![CtData::default(); HEAP_SIZE],
dyn_dtree: vec![CtData::default(); 2 * D_CODES + 1],
bl_tree: vec![CtData::default(); 2 * BL_CODES + 1],
l_max_code: 0,
d_max_code: 0,
bl_count: [0; MAX_BITS + 1],
heap: [0; HEAP_SIZE],
heap_len: 0,
heap_max: 0,
depth: [0; HEAP_SIZE],
sym_buf: vec![0u8; LIT_BUFSIZE * 3],
sym_next: 0,
sym_end: (LIT_BUFSIZE - 1) * 3,
opt_len: 0,
static_len: 0,
}
}
#[inline]
pub(crate) fn max_dist(&self) -> usize {
self.w_size - MIN_LOOKAHEAD
}
fn read_buf(&mut self, buf_off: usize, size: usize) -> usize {
let mut len = self.avail_in;
if len > size {
len = size;
}
if len == 0 {
return 0;
}
self.avail_in -= len;
let input = self.input; self.window[buf_off..buf_off + len]
.copy_from_slice(&input[self.next_in..self.next_in + len]);
self.next_in += len;
len
}
fn slide_hash(&mut self) {
let wsize = self.w_size as u16;
for h in self.head.iter_mut() {
*h = (*h).saturating_sub(wsize);
}
for p in self.prev.iter_mut() {
*p = (*p).saturating_sub(wsize);
}
}
fn fill_window(&mut self) {
let wsize = self.w_size;
loop {
let mut more = self.window_size - self.lookahead - self.strstart;
if self.strstart >= wsize + self.max_dist() {
self.window.copy_within(wsize..wsize + (wsize - more), 0);
self.match_start -= wsize;
self.strstart -= wsize;
self.block_start -= wsize as isize;
if self.insert > self.strstart {
self.insert = self.strstart;
}
self.slide_hash();
more += wsize;
}
if self.avail_in == 0 {
break;
}
let n = self.read_buf(self.strstart + self.lookahead, more);
self.lookahead += n;
if self.lookahead + self.insert >= MIN_MATCH {
let mut str = self.strstart - self.insert;
self.ins_h = self.window[str] as u32;
self.update_hash(self.window[str + 1]);
while self.insert != 0 {
self.update_hash(self.window[str + MIN_MATCH - 1]);
self.prev[str & self.w_mask] = self.head[self.ins_h as usize];
self.head[self.ins_h as usize] = str as u16;
str += 1;
self.insert -= 1;
if self.lookahead + self.insert < MIN_MATCH {
break;
}
}
}
if !(self.lookahead < MIN_LOOKAHEAD && self.avail_in != 0) {
break;
}
}
if self.high_water < self.window_size {
let curr = self.strstart + self.lookahead;
if self.high_water < curr {
let mut init = self.window_size - curr;
if init > WIN_INIT {
init = WIN_INIT;
}
for b in &mut self.window[curr..curr + init] {
*b = 0;
}
self.high_water = curr + init;
} else if self.high_water < curr + WIN_INIT {
let mut init = curr + WIN_INIT - self.high_water;
if init > self.window_size - self.high_water {
init = self.window_size - self.high_water;
}
let hw = self.high_water;
for b in &mut self.window[hw..hw + init] {
*b = 0;
}
self.high_water += init;
}
}
}
fn flush_block(&mut self, last: bool) {
let (buf, stored_len) = if self.block_start >= 0 {
let bs = self.block_start as usize;
(Some(bs), self.strstart - bs)
} else {
(None, (self.strstart as isize - self.block_start) as usize)
};
self.tr_flush_block(buf, stored_len, last);
self.block_start = self.strstart as isize;
}
fn deflate_slow(&mut self) {
loop {
if self.lookahead < MIN_LOOKAHEAD {
self.fill_window();
if self.lookahead == 0 {
break;
}
}
let mut hash_head = 0usize; if self.lookahead >= MIN_MATCH {
hash_head = self.insert_string(self.strstart);
}
self.prev_length = self.match_length;
self.prev_match = self.match_start;
self.match_length = MIN_MATCH - 1;
if hash_head != 0
&& self.prev_length < self.max_lazy_match
&& self.strstart - hash_head <= self.max_dist()
{
self.match_length = self.longest_match(hash_head);
if self.match_length == MIN_MATCH && self.strstart - self.match_start > TOO_FAR {
self.match_length = MIN_MATCH - 1;
}
}
if self.prev_length >= MIN_MATCH && self.match_length <= self.prev_length {
let max_insert = self.strstart + self.lookahead - MIN_MATCH;
let bflush = self.tr_tally(
self.strstart - 1 - self.prev_match,
self.prev_length - MIN_MATCH,
);
self.lookahead -= self.prev_length - 1;
self.prev_length -= 2;
loop {
self.strstart += 1;
if self.strstart <= max_insert {
hash_head = self.insert_string(self.strstart);
}
self.prev_length -= 1;
if self.prev_length == 0 {
break;
}
}
let _ = hash_head; self.match_available = false;
self.match_length = MIN_MATCH - 1;
self.strstart += 1;
if bflush {
self.flush_block(false);
}
} else if self.match_available {
let bflush = self.tr_tally(0, self.window[self.strstart - 1] as usize);
if bflush {
self.flush_block(false);
}
self.strstart += 1;
self.lookahead -= 1;
} else {
self.match_available = true;
self.strstart += 1;
self.lookahead -= 1;
}
}
if self.match_available {
self.tr_tally(0, self.window[self.strstart - 1] as usize);
self.match_available = false;
}
self.insert = if self.strstart < MIN_MATCH - 1 {
self.strstart
} else {
MIN_MATCH - 1
};
self.flush_block(true);
}
}
pub(crate) fn deflate_raw_level9(input: &[u8]) -> Vec<u8> {
let mut s = DeflateState::new(input);
s.tr_init();
s.deflate_slow();
s.bw.out
}