use super::{
DecodeConfigError, DecodeError, DecodeOperation, DecodeStreamConfig, DecoderConfig,
DecoderSession, DecoderStatus, core::Stream,
};
use crate::{Backend, RetentionPolicy, dictionary::DictionaryRef};
use ::core::ops::Range;
use alloc::vec::Vec;
#[derive(Debug)]
pub struct Decompressor {
pub(super) config: DecoderConfig,
pub(super) backend: Backend,
retention: RetentionPolicy,
pub(super) workspace: Stream,
pub(super) active: bool,
}
#[derive(Debug, Clone, Copy)]
pub struct DecompressorBuilder {
config: DecoderConfig,
backend: Option<Backend>,
retention: RetentionPolicy,
}
impl DecompressorBuilder {
pub const fn with_retention(mut self, value: RetentionPolicy) -> Self {
self.retention = value;
self
}
pub const fn with_backend(mut self, value: Backend) -> Self {
self.backend = Some(value);
self
}
#[inline]
pub fn build(self) -> Result<Decompressor, DecodeConfigError> {
Ok(Decompressor {
config: self.config,
backend: self.backend.unwrap_or_default(),
retention: self.retention,
workspace: Stream::default(),
active: false,
})
}
}
impl Decompressor {
#[inline]
pub fn new(config: DecoderConfig) -> Result<Self, DecodeConfigError> {
Self::builder(config).build()
}
pub const fn builder(config: DecoderConfig) -> DecompressorBuilder {
DecompressorBuilder {
config,
backend: None,
retention: RetentionPolicy::Aggressive,
}
}
pub const fn config(&self) -> &DecoderConfig {
&self.config
}
pub const fn retention(&self) -> RetentionPolicy {
self.retention
}
pub fn reconfigure(&mut self, config: DecoderConfig) -> Result<(), DecodeConfigError> {
if self.active
|| self.retention == RetentionPolicy::ReleaseAll
|| (config != self.config && self.retention == RetentionPolicy::CurrentConfig)
{
self.recover();
}
self.config = config;
self.workspace.reset(config);
self.active = false;
Ok(())
}
pub const fn retained_bytes(&self) -> usize {
self.workspace.retained_bytes()
}
pub fn trim(&mut self, policy: RetentionPolicy) {
if policy == RetentionPolicy::ReleaseAll
|| matches!(policy, RetentionPolicy::Bounded { max_bytes } if self.retained_bytes() > max_bytes)
{
self.workspace = Stream::default();
}
}
pub fn recover(&mut self) {
self.workspace = Stream::default();
self.active = false;
}
pub fn fork_empty(&self) -> Self {
Self {
config: self.config,
backend: self.backend,
retention: self.retention,
workspace: Stream::default(),
active: false,
}
}
pub fn start(
&mut self,
stream: DecodeStreamConfig,
) -> Result<DecoderSession<'_, 'static>, DecodeError> {
DecoderSession::start(self, stream, None)
}
pub fn decompress(&mut self, src: &[u8]) -> Result<Vec<u8>, DecodeError> {
if !self.active
&& self.config.member_mode() == super::MemberMode::Single
&& self.config.limits() == super::DecodeLimits::default()
&& let Some(payload) = super::core::stored_payload(src, self.config.window_limit())
{
let mut dst = Vec::new();
dst.try_reserve_exact(payload.len())
.map_err(|_| DecodeError::AllocationFailed)?;
dst.extend_from_slice(payload);
self.trim(self.retention);
return Ok(dst);
}
let mut dst = Vec::new();
self.decompress_into(src, &mut dst)?;
Ok(dst)
}
pub fn decompress_into(
&mut self,
src: &[u8],
dst: &mut Vec<u8>,
) -> Result<Range<usize>, DecodeError> {
self.decode_into(None, src, dst)
}
fn decode_into(
&mut self,
dictionary: Option<DictionaryRef<'_>>,
src: &[u8],
dst: &mut Vec<u8>,
) -> Result<Range<usize>, DecodeError> {
let start = dst.len();
let collect = start == 0
&& dst.capacity() == 0
&& self.retained_bytes() == 0
&& dictionary.is_none()
&& self.config.member_mode() == super::MemberMode::Single;
let result = (|| {
let mut session =
DecoderSession::start(self, DecodeStreamConfig::default(), dictionary)?;
let mut consumed = 0;
let mut written = 0;
let probe = session
.process(src, &mut [], DecodeOperation::Finish)
.map_err(super::DecodeFailure::into_error)?;
consumed += probe.consumed;
if probe.status == DecoderStatus::Finished {
if consumed != src.len() {
return Err(DecodeError::TrailingData {
offset: consumed as u64,
});
}
return Ok(start..start);
}
if collect {
let progress = session
.collect(&src[consumed..])
.map_err(super::DecodeFailure::into_error)?;
consumed += progress.consumed;
if progress.status == DecoderStatus::Finished {
if consumed != src.len() {
return Err(DecodeError::TrailingData {
offset: consumed as u64,
});
}
*dst = session.take_collected();
return Ok(0..dst.len());
}
dst.try_reserve(session.collected().len())
.map_err(|_| DecodeError::AllocationFailed)?;
dst.extend_from_slice(session.collected());
written = dst.len();
}
let mut chunk = src
.len()
.saturating_mul(4)
.clamp(256, 1 << 16)
.max(session.declared_remaining())
.min(1 << 24);
loop {
dst.try_reserve(written + chunk)
.map_err(|_| DecodeError::AllocationFailed)?;
dst.resize(start + written + chunk, 0);
let progress = session
.process(
&src[consumed..],
&mut dst[start + written..],
DecodeOperation::Finish,
)
.map_err(super::DecodeFailure::into_error)?;
consumed += progress.consumed;
written += progress.produced;
dst.truncate(start + written);
if progress.status == DecoderStatus::Finished {
if consumed != src.len() {
return Err(DecodeError::TrailingData {
offset: consumed as u64,
});
}
return Ok(start..dst.len());
}
chunk = chunk
.saturating_mul(2)
.max(session.declared_remaining())
.min(1 << 24);
}
})();
if result.is_err() {
dst.truncate(start);
}
result
}
pub fn decompress_to_slice(
&mut self,
src: &[u8],
dst: &mut [u8],
) -> Result<usize, DecodeError> {
self.decode_to_slice(None, src, dst)
}
fn decode_to_slice(
&mut self,
dictionary: Option<DictionaryRef<'_>>,
src: &[u8],
dst: &mut [u8],
) -> Result<usize, DecodeError> {
let mut session = DecoderSession::start(self, DecodeStreamConfig::default(), dictionary)?;
let progress = session
.process(src, dst, DecodeOperation::Finish)
.map_err(super::DecodeFailure::into_error)?;
if progress.status == DecoderStatus::NeedsOutput {
return Err(DecodeError::OutputTooSmall {
written: progress.produced,
});
}
if progress.consumed != src.len() {
return Err(DecodeError::TrailingData {
offset: progress.consumed as u64,
});
}
Ok(progress.produced)
}
}
impl Decompressor {
pub fn start_with_dictionary<'d, 'dict>(
&'d mut self,
dictionary: impl Into<DictionaryRef<'dict>>,
stream: DecodeStreamConfig,
) -> Result<DecoderSession<'d, 'dict>, DecodeError> {
DecoderSession::start(self, stream, Some(dictionary.into()))
}
pub fn decompress_with_dictionary<'dict>(
&mut self,
dictionary: impl Into<DictionaryRef<'dict>>,
src: &[u8],
) -> Result<Vec<u8>, DecodeError> {
let mut output = Vec::new();
self.decode_into(Some(dictionary.into()), src, &mut output)?;
Ok(output)
}
pub fn decompress_with_dictionary_into<'dict>(
&mut self,
dictionary: impl Into<DictionaryRef<'dict>>,
src: &[u8],
dst: &mut Vec<u8>,
) -> Result<Range<usize>, DecodeError> {
self.decode_into(Some(dictionary.into()), src, dst)
}
pub fn decompress_with_dictionary_to_slice<'dict>(
&mut self,
dictionary: impl Into<DictionaryRef<'dict>>,
src: &[u8],
dst: &mut [u8],
) -> Result<usize, DecodeError> {
self.decode_to_slice(Some(dictionary.into()), src, dst)
}
}