use super::super::blocks::literals_section::{LiteralsSection, LiteralsSectionType};
use super::scratch::HuffmanScratch;
use super::wiping::WipingBytes;
use crate::bit_io::BitReaderReversed;
use crate::common::MAX_BLOCK_SIZE;
use crate::decoding::errors::DecompressLiteralsError;
use crate::huff0::HuffmanDecoder;
use alloc::vec::Vec;
pub(crate) trait LiteralOutput {
fn len(&self) -> usize;
fn reserve(&mut self, additional: usize) -> Result<(), crate::workspace::CapacityError>;
fn extend_bytes(&mut self, bytes: &[u8]) -> Result<(), crate::workspace::CapacityError>;
fn resize(&mut self, length: usize, byte: u8) -> Result<(), crate::workspace::CapacityError>;
fn push(&mut self, byte: u8) -> Result<(), crate::workspace::CapacityError>;
}
impl LiteralOutput for Vec<u8> {
fn len(&self) -> usize { Vec::len(self) }
fn reserve(&mut self, additional: usize) -> Result<(), crate::workspace::CapacityError> {
self.try_reserve(additional).map_err(|_| crate::workspace::CapacityError::AllocationFailed)
}
fn extend_bytes(&mut self, bytes: &[u8]) -> Result<(), crate::workspace::CapacityError> {
LiteralOutput::reserve(self, bytes.len())?;
self.extend_from_slice(bytes); Ok(())
}
fn resize(&mut self, length: usize, byte: u8) -> Result<(), crate::workspace::CapacityError> {
LiteralOutput::reserve(self, length.saturating_sub(self.len()))?;
Vec::resize(self, length, byte); Ok(())
}
fn push(&mut self, byte: u8) -> Result<(), crate::workspace::CapacityError> {
LiteralOutput::reserve(self, 1)?;
Vec::push(self, byte); Ok(())
}
}
impl LiteralOutput for WipingBytes {
fn len(&self) -> usize { self.as_slice().len() }
fn reserve(&mut self, additional: usize) -> Result<(), crate::workspace::CapacityError> { WipingBytes::reserve(self, additional) }
fn extend_bytes(&mut self, bytes: &[u8]) -> Result<(), crate::workspace::CapacityError> { self.extend_from_slice(bytes) }
fn resize(&mut self, length: usize, byte: u8) -> Result<(), crate::workspace::CapacityError> { WipingBytes::resize(self, length, byte) }
fn push(&mut self, byte: u8) -> Result<(), crate::workspace::CapacityError> { WipingBytes::push(self, byte) }
}
fn push_literal(
target: &mut impl LiteralOutput,
byte: u8,
expected: usize,
) -> Result<(), DecompressLiteralsError> {
if target.len() >= expected {
return Err(DecompressLiteralsError::DecodedLiteralCountMismatch {
decoded: target.len().saturating_add(1),
expected,
});
}
target.push(byte)?;
Ok(())
}
pub(crate) fn decode_literals(
section: &LiteralsSection,
scratch: &mut HuffmanScratch,
source: &[u8],
target: &mut impl LiteralOutput,
) -> Result<u32, DecompressLiteralsError> {
if section.regenerated_size > MAX_BLOCK_SIZE {
return Err(DecompressLiteralsError::TooManyLiterals {
declared: section.regenerated_size,
});
}
let needed = match section.ls_type {
LiteralsSectionType::Raw => section.regenerated_size as usize,
LiteralsSectionType::RLE => 1,
_ => section
.compressed_size
.ok_or(DecompressLiteralsError::MissingCompressedSize)? as usize,
};
if source.len() < needed {
return Err(DecompressLiteralsError::MissingBytesForLiterals {
got: source.len(),
needed,
});
}
match section.ls_type {
LiteralsSectionType::Raw => {
target.extend_bytes(&source[0..section.regenerated_size as usize])?;
Ok(section.regenerated_size)
}
LiteralsSectionType::RLE => {
target.resize(target.len() + section.regenerated_size as usize, source[0])?;
Ok(1)
}
LiteralsSectionType::Compressed | LiteralsSectionType::Treeless => {
let bytes_read = decompress_literals(section, scratch, source, target)?;
Ok(bytes_read)
}
}
}
fn decompress_literals(
section: &LiteralsSection,
scratch: &mut HuffmanScratch,
source: &[u8],
target: &mut impl LiteralOutput,
) -> Result<u32, DecompressLiteralsError> {
use DecompressLiteralsError as err;
let compressed_size = section.compressed_size.ok_or(err::MissingCompressedSize)? as usize;
let num_streams = section.num_streams.ok_or(err::MissingNumStreams)?;
target.reserve(section.regenerated_size as usize)?;
let source = &source[0..compressed_size];
let mut bytes_read = 0;
match section.ls_type {
LiteralsSectionType::Compressed => {
bytes_read += scratch.table.build_decoder(source)?;
vprintln!("Built huffman table using {} bytes", bytes_read);
}
LiteralsSectionType::Treeless if scratch.table.max_num_bits == 0 => {
return Err(err::UninitializedHuffmanTable);
}
_ => { }
}
let source = &source[bytes_read as usize..];
if num_streams == 4 {
if source.len() < 6 {
return Err(err::MissingBytesForJumpHeader { got: source.len() });
}
let jump1 = source[0] as usize + ((source[1] as usize) << 8);
let jump2 = jump1 + source[2] as usize + ((source[3] as usize) << 8);
let jump3 = jump2 + source[4] as usize + ((source[5] as usize) << 8);
bytes_read += 6;
let source = &source[6..];
if source.len() < jump3 {
return Err(err::MissingBytesForLiterals {
got: source.len(),
needed: jump3,
});
}
let stream1 = &source[..jump1];
let stream2 = &source[jump1..jump2];
let stream3 = &source[jump2..jump3];
let stream4 = &source[jump3..];
for stream in &[stream1, stream2, stream3, stream4] {
let mut decoder = HuffmanDecoder::new(&scratch.table);
let mut br = BitReaderReversed::new(stream);
let mut skipped_bits = 0;
loop {
let val = br.get_bits(1);
skipped_bits += 1;
if val == 1 || skipped_bits > 8 {
break;
}
}
if skipped_bits > 8 {
return Err(DecompressLiteralsError::ExtraPadding { skipped_bits });
}
decoder.init_state(&mut br);
while br.bits_remaining() > -(scratch.table.max_num_bits as isize) {
push_literal(
target,
decoder.decode_symbol(),
section.regenerated_size as usize,
)?;
decoder.next_state(&mut br);
}
if br.bits_remaining() != -(scratch.table.max_num_bits as isize) {
return Err(DecompressLiteralsError::BitstreamReadMismatch {
read_til: br.bits_remaining(),
expected: -(scratch.table.max_num_bits as isize),
});
}
}
bytes_read += source.len() as u32;
} else {
assert!(num_streams == 1);
let mut decoder = HuffmanDecoder::new(&scratch.table);
let mut br = BitReaderReversed::new(source);
let mut skipped_bits = 0;
loop {
let val = br.get_bits(1);
skipped_bits += 1;
if val == 1 || skipped_bits > 8 {
break;
}
}
if skipped_bits > 8 {
return Err(DecompressLiteralsError::ExtraPadding { skipped_bits });
}
decoder.init_state(&mut br);
while br.bits_remaining() > -(scratch.table.max_num_bits as isize) {
push_literal(
target,
decoder.decode_symbol(),
section.regenerated_size as usize,
)?;
decoder.next_state(&mut br);
}
bytes_read += source.len() as u32;
}
if target.len() != section.regenerated_size as usize {
return Err(DecompressLiteralsError::DecodedLiteralCountMismatch {
decoded: target.len(),
expected: section.regenerated_size as usize,
});
}
Ok(bytes_read)
}
#[cfg(test)]
mod bounds_tests {
use super::*;
use alloc::vec;
#[test]
fn short_literal_sources_return_errors_before_output_changes() {
for kind in [
LiteralsSectionType::Raw,
LiteralsSectionType::RLE,
LiteralsSectionType::Compressed,
] {
let mut section = LiteralsSection::new();
section.ls_type = kind;
section.regenerated_size = 1;
section.compressed_size = Some(1);
let mut scratch = HuffmanScratch::new();
let mut output = vec![0x6d];
assert!(matches!(
decode_literals(§ion, &mut scratch, &[], &mut output),
Err(DecompressLiteralsError::MissingBytesForLiterals { got: 0, needed: 1 })
));
assert_eq!(output, [0x6d]);
}
}
#[test]
fn excessive_literal_header_rejected_before_reserving() {
let mut section = LiteralsSection::new();
section.ls_type = LiteralsSectionType::RLE;
section.regenerated_size = MAX_BLOCK_SIZE + 1;
let mut output = Vec::new();
assert!(matches!(
decode_literals(§ion, &mut HuffmanScratch::new(), &[0xab], &mut output),
Err(DecompressLiteralsError::TooManyLiterals { .. })
));
assert_eq!(output.capacity(), 0);
}
}