Skip to main content

sark_core/http/
compress.rs

1//! ```no_run
2//! use sark_core::http::compress::Gzip;
3//! let body: &[u8] = b"hello";
4//! let zipped = Gzip::new().encode(body).unwrap();
5//! ```
6
7use std::ptr::NonNull;
8
9use libdeflate_sys::{
10    libdeflate_alloc_compressor, libdeflate_compressor, libdeflate_free_compressor,
11    libdeflate_gzip_compress, libdeflate_gzip_compress_bound,
12};
13use o3::buffer::{Pooled, SharedPool};
14
15const GZIP_SLOTS: usize = 32;
16const GZIP_CAPACITY: usize = 256 * 1024;
17
18pub struct Gzip {
19    encoder: NonNull<libdeflate_compressor>,
20    pool: SharedPool,
21}
22
23impl Gzip {
24    const LEVEL: i32 = 3;
25
26    pub fn new() -> Self {
27        Self::with_pool(GZIP_SLOTS, GZIP_CAPACITY)
28    }
29
30    pub fn with_pool(slots: usize, capacity: usize) -> Self {
31        let encoder = NonNull::new(unsafe { libdeflate_alloc_compressor(Self::LEVEL) })
32            .expect("libdeflate compressor allocation failed");
33        Self {
34            encoder,
35            pool: SharedPool::new(slots, capacity),
36        }
37    }
38
39    pub fn encode(&mut self, src: &[u8]) -> Option<Pooled> {
40        let cap = unsafe { libdeflate_gzip_compress_bound(self.encoder.as_ptr(), src.len()) };
41        if cap > self.pool.capacity() {
42            return None;
43        }
44        let mut lease = self.pool.try_acquire()?;
45        let mut writer = lease.spare_writer();
46        let ptr = writer.as_mut_ptr();
47        let capacity = writer.remaining();
48        let n = unsafe {
49            libdeflate_gzip_compress(
50                self.encoder.as_ptr(),
51                src.as_ptr().cast(),
52                src.len(),
53                ptr.cast(),
54                capacity,
55            )
56        };
57        assert!(n != 0 && n <= capacity, "gzip_compress_bound undersized");
58        let output = unsafe { std::slice::from_raw_parts(ptr, n) };
59        writer
60            .try_commit_initialized(output)
61            .expect("gzip_compress_bound undersized");
62        drop(writer);
63        Some(lease.freeze())
64    }
65}
66
67impl Drop for Gzip {
68    fn drop(&mut self) {
69        unsafe { libdeflate_free_compressor(self.encoder.as_ptr()) };
70    }
71}
72
73impl Default for Gzip {
74    fn default() -> Self {
75        Self::new()
76    }
77}