use alloc::string::String;
use core::fmt;
#[derive(Debug, Clone, PartialEq, Eq)]
#[non_exhaustive]
pub enum CompressionError {
BufferTooSmall { required: usize, available: usize },
InvalidData(String),
Internal(String),
UnexpectedEof,
AlreadyFinished,
}
impl fmt::Display for CompressionError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
CompressionError::BufferTooSmall {
required,
available,
} => {
write!(
f,
"buffer too small: required {} bytes, available {} bytes",
required, available
)
}
CompressionError::InvalidData(msg) => write!(f, "invalid data: {}", msg),
CompressionError::Internal(msg) => write!(f, "internal error: {}", msg),
CompressionError::UnexpectedEof => write!(f, "unexpected end of input"),
CompressionError::AlreadyFinished => write!(f, "compression already finished"),
}
}
}
impl core::error::Error for CompressionError {}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub enum CompressionStatus {
Continue {
consumed: usize,
produced: usize,
},
Complete {
consumed: usize,
produced: usize,
},
OutputFull {
consumed: usize,
produced: usize,
},
}
impl CompressionStatus {
#[inline]
pub fn consumed(&self) -> usize {
match self {
CompressionStatus::Continue { consumed, .. } => *consumed,
CompressionStatus::Complete { consumed, .. } => *consumed,
CompressionStatus::OutputFull { consumed, .. } => *consumed,
}
}
#[inline]
pub fn produced(&self) -> usize {
match self {
CompressionStatus::Continue { produced, .. } => *produced,
CompressionStatus::Complete { produced, .. } => *produced,
CompressionStatus::OutputFull { produced, .. } => *produced,
}
}
#[inline]
pub fn is_complete(&self) -> bool {
matches!(self, CompressionStatus::Complete { .. })
}
#[inline]
pub fn is_output_full(&self) -> bool {
matches!(self, CompressionStatus::OutputFull { .. })
}
}
pub trait Compressor {
fn compress(
&mut self,
input: &[u8],
output: &mut [u8],
) -> Result<CompressionStatus, CompressionError>;
fn finish(&mut self, output: &mut [u8]) -> Result<CompressionStatus, CompressionError>;
fn reset(&mut self);
}
pub trait Decompressor {
fn decompress(
&mut self,
input: &[u8],
output: &mut [u8],
) -> Result<CompressionStatus, CompressionError>;
fn reset(&mut self);
}
#[derive(Debug, Clone, Default)]
pub struct NoCompression {
finished: bool,
}
impl NoCompression {
pub fn new() -> Self {
Self { finished: false }
}
}
impl Compressor for NoCompression {
fn compress(
&mut self,
input: &[u8],
output: &mut [u8],
) -> Result<CompressionStatus, CompressionError> {
if self.finished {
return Err(CompressionError::AlreadyFinished);
}
let len = input.len().min(output.len());
output[..len].copy_from_slice(&input[..len]);
if len < input.len() {
Ok(CompressionStatus::OutputFull {
consumed: len,
produced: len,
})
} else {
Ok(CompressionStatus::Continue {
consumed: len,
produced: len,
})
}
}
fn finish(&mut self, _output: &mut [u8]) -> Result<CompressionStatus, CompressionError> {
if self.finished {
return Err(CompressionError::AlreadyFinished);
}
self.finished = true;
Ok(CompressionStatus::Complete {
consumed: 0,
produced: 0,
})
}
fn reset(&mut self) {
self.finished = false;
}
}
impl Decompressor for NoCompression {
fn decompress(
&mut self,
input: &[u8],
output: &mut [u8],
) -> Result<CompressionStatus, CompressionError> {
let len = input.len().min(output.len());
output[..len].copy_from_slice(&input[..len]);
if len < input.len() {
Ok(CompressionStatus::OutputFull {
consumed: len,
produced: len,
})
} else if input.is_empty() {
Ok(CompressionStatus::Complete {
consumed: 0,
produced: 0,
})
} else {
Ok(CompressionStatus::Continue {
consumed: len,
produced: len,
})
}
}
fn reset(&mut self) {
self.finished = false;
}
}