#![allow(dead_code)]
#[derive(Debug, Clone)]
pub struct BrotliConfig {
pub quality: u8,
pub window_bits: u8,
}
impl Default for BrotliConfig {
fn default() -> Self {
Self {
quality: 6,
window_bits: 22,
}
}
}
#[derive(Debug, Clone)]
pub struct BrotliCompressor {
pub config: BrotliConfig,
}
impl BrotliCompressor {
pub fn new(config: BrotliConfig) -> Self {
Self { config }
}
pub fn default_compressor() -> Self {
Self::new(BrotliConfig::default())
}
pub fn with_quality(quality: u8) -> Self {
Self::new(BrotliConfig {
quality: quality.min(11),
window_bits: 22,
})
}
}
pub fn brotli_compress(data: &[u8]) -> Vec<u8> {
let mut out = Vec::with_capacity(data.len() + 4);
out.extend_from_slice(&(data.len() as u32).to_le_bytes());
out.extend_from_slice(data);
out
}
pub fn brotli_decompress(data: &[u8]) -> Result<Vec<u8>, String> {
if data.len() < 4 {
return Err("brotli: input too short".to_string());
}
let expected = u32::from_le_bytes(data[..4].try_into().unwrap_or_default()) as usize;
let payload = &data[4..];
if payload.len() < expected {
return Err("brotli: truncated payload".to_string());
}
Ok(payload[..expected].to_vec())
}
pub fn brotli_max_compressed_size(input_len: usize) -> usize {
input_len + 10
}
pub fn brotli_quality_valid(quality: u8) -> bool {
quality <= 11
}
pub fn brotli_roundtrip_ok(data: &[u8]) -> bool {
brotli_decompress(&brotli_compress(data))
.map(|d| d == data)
.unwrap_or(false)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_default_quality() {
assert_eq!(BrotliConfig::default().quality, 6);
}
#[test]
fn test_quality_clamped() {
let c = BrotliCompressor::with_quality(20);
assert_eq!(c.config.quality, 11);
}
#[test]
fn test_quality_valid() {
assert!(brotli_quality_valid(0));
assert!(brotli_quality_valid(11));
assert!(!brotli_quality_valid(12));
}
#[test]
fn test_compress_empty() {
assert_eq!(brotli_compress(&[]).len(), 4);
}
#[test]
fn test_roundtrip_hello() {
assert!(brotli_roundtrip_ok(b"Hello Brotli!"));
}
#[test]
fn test_roundtrip_binary() {
let data: Vec<u8> = (0u8..=200).collect();
assert!(brotli_roundtrip_ok(&data));
}
#[test]
fn test_decompress_short() {
assert!(brotli_decompress(&[]).is_err());
}
#[test]
fn test_max_compressed_size() {
assert!(brotli_max_compressed_size(50) > 50);
}
#[test]
fn test_default_window_bits() {
assert_eq!(BrotliConfig::default().window_bits, 22);
}
}