use std::io::Read as _;
use bytes::Bytes;
use crate::error::{Error, Result};
use super::container::{CompressionMethod, MAX_BLOCK_RAW_SIZE};
pub(crate) mod arith;
pub(crate) mod bzip2;
pub(crate) mod fqzcomp;
pub(crate) mod lzma;
pub(crate) mod rans4x16;
pub(crate) mod rans4x8;
pub(crate) mod tokenise;
pub fn decode(
method: CompressionMethod,
data: &[u8],
raw_size: usize,
path: &str,
offset: u64,
) -> Result<Bytes> {
let out = match method {
CompressionMethod::Raw => {
if data.len() != raw_size {
return Err(Error::corrupt(
path,
offset,
format!(
"a raw block declares {raw_size} bytes and carries {}",
data.len()
),
));
}
return Ok(Bytes::copy_from_slice(data));
}
CompressionMethod::Gzip => gzip(data, raw_size, path, offset)?,
CompressionMethod::Rans4x8 => rans4x8::decode(data, path, offset)?,
CompressionMethod::Rans4x16 => rans4x16::decode(data, path, offset)?,
CompressionMethod::Arith => arith::decode(data, path, offset)?,
CompressionMethod::NameTok => tokenise::decode(data, path, offset)?,
CompressionMethod::Bzip2 => bzip2::decode(data, raw_size, path, offset)?,
CompressionMethod::Fqzcomp => fqzcomp::decode(data, path, offset)?,
CompressionMethod::Lzma => lzma::decode(data, raw_size, path, offset)?,
};
if out.len() != raw_size {
return Err(Error::corrupt(
path,
offset,
format!(
"a {} block declares {raw_size} bytes and decoded to {}",
method.name(),
out.len()
),
));
}
Ok(Bytes::from(out))
}
fn gzip(data: &[u8], raw_size: usize, path: &str, offset: u64) -> Result<Vec<u8>> {
let mut out = Vec::with_capacity(raw_size.min(1 << 20));
inflate_bounded(
flate2::read::MultiGzDecoder::new(data),
&mut out,
raw_size,
"gzip",
path,
offset,
)?;
Ok(out)
}
pub(crate) fn inflate_bounded<R: std::io::Read>(
decoder: R,
out: &mut Vec<u8>,
limit: usize,
what: &str,
path: &str,
offset: u64,
) -> Result<()> {
let capped = limit.min(MAX_BLOCK_RAW_SIZE) as u64;
let read = decoder.take(capped + 1).read_to_end(out).map_err(|e| {
Error::corrupt(
path,
offset,
format!("could not inflate a {what} block: {e}"),
)
})?;
if read as u64 > capped {
return Err(Error::corrupt(
path,
offset,
format!("a {what} block inflating past the {limit} bytes it declared"),
));
}
Ok(())
}
pub(crate) fn short(path: &str, offset: u64, what: &str) -> Error {
Error::corrupt(
path,
offset,
format!("{what} ran past the end of the block"),
)
}
pub(crate) const MAX_CODEC_LEN: usize = 1 << 30;
pub(crate) struct PackMeta {
map: Vec<u8>,
pub(crate) packed_len: usize,
n_symbols: usize,
}
impl PackMeta {
pub(crate) fn read(reader: &mut ByteReader<'_>) -> Result<Self> {
let n_symbols = reader.u8()? as usize;
if n_symbols == 0 || n_symbols > 16 {
return Err(Error::corrupt(
reader.path(),
reader.offset(),
format!("a pack map of {n_symbols} symbols, which cannot be packed"),
));
}
let mut map = reader.take(n_symbols)?.to_vec();
let width = match n_symbols {
1 => 1,
2 => 2,
3..=4 => 4,
_ => 16,
};
map.resize(width, 0);
Ok(Self {
map,
packed_len: reader.length()?,
n_symbols,
})
}
pub(crate) fn unpack(
&self,
data: &[u8],
len: usize,
path: &str,
offset: u64,
) -> Result<Vec<u8>> {
let per_byte = match self.n_symbols {
1 => return Ok(vec![self.map[0]; len]),
2 => 8,
3..=4 => 4,
_ => 2,
};
let bits = 8 / per_byte;
let mask = (1u8 << bits) - 1;
let wanted = len.div_ceil(per_byte);
if data.len() < wanted {
return Err(Error::corrupt(
path,
offset,
format!(
"a packed stream of {} bytes where {wanted} were needed for {len} values",
data.len()
),
));
}
let mut out = Vec::with_capacity(len.min(1 << 20));
'outer: for &byte in data {
for k in 0..per_byte {
if out.len() >= len {
break 'outer;
}
out.push(self.map[((byte >> (k * bits)) & mask) as usize]);
}
}
Ok(out)
}
}
pub(crate) struct ByteReader<'a> {
data: &'a [u8],
pos: usize,
path: &'a str,
base: u64,
}
impl<'a> ByteReader<'a> {
pub fn new(data: &'a [u8], path: &'a str, base: u64) -> Self {
Self {
data,
pos: 0,
path,
base,
}
}
pub fn remaining(&self) -> usize {
self.data.len() - self.pos
}
pub fn is_empty(&self) -> bool {
self.remaining() == 0
}
fn fail(&self, what: &str) -> Error {
short(self.path, self.base + self.pos as u64, what)
}
pub fn u8(&mut self) -> Result<u8> {
let byte = *self.data.get(self.pos).ok_or_else(|| self.fail("a byte"))?;
self.pos += 1;
Ok(byte)
}
pub fn u16(&mut self) -> Result<u16> {
Ok(u16::from_le_bytes(
self.take(2)?.try_into().expect("two bytes"),
))
}
pub fn u32(&mut self) -> Result<u32> {
Ok(u32::from_le_bytes(
self.take(4)?.try_into().expect("four bytes"),
))
}
pub fn uint7(&mut self) -> Result<u32> {
let mut value: u32 = 0;
for _ in 0..5 {
let byte = self.u8()?;
value = (value << 7) | u32::from(byte & 0x7f);
if byte < 128 {
return Ok(value);
}
}
Err(Error::corrupt(
self.path,
self.base + self.pos as u64,
"a uint7 longer than the 32 bits it can hold",
))
}
pub fn itf8(&mut self) -> Result<u32> {
let first = self.u8()?;
Ok(if first & 0x80 == 0 {
u32::from(first)
} else if first & 0x40 == 0 {
(u32::from(first & 0x7f) << 8) | u32::from(self.u8()?)
} else if first & 0x20 == 0 {
let rest = self.take(2)?;
(u32::from(first & 0x3f) << 16) | (u32::from(rest[0]) << 8) | u32::from(rest[1])
} else if first & 0x10 == 0 {
let rest = self.take(3)?;
(u32::from(first & 0x1f) << 24)
| (u32::from(rest[0]) << 16)
| (u32::from(rest[1]) << 8)
| u32::from(rest[2])
} else {
let rest = self.take(4)?;
(u32::from(first & 0x0f) << 28)
| (u32::from(rest[0]) << 20)
| (u32::from(rest[1]) << 12)
| (u32::from(rest[2]) << 4)
| u32::from(rest[3] & 0x0f)
})
}
pub fn length(&mut self) -> Result<usize> {
let value = self.uint7()? as usize;
if value > MAX_CODEC_LEN {
return Err(Error::corrupt(
self.path,
self.base + self.pos as u64,
format!("a declared length of {value} bytes, past this reader's ceiling"),
));
}
Ok(value)
}
pub fn take(&mut self, n: usize) -> Result<&'a [u8]> {
let end = self
.pos
.checked_add(n)
.filter(|end| *end <= self.data.len())
.ok_or_else(|| self.fail(&format!("{n} bytes")))?;
let out = &self.data[self.pos..end];
self.pos = end;
Ok(out)
}
pub fn path(&self) -> &'a str {
self.path
}
pub fn offset(&self) -> u64 {
self.base + self.pos as u64
}
}