use crate::{McapError, McapResult};
#[derive(Debug, Clone, Copy)]
pub struct DecompressResult {
pub consumed: usize,
pub wrote: usize,
}
pub trait Decompressor: Send {
fn next_read_size(&self) -> usize;
fn decompress(&mut self, src: &[u8], dst: &mut [u8]) -> McapResult<DecompressResult>;
fn reset(&mut self) -> McapResult<()>;
fn name(&self) -> &'static str;
}
pub(crate) struct Decompressors(Vec<Box<dyn Decompressor>>);
impl Decompressors {
pub(crate) fn builtin() -> Self {
Self(vec![
#[cfg(feature = "zstd")]
Box::new(crate::sans_io::zstd::ZstdDecoder::new()),
#[cfg(feature = "lz4")]
Box::new(crate::sans_io::lz4::Lz4Decoder::new()),
])
}
pub(crate) fn register(&mut self, decompressor: impl Decompressor + 'static) -> McapResult<()> {
let name = decompressor.name();
if name.is_empty() {
return Err(McapError::EmptyDecompressorName);
}
match self.position(name) {
Some(i) => self.0[i] = Box::new(decompressor),
None => self.0.push(Box::new(decompressor)),
}
Ok(())
}
pub(crate) fn decompress_chunk(
&mut self,
name: &str,
src: &[u8],
dst: &mut Vec<u8>,
uncompressed_size: usize,
) -> McapResult<()> {
let decompressor = match self.position(name) {
Some(i) => self.0[i].as_mut(),
None => return Err(McapError::UnsupportedCompression(name.into())),
};
dst.clear();
dst.resize(uncompressed_size, 0);
let mut input_pos = 0;
let mut output_pos = 0;
let decompressed = (|| {
while output_pos < uncompressed_size {
let input = &src[input_pos..];
let output = &mut dst[output_pos..uncompressed_size];
let output_len = output.len();
let res = check_decompress_result(
decompressor.decompress(input, output)?,
input.len(),
output_len,
)?;
if res.consumed == 0 && res.wrote == 0 {
return Err(no_progress_error());
}
input_pos += res.consumed;
output_pos += res.wrote;
}
Ok(())
})();
decompressed.and(decompressor.reset())
}
pub(crate) fn take(&mut self, name: &str) -> Option<Box<dyn Decompressor>> {
let i = self.position(name)?;
Some(self.0.swap_remove(i))
}
pub(crate) fn restore(&mut self, mut decompressor: Box<dyn Decompressor>) -> McapResult<()> {
if self.position(decompressor.name()).is_some() {
return Ok(());
}
let reset = decompressor.reset();
self.0.push(decompressor);
reset
}
#[cfg(test)]
pub(crate) fn contains(&self, name: &str) -> bool {
self.position(name).is_some()
}
fn position(&self, name: &str) -> Option<usize> {
self.0
.iter()
.position(|decompressor| decompressor.name() == name)
}
}
pub(crate) fn check_decompress_result(
result: DecompressResult,
src_len: usize,
dst_len: usize,
) -> McapResult<DecompressResult> {
if result.consumed > src_len || result.wrote > dst_len {
return Err(McapError::DecompressionError(
"decompressor reported more bytes than the buffers provided".into(),
));
}
Ok(result)
}
pub(crate) fn no_progress_error() -> McapError {
McapError::DecompressionError("decompressor made no progress".into())
}