mod chromoharmonic;
mod delta_pulse;
mod rle;
use std::error::Error;
use std::fmt;
use chromoharmonic::Chromoharmonic;
use delta_pulse::DeltaPulse;
use rle::RunLength;
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum Codec {
Chromoharmonic,
DeltaPulse,
RunLength,
}
impl Codec {
pub const ALL: [Codec; 3] = [Codec::Chromoharmonic, Codec::DeltaPulse, Codec::RunLength];
pub fn name(self) -> &'static str {
match self {
Codec::Chromoharmonic => "chromoharmonic",
Codec::DeltaPulse => "delta-pulse",
Codec::RunLength => "run-length",
}
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum CompressionError {
CorruptStream(&'static str),
ValueOverflow,
}
impl fmt::Display for CompressionError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
CompressionError::CorruptStream(msg) => write!(f, "corrupt stream: {msg}"),
CompressionError::ValueOverflow => {
write!(f, "decoded value overflowed byte range")
}
}
}
}
impl Error for CompressionError {}
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct CompressionStats {
pub input_bytes: usize,
pub output_bytes: usize,
}
impl CompressionStats {
pub fn ratio(self) -> f64 {
if self.input_bytes == 0 {
return 1.0;
}
self.output_bytes as f64 / self.input_bytes as f64
}
}
pub trait Compressor: Sync {
fn name(&self) -> &'static str;
fn compress(&self, input: &[u8]) -> Vec<u8>;
fn decompress(&self, input: &[u8]) -> Result<Vec<u8>, CompressionError>;
fn stats(&self, input: &[u8]) -> Result<CompressionStats, CompressionError> {
let compressed = self.compress(input);
let decoded = self.decompress(&compressed)?;
if decoded != input {
return Err(CompressionError::CorruptStream(
"self-check roundtrip failed",
));
}
Ok(CompressionStats {
input_bytes: input.len(),
output_bytes: compressed.len(),
})
}
}
static CHROMOHARMONIC: Chromoharmonic = Chromoharmonic;
static DELTA_PULSE: DeltaPulse = DeltaPulse;
static RUN_LENGTH: RunLength = RunLength;
pub fn compressor_for(codec: Codec) -> &'static dyn Compressor {
match codec {
Codec::Chromoharmonic => &CHROMOHARMONIC,
Codec::DeltaPulse => &DELTA_PULSE,
Codec::RunLength => &RUN_LENGTH,
}
}
pub fn compress_with(codec: Codec, input: &[u8]) -> Vec<u8> {
compressor_for(codec).compress(input)
}
pub fn decompress_with(codec: Codec, input: &[u8]) -> Result<Vec<u8>, CompressionError> {
compressor_for(codec).decompress(input)
}
pub fn stats_with(codec: Codec, input: &[u8]) -> Result<CompressionStats, CompressionError> {
compressor_for(codec).stats(input)
}