use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CompressionPolicy {
pub text_level: i32,
pub entropy_bypass_threshold: f64,
pub enable_bypass_for_precompressed: bool,
}
impl Default for CompressionPolicy {
fn default() -> Self {
Self {
text_level: 3,
entropy_bypass_threshold: 7.6,
enable_bypass_for_precompressed: true,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum CompressionCodec {
Zstd,
Bypass,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CompressionDecision {
pub codec: CompressionCodec,
pub level: i32,
pub bypass_reason: Option<String>,
pub entropy_score: f64,
pub mime: String,
}
impl CompressionDecision {
pub fn to_json(&self) -> String {
serde_json::to_string(self).unwrap_or_else(|_| "{}".to_string())
}
}
pub fn decide(content_type: Option<&str>, payload: &[u8], policy: &CompressionPolicy) -> CompressionDecision {
let mime = content_type.unwrap_or("application/octet-stream").to_ascii_lowercase();
let entropy = shannon_entropy(payload);
let looks_precompressed = mime.contains("zip")
|| mime.contains("gzip")
|| mime.contains("video/")
|| mime.contains("audio/")
|| mime.contains("image/")
|| mime.contains("application/pdf");
if policy.enable_bypass_for_precompressed
&& looks_precompressed
&& entropy >= policy.entropy_bypass_threshold
{
return CompressionDecision {
codec: CompressionCodec::Bypass,
level: 0,
bypass_reason: Some("high_entropy_or_precompressed".to_string()),
entropy_score: entropy,
mime,
};
}
let level = if mime.contains("json") || mime.contains("text/") || mime.contains("xml") {
policy.text_level.clamp(1, 5)
} else {
1
};
CompressionDecision {
codec: CompressionCodec::Zstd,
level,
bypass_reason: None,
entropy_score: entropy,
mime,
}
}
fn shannon_entropy(payload: &[u8]) -> f64 {
if payload.is_empty() {
return 0.0;
}
let mut counts = [0_u64; 256];
for b in payload {
counts[*b as usize] += 1;
}
let len = payload.len() as f64;
let mut entropy = 0.0_f64;
for c in counts {
if c == 0 {
continue;
}
let p = c as f64 / len;
entropy -= p * p.log2();
}
entropy
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn text_payload_prefers_zstd() {
let payload = b"{\"a\":1,\"b\":2}";
let decision = decide(Some("application/json"), payload, &CompressionPolicy::default());
assert!(matches!(decision.codec, CompressionCodec::Zstd));
assert!(decision.level >= 1);
}
#[test]
fn high_entropy_video_prefers_bypass() {
let payload = (0..=255_u8).collect::<Vec<_>>();
let decision = decide(Some("video/mp4"), &payload, &CompressionPolicy::default());
assert!(matches!(decision.codec, CompressionCodec::Bypass));
}
}