use crate::codec::{Codec, CodecTunables, PerCodecTunables};
use crate::error::CoreError;
#[derive(Clone, Debug)]
pub struct ZstdTunables {
pub quality: u8,
}
impl Default for ZstdTunables {
fn default() -> Self {
Self { quality: 6 }
}
}
fn level_for_quality(quality: u8) -> omnizip_zstd::ZstdLevel {
match quality {
0..=2 => omnizip_zstd::ZstdLevel::Fastest,
3..=5 => omnizip_zstd::ZstdLevel::Fast,
6..=11 => omnizip_zstd::ZstdLevel::Default,
12..=21 => omnizip_zstd::ZstdLevel::Better,
_ => omnizip_zstd::ZstdLevel::Best,
}
}
pub struct ZstdCodec;
impl Codec for ZstdCodec {
fn id(&self) -> u8 {
super::CODEC_ZSTD
}
fn name(&self) -> &'static str {
"zstd"
}
fn compress(&self, plaintext: &[u8]) -> Result<Vec<u8>, CoreError> {
compress(plaintext)
}
fn decompress(&self, compressed: &[u8], expected_len: u32) -> Result<Vec<u8>, CoreError> {
let result =
omnizip_zstd::decompress(compressed, expected_len).map_err(|e| CoreError::Corrupt {
reason: format!("zstd decompress failed: {e}"),
})?;
let expected_us = usize::try_from(expected_len).map_err(|_| CoreError::Corrupt {
reason: format!("decompress: expected_len {expected_len} exceeds usize"),
})?;
if result.len() != expected_us {
return Err(CoreError::Corrupt {
reason: format!(
"zstd decompress: result length {} does not match plaintext_len {expected_us}",
result.len()
),
});
}
Ok(result)
}
fn compress_with_tunables(
&self,
plaintext: &[u8],
t: &CodecTunables,
) -> Result<Vec<u8>, CoreError> {
let quality = if t.quality > 0 { t.quality } else { 6 };
let level = level_for_quality(quality);
omnizip_zstd::compress(plaintext, level).map_err(|e| CoreError::Corrupt {
reason: format!("zstd compress (level {level}) failed: {e}"),
})
}
}
impl PerCodecTunables for ZstdCodec {
type Tunables = ZstdTunables;
fn compress_with_owned_tunables(
&self,
plaintext: &[u8],
t: &Self::Tunables,
) -> Result<Vec<u8>, CoreError> {
let level = level_for_quality(t.quality);
omnizip_zstd::compress(plaintext, level).map_err(|e| CoreError::Corrupt {
reason: format!("zstd compress (level {level}) failed: {e}"),
})
}
}
#[allow(clippy::unnecessary_wraps)]
pub(crate) fn compress(plaintext: &[u8]) -> Result<Vec<u8>, CoreError> {
omnizip_zstd::compress(plaintext, omnizip_zstd::ZstdLevel::Default).map_err(|e| {
CoreError::Corrupt {
reason: format!("zstd compress failed: {e}"),
}
})
}
#[allow(dead_code)]
pub(crate) fn compress_at_level(
plaintext: &[u8],
level: omnizip_zstd::ZstdLevel,
) -> Result<Vec<u8>, CoreError> {
omnizip_zstd::compress(plaintext, level).map_err(|e| CoreError::Corrupt {
reason: format!("zstd compress (level {level}) failed: {e}"),
})
}