mod bcj_composites;
mod bitshuffle_lz4;
mod brotli;
mod bzip2;
mod composite;
mod deflate;
mod deflate64;
mod flac;
pub mod fsst_brotli;
mod glza;
mod libdeflate;
mod lz4;
mod ppmd;
mod ppmd8;
mod ricepp;
mod shuffle_lz4;
mod shuffle_zstd;
mod snappy;
mod store;
mod xz;
mod zpaq;
mod zstd;
pub mod zstd_dict;
use std::sync::OnceLock;
use crate::error::CoreError;
pub const CODEC_STORE: u8 = 0x00;
pub const CODEC_LZ4: u8 = 0x01;
pub const CODEC_ZSTD: u8 = 0x02;
pub const CODEC_XZ: u8 = 0x03;
pub const CODEC_BROTLI: u8 = 0x04;
pub const CODEC_DEFLATE: u8 = 0x05;
pub const CODEC_SNAPPY: u8 = 0x06;
pub const CODEC_FLAC: u8 = 0x07;
pub const CODEC_RICEPP: u8 = 0x08;
pub const CODEC_FSST_BROTLI: u8 = 0x09;
pub const CODEC_BLOSC2_SHUFFLE_LZ4: u8 = 0x0A;
pub const CODEC_ZPAQ: u8 = 0x0B;
pub const CODEC_PPMD: u8 = 0x0C;
pub const CODEC_GLZA: u8 = 0x0D;
pub const CODEC_SHUFFLE_ZSTD: u8 = 0x0E;
pub const CODEC_BITSHUFFLE_LZ4: u8 = 0x0F;
pub const CODEC_BZIP2: u8 = 0x10;
pub const CODEC_DEFLATE64: u8 = 0x11;
pub const CODEC_PPMD8: u8 = 0x12;
pub const CODEC_LZ4_HC: u8 = 0x13;
pub const CODEC_LIBDEFLATE: u8 = 0x14;
pub const CODEC_BCJ_X86_LZ4: u8 = 0x20;
pub const CODEC_BCJ_X86_ZSTD: u8 = 0x21;
pub const CODEC_BCJ_ARM64_LZ4: u8 = 0x23;
pub const CODEC_BCJ_ARM64_ZSTD: u8 = 0x24;
pub const CODEC_REFERENCED: u8 = 0xFE;
#[derive(Clone, Debug)]
pub struct CodecTunables {
pub quality: u8,
pub zstd_quality: u8,
pub xz_level: u8,
pub ppmd_order: u8,
pub ppmd7_budget: usize,
pub ppmd8_budget: usize,
pub bzip2_block_kb: u32,
pub lzma_dict_mb: u32,
}
impl CodecTunables {
#[must_use]
pub fn from_quality(quality: u8) -> Self {
Self {
quality,
zstd_quality: quality,
xz_level: 0,
ppmd_order: 0,
ppmd7_budget: 0,
ppmd8_budget: 0,
bzip2_block_kb: 0,
lzma_dict_mb: 0,
}
}
}
impl Default for CodecTunables {
fn default() -> Self {
Self {
quality: 0,
zstd_quality: 0,
xz_level: 0,
ppmd_order: 0,
ppmd7_budget: 0,
ppmd8_budget: 0,
bzip2_block_kb: 0,
lzma_dict_mb: 0,
}
}
}
pub trait Codec: Send + Sync {
fn id(&self) -> u8;
fn name(&self) -> &'static str;
fn compress(&self, plaintext: &[u8]) -> Result<Vec<u8>, CoreError>;
fn decompress(&self, compressed: &[u8], expected_len: u32) -> Result<Vec<u8>, CoreError>;
fn min_compress_size(&self) -> usize {
0
}
fn compress_with_tunables(
&self,
plaintext: &[u8],
tunables: &CodecTunables,
) -> Result<Vec<u8>, CoreError> {
let _ = tunables;
self.compress(plaintext)
}
}
pub trait PerCodecTunables: Codec {
type Tunables: Clone + Send + Sync + 'static;
fn compress_with_owned_tunables(
&self,
plaintext: &[u8],
tunables: &Self::Tunables,
) -> Result<Vec<u8>, CoreError>;
}
pub struct CodecRegistry {
codecs: Vec<Box<dyn Codec>>,
}
impl CodecRegistry {
#[must_use]
pub fn new() -> Self {
Self { codecs: Vec::new() }
}
pub fn register(&mut self, codec: Box<dyn Codec>) {
let id = codec.id();
assert!(
!self.codecs.iter().any(|c| c.id() == id),
"codec id 0x{id:02X} already registered",
);
self.codecs.push(codec);
}
fn find(&self, id: u8) -> Option<&dyn Codec> {
self.codecs.iter().find(|c| c.id() == id).map(Box::as_ref)
}
fn registered_names(&self) -> String {
self.codecs
.iter()
.map(|c| format!("0x{:02X}={}", c.id(), c.name()))
.collect::<Vec<_>>()
.join(", ")
}
pub fn compress(&self, id: u8, plaintext: &[u8]) -> Result<Vec<u8>, CoreError> {
match self.find(id) {
Some(codec) => codec_call(|| codec.compress(plaintext)),
None => Err(CoreError::UnsupportedFeature {
feature: format!(
"compress codec 0x{id:02X} (registered: {registered})",
registered = self.registered_names()
),
}),
}
}
pub fn decompress(
&self,
id: u8,
compressed: &[u8],
expected_len: u32,
) -> Result<Vec<u8>, CoreError> {
match self.find(id) {
Some(codec) => codec_call(|| codec.decompress(compressed, expected_len)),
None => Err(CoreError::UnsupportedFeature {
feature: format!(
"decompress codec 0x{id:02X} (registered: {registered})",
registered = self.registered_names()
),
}),
}
}
pub fn compress_with_tunables(
&self,
id: u8,
plaintext: &[u8],
tunables: &CodecTunables,
) -> Result<Vec<u8>, CoreError> {
match self.find(id) {
Some(codec) => codec_call(|| codec.compress_with_tunables(plaintext, tunables)),
None => Err(CoreError::UnsupportedFeature {
feature: format!(
"compress_with_tunables codec 0x{id:02X} (registered: {registered})",
registered = self.registered_names()
),
}),
}
}
}
pub(crate) fn codec_call<F>(f: F) -> Result<Vec<u8>, CoreError>
where
F: FnOnce() -> Result<Vec<u8>, CoreError>,
{
match std::panic::catch_unwind(std::panic::AssertUnwindSafe(f)) {
Ok(r) => r,
Err(payload) => {
let reason = if let Some(s) = payload.downcast_ref::<&str>() {
s.to_string()
} else if let Some(s) = payload.downcast_ref::<String>() {
s.clone()
} else {
"unknown panic payload".into()
};
Err(CoreError::Corrupt {
reason: format!("codec panicked: {reason}"),
})
}
}
}
impl CodecRegistry {
pub fn codec_name(&self, codec_id: u8) -> Option<&'static str> {
self.codecs
.iter()
.find(|c| c.id() == codec_id)
.map(|c| c.name())
}
}
impl Default for CodecRegistry {
fn default() -> Self {
let mut registry = Self::new();
registry.register(Box::new(store::StoreCodec));
registry.register(Box::new(lz4::Lz4Codec));
registry.register(Box::new(lz4::Lz4HcCodec));
registry.register(Box::new(zstd::ZstdCodec));
registry.register(Box::new(xz::XzCodec));
registry.register(Box::new(brotli::BrotliCodec));
registry.register(Box::new(deflate::DeflateCodec));
registry.register(Box::new(libdeflate::LibdeflateCodec));
registry.register(Box::new(snappy::SnappyCodec));
registry.register(Box::new(flac::FlacCodec));
registry.register(Box::new(ricepp::RiceppCodec::fits_default()));
registry.register(Box::new(fsst_brotli::FsstBrotliCodec));
registry.register(Box::new(shuffle_lz4::float32()));
registry.register(Box::new(zpaq::ZpaqCodec));
registry.register(Box::new(ppmd::PpmdCodec::new()));
registry.register(Box::new(ppmd8::Ppmd8Codec::new()));
registry.register(Box::new(glza::GlzaCodec));
registry.register(Box::new(shuffle_zstd::shuffle_zstd()));
registry.register(Box::new(bitshuffle_lz4::bitshuffle_lz4()));
registry.register(Box::new(bzip2::Bzip2Codec::new()));
registry.register(Box::new(deflate64::Deflate64Codec::new()));
registry.register(Box::new(bcj_composites::bcj_x86_lz4()));
registry.register(Box::new(bcj_composites::bcj_x86_zstd()));
registry.register(Box::new(bcj_composites::bcj_arm64_lz4()));
registry.register(Box::new(bcj_composites::bcj_arm64_zstd()));
registry
}
}
impl std::fmt::Debug for CodecRegistry {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("CodecRegistry")
.field("codecs", &self.registered_names())
.finish()
}
}
static DEFAULT_REGISTRY: OnceLock<CodecRegistry> = OnceLock::new();
fn default_registry() -> &'static CodecRegistry {
DEFAULT_REGISTRY.get_or_init(CodecRegistry::default)
}
#[must_use]
pub fn best_compressible_codec() -> u8 {
CODEC_BROTLI
}
#[must_use]
pub fn best_binary_codec() -> u8 {
CODEC_LZ4
}
pub fn codec_name(codec_id: u8) -> Option<&'static str> {
default_registry().codec_name(codec_id)
}
pub fn compress(codec_id: u8, plaintext: &[u8]) -> Result<Vec<u8>, CoreError> {
default_registry().compress(codec_id, plaintext)
}
pub fn compress_with_options(
codec_id: u8,
plaintext: &[u8],
quality: u8,
) -> Result<Vec<u8>, CoreError> {
let tunables = CodecTunables::from_quality(quality);
compress_with_tunables(codec_id, plaintext, &tunables)
}
pub fn compress_with_tunables(
codec_id: u8,
plaintext: &[u8],
tunables: &CodecTunables,
) -> Result<Vec<u8>, CoreError> {
default_registry().compress_with_tunables(codec_id, plaintext, tunables)
}
pub fn decompress(
codec_id: u8,
compressed: &[u8],
expected_len: u32,
) -> Result<Vec<u8>, CoreError> {
default_registry().decompress(codec_id, compressed, expected_len)
}
#[must_use]
pub fn compress_lz4_with_size(plaintext: &[u8]) -> Vec<u8> {
let codec = omnizip_lz4::Lz4FastCodec;
omnizip_codecs::Codec::compress(
&codec,
plaintext,
omnizip_codecs::CompressionLevel::default(),
)
.unwrap_or_else(|_| plaintext.to_vec())
}
pub fn compress_zstd(plaintext: &[u8]) -> Result<Vec<u8>, CoreError> {
zstd::compress(plaintext)
}
pub fn compress_brotli(plaintext: &[u8]) -> Result<Vec<u8>, CoreError> {
brotli::compress(plaintext, brotli::DEFAULT_QUALITY)
}
pub fn compress_brotli_with_quality(plaintext: &[u8], quality: i32) -> Result<Vec<u8>, CoreError> {
let tunables = CodecTunables::from_quality(quality.clamp(0, 11) as u8);
default_registry().compress_with_tunables(CODEC_BROTLI, plaintext, &tunables)
}
pub fn compress_deflate(plaintext: &[u8]) -> Result<Vec<u8>, CoreError> {
deflate::compress(plaintext, deflate::DEFAULT_LEVEL)
}
#[cfg(test)]
mod tests {
use super::*;
struct PanickingCodec;
impl Codec for PanickingCodec {
fn id(&self) -> u8 {
0xEE
}
fn name(&self) -> &'static str {
"panicking-test"
}
fn compress(&self, _plaintext: &[u8]) -> Result<Vec<u8>, CoreError> {
panic!("simulated encoder bug");
}
fn decompress(&self, _compressed: &[u8], _expected_len: u32) -> Result<Vec<u8>, CoreError> {
panic!("simulated decoder bug");
}
}
#[test]
fn panicking_codec_returns_err_not_unwind() {
let mut registry = CodecRegistry::new();
registry.register(Box::new(PanickingCodec));
let err = registry.compress(0xEE, b"data").expect_err("must be Err");
assert!(
matches!(err, CoreError::Corrupt { ref reason } if reason.contains("panicked")),
"got {err:?}"
);
let err = registry
.decompress(0xEE, b"data", 4)
.expect_err("must be Err");
assert!(
matches!(err, CoreError::Corrupt { ref reason } if reason.contains("panicked")),
"got {err:?}"
);
}
#[test]
fn tunables_ppmd7_bigger_budget_helps_ratio() {
let mut input = Vec::with_capacity(1 * 1024 * 1024);
let paragraph = b"the quick brown fox jumps over the lazy dog. ";
while input.len() + paragraph.len() <= 1 * 1024 * 1024 {
input.extend_from_slice(paragraph);
}
let small = CodecTunables {
quality: 0,
zstd_quality: 0,
xz_level: 0,
ppmd_order: 4,
ppmd7_budget: 8 * 1024 * 1024,
ppmd8_budget: 0,
bzip2_block_kb: 0,
lzma_dict_mb: 0,
};
let big = CodecTunables {
ppmd7_budget: 256 * 1024 * 1024,
..small.clone()
};
let small_c = compress_with_tunables(CODEC_PPMD, &input, &small).expect("ppmd7 small");
let big_c = compress_with_tunables(CODEC_PPMD, &input, &big).expect("ppmd7 big");
assert!(
big_c.len() <= small_c.len(),
"256MB budget should not be worse than 8MB ({} vs {})",
big_c.len(),
small_c.len()
);
let recovered = decompress(CODEC_PPMD, &small_c, input.len() as u32).expect("d");
assert_eq!(recovered, input);
}
#[test]
fn tunables_brotli_quality_flows_through() {
let paragraph = b"Lorem ipsum dolor sit amet, consectetur adipiscing elit, \
sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.";
let mut input = Vec::with_capacity(10_000);
let mut i = 0;
while input.len() < 10_000 {
input.extend_from_slice(format!("{i:04}: {paragraph:?}\n").as_bytes());
i += 1;
}
let q0 = CodecTunables::from_quality(0);
let q11 = CodecTunables::from_quality(11);
let c0 = compress_with_tunables(CODEC_BROTLI, &input, &q0).expect("brotli q0");
let c11 = compress_with_tunables(CODEC_BROTLI, &input, &q11).expect("brotli q11");
assert!(!c0.is_empty() && !c11.is_empty());
}
#[test]
fn tunables_bzip2_block_size_maps_to_level() {
let input = b"the quick brown fox jumps over the lazy dog. ".repeat(2000);
let small = CodecTunables {
bzip2_block_kb: 100,
..CodecTunables::default()
};
let big = CodecTunables {
bzip2_block_kb: 900,
..CodecTunables::default()
};
let cs = compress_with_tunables(CODEC_BZIP2, &input, &small).expect("bzip2 100k");
let cb = compress_with_tunables(CODEC_BZIP2, &input, &big).expect("bzip2 900k");
assert!(
cb.len() <= cs.len(),
"900k ({}) <= 100k ({})",
cb.len(),
cs.len()
);
}
#[test]
fn store_compress_is_identity() {
let data = b"hello world";
let compressed = compress(CODEC_STORE, data).expect("store compress");
assert_eq!(compressed, data);
}
#[test]
fn store_decompress_validates_length() {
let data = b"hello world";
let result = decompress(CODEC_STORE, data, 11).expect("store decompress");
assert_eq!(result, data);
}
#[test]
fn zstd_higher_levels_compress_better_than_lower() {
let input: Vec<u8> = b"The quick brown fox jumps over the lazy dog. ".repeat(2000);
let l1 = omnizip_zstd::compress(&input, omnizip_zstd::ZstdLevel::Fastest).expect("zstd L1");
let l6 = omnizip_zstd::compress(&input, omnizip_zstd::ZstdLevel::Default).expect("zstd L6");
assert!(
l6.len() <= l1.len() + 64,
"ZSTD L6 ({}) grossly worse than L1 ({}); level differentiation broken",
l6.len(),
l1.len()
);
}
#[test]
fn xz_lzma_round_trips_via_lazy_parsing() {
let input: Vec<u8> = b"The quick brown fox jumps over the lazy dog. \
Lorem ipsum dolor sit amet. \
SVG is a vector image format."
.repeat(500);
let xz = omnizip_lzma::xz_compress(&input).expect("xz encode");
let recovered = omnizip_lzma::xz_container::xz_decompress(&xz).expect("xz decode");
assert_eq!(recovered, input);
assert!(
xz.len() < input.len(),
"LZMA should compress real-world text; got {} vs {}",
xz.len(),
input.len()
);
}
#[test]
fn store_decompress_rejects_length_mismatch() {
let data = b"hello world";
match decompress(CODEC_STORE, data, 99) {
Err(CoreError::Corrupt { reason }) => {
assert!(reason.contains("does not match"), "got: {reason}");
}
other => panic!("expected Corrupt, got {other:?}"),
}
}
#[test]
fn lz4_round_trips() {
let data = b"Lorem ipsum dolor sit amet, consectetur adipiscing elit. \
Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.";
let compressed = compress(CODEC_LZ4, data).expect("lz4 compress");
let decompressed = decompress(
CODEC_LZ4,
&compressed,
u32::try_from(data.len()).expect("fits u32"),
)
.expect("lz4 decompress");
assert_eq!(decompressed, data);
}
#[test]
fn lz4_compresses_repetitive_data() {
let data = vec![0x41u8; 10_000];
let compressed = compress(CODEC_LZ4, &data).expect("lz4 compress");
assert!(
compressed.len() < data.len(),
"lz4 should compress repetitive data: {} vs {}",
compressed.len(),
data.len()
);
}
#[test]
fn zstd_round_trips() {
let data = b"The quick brown fox jumps over the lazy dog. ".repeat(1000);
let compressed = compress_zstd(&data).expect("zstd compress");
let decompressed = decompress(
CODEC_ZSTD,
&compressed,
u32::try_from(data.len()).expect("fits u32"),
)
.expect("zstd decompress");
assert_eq!(decompressed, data);
}
#[test]
fn zstd_compresses_repetitive_data() {
let data = vec![0x41u8; 10_000];
let compressed = compress_zstd(&data).expect("zstd compress");
assert!(
compressed.len() < data.len(),
"zstd should compress repetitive data: {} vs {}",
compressed.len(),
data.len()
);
}
#[test]
fn zstd_compresses_better_than_lz4_on_text() {
let data = b"The quick brown fox. ".repeat(10_000);
let lz4 = compress(CODEC_LZ4, &data).expect("lz4");
let zstd = compress_zstd(&data).expect("zstd");
assert!(
zstd.len() < lz4.len(),
"zstd ({}) should be smaller than lz4 ({}) on text",
zstd.len(),
lz4.len()
);
}
#[test]
fn zstd_compresses_binary_data() {
let data: Vec<u8> = (0..100_000u32)
.map(|i| u8::try_from(i % 256).expect("fits u8"))
.collect();
let compressed = compress_zstd(&data).expect("zstd compress");
assert!(compressed.len() < data.len());
let decompressed = decompress(
CODEC_ZSTD,
&compressed,
u32::try_from(data.len()).expect("fits u32"),
)
.expect("zstd decompress");
assert_eq!(decompressed, data);
}
#[test]
fn xz_encode_round_trips() {
let plaintext = b"xz round-trip data";
let compressed = compress(CODEC_XZ, plaintext).expect("xz encode succeeds");
let decompressed =
decompress(CODEC_XZ, &compressed, plaintext.len() as u32).expect("xz decode succeeds");
assert_eq!(decompressed.as_slice(), plaintext);
}
#[test]
fn reject_unknown_codec() {
let result = compress(0xFF, b"data");
assert!(matches!(result, Err(CoreError::UnsupportedFeature { .. })));
}
#[test]
fn brotli_round_trips() {
let data = b"The quick brown fox jumps over the lazy dog. ".repeat(1000);
let compressed = compress_brotli(&data).expect("brotli compress");
let decompressed = decompress(
CODEC_BROTLI,
&compressed,
u32::try_from(data.len()).expect("fits u32"),
)
.expect("brotli decompress");
assert_eq!(decompressed, data);
}
#[test]
fn brotli_compresses_repetitive_data() {
let data = vec![0x41u8; 10_000];
let compressed = compress_brotli(&data).expect("brotli compress");
assert!(
compressed.len() < data.len(),
"brotli should compress repetitive data: {} vs {}",
compressed.len(),
data.len()
);
}
#[test]
fn brotli_and_zstd_both_compress_text() {
let data = b"The quick brown fox. ".repeat(10_000);
let zstd = compress_zstd(&data).expect("zstd");
assert!(zstd.len() < data.len(), "zstd should compress text");
let _ = compress_brotli(&data).expect("brotli should not error");
}
#[test]
fn brotli_decompress_rejects_length_mismatch() {
let data = b"hello world";
let compressed = compress_brotli(data).expect("brotli compress");
match decompress(CODEC_BROTLI, &compressed, 99) {
Err(CoreError::Corrupt { reason }) => {
assert!(
reason.contains("does not match") || reason.contains("mismatch"),
"got: {reason}"
);
}
other => panic!("expected Corrupt, got {other:?}"),
}
}
#[test]
fn deflate_round_trips() {
let data = b"The quick brown fox jumps over the lazy dog. ".repeat(1000);
let compressed = compress_deflate(&data).expect("deflate compress");
let decompressed = decompress(
CODEC_DEFLATE,
&compressed,
u32::try_from(data.len()).expect("fits u32"),
)
.expect("deflate decompress");
assert_eq!(decompressed, data);
}
#[test]
fn deflate_compresses_repetitive_data() {
let data = vec![0x41u8; 10_000];
let compressed = compress_deflate(&data).expect("deflate compress");
assert!(
compressed.len() < data.len(),
"deflate should compress repetitive data: {} vs {}",
compressed.len(),
data.len()
);
}
#[test]
fn deflate_decompress_rejects_length_mismatch() {
let data = b"hello world";
let compressed = compress_deflate(data).expect("deflate compress");
match decompress(CODEC_DEFLATE, &compressed, 99) {
Err(CoreError::Corrupt { reason }) => {
assert!(
reason.contains("does not match") || reason.contains("mismatch"),
"got: {reason}"
);
}
other => panic!("expected Corrupt, got {other:?}"),
}
}
#[test]
fn snappy_round_trips() {
let data = b"The quick brown fox jumps over the lazy dog. ".repeat(100);
let compressed = compress(CODEC_SNAPPY, &data).expect("snappy compress");
let decompressed = decompress(
CODEC_SNAPPY,
&compressed,
u32::try_from(data.len()).expect("fits u32"),
)
.expect("snappy decompress");
assert_eq!(decompressed, data);
}
#[test]
fn snappy_compresses_repetitive_data() {
let data = vec![0x41u8; 10_000];
let compressed = compress(CODEC_SNAPPY, &data).expect("snappy compress");
assert!(
compressed.len() < data.len(),
"snappy should compress repetitive data: {} vs {}",
compressed.len(),
data.len()
);
}
#[test]
fn snappy_decompress_rejects_length_mismatch() {
let data = b"hello world";
let compressed = compress(CODEC_SNAPPY, data).expect("snappy compress");
match decompress(CODEC_SNAPPY, &compressed, 99) {
Err(CoreError::Corrupt { reason }) => {
assert!(
reason.contains("length mismatch") || reason.contains("does not match"),
"got: {reason}"
);
}
other => panic!("expected Corrupt, got {other:?}"),
}
}
#[test]
fn registry_registers_custom_codec_without_changing_dispatch() {
struct NoopCodec;
const NOOP_ID: u8 = 0xFE;
impl Codec for NoopCodec {
fn id(&self) -> u8 {
NOOP_ID
}
fn name(&self) -> &'static str {
"noop"
}
fn compress(&self, plaintext: &[u8]) -> Result<Vec<u8>, CoreError> {
Ok(plaintext.to_vec())
}
fn decompress(
&self,
compressed: &[u8],
expected_len: u32,
) -> Result<Vec<u8>, CoreError> {
let expected = usize::try_from(expected_len).map_err(|_| CoreError::Corrupt {
reason: format!("noop: expected_len {expected_len} exceeds usize"),
})?;
if compressed.len() != expected {
return Err(CoreError::Corrupt {
reason: "noop: length mismatch".into(),
});
}
Ok(compressed.to_vec())
}
}
let mut registry = CodecRegistry::new();
registry.register(Box::new(NoopCodec));
assert_eq!(registry.compress(NOOP_ID, b"abc").expect("noop"), b"abc");
assert_eq!(
registry
.decompress(NOOP_ID, b"abc", 3)
.expect("noop decompress"),
b"abc"
);
}
#[test]
#[should_panic(expected = "codec id 0x00 already registered")]
fn registry_rejects_duplicate_id() {
let mut registry = CodecRegistry::new();
registry.register(Box::new(store::StoreCodec));
registry.register(Box::new(store::StoreCodec));
}
#[test]
fn default_registry_has_all_seven_codecs() {
let registry = default_registry();
assert!(registry.find(CODEC_STORE).is_some());
assert!(registry.find(CODEC_LZ4).is_some());
assert!(registry.find(CODEC_ZSTD).is_some());
assert!(registry.find(CODEC_XZ).is_some());
assert!(registry.find(CODEC_BROTLI).is_some());
assert!(registry.find(CODEC_DEFLATE).is_some());
assert!(registry.find(CODEC_SNAPPY).is_some());
assert!(registry.find(0xFF).is_none());
}
}
#[cfg(test)]
mod per_codec_tunables_ocp_tests {
use super::*;
#[derive(Clone, Debug)]
struct StrideTunables {
stride: usize,
}
struct DeltaStrideCodec;
impl Codec for DeltaStrideCodec {
fn id(&self) -> u8 {
0xFE }
fn name(&self) -> &'static str {
"delta-stride(test)"
}
fn compress(&self, plaintext: &[u8]) -> Result<Vec<u8>, CoreError> {
Ok(self.delta(plaintext, 1))
}
fn decompress(&self, compressed: &[u8], _expected_len: u32) -> Result<Vec<u8>, CoreError> {
let mut out = compressed.to_vec();
for i in 1..out.len() {
out[i] = out[i].wrapping_add(out[i - 1]);
}
Ok(out)
}
}
impl DeltaStrideCodec {
fn delta(&self, data: &[u8], stride: usize) -> Vec<u8> {
let mut out = data.to_vec();
let stride = stride.max(1);
for i in (stride..out.len()).rev() {
out[i] = out[i].wrapping_sub(out[i - stride]);
}
out
}
}
impl PerCodecTunables for DeltaStrideCodec {
type Tunables = StrideTunables;
fn compress_with_owned_tunables(
&self,
plaintext: &[u8],
t: &Self::Tunables,
) -> Result<Vec<u8>, CoreError> {
Ok(self.delta(plaintext, t.stride))
}
}
#[test]
fn new_codec_tunables_require_no_edits_to_shared_struct() {
let codec = DeltaStrideCodec;
let period: [u8; 4] = [0x11, 0x22, 0x33, 0x44];
let payload: Vec<u8> = period.iter().cycle().copied().take(4096).collect();
let s1 = codec
.compress_with_owned_tunables(&payload, &StrideTunables { stride: 1 })
.expect("stride 1");
let s4 = codec
.compress_with_owned_tunables(&payload, &StrideTunables { stride: 4 })
.expect("stride 4");
assert_ne!(s1, s4, "different tunables must change the output");
assert!(
s4.iter().filter(|&&b| b == 0).count() > s1.iter().filter(|&&b| b == 0).count(),
"stride 4 zeroes the periodic pattern; stride 1 does not"
);
let recovered = codec
.decompress(&s1, payload.len() as u32)
.expect("decompress");
assert_eq!(recovered, payload);
let flat = CodecTunables::from_quality(9);
let via_default = codec.compress(&payload).expect("default path ignores flat");
let _ = flat;
assert_eq!(
via_default, s1,
"default compress == owned tunables stride 1"
);
}
}