use alloc::boxed::Box;
use alloc::vec;
use alloc::vec::Vec;
use core::mem::MaybeUninit;
use core::ops::Range;
use super::config::{ConfigError, EncoderConfig, SizeOverflow};
use super::core::cctx::{Engine, RequestedParams, compress_bound};
use super::core::stream::Stream;
use super::error::EncodeError;
use super::internal::requested_params;
use super::session::{EncoderSession, EncoderSessionOwned, StreamConfig};
use crate::{Backend, RetentionPolicy};
#[derive(Debug)]
pub struct Compressor {
config: EncoderConfig,
pub(crate) requested: RequestedParams,
retention: RetentionPolicy,
backend: Backend,
pub(crate) engine: Box<Engine>,
pub(crate) stream: Stream,
next_input_id: u64,
}
impl Default for Compressor {
fn default() -> Self {
Self::validated(
EncoderConfig::default(),
RetentionPolicy::Aggressive,
Backend::default(),
)
}
}
impl Compressor {
fn validated(config: EncoderConfig, retention: RetentionPolicy, backend: Backend) -> Self {
Self {
config,
requested: requested_params(&config),
retention,
backend,
engine: Box::new(Engine::with_backend(backend)),
stream: Stream::new(),
next_input_id: 0,
}
}
pub fn new(config: EncoderConfig) -> Result<Self, ConfigError> {
Self::builder(config).build()
}
#[must_use]
pub const fn builder(config: EncoderConfig) -> CompressorBuilder {
CompressorBuilder {
config,
retention: RetentionPolicy::Aggressive,
backend: None,
}
}
#[must_use]
pub const fn config(&self) -> &EncoderConfig {
&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: EncoderConfig) -> Result<(), ConfigError> {
config.validate()?;
self.config = config;
self.requested = requested_params(&config);
self.stream.reset_session();
Ok(())
}
pub const fn max_compressed_size(input_size: usize) -> Result<usize, SizeOverflow> {
match compress_bound(input_size) {
Some(bound) => Ok(bound),
None => Err(SizeOverflow),
}
}
fn next_id(&mut self) -> u64 {
self.next_input_id += 1;
self.next_input_id
}
pub fn compress(&mut self, src: &[u8]) -> Result<Vec<u8>, EncodeError> {
let mut out = Vec::new();
self.compress_into(src, &mut out)?;
Ok(out)
}
pub fn compress_into(
&mut self,
src: &[u8],
dst: &mut Vec<u8>,
) -> Result<Range<usize>, EncodeError> {
let bound = Self::max_compressed_size(src.len())?;
let start = dst.len();
let limit = start.checked_add(bound).ok_or(SizeOverflow)?;
let result = if src.len() <= crate::shared::format::BLOCK_SIZE_MAX {
dst.resize(limit, 0);
self.compress_to_slice(src, &mut dst[start..])
} else {
self.stream.reset_session();
let id = self.next_id();
let result = super::core::compress_once_append(
&mut self.engine,
&self.requested,
src,
id,
dst,
bound,
)
.map_err(|error| EncodeError::from_core(error, bound, Some(src.len() as u64)));
self.apply_retention();
result
};
match result {
Ok(written) => {
dst.truncate(start + written);
Ok(start..start + written)
}
Err(error) => {
dst.truncate(start);
Err(error)
}
}
}
pub fn compress_to_slice(&mut self, src: &[u8], dst: &mut [u8]) -> Result<usize, EncodeError> {
self.stream.reset_session();
let id = self.next_id();
let result =
super::core::compress_once_into(&mut self.engine, &self.requested, src, id, dst)
.map_err(|error| EncodeError::from_core(error, dst.len(), Some(src.len() as u64)));
self.apply_retention();
result
}
pub fn compress_to_uninit(
&mut self,
src: &[u8],
dst: &mut [MaybeUninit<u8>],
) -> Result<usize, EncodeError> {
let mut scratch = vec![0u8; dst.len()];
let written = self.compress_to_slice(src, &mut scratch)?;
for (slot, &byte) in dst.iter_mut().zip(&scratch[..written]) {
slot.write(byte);
}
Ok(written)
}
pub fn start(&mut self, stream: StreamConfig) -> EncoderSession<'_> {
self.begin_stream(stream);
EncoderSession::new(self, stream)
}
#[must_use]
pub fn into_session(mut self, stream: StreamConfig) -> EncoderSessionOwned {
self.begin_stream(stream);
EncoderSessionOwned::new(self, stream)
}
pub(crate) fn begin_stream(&mut self, stream: StreamConfig) {
self.stream.reset_session();
if let super::session::InputSize::Exact(size) = stream.input_size() {
let _ = self.stream.set_pledged_src_size(size);
}
}
#[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.retained_bytes()) {
*self.engine = Engine::with_backend(self.backend);
self.stream.release();
}
}
pub(crate) fn apply_retention(&mut self) {
self.trim(self.retention);
}
}
#[derive(Debug, Clone, Copy)]
pub struct CompressorBuilder {
config: EncoderConfig,
retention: RetentionPolicy,
backend: Option<Backend>,
}
impl CompressorBuilder {
#[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
}
pub fn build(self) -> Result<Compressor, ConfigError> {
self.config.validate()?;
Ok(Compressor::validated(
self.config,
self.retention,
self.backend.unwrap_or_default(),
))
}
}