use alloc::boxed::Box;
use core::mem::MaybeUninit;
use super::core::stream::{Op, StepStatus};
use super::encoder::Compressor;
use super::error::EncodeError;
use crate::shared::destination::Destination;
#[derive(Copy, Clone, Debug, Default, Eq, PartialEq, Hash)]
pub enum InputSize {
#[default]
Unknown,
Exact(u64),
}
impl From<u64> for InputSize {
fn from(value: u64) -> Self {
Self::Exact(value)
}
}
#[derive(Copy, Clone, Debug, Default, Eq, PartialEq, Hash)]
pub struct StreamConfig {
input_size: InputSize,
}
impl StreamConfig {
#[must_use]
pub const fn with_input_size(mut self, input_size: InputSize) -> Self {
self.input_size = input_size;
self
}
#[must_use]
pub const fn input_size(&self) -> InputSize {
self.input_size
}
}
impl From<InputSize> for StreamConfig {
fn from(input_size: InputSize) -> Self {
Self { input_size }
}
}
#[cfg_attr(
feature = "decompression",
doc = "[`DecodeOperation`]: crate::DecodeOperation"
)]
#[cfg_attr(
not(feature = "decompression"),
doc = "[`DecodeOperation`]: crate#select-codecs"
)]
#[derive(Copy, Clone, Debug, Default, Eq, PartialEq, Hash)]
pub enum Operation {
#[default]
Continue,
Flush,
End,
}
impl Operation {
const fn to_core(self) -> Op {
match self {
Self::Continue => Op::Continue,
Self::Flush => Op::Flush,
Self::End => Op::End,
}
}
}
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
pub enum EncoderStatus {
NeedsInput,
NeedsOutput,
Finished,
}
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
pub struct Progress {
pub consumed: usize,
pub produced: usize,
pub status: EncoderStatus,
}
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
enum State {
Open,
Finished,
Failed,
}
#[derive(Debug)]
struct Core {
state: State,
declared: Option<u64>,
}
impl Core {
const fn new(stream: StreamConfig) -> Self {
Self {
state: State::Open,
declared: match stream.input_size() {
InputSize::Exact(size) => Some(size),
InputSize::Unknown => None,
},
}
}
fn process<D: Destination + ?Sized>(
&mut self,
compressor: &mut Compressor,
input: &[u8],
output: &mut D,
operation: Operation,
) -> Result<Progress, EncodeError> {
match self.state {
State::Failed => return Err(EncodeError::InvalidState),
State::Finished => {
return Ok(Progress {
consumed: 0,
produced: 0,
status: EncoderStatus::Finished,
});
}
State::Open => {}
}
if let Some(error) = compressor.take_plan_error() {
self.state = State::Failed;
return Err(error);
}
let params = *compressor.params();
let result = compressor.stream.process(
&mut compressor.engine,
¶ms,
self.declared,
input,
output,
operation.to_core(),
);
let step = match result {
Ok(step) => step,
Err(error) => {
self.state = State::Failed;
compressor.stream.reset();
compressor.apply_retention();
return Err(EncodeError::from_stream(error, self.declared));
}
};
let status = match step.status {
StepStatus::NeedsInput => EncoderStatus::NeedsInput,
StepStatus::NeedsOutput => EncoderStatus::NeedsOutput,
StepStatus::Finished => {
self.state = State::Finished;
compressor.stream.reset();
compressor.apply_retention();
EncoderStatus::Finished
}
};
Ok(Progress {
consumed: step.consumed,
produced: step.produced,
status,
})
}
}
macro_rules! session_methods {
() => {
pub fn process(
&mut self,
input: &[u8],
output: &mut [u8],
operation: Operation,
) -> Result<Progress, EncodeError> {
self.core
.process(&mut self.compressor, input, output, operation)
}
pub fn process_uninit(
&mut self,
input: &[u8],
output: &mut [MaybeUninit<u8>],
operation: Operation,
) -> Result<Progress, EncodeError> {
self.core
.process(&mut self.compressor, input, output, operation)
}
pub fn flush(&mut self, output: &mut [u8]) -> Result<Progress, EncodeError> {
self.process(&[], output, Operation::Flush)
}
pub fn finish(&mut self, output: &mut [u8]) -> Result<Progress, EncodeError> {
self.process(&[], output, Operation::End)
}
#[must_use]
pub fn is_finished(&self) -> bool {
self.core.state == State::Finished
}
};
}
#[derive(Debug)]
pub struct EncoderSession<'c> {
compressor: &'c mut Compressor,
core: Core,
}
impl<'c> EncoderSession<'c> {
pub(crate) const fn new(compressor: &'c mut Compressor, stream: StreamConfig) -> Self {
Self {
compressor,
core: Core::new(stream),
}
}
session_methods!();
}
#[derive(Debug)]
pub struct EncoderSessionOwned {
compressor: Box<Compressor>,
core: Core,
}
impl EncoderSessionOwned {
pub(crate) const fn new(compressor: Box<Compressor>, stream: StreamConfig) -> Self {
Self {
compressor,
core: Core::new(stream),
}
}
session_methods!();
pub fn reinit(&mut self, stream: StreamConfig) {
self.compressor.begin_stream();
self.core = Core::new(stream);
}
#[must_use]
pub fn into_compressor(self) -> Compressor {
let mut compressor = *self.compressor;
compressor.begin_stream();
compressor
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::EncoderConfig;
use alloc::vec::Vec;
#[test]
fn stream_configs_carry_their_input_size() {
assert_eq!(InputSize::from(7u64), InputSize::Exact(7));
let stream = StreamConfig::default().with_input_size(InputSize::Exact(3));
assert_eq!(stream.input_size(), InputSize::Exact(3));
assert_eq!(
StreamConfig::from(InputSize::Unknown),
StreamConfig::default()
);
}
#[test]
fn a_failed_session_refuses_further_work() {
let mut encoder = Compressor::default();
let mut session = encoder.start(InputSize::Exact(2).into());
let mut out = [0u8; 64];
assert_eq!(
session.process(b"abc", &mut out, Operation::Continue),
Err(EncodeError::InputSizeMismatch { declared: 2 })
);
assert_eq!(session.flush(&mut out), Err(EncodeError::InvalidState));
assert!(!session.is_finished());
}
#[test]
fn owned_sessions_restart_and_return_the_compressor() {
let mut session = Compressor::new(EncoderConfig::default())
.unwrap()
.into_session(InputSize::Unknown.into());
let mut out = [0u8; 128];
let first = session.process(b"one", &mut out, Operation::End).unwrap();
assert!(session.is_finished());
let frame: Vec<u8> = out[..first.produced].to_vec();
session.reinit(Default::default());
let second = session.process(b"one", &mut out, Operation::End).unwrap();
assert_eq!(&out[..second.produced], frame.as_slice());
let mut compressor = session.into_compressor();
assert_eq!(compressor.compress(b"one").unwrap(), frame);
}
#[test]
fn small_outputs_are_resumed_until_finished() {
let mut encoder = Compressor::default();
let expected = encoder.compress(b"resumable").unwrap();
let mut session = encoder.start(Default::default());
let mut frame = Vec::new();
let mut byte = [0u8; 1];
let mut input: &[u8] = b"resumable";
loop {
let progress = session.process(input, &mut byte, Operation::End).unwrap();
input = &input[progress.consumed..];
frame.extend_from_slice(&byte[..progress.produced]);
if progress.status == EncoderStatus::Finished {
break;
}
assert_eq!(progress.status, EncoderStatus::NeedsOutput);
}
assert_eq!(frame, expected);
assert_eq!(
session.finish(&mut byte).unwrap().status,
EncoderStatus::Finished
);
}
}