use super::{
core::{driver::Driver, engine::Engine},
*,
};
use crate::{Backend, Compressor, RetentionPolicy};
use ::core::ops::Range;
use FramedEncodeError as E;
use alloc::vec::Vec;
#[derive(Debug)]
pub struct FramedCompressor {
pub(super) raw: Compressor,
pub(super) engine: Engine,
config: FramedEncodeConfig,
retention: RetentionPolicy,
active: bool,
}
#[derive(Clone, Copy, Debug)]
pub struct FramedCompressorBuilder {
config: FramedEncodeConfig,
backend: Option<Backend>,
retention: RetentionPolicy,
}
impl FramedCompressorBuilder {
#[must_use]
pub const fn with_backend(mut self, backend: Backend) -> Self {
self.backend = Some(backend);
self
}
#[must_use]
pub const fn with_retention(mut self, retention: RetentionPolicy) -> Self {
self.retention = retention;
self
}
pub fn build(self) -> Result<FramedCompressor, E> {
Engine::validate(*self.config.framing_config())?;
let mut builder = Compressor::builder(*self.config.encoder_config());
if let Some(backend) = self.backend {
builder = builder.with_backend(backend);
}
Ok(FramedCompressor {
raw: builder.build()?,
engine: Engine::new(*self.config.framing_config()),
config: self.config,
retention: self.retention,
active: false,
})
}
}
impl FramedCompressor {
pub fn new(config: FramedEncodeConfig) -> Result<Self, E> {
Self::builder(config).build()
}
#[must_use]
pub const fn builder(config: FramedEncodeConfig) -> FramedCompressorBuilder {
FramedCompressorBuilder {
config,
backend: None,
retention: RetentionPolicy::Aggressive,
}
}
#[must_use]
pub const fn config(&self) -> &FramedEncodeConfig {
&self.config
}
#[must_use]
pub const fn retention(&self) -> RetentionPolicy {
self.retention
}
#[must_use]
pub fn retained_bytes(&self) -> usize {
self.raw.retained_bytes() + self.engine.retained_bytes()
}
pub fn reconfigure(&mut self, config: FramedEncodeConfig) -> Result<(), E> {
Engine::validate(*config.framing_config())?;
config.encoder_config().validate()?;
if self.active {
self.recover();
} else {
self.cancel();
}
self.raw.reconfigure(*config.encoder_config())?;
self.config = config;
self.engine.reconfigure(*config.framing_config());
self.trim(self.retention);
Ok(())
}
pub fn trim(&mut self, policy: RetentionPolicy) {
if !policy.allows(self.retained_bytes()) {
self.engine.clear();
self.raw.trim(RetentionPolicy::ReleaseAll);
self.engine = Engine::new(*self.config.framing_config());
}
}
pub fn recover(&mut self) {
self.raw.trim(RetentionPolicy::ReleaseAll);
self.engine = Engine::new(*self.config.framing_config());
self.active = false;
}
#[must_use]
pub fn fork_empty(&self) -> Self {
Self {
raw: self.raw.fork_empty(),
engine: Engine::new(*self.config.framing_config()),
config: self.config,
retention: self.retention,
active: false,
}
}
pub(super) fn cancel(&mut self) {
self.engine.clear();
self.active = false;
self.trim(self.retention);
}
pub(super) fn begin_session(&mut self, stream: FramedEncodeStreamConfig) -> Result<(), E> {
if self.active {
return Err(E::AbandonedSession);
}
if self.engine.retained_bytes() > self.config.framing_config().max_buffer_bytes {
self.recover();
}
self.engine.start(stream)?;
self.active = true;
Ok(())
}
pub fn start(
&mut self,
stream: FramedEncodeStreamConfig,
) -> Result<FramedEncoderSession<'_>, E> {
self.begin_session(stream)?;
Ok(FramedEncoderSession { owner: self })
}
pub fn into_session(
mut self,
stream: FramedEncodeStreamConfig,
) -> Result<FramedEncoderSessionOwned, E> {
self.begin_session(stream)?;
Ok(FramedEncoderSessionOwned { owner: self })
}
pub fn compress(&mut self, input: FramedInput<'_>) -> Result<Vec<u8>, E> {
let mut output = Vec::new();
self.compress_into(input, &mut output)?;
Ok(output)
}
pub fn compress_into(
&mut self,
input: FramedInput<'_>,
dst: &mut Vec<u8>,
) -> Result<Range<usize>, E> {
let start = dst.len();
let result = (|| {
let stream = Driver::aggregate(input)?.into();
let mut driver = Driver::new(self.start(stream)?, input);
let mut buffer = [0; 8192];
loop {
let p = driver
.process(&mut buffer)
.map_err(FramedEncodeFailure::into_error)?;
dst.try_reserve(p.produced)
.map_err(|_| E::AllocationFailed)?;
dst.extend_from_slice(&buffer[..p.produced]);
if p.status == FramedEncoderStatus::Finished {
return Ok(start..dst.len());
}
}
})();
if result.is_err() {
dst.truncate(start);
}
result
}
pub fn compress_to_slice(
&mut self,
input: FramedInput<'_>,
dst: &mut [u8],
) -> Result<usize, FramedEncodeFailure> {
let stream = match Driver::aggregate(input) {
Ok(size) => size.into(),
Err(error) => return Err(self.engine.failure(error, 0, 0)),
};
let session = self.start(stream).map_err(|error| FramedEncodeFailure {
error,
consumed: 0,
produced: 0,
location: FramedEncodeLocation::default(),
})?;
let mut driver = Driver::new(session, input);
let mut consumed = 0;
let mut produced = 0;
loop {
let p = driver.process(&mut dst[produced..]).map_err(|mut e| {
e.consumed += consumed;
e.produced += produced;
e
})?;
consumed += p.consumed;
produced += p.produced;
if p.status == FramedEncoderStatus::Finished {
return Ok(produced);
}
if produced == dst.len() {
return Err(driver.failure(E::OutputTooSmall, consumed, produced));
}
}
}
}