use super::block::{
calculate_min_len, carve_seqs, Block, Seqs, MIN_BLOCK_LENGTH, SEQS_LEN, SEQ_STORE_LENGTH,
SOFT_MAX_BLOCK_LENGTH,
};
use super::lz::{Limits, LzTables, MatchFinder, LZ_TABLES_LEN, SEARCH_NBYTES};
use super::tables::{MAX_MATCH, MIN_MATCH};
pub(super) const LOOKAHEAD: usize = MAX_MATCH as usize + 1 + 4;
const FAST_SOFT_MAX_BLOCK_LENGTH: usize = 0xFFFF;
const FAST_SEQ_STORE_LENGTH: usize = 8192;
const _: () = assert!(FAST_SEQ_STORE_LENGTH <= SEQ_STORE_LENGTH);
const _: () = assert!(FAST_SEQ_STORE_LENGTH * 4 >= MIN_BLOCK_LENGTH);
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub(super) enum Strategy {
Stored,
Fast,
Greedy,
Lazy,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub(super) struct Params {
pub(super) strategy: Strategy,
pub(super) depth: u32,
pub(super) nice: u32,
pub(super) insert: u32,
pub(super) accel: u32,
}
pub(super) const TABLES_LEN: usize = LZ_TABLES_LEN + SEQS_LEN;
pub(super) struct Tables<'a> {
pub(super) lz: LzTables<'a>,
pub(super) seqs: &'a mut Seqs,
}
impl<'a> Tables<'a> {
pub(super) fn carve(mem: &'a mut [u16]) -> Self {
assert!(
mem.len() >= TABLES_LEN,
"an encoder's tables hold TABLES_LEN u16s"
);
let (lz, rest) = LzTables::carve(mem);
Tables {
lz,
seqs: carve_seqs(rest),
}
}
}
pub(super) struct Parser {
pub(super) mf: MatchFinder,
pub(super) block: Block,
pub(super) pos: usize,
pub(super) open: bool,
min_len: u32,
next_recalc: usize,
}
#[inline(always)]
fn bsr(x: u32) -> i32 {
31 - x.leading_zeros() as i32
}
impl Parser {
pub(super) const fn new() -> Self {
Parser {
mf: MatchFinder::new(),
block: Block::new(),
pos: 0,
open: false,
min_len: MIN_MATCH,
next_recalc: 0,
}
}
pub(super) fn begin_block(&mut self, p: Params, buf: &[u8], end: Option<usize>) {
self.block.begin(self.pos);
self.open = true;
let avail = &buf[self.pos..];
let len = end.map_or(SOFT_MAX_BLOCK_LENGTH, |e| {
(e - self.pos).min(SOFT_MAX_BLOCK_LENGTH)
});
if matches!(p.strategy, Strategy::Greedy | Strategy::Lazy) {
self.min_len = calculate_min_len(avail, len, p.depth);
self.next_recalc = avail.len().min(10_000);
}
}
fn max_block_end(&self, soft: usize, end: Option<usize>) -> usize {
let start = self.block.start;
match end {
Some(end) if end - start < soft + MIN_BLOCK_LENGTH => end,
_ => start + soft,
}
}
pub(super) fn parse(
&mut self,
t: &mut Tables<'_>,
p: Params,
buf: &[u8],
stop: usize,
end: Option<usize>,
) -> bool {
let mut mf = self.mf;
let ended = match p.strategy {
Strategy::Stored => self.stored(stop, end),
Strategy::Fast if p.depth == 1 => self.fast::<1>(&mut mf, t, p, buf, stop, end),
Strategy::Fast => self.fast::<2>(&mut mf, t, p, buf, stop, end),
Strategy::Greedy => self.greedy(&mut mf, t, p, buf, stop, end),
Strategy::Lazy => self.lazy(&mut mf, t, p, buf, stop, end),
};
self.mf = mf;
ended
}
fn stored(&mut self, stop: usize, end: Option<usize>) -> bool {
let max_end = self.block.start + 0xFFFF;
self.pos = stop.min(max_end);
self.pos == max_end && end != Some(max_end)
}
#[inline(always)]
fn fast<const B: usize>(
&mut self,
mf: &mut MatchFinder,
t: &mut Tables<'_>,
p: Params,
buf: &[u8],
stop: usize,
end: Option<usize>,
) -> bool {
let max_end = self.max_block_end(FAST_SOFT_MAX_BLOCK_LENGTH, end);
let limit = stop.min(max_end);
let block = &mut self.block;
let (lz, seqs) = (&mut t.lz, &mut *t.seqs);
let mut pos = self.pos;
let mut misses = 0u32;
let (mut n, mut lits) = block.open_seq();
while pos < limit {
let lim = Limits::at(buf.len(), pos, p.nice);
if lim.max < SEARCH_NBYTES {
for &b in &buf[pos..limit] {
block.count_literal::<false>(b);
}
lits += (limit - pos) as u32;
pos = limit;
break;
}
let (mut len, mut offset) = mf.ht_longest::<B>(lz, buf, pos, lim);
if len > 0 {
if n >= FAST_SEQ_STORE_LENGTH {
block.set_open_seq((n, lits));
self.pos = pos;
return true;
}
let mut ahead = 0;
let mut nice = lim.nice;
while len < nice && pos + 1 < buf.len() {
let lim = Limits::at(buf.len(), pos + 1, p.nice);
if lim.max <= len {
break;
}
let (next_len, next_offset) = mf.ht_longer::<B>(lz, buf, pos + 1, lim, len);
if next_len > len
&& 4 * (next_len - len) as i32 + bsr(offset) - bsr(next_offset) > 2
{
block.count_literal::<false>(buf[pos]);
lits += 1;
pos += 1;
(len, offset, nice) = (next_len, next_offset, lim.nice);
continue;
}
ahead = 1;
break;
}
block.store_match::<false>(seqs, n, lits, len, offset);
(n, lits) = (n + 1, 0);
if len <= p.insert {
mf.ht_skip::<B>(lz, buf, pos + 1 + ahead, len - 1 - ahead as u32);
} else {
let last = pos + len as usize - 1;
mf.jump(lz, buf, last);
mf.ht_skip::<B>(lz, buf, last, 1);
}
pos += len as usize;
misses = 0;
} else {
block.count_literal::<false>(buf[pos]);
lits += 1;
pos += 1;
if p.accel > 0 && mf.warm {
misses += 1;
let step = misses * p.accel / 64;
if step > 0 {
let end = (pos + step as usize).min(limit);
for &b in &buf[pos..end] {
block.count_literal::<false>(b);
}
lits += (end - pos) as u32;
pos = end;
mf.jump(lz, buf, pos);
}
}
}
}
block.set_open_seq((n, lits));
self.pos = pos;
pos >= max_end && end != Some(pos)
}
#[inline(always)]
fn greedy(
&mut self,
mf: &mut MatchFinder,
t: &mut Tables<'_>,
p: Params,
buf: &[u8],
stop: usize,
end: Option<usize>,
) -> bool {
let max_end = self.max_block_end(SOFT_MAX_BLOCK_LENGTH, end);
let limit = stop.min(max_end);
let block = &mut self.block;
let (lz, seqs) = (&mut t.lz, &mut *t.seqs);
let min_len = self.min_len;
let mut pos = self.pos;
let mut misses = 0u32;
while pos < limit {
let lim = Limits::at(buf.len(), pos, p.nice);
let (len, offset) = mf.hc_longest(lz, buf, pos, min_len - 1, lim, p.depth);
if len >= min_len && (len > MIN_MATCH || offset <= 4096) {
misses = 0;
block.matched::<true>(seqs, len, offset);
if len <= p.insert {
mf.hc_skip(lz, buf, pos + 1, len - 1);
} else {
let last = pos + len as usize - 1;
mf.jump(lz, buf, last);
mf.hc_skip(lz, buf, last, 1);
}
pos += len as usize;
} else {
block.literal::<true>(buf[pos]);
pos += 1;
if p.accel > 0 && mf.warm {
misses += 1;
let step = misses * p.accel / 64;
if step > 0 {
let end = (pos + step as usize).min(limit);
for &b in &buf[pos..end] {
block.literal::<true>(b);
}
pos = end;
mf.jump(lz, buf, pos);
}
}
}
if !block.has_room(SEQ_STORE_LENGTH) || block.should_end(pos, end.map(|e| e - pos)) {
self.pos = pos;
return true;
}
}
self.pos = pos;
pos >= max_end && end != Some(pos)
}
#[inline(always)]
fn lazy(
&mut self,
mf: &mut MatchFinder,
t: &mut Tables<'_>,
p: Params,
buf: &[u8],
stop: usize,
end: Option<usize>,
) -> bool {
let max_end = self.max_block_end(SOFT_MAX_BLOCK_LENGTH, end);
let limit = stop.min(max_end);
let block = &mut self.block;
let (lz, seqs) = (&mut t.lz, &mut *t.seqs);
let mut pos = self.pos;
while pos < limit {
if pos - block.start >= self.next_recalc {
self.min_len = block.recalculate_min_len(p.depth);
self.next_recalc += (pos - block.start).max(1);
}
let min_len = self.min_len;
let lim = Limits::at(buf.len(), pos, p.nice);
let (mut cur_len, mut cur_offset) =
mf.hc_longest(lz, buf, pos, min_len - 1, lim, p.depth);
if cur_len < min_len || (cur_len == MIN_MATCH && cur_offset > 8192) {
block.literal::<true>(buf[pos]);
pos += 1;
} else {
pos += 1;
loop {
if cur_len >= lim.nice {
block.matched::<true>(seqs, cur_len, cur_offset);
mf.hc_skip(lz, buf, pos, cur_len - 1);
pos += cur_len as usize - 1;
break;
}
let lim = Limits::at(buf.len(), pos, p.nice);
let depth = (p.depth >> 1).max(1);
let (next_len, next_offset) =
mf.hc_longest(lz, buf, pos, cur_len - 1, lim, depth);
pos += 1;
if next_len >= cur_len
&& 4 * (next_len - cur_len) as i32 + bsr(cur_offset) - bsr(next_offset) > 2
{
block.literal::<true>(buf[pos - 2]);
(cur_len, cur_offset) = (next_len, next_offset);
continue;
}
block.matched::<true>(seqs, cur_len, cur_offset);
mf.hc_skip(lz, buf, pos, cur_len - 2);
pos += cur_len as usize - 2;
break;
}
}
if !block.has_room(SEQ_STORE_LENGTH) || block.should_end(pos, end.map(|e| e - pos)) {
self.pos = pos;
return true;
}
}
self.pos = pos;
pos >= max_end && end != Some(pos)
}
}