use crate::codec::{Codec, CodecTunables, PerCodecTunables};
use crate::error::CoreError;
pub(crate) const DEFAULT_QUALITY: i32 = 5;
#[derive(Clone, Debug)]
pub struct BrotliTunables {
pub quality: i32,
}
impl Default for BrotliTunables {
fn default() -> Self {
Self {
quality: DEFAULT_QUALITY,
}
}
}
pub struct BrotliCodec;
impl Codec for BrotliCodec {
fn id(&self) -> u8 {
super::CODEC_BROTLI
}
fn name(&self) -> &'static str {
"brotli"
}
fn compress(&self, plaintext: &[u8]) -> Result<Vec<u8>, CoreError> {
compress(plaintext, DEFAULT_QUALITY)
}
fn decompress(&self, compressed: &[u8], expected_len: u32) -> Result<Vec<u8>, CoreError> {
let expected_us = usize::try_from(expected_len).map_err(|_| CoreError::Corrupt {
reason: format!("decompress: expected_len {expected_len} exceeds usize"),
})?;
let codec = omnizip_brotli::BrotliCodec;
let result = omnizip_codecs::Codec::decompress(&codec, compressed, expected_len)
.map_err(brotli_err)?;
if result.len() != expected_us {
return Err(CoreError::Corrupt {
reason: format!(
"brotli 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 q = i32::from(t.quality).clamp(0, 11);
compress(plaintext, q)
}
}
impl PerCodecTunables for BrotliCodec {
type Tunables = BrotliTunables;
fn compress_with_owned_tunables(
&self,
plaintext: &[u8],
t: &Self::Tunables,
) -> Result<Vec<u8>, CoreError> {
compress(plaintext, t.quality.clamp(0, 11))
}
}
fn brotli_err(e: omnizip_codecs::OmnizipError) -> CoreError {
CoreError::Corrupt {
reason: format!("brotli: {e}"),
}
}
pub(crate) fn compress(plaintext: &[u8], quality: i32) -> Result<Vec<u8>, CoreError> {
let codec = omnizip_brotli::BrotliCodec;
let q = quality.clamp(0, 11) as u8;
let level = omnizip_codecs::CompressionLevel::new(q);
omnizip_codecs::Codec::compress(&codec, plaintext, level).map_err(brotli_err)
}
pub(crate) fn decompress_at_quality(
compressed: &[u8],
expected_len: u32,
) -> Result<Vec<u8>, CoreError> {
let codec = omnizip_brotli::BrotliCodec;
omnizip_codecs::Codec::decompress(&codec, compressed, expected_len).map_err(brotli_err)
}