use flate2::read::ZlibDecoder;
use flate2::write::ZlibEncoder;
use flate2::Compression;
use std::io::{self, Read, Write};
#[cfg(feature = "lzma")]
pub use crate::quantize::{dequantize_16bit, dequantize_8bit, quantize_16bit, quantize_8bit};
pub fn zlib_compress(data: &[u8], level: u32) -> io::Result<Vec<u8>> {
let level = level.min(9);
let mut encoder = ZlibEncoder::new(Vec::new(), Compression::new(level));
encoder.write_all(data)?;
encoder.finish()
}
pub fn zlib_decompress(data: &[u8]) -> io::Result<Vec<u8>> {
let mut decoder = ZlibDecoder::new(data);
let mut out = Vec::new();
decoder.read_to_end(&mut out)?;
Ok(out)
}
#[cfg(feature = "lzma")]
pub fn lzma_compress(data: &[u8], _preset: u32) -> io::Result<Vec<u8>> {
let mut output = Vec::new();
lzma_rs::lzma_compress(&mut io::Cursor::new(data), &mut output)
.map_err(|e| io::Error::other(format!("LZMA compress error: {e}")))?;
Ok(output)
}
#[cfg(feature = "lzma")]
pub fn lzma_decompress(data: &[u8]) -> io::Result<Vec<u8>> {
let mut output = Vec::new();
lzma_rs::lzma_decompress(&mut io::Cursor::new(data), &mut output).map_err(|e| {
io::Error::new(
io::ErrorKind::InvalidData,
format!("LZMA decompress error: {e}"),
)
})?;
Ok(output)
}
#[cfg(feature = "lzma")]
const QUANTIZED_HEADER: usize = 1 + 8 + 8 + 4;
#[cfg(feature = "lzma")]
const LOSSLESS_HEADER: usize = 1 + 4;
#[cfg(feature = "lzma")]
const LOSSLESS_MARKER: u8 = 0xFF;
#[cfg(feature = "lzma")]
fn invalid(msg: impl Into<String>) -> io::Error {
io::Error::new(io::ErrorKind::InvalidData, msg.into())
}
#[cfg(feature = "lzma")]
#[allow(clippy::cast_possible_truncation)]
pub fn compress_residual_quantized(
residual: &[f32],
bits: u8,
lzma_preset: u32,
) -> io::Result<Vec<u8>> {
let (quantized, min_val, scale) = if bits == 16 {
quantize_16bit(residual)
} else {
quantize_8bit(residual)
};
let compressed = lzma_compress(&quantized, lzma_preset)?;
let mut output = Vec::with_capacity(QUANTIZED_HEADER + compressed.len());
output.push(bits);
output.extend_from_slice(&min_val.to_le_bytes());
output.extend_from_slice(&scale.to_le_bytes());
output.extend_from_slice(&(compressed.len() as u32).to_le_bytes());
output.extend_from_slice(&compressed);
Ok(output)
}
#[cfg(feature = "lzma")]
pub fn decompress_residual_quantized(data: &[u8]) -> io::Result<Vec<f32>> {
if data.len() < QUANTIZED_HEADER {
return Err(invalid(format!(
"Data too short for residual header (need at least {QUANTIZED_HEADER} bytes)"
)));
}
let bits = data[0];
let min_val = f64::from_le_bytes(data[1..9].try_into().map_err(|_| invalid("min_val"))?);
let scale = f64::from_le_bytes(data[9..17].try_into().map_err(|_| invalid("scale"))?);
let compressed_len = u32::from_le_bytes(
data[17..21]
.try_into()
.map_err(|_| invalid("compressed_len"))?,
) as usize;
let end = QUANTIZED_HEADER
.checked_add(compressed_len)
.ok_or_else(|| invalid("compressed_len overflow"))?;
if data.len() < end {
return Err(invalid(format!(
"Data truncated: expected {end} bytes, got {}",
data.len()
)));
}
let quantized = lzma_decompress(&data[QUANTIZED_HEADER..end])?;
Ok(if bits == 16 {
dequantize_16bit(&quantized, min_val, scale)
} else {
dequantize_8bit(&quantized, min_val, scale)
})
}
#[cfg(feature = "lzma")]
#[allow(clippy::cast_possible_truncation)]
pub fn compress_residual_lossless(residual: &[f32], lzma_preset: u32) -> io::Result<Vec<u8>> {
let bytes: Vec<u8> = residual.iter().flat_map(|&v| v.to_le_bytes()).collect();
let compressed = lzma_compress(&bytes, lzma_preset)?;
let mut output = Vec::with_capacity(LOSSLESS_HEADER + compressed.len());
output.push(LOSSLESS_MARKER);
output.extend_from_slice(&(compressed.len() as u32).to_le_bytes());
output.extend_from_slice(&compressed);
Ok(output)
}
#[cfg(feature = "lzma")]
pub fn decompress_residual_lossless(data: &[u8]) -> io::Result<Vec<f32>> {
if data.len() < LOSSLESS_HEADER {
return Err(invalid(format!(
"Data too short for lossless header (need at least {LOSSLESS_HEADER} bytes)"
)));
}
if data[0] != LOSSLESS_MARKER {
return Err(invalid(format!(
"Invalid lossless marker: expected 0x{LOSSLESS_MARKER:02X}, got 0x{:02X}",
data[0]
)));
}
let compressed_len = u32::from_le_bytes(
data[1..5]
.try_into()
.map_err(|_| invalid("compressed_len"))?,
) as usize;
let end = LOSSLESS_HEADER
.checked_add(compressed_len)
.ok_or_else(|| invalid("compressed_len overflow"))?;
if data.len() < end {
return Err(invalid(format!(
"Data truncated: expected {end} bytes, got {}",
data.len()
)));
}
let bytes = lzma_decompress(&data[LOSSLESS_HEADER..end])?;
Ok(bytes
.chunks_exact(4)
.map(|c| f32::from_le_bytes([c[0], c[1], c[2], c[3]]))
.collect())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn roundtrip_ascii() {
let payload = b"the quick brown fox jumps over the lazy dog".repeat(8);
let compressed = zlib_compress(&payload, 6).unwrap();
let decompressed = zlib_decompress(&compressed).unwrap();
assert_eq!(payload, decompressed);
assert!(
compressed.len() < payload.len(),
"compression should shrink"
);
}
#[test]
fn roundtrip_empty() {
let compressed = zlib_compress(&[], 6).unwrap();
let decompressed = zlib_decompress(&compressed).unwrap();
assert!(decompressed.is_empty());
}
#[test]
fn roundtrip_binary() {
let payload: Vec<u8> = (0..=255u8).cycle().take(1024).collect();
let compressed = zlib_compress(&payload, 9).unwrap();
let decompressed = zlib_decompress(&compressed).unwrap();
assert_eq!(payload, decompressed);
}
#[test]
fn level_clamping() {
let payload = b"hello world";
let over_max = zlib_compress(payload, 42).unwrap();
let normal = zlib_compress(payload, 9).unwrap();
assert_eq!(over_max, normal, "level > 9 should clamp to 9");
}
#[test]
fn level_zero_stores() {
let payload = b"abcdef".repeat(16);
let stored = zlib_compress(&payload, 0).unwrap();
let decompressed = zlib_decompress(&stored).unwrap();
assert_eq!(payload, decompressed);
}
#[test]
fn decompress_invalid_returns_error() {
let garbage = vec![0xff, 0xfe, 0xfd, 0xfc];
assert!(zlib_decompress(&garbage).is_err());
}
#[cfg(feature = "lzma")]
mod lzma {
use super::super::*;
#[test]
fn lzma_roundtrip_and_invalid() {
let payload = b"alice alice alice alice".repeat(32);
let c = lzma_compress(&payload, 6).unwrap();
assert!(c.len() < payload.len());
assert_eq!(lzma_decompress(&c).unwrap(), payload);
assert!(lzma_decompress(b"not lzma").is_err());
assert_eq!(
lzma_decompress(&lzma_compress(&[], 6).unwrap()).unwrap(),
Vec::<u8>::new()
);
}
#[test]
fn quantized_container_roundtrip_both_widths() {
let residual: Vec<f32> = (0..300).map(|i| (i as f32 * 0.37).sin() * 5.0).collect();
for bits in [8u8, 16, 0, 255] {
let c = compress_residual_quantized(&residual, bits, 6).unwrap();
assert_eq!(c[0], bits);
let out = decompress_residual_quantized(&c).unwrap();
assert_eq!(out.len(), residual.len());
let tol = if bits == 16 {
10.0 / 65535.0
} else {
10.0 / 255.0
};
for (a, b) in out.iter().zip(&residual) {
assert!((a - b).abs() <= tol / 2.0 + 1e-5, "bits={bits}: {a} vs {b}");
}
}
assert!(decompress_residual_quantized(&[16; 20]).is_err());
let mut truncated = compress_residual_quantized(&residual, 8, 6).unwrap();
truncated.truncate(30);
assert!(decompress_residual_quantized(&truncated).is_err());
}
#[test]
fn lossless_container_is_bit_exact() {
let residual: Vec<f32> = vec![0.0, -0.0, 1.5e-30, f32::MAX, -7.25, 3.0e10];
let c = compress_residual_lossless(&residual, 6).unwrap();
assert_eq!(c[0], LOSSLESS_MARKER);
let out = decompress_residual_lossless(&c).unwrap();
assert_eq!(out.len(), residual.len());
for (a, b) in out.iter().zip(&residual) {
assert_eq!(a.to_bits(), b.to_bits());
}
assert!(decompress_residual_lossless(&[0x00, 0, 0, 0, 0]).is_err());
assert!(decompress_residual_lossless(&[0xFF, 9, 0, 0, 0, 1]).is_err());
}
}
}