use super::deflate::{
BASE_MATCH_LENGTH, BASE_MATCH_OFFSET, MAX_MATCH_LENGTH, MAX_MATCH_OFFSET, MAX_STORE_BLOCK_SIZE,
};
use super::token::{literal_token, match_token, Token};
const TABLE_BITS: usize = 14; pub(super) const TABLE_SIZE: u32 = 1 << TABLE_BITS; const TABLE_MASK: u32 = TABLE_SIZE - 1; const TABLE_SHIFT: usize = 32 - TABLE_BITS;
pub(super) const BUFFER_RESET: i32 = i32::MAX - (MAX_STORE_BLOCK_SIZE as i32) * 2;
fn load32(b: &[u8], i: usize) -> u32 {
let b = &b[i..i + 4];
(b[0] as u32) | (b[1] as u32) << 8 | (b[2] as u32) << 16 | (b[3] as u32) << 24
}
fn load64(b: &[u8], i: usize) -> u64 {
let b = &b[i..i + 8];
(b[0] as u64)
| (b[1] as u64) << 8
| (b[2] as u64) << 16
| (b[3] as u64) << 24
| (b[4] as u64) << 32
| (b[5] as u64) << 40
| (b[6] as u64) << 48
| (b[7] as u64) << 56
}
fn hash(u: u32) -> u32 {
u.overflowing_mul(0x1e35a7bd).0 >> TABLE_SHIFT
}
const INPUT_MARGIN: usize = 16 - 1;
const MIN_NON_LITERAL_BLOCK_SIZE: usize = 1 + 1 + INPUT_MARGIN;
#[derive(Default, Clone, Copy)]
pub(super) struct TableEntry {
val: u32, offset: i32,
}
pub(super) struct DeflateFast {
pub(super) table: Vec<TableEntry>,
pub(super) prev: Vec<u8>, pub(super) cur: i32, }
impl DeflateFast {
pub(super) fn new() -> Self {
Self {
table: vec![TableEntry::default(); TABLE_SIZE as usize],
prev: vec![0; MAX_STORE_BLOCK_SIZE],
cur: MAX_STORE_BLOCK_SIZE as i32,
}
}
}
impl DeflateFast {
pub(super) fn encode(&mut self, dst: &mut Vec<Token>, src: &[u8]) {
if self.cur >= BUFFER_RESET {
self.shift_offsets();
}
if src.len() < MIN_NON_LITERAL_BLOCK_SIZE {
self.cur += MAX_STORE_BLOCK_SIZE as i32;
self.prev.truncate(0);
emit_literal(dst, src);
return;
}
let s_limit = src.len() - INPUT_MARGIN;
let mut next_emit = 0_usize;
let mut s = 0_usize;
let mut cv = load32(src, s);
let mut next_hash = hash(cv);
'outer: loop {
let mut skip = 32_usize;
let mut next_s = s;
let mut candidate: TableEntry;
loop {
s = next_s;
let bytes_between_hash_lookups = skip >> 5;
next_s = s + bytes_between_hash_lookups;
skip += bytes_between_hash_lookups;
if next_s > s_limit {
break 'outer;
}
candidate = self.table[(next_hash & TABLE_MASK) as usize];
let now = load32(src, next_s);
self.table[(next_hash & TABLE_MASK) as usize] = TableEntry {
offset: s as i32 + self.cur,
val: cv,
};
next_hash = hash(now);
let offset = s as i32 - (candidate.offset - self.cur);
if offset > MAX_MATCH_OFFSET as i32 || cv != candidate.val {
cv = now;
continue;
}
break;
}
emit_literal(dst, &src[next_emit..s]);
loop {
s += 4;
let t = candidate.offset - self.cur + 4;
let l = self.match_len(s, t, src);
dst.push(match_token(
(l + 4 - BASE_MATCH_LENGTH) as u32,
(s as i32 - t - BASE_MATCH_OFFSET as i32) as u32,
));
s += l;
next_emit = s;
if s >= s_limit {
break 'outer;
}
let mut x = load64(src, s - 1);
let prev_hash = hash(x as u32);
self.table[(prev_hash & TABLE_MASK) as usize] = TableEntry {
offset: self.cur + s as i32 - 1,
val: x as u32,
};
x >>= 8;
let curr_hash = hash(x as u32);
candidate = self.table[(curr_hash & TABLE_MASK) as usize];
self.table[(curr_hash & TABLE_MASK) as usize] = TableEntry {
offset: self.cur + s as i32,
val: x as u32,
};
let offset = (s as i32) - (candidate.offset - self.cur);
if offset > MAX_MATCH_OFFSET as i32 || (x as u32) != candidate.val {
cv = (x >> 8) as u32;
next_hash = hash(cv);
s += 1;
break;
}
}
}
if next_emit < src.len() {
emit_literal(dst, &src[next_emit..])
}
self.cur += src.len() as i32;
self.prev.resize(src.len(), 0);
self.prev.copy_from_slice(src);
}
}
fn emit_literal(dst: &mut Vec<Token>, lit: &[u8]) {
for v in lit {
dst.push(literal_token(*v as u32));
}
}
impl DeflateFast {
pub(super) fn match_len(&mut self, s: usize, t: i32, src: &[u8]) -> usize {
let mut s1 = s + MAX_MATCH_LENGTH - 4;
if s1 > src.len() {
s1 = src.len();
}
if t >= 0 {
let t = t as usize;
let b = &src[t..];
let a = &src[s..s1];
let b = &b[..a.len()];
for i in 0..a.len() {
if a[i] != b[i] {
return i;
}
}
return a.len();
}
let tp = self.prev.len() as i32 + t;
if tp < 0 {
return 0;
}
let tp = tp as usize;
let mut a = &src[s..s1];
let mut b = &self.prev[tp..];
if b.len() > a.len() {
b = &b[..a.len()];
}
a = &a[..b.len()];
for i in 0..b.len() {
if a[i] != b[i] {
return i;
}
}
let n = b.len();
if (s + n) == s1 {
return n;
}
a = &src[s + n..s1];
b = &src[..a.len()];
for i in 0..a.len() {
if a[i] != b[i] {
return i + n;
}
}
a.len() + n
}
pub(super) fn reset(&mut self) {
self.prev.truncate(0);
self.cur += MAX_MATCH_OFFSET as i32;
if self.cur >= BUFFER_RESET {
self.shift_offsets();
}
}
pub(super) fn shift_offsets(&mut self) {
if self.prev.is_empty() {
for i in 0..self.table.len() {
self.table[i] = TableEntry::default();
}
self.cur = MAX_MATCH_OFFSET as i32 + 1;
return;
}
for i in 0..self.table.len() {
let mut v = self.table[i].offset - self.cur + MAX_MATCH_OFFSET as i32 + 1;
if v < 0 {
v = 0;
}
self.table[i].offset = v;
}
self.cur = MAX_MATCH_OFFSET as i32 + 1;
}
}