paracletics_hypercube/compression/
mod.rs1mod chromoharmonic;
2mod delta_pulse;
3mod rle;
4
5use std::error::Error;
6use std::fmt;
7
8use chromoharmonic::Chromoharmonic;
9use delta_pulse::DeltaPulse;
10use rle::RunLength;
11
12#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
13pub enum Codec {
14 Chromoharmonic,
15 DeltaPulse,
16 RunLength,
17}
18
19impl Codec {
20 pub const ALL: [Codec; 3] = [Codec::Chromoharmonic, Codec::DeltaPulse, Codec::RunLength];
21
22 pub fn name(self) -> &'static str {
23 match self {
24 Codec::Chromoharmonic => "chromoharmonic",
25 Codec::DeltaPulse => "delta-pulse",
26 Codec::RunLength => "run-length",
27 }
28 }
29}
30
31#[derive(Clone, Debug, PartialEq, Eq)]
32pub enum CompressionError {
33 CorruptStream(&'static str),
34 ValueOverflow,
35}
36
37impl fmt::Display for CompressionError {
38 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
39 match self {
40 CompressionError::CorruptStream(msg) => write!(f, "corrupt stream: {msg}"),
41 CompressionError::ValueOverflow => {
42 write!(f, "decoded value overflowed byte range")
43 }
44 }
45 }
46}
47
48impl Error for CompressionError {}
49
50#[derive(Clone, Copy, Debug, PartialEq)]
51pub struct CompressionStats {
52 pub input_bytes: usize,
53 pub output_bytes: usize,
54}
55
56impl CompressionStats {
57 pub fn ratio(self) -> f64 {
58 if self.input_bytes == 0 {
59 return 1.0;
60 }
61 self.output_bytes as f64 / self.input_bytes as f64
62 }
63}
64
65pub trait Compressor: Sync {
66 fn name(&self) -> &'static str;
67 fn compress(&self, input: &[u8]) -> Vec<u8>;
68 fn decompress(&self, input: &[u8]) -> Result<Vec<u8>, CompressionError>;
69 fn stats(&self, input: &[u8]) -> Result<CompressionStats, CompressionError> {
70 let compressed = self.compress(input);
71 let decoded = self.decompress(&compressed)?;
72 if decoded != input {
73 return Err(CompressionError::CorruptStream(
74 "self-check roundtrip failed",
75 ));
76 }
77 Ok(CompressionStats {
78 input_bytes: input.len(),
79 output_bytes: compressed.len(),
80 })
81 }
82}
83
84static CHROMOHARMONIC: Chromoharmonic = Chromoharmonic;
85static DELTA_PULSE: DeltaPulse = DeltaPulse;
86static RUN_LENGTH: RunLength = RunLength;
87
88pub fn compressor_for(codec: Codec) -> &'static dyn Compressor {
89 match codec {
90 Codec::Chromoharmonic => &CHROMOHARMONIC,
91 Codec::DeltaPulse => &DELTA_PULSE,
92 Codec::RunLength => &RUN_LENGTH,
93 }
94}
95
96pub fn compress_with(codec: Codec, input: &[u8]) -> Vec<u8> {
97 compressor_for(codec).compress(input)
98}
99
100pub fn decompress_with(codec: Codec, input: &[u8]) -> Result<Vec<u8>, CompressionError> {
101 compressor_for(codec).decompress(input)
102}
103
104pub fn stats_with(codec: Codec, input: &[u8]) -> Result<CompressionStats, CompressionError> {
105 compressor_for(codec).stats(input)
106}