use alloc::boxed::Box;
use alloc::sync::Arc;
use alloc::vec::Vec;
use core::mem::MaybeUninit;
use core::ops::Range;
use super::config::{DecoderConfig, FrameMode};
use super::core::Engine;
use super::core::out::{SliceOut, UninitOut, VecOut};
use super::core::stream::Stream;
use super::error::DecodeError;
use super::info::FrameInfo;
use super::session::{DecoderSession, DecoderSessionOwned};
use super::typed::TypedOutput;
use crate::dict::BundleStore;
use crate::{Backend, RetentionPolicy};
#[derive(Debug)]
pub struct Decompressor {
config: DecoderConfig,
retention: RetentionPolicy,
backend: Backend,
pub(crate) engine: Engine,
pub(crate) stream: Stream,
}
impl Default for Decompressor {
fn default() -> Self {
Self::new(DecoderConfig::DEFAULT)
}
}
#[derive(Clone, Debug)]
pub struct DecompressorBuilder {
config: DecoderConfig,
retention: RetentionPolicy,
backend: Option<Backend>,
dictionaries: Option<Arc<BundleStore>>,
}
impl DecompressorBuilder {
#[must_use]
pub const fn with_retention(mut self, retention: RetentionPolicy) -> Self {
self.retention = retention;
self
}
#[must_use]
pub const fn with_backend(mut self, backend: Backend) -> Self {
self.backend = Some(backend);
self
}
#[must_use]
pub fn with_dictionaries(mut self, store: impl Into<Arc<BundleStore>>) -> Self {
self.dictionaries = Some(store.into());
self
}
#[must_use]
pub fn build(self) -> Decompressor {
let backend = self.backend.unwrap_or_default();
let mut engine = Engine::new(backend);
engine.set_store(self.dictionaries);
Decompressor {
config: self.config,
retention: self.retention,
backend,
engine,
stream: Stream::default(),
}
}
}
impl Decompressor {
#[must_use]
pub fn new(config: DecoderConfig) -> Self {
Self::builder(config).build()
}
#[must_use]
pub const fn builder(config: DecoderConfig) -> DecompressorBuilder {
DecompressorBuilder {
config,
retention: RetentionPolicy::Aggressive,
backend: None,
dictionaries: None,
}
}
#[must_use]
pub const fn config(&self) -> &DecoderConfig {
&self.config
}
#[must_use]
pub const fn backend(&self) -> Backend {
self.backend
}
#[must_use]
pub const fn retention(&self) -> RetentionPolicy {
self.retention
}
pub fn reconfigure(&mut self, config: DecoderConfig) {
self.config = config;
self.stream.reset();
}
#[must_use]
pub fn retained_bytes(&self) -> usize {
self.engine.retained_bytes() + self.stream.retained_bytes()
}
pub fn trim(&mut self, policy: RetentionPolicy) {
if !policy.allows(self.stream.retained_bytes()) {
self.stream.release();
}
self.engine.trim(policy);
}
pub(crate) fn apply_retention(&mut self) {
if !self.retention.allows(self.retained_bytes()) {
self.trim(self.retention);
}
}
pub fn start(&mut self) -> DecoderSession<'_> {
self.stream.reset();
DecoderSession::new(self)
}
#[must_use]
pub fn into_session(mut self) -> DecoderSessionOwned {
self.stream.reset();
DecoderSessionOwned::new(Box::new(self))
}
fn check_consumed(&self, consumed: usize, src: &[u8]) -> Result<(), DecodeError> {
if self.config.frame_mode() == FrameMode::Single && consumed != src.len() {
return Err(DecodeError::TrailingData {
offset: consumed as u64,
});
}
Ok(())
}
pub fn decompress(&mut self, src: &[u8]) -> Result<Vec<u8>, DecodeError> {
let mut dst = Vec::new();
self.decompress_into(src, &mut dst)?;
Ok(dst)
}
pub fn frame_info(&self, src: &[u8]) -> Result<FrameInfo, DecodeError> {
FrameInfo::parse(
src,
self.backend().0,
self.config.compressed_checksum_check(),
)
}
pub fn decompress_typed(&mut self, src: &[u8]) -> Result<Vec<TypedOutput>, DecodeError> {
let result = self
.engine
.decode_typed(&self.config, src)
.and_then(|(outputs, consumed)| {
if consumed == src.len() {
Ok(outputs)
} else {
Err(DecodeError::TrailingData {
offset: consumed as u64,
})
}
});
self.apply_retention();
result
}
pub fn decompress_into(
&mut self,
src: &[u8],
dst: &mut Vec<u8>,
) -> Result<Range<usize>, DecodeError> {
let start = dst.len();
let result = self
.engine
.decode_frames(&self.config, src, &mut VecOut::new(dst))
.and_then(|consumed| self.check_consumed(consumed, src));
self.apply_retention();
match result {
Ok(()) => Ok(start..dst.len()),
Err(error) => {
dst.truncate(start);
Err(error)
}
}
}
pub fn decompress_to_slice(
&mut self,
src: &[u8],
dst: &mut [u8],
) -> Result<usize, DecodeError> {
let mut out = SliceOut::new(dst);
let result = self
.engine
.decode_frames(&self.config, src, &mut out)
.and_then(|consumed| self.check_consumed(consumed, src));
let written = super::core::out::Out::len(&out);
self.apply_retention();
result.map(|()| written)
}
pub fn decompress_to_uninit(
&mut self,
src: &[u8],
dst: &mut [MaybeUninit<u8>],
) -> Result<usize, DecodeError> {
let mut out = UninitOut::from(dst);
let result = self
.engine
.decode_frames(&self.config, src, &mut out)
.and_then(|consumed| self.check_consumed(consumed, src));
let written = super::core::out::Out::len(&out);
self.apply_retention();
result.map(|()| written)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn concatenated_mode_reads_every_frame() {
let frame = openzl_ffi::compress(b"ab", openzl_ffi::Params::default()).unwrap();
let both = [&frame[..], &frame].concat();
let mut decoder = Decompressor::new(FrameMode::Concatenated.into());
assert_eq!(decoder.decompress(&both).unwrap(), b"abab");
let mut decoder = Decompressor::default();
assert_eq!(
decoder.decompress(&both),
Err(DecodeError::TrailingData {
offset: frame.len() as u64
})
);
}
#[test]
fn slices_reconfiguration_and_trimming() {
let frame = openzl_ffi::compress(b"slice me", openzl_ffi::Params::default()).unwrap();
let mut decoder = Decompressor::default();
assert_eq!(decoder.retention(), RetentionPolicy::Aggressive);
let mut dst = [0u8; 16];
assert_eq!(decoder.decompress_to_slice(&frame, &mut dst), Ok(8));
assert_eq!(&dst[..8], b"slice me");
let trailing = [&frame[..], b"x"].concat();
assert_eq!(
decoder.decompress_to_slice(&trailing, &mut dst),
Err(DecodeError::TrailingData {
offset: frame.len() as u64
})
);
decoder.reconfigure(FrameMode::Concatenated.into());
assert_eq!(decoder.config().frame_mode(), FrameMode::Concatenated);
assert!(decoder.retained_bytes() > 0);
decoder.trim(RetentionPolicy::ReleaseAll);
assert_eq!(decoder.retained_bytes(), 0);
}
#[test]
fn failed_appends_roll_back() {
let mut decoder = Decompressor::builder(DecoderConfig::default())
.with_retention(RetentionPolicy::ReleaseAll)
.build();
let mut out = b"keep".to_vec();
assert!(decoder.decompress_into(b"bad", &mut out).is_err());
assert_eq!(out, b"keep");
assert_eq!(decoder.retained_bytes(), 0);
assert_eq!(decoder.backend(), Backend::default());
}
}