use crate::codec::{Codec, CodecTunables, PerCodecTunables};
use crate::error::CoreError;
pub struct XzCodec;
impl Codec for XzCodec {
fn id(&self) -> u8 {
super::CODEC_XZ
}
fn name(&self) -> &'static str {
"xz"
}
fn compress(&self, plaintext: &[u8]) -> Result<Vec<u8>, CoreError> {
compress_at_level(plaintext, 6)
}
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 result = omnizip_lzma::xz_container::xz_decompress(compressed).map_err(|e| {
CoreError::Corrupt {
reason: format!("xz decompress failed: {e}"),
}
})?;
if result.len() != expected_us {
return Err(CoreError::Corrupt {
reason: format!(
"xz 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 level = if t.xz_level > 0 { t.xz_level.min(9) } else { 6 };
compress_at_level(plaintext, level)
}
}
#[derive(Clone, Debug)]
pub struct XzTunables {
pub lc: u8,
pub lp: u8,
pub pb: u8,
pub dict_size_mb: u32,
pub level: u8,
}
impl Default for XzTunables {
fn default() -> Self {
Self {
lc: 3,
lp: 0,
pb: 2,
dict_size_mb: 16,
level: 6,
}
}
}
impl PerCodecTunables for XzCodec {
type Tunables = XzTunables;
fn compress_with_owned_tunables(
&self,
plaintext: &[u8],
t: &Self::Tunables,
) -> Result<Vec<u8>, CoreError> {
compress_with_tunables_inner(plaintext, t)
}
}
fn compress_at_level(plaintext: &[u8], level: u8) -> Result<Vec<u8>, CoreError> {
thread_local! {
static COMPRESSOR: std::cell::RefCell<omnizip_lzma::LzmaCompressor> = {
let c = omnizip_lzma::LzmaCompressor::new();
if std::env::var_os("LIMNIFS_XZ_REUSE_STATE").is_some() {
c.with_reset_mode(omnizip_lzma::ResetMode::ReuseState)
} else {
c
}
.into()
};
}
COMPRESSOR.with(|c| {
let mut borrowed = c.borrow_mut();
let lv = omnizip_codecs::CompressionLevel::new(level.clamp(1, 9));
omnizip_lzma::LzmaCompressor::compress(&mut *borrowed, plaintext, lv).map_err(|e| {
CoreError::Corrupt {
reason: format!("xz compress (level {level}) failed: {e}"),
}
})
})
}
fn compress_with_tunables_inner(plaintext: &[u8], t: &XzTunables) -> Result<Vec<u8>, CoreError> {
thread_local! {
static COMPRESSOR: std::cell::RefCell<omnizip_lzma::LzmaCompressor> =
std::cell::RefCell::new(omnizip_lzma::LzmaCompressor::new());
}
COMPRESSOR.with(|c| {
let mut borrowed = c.borrow_mut();
let lv = omnizip_codecs::CompressionLevel::new(t.level.clamp(1, 9));
let _ = (t.lc, t.lp, t.pb, t.dict_size_mb); omnizip_lzma::LzmaCompressor::compress(&mut *borrowed, plaintext, lv).map_err(|e| {
CoreError::Corrupt {
reason: format!("xz compress failed: {e}"),
}
})
})
}