use alloc::{vec, vec::Vec};
use super::huffman;
use crate::{Error, ErrorKind, Level, Progress, Status};
const MIN_MATCH: usize = 3;
const MAX_MATCH: usize = 258;
const MIN_LOOKAHEAD: usize = MAX_MATCH + MIN_MATCH + 1;
const TOO_FAR: usize = 4096;
const WIN_PAD: usize = 8;
const STORED_CHUNK: usize = 16 * 1024;
const LENGTH_BASE: [u16; 29] = [
3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, 35, 43, 51, 59, 67, 83, 99, 115, 131,
163, 195, 227, 258,
];
const LENGTH_EXTRA: [u8; 29] = [
0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0,
];
const DIST_BASE: [u16; 30] = [
1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, 257, 385, 513, 769, 1025, 1537,
2049, 3073, 4097, 6145, 8193, 12289, 16385, 24577,
];
const DIST_EXTRA: [u8; 30] = [
0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13,
13,
];
const PRECODE_ORDER: [usize; 19] = [
16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15,
];
static LENGTH_CODE: [u8; 256] = {
let mut t = [0u8; 256];
let mut code = 0;
while code < 28 {
let mut i = 0;
while i < (1 << LENGTH_EXTRA[code]) {
t[LENGTH_BASE[code] as usize - 3 + i] = code as u8;
i += 1;
}
code += 1;
}
t[255] = 28;
t
};
static DIST_CODE: [u8; 512] = {
let mut t = [0u8; 512];
let mut code = 0;
while code < 30 {
let base = DIST_BASE[code] as usize - 1;
let count = 1usize << DIST_EXTRA[code];
let mut i = 0;
while i < count {
let d = base + i;
if d < 256 {
t[d] = code as u8;
} else {
t[256 + (d >> 7)] = code as u8;
}
i += 1;
}
code += 1;
}
t
};
#[inline]
fn dist_code(dist: usize) -> usize {
let d = dist - 1;
if d < 256 {
DIST_CODE[d] as usize
} else {
DIST_CODE[256 + (d >> 7)] as usize
}
}
const fn fixed_lit_lens() -> [u8; 288] {
let mut l = [0u8; 288];
let mut i = 0;
while i < 288 {
l[i] = if i < 144 {
8
} else if i < 256 {
9
} else if i < 280 {
7
} else {
8
};
i += 1;
}
l
}
static FIXED_LIT_LENS: [u8; 288] = fixed_lit_lens();
#[derive(Clone, Copy)]
struct Params {
good: usize,
lazy: usize,
nice: usize,
chain: usize,
lazy_matching: bool,
}
const fn params(level: u8) -> Params {
let (good, lazy, nice, chain, lazy_matching) = match level {
1 => (4, 4, 8, 1, false),
2 => (4, 5, 16, 8, false),
3 => (4, 6, 32, 32, false),
4 => (4, 4, 16, 16, true),
5 => (8, 16, 32, 32, true),
6 => (8, 16, 128, 128, true),
7 => (8, 32, 128, 256, true),
8 => (32, 128, 258, 1024, true),
9 => (32, 258, 258, 4096, true),
_ => (0, 0, 0, 0, false),
};
Params {
good,
lazy,
nice,
chain,
lazy_matching,
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum Flush {
None,
Sync,
Full,
Finish,
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub struct MemoryConfig {
hash_bits: u8,
block_symbols: usize,
}
impl MemoryConfig {
#[must_use]
pub const fn new() -> Self {
Self {
hash_bits: 0,
block_symbols: 0,
}
}
#[must_use]
pub const fn with_hash_bits(mut self, bits: u8) -> Self {
self.hash_bits = if bits == 0 {
0
} else if bits < 8 {
8
} else if bits > 16 {
16
} else {
bits
};
self
}
#[must_use]
pub const fn with_block_symbols(mut self, symbols: usize) -> Self {
self.block_symbols = if symbols == 0 {
0
} else if symbols < 256 {
256
} else if symbols > 65_536 {
65_536
} else {
symbols
};
self
}
#[must_use]
pub const fn hash_bits(&self) -> u8 {
self.hash_bits
}
#[must_use]
pub const fn block_symbols(&self) -> usize {
self.block_symbols
}
}
struct BlockScratch {
lit_freq: [u32; 286],
dist_freq: [u32; 30],
lit_lens: [u8; 286],
dist_lens: [u8; 30],
lit_codes: [u16; 288],
dist_codes: [u16; 30],
rle: [(u8, u8); 316],
rle_len: usize,
pre_freq: [u32; 19],
pre_lens: [u8; 19],
pre_codes: [u16; 19],
}
pub struct Deflater {
memory: MemoryConfig,
level: u8,
params: Params,
window_bits: u8,
win: Vec<u8>,
size: usize,
wsize: usize,
keep: usize,
strstart: usize,
lookahead: usize,
block_start: isize,
hash_bits: u32,
head: Vec<u16>,
prev: Vec<u16>,
match_available: bool,
match_length: usize,
match_start: usize,
prev_length: usize,
prev_match: usize,
syms: Vec<u32>,
sym_cap: usize,
scratch: alloc::boxed::Box<BlockScratch>,
pending: Vec<u8>,
pending_pos: usize,
bitbuf: u64,
bitcnt: u32,
total_in: u64,
total_out: u64,
flush_written: bool,
finished: bool,
}
impl core::fmt::Debug for Deflater {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("Deflater")
.field("level", &self.level)
.field("window_bits", &self.window_bits)
.field("total_in", &self.total_in)
.field("total_out", &self.total_out)
.field("finished", &self.finished)
.finish_non_exhaustive()
}
}
impl Default for Deflater {
fn default() -> Self {
Self::new(Level::DEFAULT)
}
}
impl Deflater {
#[must_use]
pub fn new(level: Level) -> Self {
Self::build(level.get(), 15, MemoryConfig::new())
}
fn build(level: u8, window_bits: u8, memory: MemoryConfig) -> Self {
let wsize = 1usize << window_bits;
let span = (8192 / wsize).max(2);
let max_hash = if level == 1 { 12 } else { 15 };
let hash_bits = match memory.hash_bits {
0 => u32::from(window_bits + 1).clamp(9, max_hash),
bits => u32::from(bits),
};
let sym_cap = match memory.block_symbols {
0 => (1usize << (window_bits + 1)).clamp(1024, 16_384),
n => n,
};
Self {
memory,
level,
params: params(level),
window_bits,
win: vec![0; wsize * span + WIN_PAD],
size: wsize * span,
wsize,
keep: if span >= 3 {
wsize
} else {
wsize - MIN_LOOKAHEAD
},
strstart: 1,
lookahead: 0,
block_start: 1,
hash_bits,
head: vec![0; 1 << hash_bits],
prev: vec![0; wsize],
match_available: false,
match_length: MIN_MATCH - 1,
match_start: 0,
prev_length: MIN_MATCH - 1,
prev_match: 0,
syms: Vec::with_capacity(sym_cap),
sym_cap,
scratch: alloc::boxed::Box::new(BlockScratch {
lit_freq: [0; 286],
dist_freq: [0; 30],
lit_lens: [0; 286],
dist_lens: [0; 30],
lit_codes: [0; 288],
dist_codes: [0; 30],
rle: [(0, 0); 316],
rle_len: 0,
pre_freq: [0; 19],
pre_lens: [0; 19],
pre_codes: [0; 19],
}),
pending: Vec::with_capacity(sym_cap * 4 + wsize * span + 64),
pending_pos: 0,
bitbuf: 0,
bitcnt: 0,
total_in: 0,
total_out: 0,
flush_written: false,
finished: false,
}
}
pub fn with_window_bits(self, bits: u8) -> Result<Self, Error> {
if !(8..=15).contains(&bits) {
return Err(Error::config(ErrorKind::InvalidWindowBits));
}
Ok(Self::build(self.level, bits, self.memory))
}
#[must_use]
pub fn with_memory(self, memory: MemoryConfig) -> Self {
Self::build(self.level, self.window_bits, memory)
}
#[must_use]
pub fn level(&self) -> Level {
Level::new(self.level).unwrap_or(Level::DEFAULT)
}
#[must_use]
pub fn window_bits(&self) -> u8 {
self.window_bits
}
pub fn set_level(&mut self, level: Level) {
let level = level.get();
let next = params(level);
if (level == 0) != (self.level == 0) {
self.process(true);
self.end_block(false);
} else if self.params.lazy_matching && !next.lazy_matching && self.match_available {
self.literal(self.win[self.strstart - 1]);
self.match_available = false;
self.block_full();
}
self.match_length = MIN_MATCH - 1;
self.prev_length = MIN_MATCH - 1;
self.level = level;
self.params = next;
}
#[must_use]
pub fn total_in(&self) -> u64 {
self.total_in
}
#[must_use]
pub fn total_out(&self) -> u64 {
self.total_out
}
#[must_use]
pub fn memory_in_use(&self) -> usize {
self.win.capacity()
+ 2 * (self.head.capacity() + self.prev.capacity())
+ 4 * self.syms.capacity()
+ self.pending.capacity()
+ core::mem::size_of::<BlockScratch>()
}
pub fn reset(&mut self) {
let end = self.strstart + self.lookahead;
if self.total_in as usize + 1 < self.size && end < self.head.len() / 4 {
let shift = 32 - self.hash_bits;
for pos in 1..end.saturating_sub(MIN_MATCH - 1) {
self.head[hash3(load32(&self.win, pos), shift)] = 0;
}
} else {
self.head.fill(0);
}
self.strstart = 1;
self.lookahead = 0;
self.block_start = 1;
self.match_available = false;
self.match_length = MIN_MATCH - 1;
self.prev_length = MIN_MATCH - 1;
self.syms.clear();
self.pending.clear();
self.pending_pos = 0;
self.bitbuf = 0;
self.bitcnt = 0;
self.total_in = 0;
self.total_out = 0;
self.flush_written = false;
self.finished = false;
}
pub fn set_dictionary(&mut self, dictionary: &[u8]) -> Result<(), Error> {
if self.total_in != 0 || self.lookahead != 0 || self.strstart != 1 {
return Err(Error::config(ErrorKind::InvalidState));
}
let take = dictionary.len().min(self.wsize);
let dict = &dictionary[dictionary.len() - take..];
self.win[1..1 + take].copy_from_slice(dict);
self.strstart = 1;
self.lookahead = take;
if self.level != 0 {
let end = 1 + take;
let mut pos = 1;
while pos + MIN_MATCH <= end {
self.insert(pos);
pos += 1;
}
}
self.strstart = 1 + take;
self.lookahead = 0;
self.block_start = self.strstart as isize;
Ok(())
}
pub fn compress(
&mut self,
input: &[u8],
output: &mut [u8],
flush: Flush,
) -> Result<Progress, Error> {
let mut consumed = 0;
let mut produced = 0;
if self.finished {
if !input.is_empty() {
return Err(Error::config(ErrorKind::InvalidState));
}
self.drain(output, &mut produced);
let status = if self.pending_left() == 0 {
Status::Done
} else {
Status::NeedsOutput
};
return Ok(Progress::new(0, produced, status));
}
loop {
self.drain(output, &mut produced);
if self.pending_left() > 0 {
return Ok(Progress::new(consumed, produced, Status::NeedsOutput));
}
if self.flush_written && consumed == input.len() {
self.flush_written = false;
if flush == Flush::Finish {
self.finished = true;
}
return Ok(Progress::new(consumed, produced, Status::Done));
}
self.flush_written = false;
let before = (consumed, self.strstart, self.lookahead);
consumed += self.fill_window(&input[consumed..]);
let all_in = consumed == input.len();
let flushing = flush != Flush::None && all_in;
self.process(flushing);
if self.pending_left() > 0 {
continue;
}
if !all_in {
if before == (consumed, self.strstart, self.lookahead) {
return Err(Error::config(ErrorKind::InvalidState));
}
continue;
}
if flush == Flush::None {
return Ok(Progress::new(consumed, produced, Status::NeedsInput));
}
self.write_flush(flush);
self.flush_written = true;
}
}
pub fn compress_vec(
&mut self,
input: &[u8],
out: &mut Vec<u8>,
flush: Flush,
) -> Result<Progress, Error> {
let start = out.len();
let mut consumed = 0;
loop {
let have = out.len();
let room = (input.len() - consumed) / 2 + 1024;
if out.capacity() - have < room && out.try_reserve(room).is_err() {
return Err(Error::config(ErrorKind::AllocationFailed));
}
let spare = out.capacity() - have;
out.resize(have + spare, 0);
let p = self.compress(&input[consumed..], &mut out[have..], flush);
match p {
Ok(p) => {
out.truncate(have + p.produced);
consumed += p.consumed;
if p.status != Status::NeedsOutput {
return Ok(Progress::new(consumed, out.len() - start, p.status));
}
}
Err(e) => {
out.truncate(have);
return Err(e);
}
}
}
}
fn pending_left(&self) -> usize {
self.pending.len() - self.pending_pos
}
fn drain(&mut self, output: &mut [u8], produced: &mut usize) {
let n = self.pending_left().min(output.len() - *produced);
output[*produced..*produced + n]
.copy_from_slice(&self.pending[self.pending_pos..self.pending_pos + n]);
self.pending_pos += n;
*produced += n;
self.total_out += n as u64;
if self.pending_pos == self.pending.len() {
self.pending.clear();
self.pending_pos = 0;
}
}
fn fill_window(&mut self, input: &[u8]) -> usize {
let size = self.size;
if size - self.strstart - self.lookahead < MIN_LOOKAHEAD
&& self.strstart >= self.keep + self.wsize
{
self.slide();
}
let room = size - self.strstart - self.lookahead;
let n = room.min(input.len());
let at = self.strstart + self.lookahead;
self.win[at..at + n].copy_from_slice(&input[..n]);
self.lookahead += n;
self.total_in += n as u64;
n
}
fn slide(&mut self) {
let shift = (self.strstart - self.keep) / self.wsize * self.wsize;
if shift == 0 {
return;
}
if self.level == 0 && self.block_start < shift as isize {
self.end_block(false);
}
let end = self.strstart + self.lookahead;
self.win.copy_within(shift..end, 0);
self.strstart -= shift;
self.match_start = self.match_start.saturating_sub(shift);
self.prev_match = self.prev_match.saturating_sub(shift);
self.block_start -= shift as isize;
let s = shift as u32;
for h in self.head.iter_mut().chain(self.prev.iter_mut()) {
*h = u32::from(*h).saturating_sub(s) as u16;
}
}
#[inline]
fn insert(&mut self, pos: usize) -> usize {
insert_at(
&mut self.head,
&mut self.prev,
&self.win,
pos,
32 - self.hash_bits,
self.wsize - 1,
)
}
fn literal(&mut self, b: u8) {
self.syms.push(u32::from(b));
self.scratch.lit_freq[b as usize] += 1;
}
#[inline]
fn block_full(&mut self) {
if self.syms.len() >= self.sym_cap {
let held = usize::from(self.match_available);
self.strstart -= held;
self.end_block(false);
self.strstart += held;
}
}
fn process(&mut self, flushing: bool) {
if self.level == 0 {
let chunk = STORED_CHUNK.min(self.size / 2);
let full = self.strstart + self.lookahead + MIN_LOOKAHEAD > self.size;
while self.lookahead >= chunk || ((flushing || full) && self.lookahead > 0) {
let n = self.lookahead.min(65_535);
self.strstart += n;
self.lookahead -= n;
self.end_block(false);
}
return;
}
loop {
let used_up = if self.params.lazy_matching {
self.lazy_run(flushing)
} else {
self.greedy_run(flushing)
};
self.block_full();
if used_up {
break;
}
}
if flushing && self.match_available {
self.literal(self.win[self.strstart - 1]);
self.match_available = false;
self.block_full();
}
}
fn greedy_run(&mut self, flushing: bool) -> bool {
let p = self.params;
let wmask = self.wsize - 1;
let keep = self.keep;
let shift = 32 - self.hash_bits;
let cap = self.sym_cap;
let mut s = self.strstart;
let mut look = self.lookahead;
let mut misses = 0usize;
let skip = self.level == 1;
let win = &self.win[..];
let head = &mut self.head[..];
let prev = &mut self.prev[..];
let syms = &mut self.syms;
let freq = &mut *self.scratch;
let used_up = loop {
if look < MIN_LOOKAHEAD && (!flushing || look == 0) {
break true;
}
if syms.len() >= cap {
break false;
}
let mut len = 0;
let mut start = 0;
if look >= MIN_MATCH {
let s4 = load32(win, s);
let h = hash3(s4, shift);
let mut cur = usize::from(head[h]);
prev[s & wmask] = head[h];
head[h] = s as u16;
let max = MAX_MATCH.min(look);
let mut chain = p.chain;
while cur != 0 && cur < s && s - cur <= keep {
if (load32(win, cur) ^ s4) & 0x00FF_FFFF == 0 {
let l = MIN_MATCH
+ common_prefix(win, cur + MIN_MATCH, s + MIN_MATCH, max - MIN_MATCH);
if l > len {
len = l;
start = cur;
if l >= p.nice.min(max) {
break;
}
}
}
chain -= 1;
if chain == 0 {
break;
}
let next = usize::from(prev[cur & wmask]);
if next >= cur {
break;
}
cur = next;
}
}
if len >= MIN_MATCH {
let dist = s - start;
syms.push(((dist as u32) << 16) | (len - MIN_MATCH) as u32);
freq.lit_freq[257 + LENGTH_CODE[len - MIN_MATCH] as usize] += 1;
freq.dist_freq[dist_code(dist)] += 1;
look -= len;
misses = 0;
if len <= p.lazy && look >= MIN_MATCH {
for _ in 1..len {
s += 1;
insert_at(head, prev, win, s, shift, wmask);
}
s += 1;
} else {
s += len;
}
} else {
let b = win[s];
syms.push(u32::from(b));
freq.lit_freq[b as usize] += 1;
look -= 1;
s += 1;
misses += 1;
if skip && misses > 64 {
let extra = ((misses - 64) >> 4).min(15).min(look).min(cap - syms.len());
for &b in &win[s..s + extra] {
syms.push(u32::from(b));
freq.lit_freq[b as usize] += 1;
}
s += extra;
look -= extra;
}
}
};
self.strstart = s;
self.lookahead = look;
self.match_length = MIN_MATCH - 1;
used_up
}
fn lazy_run(&mut self, flushing: bool) -> bool {
let p = self.params;
let wmask = self.wsize - 1;
let keep = self.keep;
let shift = 32 - self.hash_bits;
let cap = self.sym_cap;
let mut s = self.strstart;
let mut look = self.lookahead;
let mut available = self.match_available;
let mut match_length = self.match_length;
let mut match_start = self.match_start;
let mut prev_length;
let mut prev_match;
let win = &self.win[..];
let head = &mut self.head[..];
let prev = &mut self.prev[..];
let syms = &mut self.syms;
let freq = &mut *self.scratch;
let used_up = loop {
if look < MIN_LOOKAHEAD && (!flushing || look == 0) {
break true;
}
if syms.len() >= cap {
break false;
}
let mut cur = 0;
let mut s4 = 0;
if look >= MIN_MATCH {
s4 = load32(win, s);
let h = hash3(s4, shift);
cur = usize::from(head[h]);
prev[s & wmask] = head[h];
head[h] = s as u16;
}
prev_length = match_length;
prev_match = match_start;
match_length = MIN_MATCH - 1;
if cur != 0 && prev_length < p.lazy && cur < s && s - cur <= keep {
let max = MAX_MATCH.min(look);
let nice = p.nice.min(max);
let mut chain = if prev_length >= p.good {
p.chain >> 2
} else {
p.chain
};
let mut best = prev_length;
while cur != 0 && cur < s && s - cur <= keep && best < max {
if load16(win, cur + best - 1) == load16(win, s + best - 1)
&& (load32(win, cur) ^ s4) & 0x00FF_FFFF == 0
{
let l = MIN_MATCH
+ common_prefix(win, cur + MIN_MATCH, s + MIN_MATCH, max - MIN_MATCH);
if l > best {
best = l;
match_start = cur;
if l >= nice {
break;
}
}
}
chain -= 1;
if chain == 0 {
break;
}
let next = usize::from(prev[cur & wmask]);
if next >= cur {
break;
}
cur = next;
}
match_length = best.min(look);
if match_length == MIN_MATCH && s - match_start > TOO_FAR {
match_length = MIN_MATCH - 1;
}
}
if prev_length >= MIN_MATCH && match_length <= prev_length {
let max_insert = s + look - MIN_MATCH;
let dist = s - 1 - prev_match;
syms.push(((dist as u32) << 16) | (prev_length - MIN_MATCH) as u32);
freq.lit_freq[257 + LENGTH_CODE[prev_length - MIN_MATCH] as usize] += 1;
freq.dist_freq[dist_code(dist)] += 1;
look -= prev_length - 1;
for _ in 0..prev_length - 2 {
s += 1;
if s <= max_insert {
insert_at(head, prev, win, s, shift, wmask);
}
}
available = false;
match_length = MIN_MATCH - 1;
s += 1;
} else if available {
let b = win[s - 1];
syms.push(u32::from(b));
freq.lit_freq[b as usize] += 1;
s += 1;
look -= 1;
} else {
available = true;
s += 1;
look -= 1;
}
};
self.strstart = s;
self.lookahead = look;
self.match_available = available;
self.match_length = match_length;
self.match_start = match_start;
self.prev_length = match_length;
used_up
}
#[inline]
fn put(&mut self, value: u32, bits: u32) {
self.bitbuf |= u64::from(value) << self.bitcnt;
self.bitcnt += bits;
if self.bitcnt >= 32 {
self.pending
.extend_from_slice(&(self.bitbuf as u32).to_le_bytes());
self.bitbuf >>= 32;
self.bitcnt -= 32;
}
}
fn align(&mut self) {
while self.bitcnt > 0 {
self.pending.push(self.bitbuf as u8);
self.bitbuf >>= 8;
self.bitcnt = self.bitcnt.saturating_sub(8);
}
self.bitbuf = 0;
}
fn write_flush(&mut self, flush: Flush) {
match flush {
Flush::None => {}
Flush::Finish => {
self.end_block(true);
self.align();
}
Flush::Sync | Flush::Full => {
self.end_block(false);
self.put(0, 3);
self.align();
self.pending.extend_from_slice(&[0, 0, 0xFF, 0xFF]);
if flush == Flush::Full {
self.head.fill(0);
self.match_length = MIN_MATCH - 1;
self.prev_length = MIN_MATCH - 1;
}
}
}
}
fn end_block(&mut self, last: bool) {
let stored_len = (self.strstart as isize - self.block_start).max(0) as usize;
if self.syms.is_empty() && stored_len == 0 && !last {
return;
}
let stored_ok = self.block_start >= 0;
if self.level == 0 && stored_ok {
self.write_stored(self.block_start as usize, stored_len, last);
} else {
self.write_huffman_or_stored(stored_ok, stored_len, last);
}
self.syms.clear();
self.scratch.lit_freq.fill(0);
self.scratch.dist_freq.fill(0);
self.block_start = self.strstart as isize;
}
fn write_stored(&mut self, start: usize, len: usize, last: bool) {
let mut at = start;
let mut left = len;
loop {
let n = left.min(65_535);
let final_piece = n == left;
self.put(u32::from(last && final_piece), 1);
self.put(0, 2);
self.align();
self.pending.extend_from_slice(&(n as u16).to_le_bytes());
self.pending.extend_from_slice(&(!(n as u16)).to_le_bytes());
self.pending.extend_from_slice(&self.win[at..at + n]);
at += n;
left -= n;
if final_piece {
break;
}
}
}
fn write_huffman_or_stored(&mut self, stored_ok: bool, stored_len: usize, last: bool) {
let s = &mut *self.scratch;
s.lit_freq[256] = 1;
let lit_used = s.lit_freq.iter().filter(|&&f| f != 0).count();
if lit_used < 2 {
s.lit_freq[if s.lit_freq[0] == 0 { 0 } else { 1 }] += 1;
}
let dist_used = s.dist_freq.iter().filter(|&&f| f != 0).count();
if dist_used == 0 {
s.dist_freq[0] = 1;
s.dist_freq[1] = 1;
} else if dist_used == 1 {
let d = if s.dist_freq[0] == 0 { 0 } else { 1 };
s.dist_freq[d] = 1;
}
huffman::lengths(&s.lit_freq, 15, &mut s.lit_lens);
huffman::lengths(&s.dist_freq, 15, &mut s.dist_lens);
let mut extra: u64 = 0;
let mut dyn_bits: u64 = 0;
let mut fix_bits: u64 = 0;
for sym in 0..286 {
let f = u64::from(s.lit_freq[sym]);
if f == 0 {
continue;
}
dyn_bits += f * u64::from(s.lit_lens[sym]);
fix_bits += f * u64::from(FIXED_LIT_LENS[sym]);
if sym >= 257 {
extra += f * u64::from(LENGTH_EXTRA[sym - 257]);
}
}
for ((&f, &len), &x) in s.dist_freq.iter().zip(&s.dist_lens).zip(&DIST_EXTRA) {
let f = u64::from(f);
dyn_bits += f * u64::from(len);
fix_bits += f * 5;
extra += f * u64::from(x);
}
let hlit = 257
+ s.lit_lens[257..286]
.iter()
.rposition(|&l| l != 0)
.map_or(0, |i| i + 1);
let hdist = 1 + s.dist_lens.iter().rposition(|&l| l != 0).unwrap_or(0);
rle_lengths(s, hlit, hdist);
huffman::lengths(&s.pre_freq, 7, &mut s.pre_lens);
let hclen = 4 + PRECODE_ORDER
.iter()
.rposition(|&i| s.pre_lens[i] != 0)
.map_or(0, |i| (i + 1).saturating_sub(4));
let mut header_bits = 14 + 3 * hclen as u64;
for &(sym, _) in &s.rle[..s.rle_len] {
header_bits += u64::from(s.pre_lens[sym as usize]);
header_bits += match sym {
16 => 2,
17 => 3,
18 => 7,
_ => 0,
};
}
let dynamic = 3 + header_bits + dyn_bits + extra;
let fixed = 3 + fix_bits + extra;
let pad = u64::from((8 - (self.bitcnt + 3) % 8) % 8);
let pieces = (stored_len as u64).div_ceil(65_535).max(1);
let stored = pieces * (3 + 32) + pad + 8 * stored_len as u64 + (pieces - 1) * 5;
if stored_ok && stored <= fixed.min(dynamic) {
self.write_stored(self.block_start as usize, stored_len, last);
return;
}
if fixed <= dynamic {
self.put(u32::from(last), 1);
self.put(1, 2);
let mut lit_codes = [0u16; 288];
huffman::codes(&FIXED_LIT_LENS, &mut lit_codes);
let mut dist_codes = [0u16; 30];
huffman::codes(&[5u8; 30], &mut dist_codes);
self.write_symbols(&lit_codes, &FIXED_LIT_LENS, &dist_codes, &[5u8; 30]);
} else {
self.put(u32::from(last), 1);
self.put(2, 2);
self.put((hlit - 257) as u32, 5);
self.put((hdist - 1) as u32, 5);
self.put((hclen - 4) as u32, 4);
let s = &mut *self.scratch;
huffman::codes(&s.pre_lens, &mut s.pre_codes);
huffman::codes(&s.lit_lens, &mut s.lit_codes[..286]);
huffman::codes(&s.dist_lens, &mut s.dist_codes);
for &i in &PRECODE_ORDER[..hclen] {
let l = self.scratch.pre_lens[i];
self.put(u32::from(l), 3);
}
for k in 0..self.scratch.rle_len {
let (sym, value) = self.scratch.rle[k];
let code = self.scratch.pre_codes[sym as usize];
let len = self.scratch.pre_lens[sym as usize];
self.put(u32::from(code), u32::from(len));
match sym {
16 => self.put(u32::from(value), 2),
17 => self.put(u32::from(value), 3),
18 => self.put(u32::from(value), 7),
_ => {}
}
}
let lit_codes = self.scratch.lit_codes;
let lit_lens = self.scratch.lit_lens;
let dist_codes = self.scratch.dist_codes;
let dist_lens = self.scratch.dist_lens;
self.write_symbols(&lit_codes, &lit_lens, &dist_codes, &dist_lens);
}
}
fn write_symbols(
&mut self,
lit_codes: &[u16],
lit_lens: &[u8],
dist_codes: &[u16],
dist_lens: &[u8],
) {
let mut bits = self.bitbuf;
let mut count = self.bitcnt;
let out = &mut self.pending;
let mut put = |value: u64, n: u32| {
bits |= value << count;
count += n;
if count >= 32 {
out.extend_from_slice(&(bits as u32).to_le_bytes());
bits >>= 32;
count -= 32;
}
};
for &sym in &self.syms {
let dist = (sym >> 16) as usize;
let litlen = (sym & 0xFFFF) as usize;
if dist == 0 {
put(u64::from(lit_codes[litlen]), u32::from(lit_lens[litlen]));
} else {
let code = LENGTH_CODE[litlen] as usize;
let len_bits = u32::from(lit_lens[257 + code]);
let extra = (litlen + MIN_MATCH - LENGTH_BASE[code] as usize) as u64;
put(
u64::from(lit_codes[257 + code]) | (extra << len_bits),
len_bits + u32::from(LENGTH_EXTRA[code]),
);
let d = dist_code(dist);
let dist_bits = u32::from(dist_lens[d]);
let extra = (dist - DIST_BASE[d] as usize) as u64;
put(
u64::from(dist_codes[d]) | (extra << dist_bits),
dist_bits + u32::from(DIST_EXTRA[d]),
);
}
}
put(u64::from(lit_codes[256]), u32::from(lit_lens[256]));
self.bitbuf = bits;
self.bitcnt = count;
}
}
#[inline(always)]
fn load32(win: &[u8], pos: usize) -> u32 {
let mut b = [0u8; 4];
b.copy_from_slice(&win[pos..pos + 4]);
u32::from_le_bytes(b)
}
#[inline(always)]
fn load16(win: &[u8], pos: usize) -> u16 {
u16::from_le_bytes([win[pos], win[pos + 1]])
}
#[inline(always)]
fn hash3(v: u32, shift: u32) -> usize {
((v & 0x00FF_FFFF).wrapping_mul(0x9E37_79B1) >> shift) as usize
}
#[inline(always)]
fn insert_at(
head: &mut [u16],
prev: &mut [u16],
win: &[u8],
pos: usize,
shift: u32,
wmask: usize,
) -> usize {
let h = hash3(load32(win, pos), shift);
let old = head[h];
prev[pos & wmask] = old;
head[h] = pos as u16;
usize::from(old)
}
#[inline]
fn common_prefix(win: &[u8], a: usize, b: usize, max: usize) -> usize {
let mut n = 0;
while n + 8 <= max {
let mut x = [0u8; 8];
let mut y = [0u8; 8];
x.copy_from_slice(&win[a + n..a + n + 8]);
y.copy_from_slice(&win[b + n..b + n + 8]);
let diff = u64::from_le_bytes(x) ^ u64::from_le_bytes(y);
if diff != 0 {
return n + (diff.trailing_zeros() / 8) as usize;
}
n += 8;
}
while n < max && win[a + n] == win[b + n] {
n += 1;
}
n
}
fn rle_lengths(s: &mut BlockScratch, hlit: usize, hdist: usize) {
let mut all = [0u8; 316];
all[..hlit].copy_from_slice(&s.lit_lens[..hlit]);
all[hlit..hlit + hdist].copy_from_slice(&s.dist_lens[..hdist]);
let all = &all[..hlit + hdist];
s.pre_freq = [0; 19];
s.rle_len = 0;
let push = |s: &mut BlockScratch, sym: u8, value: u8| {
s.rle[s.rle_len] = (sym, value);
s.rle_len += 1;
s.pre_freq[sym as usize] += 1;
};
let mut i = 0;
while i < all.len() {
let v = all[i];
let mut run = 1;
while i + run < all.len() && all[i + run] == v {
run += 1;
}
i += run;
if v == 0 {
while run >= 11 {
let n = run.min(138);
push(s, 18, (n - 11) as u8);
run -= n;
}
if run >= 3 {
push(s, 17, (run - 3) as u8);
run = 0;
}
for _ in 0..run {
push(s, 0, 0);
}
} else {
push(s, v, 0);
run -= 1;
while run >= 3 {
let n = run.min(6);
push(s, 16, (n - 3) as u8);
run -= n;
}
for _ in 0..run {
push(s, v, 0);
}
}
}
}