Skip to main content

nano_web/
compression.rs

1use anyhow::Result;
2use bytes::Bytes;
3use flate2::write::GzEncoder;
4use flate2::Compression;
5use std::io::Write;
6
7#[derive(Debug, Clone)]
8pub struct CompressedContent {
9    pub plain: Bytes,
10    pub gzip: Option<Bytes>,
11    pub brotli: Option<Bytes>,
12    pub zstd: Option<Bytes>,
13}
14
15impl CompressedContent {
16    pub fn new(content: Vec<u8>, should_compress: bool) -> Result<Self> {
17        let plain = Bytes::from(content);
18
19        if !should_compress || plain.len() < 1024 {
20            return Ok(Self {
21                plain,
22                gzip: None,
23                brotli: None,
24                zstd: None,
25            });
26        }
27
28        let plain_ref = &plain;
29        let (gzip, (brotli, zstd)) = rayon::join(
30            || gzip_compress(plain_ref),
31            || rayon::join(|| brotli_compress(plain_ref), || zstd_compress(plain_ref)),
32        );
33
34        Ok(Self {
35            plain,
36            gzip: Some(gzip?),
37            brotli: Some(brotli?),
38            zstd: Some(zstd?),
39        })
40    }
41}
42
43pub fn gzip_compress(data: &[u8]) -> Result<Bytes> {
44    let mut encoder = GzEncoder::new(Vec::new(), Compression::best());
45    encoder.write_all(data)?;
46    let compressed = encoder.finish()?;
47    Ok(Bytes::from(compressed))
48}
49
50pub fn brotli_compress(data: &[u8]) -> Result<Bytes> {
51    let mut compressed = Vec::new();
52    let mut cursor = std::io::Cursor::new(data);
53    let params = brotli::enc::BrotliEncoderParams {
54        quality: 11, // Maximum quality (0-11)
55        ..Default::default()
56    };
57    brotli::BrotliCompress(&mut cursor, &mut compressed, &params)?;
58    Ok(Bytes::from(compressed))
59}
60
61pub fn zstd_compress(data: &[u8]) -> Result<Bytes> {
62    let compressed = zstd::bulk::compress(data, 22)?; // Maximum level (1-22)
63    Ok(Bytes::from(compressed))
64}