Skip to main content

extendable_assets/asset/serialize/
compression.rs

1use std::io::prelude::*;
2
3use flate2::{Compression, read::ZlibDecoder, write::ZlibEncoder};
4
5/// Specifies the compression algorithm used for compressed asset data.
6///
7/// This enum is marked as non-exhaustive to allow adding new compression
8/// algorithms in future versions without breaking changes.
9///
10/// Variant names are serialized in `snake_case` format (e.g., "zlib" for `Zlib`).
11#[derive(Debug, Clone, Copy, PartialOrd, PartialEq)]
12#[derive(serde::Deserialize, serde::Serialize)]
13#[serde(rename_all = "snake_case")]
14#[non_exhaustive]
15pub enum CompressionMode {
16    /// Zlib compression algorithm
17    Zlib,
18}
19impl CompressionMode {
20    /// Compresses the given bytes using this compression algorithm.
21    ///
22    /// Returns `Some(compressed_bytes)` on successful compression,
23    /// or `None` if compression fails.
24    ///
25    /// # Arguments
26    /// * `bytes` - The raw data to compress
27    pub fn compress(&self, bytes: &[u8]) -> Option<Vec<u8>> {
28        match self {
29            Self::Zlib => {
30                let mut enc = ZlibEncoder::new(Vec::new(), Compression::best());
31                enc.write_all(bytes).ok()?;
32                enc.finish().ok()
33            }
34        }
35    }
36
37    /// Decompresses the given bytes using this compression algorithm.
38    ///
39    /// Returns `Some(decompressed_bytes)` on successful decompression,
40    /// or `None` if decompression fails or the data is corrupted.
41    ///
42    /// # Arguments
43    /// * `bytes` - The compressed data to decompress
44    pub fn decompress(&self, bytes: &[u8]) -> Option<Vec<u8>> {
45        match self {
46            Self::Zlib => {
47                let mut dec = ZlibDecoder::new(bytes);
48                let mut out = Vec::new();
49                dec.read_to_end(&mut out).ok()?;
50                Some(out)
51            }
52        }
53    }
54}
55
56#[cfg(test)]
57mod test {
58    use super::*;
59    use rand::prelude::*;
60    #[test]
61    fn zlib_roundtrip() {
62        // Make compressable data
63        let values: [u8; 10] = rand::rng().random();
64        let data = values
65            .into_iter()
66            .flat_map(|v| {
67                let n: usize = rand::rng().random_range(16..48);
68                std::iter::repeat_n(v, n)
69            })
70            .collect::<Vec<_>>();
71
72        // Test compression
73        let compressed = CompressionMode::Zlib.compress(&data).unwrap();
74        assert!(compressed.len() < data.len());
75
76        // Test decompression
77        let decompress = CompressionMode::Zlib.decompress(&compressed).unwrap();
78        assert_eq!(decompress, data);
79    }
80}