use zeroize::Zeroizing;
use super::bits::BitReader;
use super::huffman::{Huffman, Kind, MAX_BITS};
use super::{
DISTANCE_TOO_FAR, EXPANSION_BOUND, INVALID_CODE, INVALID_DISTANCE_SYMBOL,
INVALID_LENGTH_SYMBOL, MISSING_END_OF_BLOCK, OUTPUT_ALLOCATION, OUTPUT_LIMIT, REPEAT_OVERRUN,
REPEAT_WITHOUT_PREVIOUS, RESERVED_BLOCK_TYPE, SINGLE_LENGTH_CODE, STORED_LENGTH_MISMATCH,
TOO_MANY_DISTANCE_CODES, TOO_MANY_LENGTH_CODES,
};
use crate::{Error, Result};
pub(super) 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,
];
pub(super) 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,
];
pub(super) const DISTANCE_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,
];
pub(super) const DISTANCE_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 CODE_LENGTH_ORDER: [usize; 19] = [
16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15,
];
const END_OF_BLOCK: u16 = 256;
const MAX_BYTES_PER_BIT: usize = 258 / 2;
pub(super) fn max_expansion(input_len: usize) -> usize {
input_len.saturating_mul(8 * MAX_BYTES_PER_BIT)
}
pub(super) const INITIAL_RESERVATION: usize = 250_000;
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
pub(super) struct Capacity {
pub(super) initial: usize,
pub(super) last: usize,
pub(super) peak: usize,
pub(super) len: usize,
}
struct Output {
buf: Zeroizing<Vec<u8>>,
max: usize,
limit: usize,
peak: usize,
}
impl Output {
fn new(input_len: usize, max_output: usize) -> Result<Self> {
let limit = max_output.min(max_expansion(input_len));
let capacity = limit.min(INITIAL_RESERVATION);
let mut buf = Vec::new();
buf.try_reserve_exact(capacity)
.map_err(|_| Error::InvalidFormat(OUTPUT_ALLOCATION))?;
Ok(Self {
buf: Zeroizing::new(buf),
max: max_output,
limit,
peak: capacity,
})
}
fn reserve(&mut self, n: usize) -> Result<()> {
let needed = self
.buf
.len()
.checked_add(n)
.ok_or(Error::InvalidFormat(OUTPUT_LIMIT))?;
if needed > self.max {
return Err(Error::InvalidFormat(OUTPUT_LIMIT));
}
debug_assert!(
needed <= self.limit,
"output beyond the RFC 1951 expansion bound"
);
if needed > self.limit {
return Err(Error::InvalidFormat(EXPANSION_BOUND));
}
if needed > self.buf.capacity() {
self.grow(needed)?;
}
Ok(())
}
fn grow(&mut self, needed: usize) -> Result<()> {
let capacity = self
.buf
.capacity()
.saturating_mul(2)
.max(needed)
.min(self.limit);
let mut grown = Vec::new();
grown
.try_reserve_exact(capacity)
.map_err(|_| Error::InvalidFormat(OUTPUT_ALLOCATION))?;
grown.extend_from_slice(&self.buf);
self.peak = self
.peak
.max(self.buf.capacity().saturating_add(grown.capacity()));
self.buf = Zeroizing::new(grown);
Ok(())
}
fn push(&mut self, byte: u8) -> Result<()> {
self.reserve(1)?;
self.buf.push(byte);
Ok(())
}
fn extend(&mut self, bytes: &[u8]) -> Result<()> {
self.reserve(bytes.len())?;
self.buf.extend_from_slice(bytes);
Ok(())
}
fn copy(&mut self, distance: usize, length: usize) -> Result<()> {
if distance == 0 || distance > self.buf.len() {
return Err(Error::InvalidFormat(DISTANCE_TOO_FAR));
}
self.reserve(length)?;
let mut remaining = length;
while remaining > 0 {
let chunk = remaining.min(distance);
let start = self.buf.len() - distance;
self.buf.extend_from_within(start..start + chunk);
remaining -= chunk;
}
Ok(())
}
}
pub(super) fn inflate_tracked(
input: &[u8],
max_output: usize,
capacity: &mut Capacity,
) -> Result<Zeroizing<Vec<u8>>> {
let mut reader = BitReader::new(input);
let mut out = Output::new(input.len(), max_output)?;
capacity.initial = out.buf.capacity();
let result = inflate_blocks(&mut reader, &mut out);
capacity.last = out.buf.capacity();
capacity.peak = out.peak;
capacity.len = out.buf.len();
result?;
Ok(out.buf)
}
fn inflate_blocks(reader: &mut BitReader<'_>, out: &mut Output) -> Result<()> {
loop {
let last = reader.bits(1)? == 1;
match reader.bits(2)? {
0b00 => stored_block(reader, out)?,
0b01 => {
let (literals, distances) = fixed_tables()?;
compressed_block(reader, out, &literals, &distances)?;
}
0b10 => {
let (literals, distances) = dynamic_tables(reader)?;
compressed_block(reader, out, &literals, &distances)?;
}
_ => return Err(Error::InvalidFormat(RESERVED_BLOCK_TYPE)),
}
if last {
return Ok(());
}
}
}
fn stored_block(reader: &mut BitReader<'_>, out: &mut Output) -> Result<()> {
reader.align_to_byte();
let header = reader.bytes(4)?;
let len = u16::from_le_bytes([header[0], header[1]]);
let nlen = u16::from_le_bytes([header[2], header[3]]);
if nlen != !len {
return Err(Error::InvalidFormat(STORED_LENGTH_MISMATCH));
}
let data = reader.bytes(usize::from(len))?;
out.extend(data)
}
fn fixed_tables() -> Result<(Huffman, Huffman)> {
let mut lengths = [0u8; 288];
lengths[..144].fill(8);
lengths[144..256].fill(9);
lengths[256..280].fill(7);
lengths[280..].fill(8);
let literals = Huffman::new(&lengths, Kind::Complete)?;
let distances = Huffman::new(&[5; 32], Kind::Complete)?;
Ok((literals, distances))
}
fn dynamic_tables(reader: &mut BitReader<'_>) -> Result<(Huffman, Huffman)> {
let hlit = reader.bits(5)? as usize + 257;
let hdist = reader.bits(5)? as usize + 1;
let hclen = reader.bits(4)? as usize + 4;
if hlit > 286 {
return Err(Error::InvalidFormat(TOO_MANY_LENGTH_CODES));
}
if hdist > 30 {
return Err(Error::InvalidFormat(TOO_MANY_DISTANCE_CODES));
}
let mut code_lengths = [0u8; 19];
for &symbol in &CODE_LENGTH_ORDER[..hclen] {
code_lengths[symbol] = reader.bits(3)? as u8;
}
let code_length_code = Huffman::new(&code_lengths, Kind::Complete)?;
let total = hlit + hdist;
let mut lengths = [0u8; 286 + 30];
let mut filled = 0;
while filled < total {
let symbol = code_length_code.decode(reader)?;
let (value, count) = match symbol {
0..=15 => (symbol as u8, 1),
16 => {
let previous = filled
.checked_sub(1)
.map(|i| lengths[i])
.ok_or(Error::InvalidFormat(REPEAT_WITHOUT_PREVIOUS))?;
(previous, 3 + reader.bits(2)? as usize)
}
17 => (0, 3 + reader.bits(3)? as usize),
18 => (0, 11 + reader.bits(7)? as usize),
_ => return Err(Error::InvalidFormat(INVALID_CODE)),
};
let end = filled + count;
if end > total {
return Err(Error::InvalidFormat(REPEAT_OVERRUN));
}
lengths[filled..end].fill(value);
filled = end;
}
debug_assert!(lengths.iter().all(|&l| u32::from(l) <= MAX_BITS));
if lengths[usize::from(END_OF_BLOCK)] == 0 {
return Err(Error::InvalidFormat(MISSING_END_OF_BLOCK));
}
if lengths[usize::from(END_OF_BLOCK)] == 1
&& lengths[..hlit].iter().filter(|&&l| l != 0).count() == 1
{
return Err(Error::InvalidFormat(SINGLE_LENGTH_CODE));
}
let literals = Huffman::new(&lengths[..hlit], Kind::Complete)?;
let distances = Huffman::new(&lengths[hlit..total], Kind::Distance)?;
Ok((literals, distances))
}
fn compressed_block(
reader: &mut BitReader<'_>,
out: &mut Output,
literals: &Huffman,
distances: &Huffman,
) -> Result<()> {
loop {
let symbol = literals.decode(reader)?;
if symbol < END_OF_BLOCK {
out.push(symbol as u8)?;
continue;
}
if symbol == END_OF_BLOCK {
return Ok(());
}
let index = usize::from(symbol - 257);
let (Some(&base), Some(&extra)) = (LENGTH_BASE.get(index), LENGTH_EXTRA.get(index)) else {
return Err(Error::InvalidFormat(INVALID_LENGTH_SYMBOL));
};
let length = usize::from(base) + reader.bits(u32::from(extra))? as usize;
let index = usize::from(distances.decode(reader)?);
let (Some(&base), Some(&extra)) = (DISTANCE_BASE.get(index), DISTANCE_EXTRA.get(index))
else {
return Err(Error::InvalidFormat(INVALID_DISTANCE_SYMBOL));
};
let distance = usize::from(base) + reader.bits(u32::from(extra))? as usize;
out.copy(distance, length)?;
}
}