1use std::io::{Read, Write};
4
5use crate::{CyaneaError, Result};
6
7#[derive(Debug, Clone, Copy, PartialEq, Eq)]
9pub enum Algorithm {
10 Zstd,
11 Gzip,
12}
13
14pub fn zstd_compress(data: &[u8], level: i32) -> Result<Vec<u8>> {
16 zstd::encode_all(data, level).map_err(|e| CyaneaError::Compression(e.to_string()))
17}
18
19pub fn zstd_decompress(data: &[u8]) -> Result<Vec<u8>> {
21 zstd::decode_all(data).map_err(|e| CyaneaError::Compression(e.to_string()))
22}
23
24pub fn gzip_compress(data: &[u8], level: u32) -> Result<Vec<u8>> {
26 use flate2::write::GzEncoder;
27 use flate2::Compression;
28
29 let mut encoder = GzEncoder::new(Vec::new(), Compression::new(level));
30 encoder
31 .write_all(data)
32 .map_err(|e| CyaneaError::Compression(e.to_string()))?;
33 encoder
34 .finish()
35 .map_err(|e| CyaneaError::Compression(e.to_string()))
36}
37
38pub fn gzip_decompress(data: &[u8]) -> Result<Vec<u8>> {
40 use flate2::read::GzDecoder;
41
42 let mut decoder = GzDecoder::new(data);
43 let mut decompressed = Vec::new();
44 decoder
45 .read_to_end(&mut decompressed)
46 .map_err(|e| CyaneaError::Compression(e.to_string()))?;
47 Ok(decompressed)
48}
49
50pub fn detect_algorithm(data: &[u8]) -> Option<Algorithm> {
54 if data.len() >= 4 && data[..4] == [0x28, 0xB5, 0x2F, 0xFD] {
55 Some(Algorithm::Zstd)
56 } else if data.len() >= 2 && data[..2] == [0x1F, 0x8B] {
57 Some(Algorithm::Gzip)
58 } else {
59 None
60 }
61}
62
63pub fn decompress(data: &[u8]) -> Result<Vec<u8>> {
67 match detect_algorithm(data) {
68 Some(Algorithm::Zstd) => zstd_decompress(data),
69 Some(Algorithm::Gzip) => gzip_decompress(data),
70 None => Err(CyaneaError::Compression(
71 "unknown compression format".into(),
72 )),
73 }
74}
75
76#[cfg(test)]
77mod tests {
78 use super::*;
79
80 #[test]
81 fn test_zstd_roundtrip() {
82 let original = b"Hello, world! This is some test data for compression.";
83 let compressed = zstd_compress(original, 3).unwrap();
84 let decompressed = zstd_decompress(&compressed).unwrap();
85 assert_eq!(original.to_vec(), decompressed);
86 }
87
88 #[test]
89 fn test_gzip_roundtrip() {
90 let original = b"Hello, world! This is gzip test data.";
91 let compressed = gzip_compress(original, 6).unwrap();
92 let decompressed = gzip_decompress(&compressed).unwrap();
93 assert_eq!(original.to_vec(), decompressed);
94 }
95
96 #[test]
97 fn test_detect_zstd() {
98 let compressed = zstd_compress(b"test", 3).unwrap();
99 assert_eq!(detect_algorithm(&compressed), Some(Algorithm::Zstd));
100 }
101
102 #[test]
103 fn test_detect_gzip() {
104 let compressed = gzip_compress(b"test", 6).unwrap();
105 assert_eq!(detect_algorithm(&compressed), Some(Algorithm::Gzip));
106 }
107
108 #[test]
109 fn test_detect_unknown() {
110 assert_eq!(detect_algorithm(b"not compressed"), None);
111 }
112
113 #[test]
114 fn test_auto_decompress_zstd() {
115 let original = b"auto-detect zstd";
116 let compressed = zstd_compress(original, 3).unwrap();
117 let decompressed = decompress(&compressed).unwrap();
118 assert_eq!(original.to_vec(), decompressed);
119 }
120
121 #[test]
122 fn test_auto_decompress_gzip() {
123 let original = b"auto-detect gzip";
124 let compressed = gzip_compress(original, 6).unwrap();
125 let decompressed = decompress(&compressed).unwrap();
126 assert_eq!(original.to_vec(), decompressed);
127 }
128
129 #[test]
130 fn test_auto_decompress_unknown() {
131 let result = decompress(b"not compressed data");
132 assert!(result.is_err());
133 }
134}