use core::marker::PhantomData;
use heapless::Vec;
use tamp_sys::{
TAMP_INPUT_EXHAUSTED, TAMP_OK, TAMP_OUTPUT_FULL, TampConf, TampDecompressor,
tamp_decompressor_decompress_cb, tamp_decompressor_init, tamp_decompressor_read_header, tamp_res,
};
use crate::{Error, Config};
pub struct Decompressor<const N: usize> {
inner: TampDecompressor,
window: Vec<u8, N>,
_marker: PhantomData<*mut ()>,
}
impl<const N: usize> Decompressor<N> {
pub fn new(config: Config) -> Result<Self, Error> {
Self::with_dictionary(config, None)
}
pub fn with_dictionary(config: Config, dictionary: Option<&[u8]>) -> Result<Self, Error> {
let expected_size = config.window_size();
if N != expected_size {
return Err(Error::InvalidConfig(
"Buffer size N must equal 2^window_bits",
));
}
let mut window = Vec::new();
window.resize(N, 0).map_err(|_| Error::BufferTooSmall)?;
if let Some(dict) = dictionary
&& config.use_custom_dictionary
{
let copy_len = dict.len().min(N);
window[..copy_len].copy_from_slice(&dict[..copy_len]);
}
let mut decompressor = Self {
inner: unsafe { core::mem::zeroed() },
window,
_marker: PhantomData,
};
let c_config = config.to_c_config();
let result = unsafe {
tamp_decompressor_init(
&mut decompressor.inner,
&c_config,
decompressor.window.as_mut_ptr(),
)
};
Error::from_tamp_res(result)?;
Ok(decompressor)
}
pub fn from_header(input: &[u8]) -> Result<(Self, usize), Error> {
let mut conf = unsafe { core::mem::zeroed::<TampConf>() };
let mut input_consumed = 0;
let result = unsafe {
tamp_decompressor_read_header(
&mut conf,
input.as_ptr(),
input.len(),
&mut input_consumed,
)
};
Error::from_tamp_res(result)?;
let config = Config {
window_bits: conf.window() as u8,
literal_bits: conf.literal() as u8,
use_custom_dictionary: conf.use_custom_dictionary() != 0,
lazy_matching: false, };
let expected_size = config.window_size();
if N != expected_size {
return Err(Error::InvalidConfig("Buffer size N doesn't match header"));
}
let decompressor = Self::new(config)?;
Ok((decompressor, input_consumed))
}
pub fn decompress_chunk(
&mut self,
input: &[u8],
output: &mut [u8],
) -> Result<(usize, usize), Error> {
let mut input_consumed = 0;
let mut output_written = 0;
let result = unsafe {
tamp_decompressor_decompress_cb(
&mut self.inner,
output.as_mut_ptr(),
output.len(),
&mut output_written,
input.as_ptr(),
input.len(),
&mut input_consumed,
None, core::ptr::null_mut(), )
};
match result {
x if x == TAMP_OK as tamp_res
|| x == TAMP_OUTPUT_FULL as tamp_res
|| x == TAMP_INPUT_EXHAUSTED as tamp_res =>
{
Ok((input_consumed, output_written))
}
_ => Error::from_tamp_res(result).map(|_| (input_consumed, output_written)),
}
}
}