1use std::io::{Read, Write};
2
3#[derive(Debug, Clone, Copy, PartialEq, Eq)]
5pub enum CompressionAlgorithm {
6 Gzip,
7 Zstd,
8 Brotli,
9 Lz4,
10 Snappy,
11 Lzma,
12}
13
14impl CompressionAlgorithm {
15 pub fn from_str(s: &str) -> Result<Self, String> {
17 match s.to_lowercase().as_str() {
18 "gzip" | "gz" => Ok(CompressionAlgorithm::Gzip),
19 "zstd" | "zst" => Ok(CompressionAlgorithm::Zstd),
20 "brotli" | "br" => Ok(CompressionAlgorithm::Brotli),
21 "lz4" => Ok(CompressionAlgorithm::Lz4),
22 "snappy" | "snap" => Ok(CompressionAlgorithm::Snappy),
23 "lzma" | "xz" => Ok(CompressionAlgorithm::Lzma),
24 _ => Err(format!("Unknown compression algorithm: {}", s)),
25 }
26 }
27
28 pub fn as_str(&self) -> &str {
29 match self {
30 CompressionAlgorithm::Gzip => "gzip",
31 CompressionAlgorithm::Zstd => "zstd",
32 CompressionAlgorithm::Brotli => "brotli",
33 CompressionAlgorithm::Lz4 => "lz4",
34 CompressionAlgorithm::Snappy => "snappy",
35 CompressionAlgorithm::Lzma => "lzma",
36 }
37 }
38}
39
40pub fn compress(data: &[u8], algorithm: CompressionAlgorithm, level: u32) -> Result<Vec<u8>, Box<dyn std::error::Error>> {
42 match algorithm {
43 CompressionAlgorithm::Gzip => compress_gzip(data, level),
44 CompressionAlgorithm::Zstd => compress_zstd(data, level),
45 CompressionAlgorithm::Brotli => compress_brotli(data, level),
46 CompressionAlgorithm::Lz4 => compress_lz4(data, level),
47 CompressionAlgorithm::Snappy => compress_snappy(data, level),
48 CompressionAlgorithm::Lzma => compress_lzma(data, level),
49 }
50}
51
52pub fn decompress(data: &[u8], algorithm: CompressionAlgorithm) -> Result<Vec<u8>, Box<dyn std::error::Error>> {
54 match algorithm {
55 CompressionAlgorithm::Gzip => decompress_gzip(data),
56 CompressionAlgorithm::Zstd => decompress_zstd(data),
57 CompressionAlgorithm::Brotli => decompress_brotli(data),
58 CompressionAlgorithm::Lz4 => decompress_lz4(data),
59 CompressionAlgorithm::Snappy => decompress_snappy(data),
60 CompressionAlgorithm::Lzma => decompress_lzma(data),
61 }
62}
63
64fn compress_gzip(data: &[u8], level: u32) -> Result<Vec<u8>, Box<dyn std::error::Error>> {
65 use flate2::Compression;
66 use flate2::write::GzEncoder;
67
68 let mut encoder = GzEncoder::new(Vec::new(), Compression::new(level));
69 encoder.write_all(data)?;
70 Ok(encoder.finish()?)
71}
72
73fn decompress_gzip(data: &[u8]) -> Result<Vec<u8>, Box<dyn std::error::Error>> {
74 use flate2::read::GzDecoder;
75
76 let mut decoder = GzDecoder::new(data);
77 let mut result = Vec::new();
78 decoder.read_to_end(&mut result)?;
79 Ok(result)
80}
81
82fn compress_zstd(data: &[u8], level: u32) -> Result<Vec<u8>, Box<dyn std::error::Error>> {
83 Ok(zstd::encode_all(data, level as i32)?)
84}
85
86fn decompress_zstd(data: &[u8]) -> Result<Vec<u8>, Box<dyn std::error::Error>> {
87 Ok(zstd::decode_all(data)?)
88}
89
90fn compress_brotli(data: &[u8], level: u32) -> Result<Vec<u8>, Box<dyn std::error::Error>> {
91 let mut result = Vec::new();
92 let mut reader = brotli::CompressorReader::new(data, 4096, level, 22);
93 reader.read_to_end(&mut result)?;
94 Ok(result)
95}
96
97fn decompress_brotli(data: &[u8]) -> Result<Vec<u8>, Box<dyn std::error::Error>> {
98 let mut result = Vec::new();
99 let mut reader = brotli::Decompressor::new(data, 4096);
100 reader.read_to_end(&mut result)?;
101 Ok(result)
102}
103
104fn compress_lz4(data: &[u8], _level: u32) -> Result<Vec<u8>, Box<dyn std::error::Error>> {
105 Ok(lz4::block::compress(data, None, false)?)
107}
108
109fn decompress_lz4(data: &[u8]) -> Result<Vec<u8>, Box<dyn std::error::Error>> {
110 Ok(lz4::block::decompress(data, Some(100 * 1024 * 1024))?)
113}
114
115fn compress_snappy(data: &[u8], _level: u32) -> Result<Vec<u8>, Box<dyn std::error::Error>> {
116 let mut encoder = snap::raw::Encoder::new();
118 Ok(encoder.compress_vec(data)?)
119}
120
121fn decompress_snappy(data: &[u8]) -> Result<Vec<u8>, Box<dyn std::error::Error>> {
122 let mut decoder = snap::raw::Decoder::new();
123 Ok(decoder.decompress_vec(data)?)
124}
125
126fn compress_lzma(data: &[u8], level: u32) -> Result<Vec<u8>, Box<dyn std::error::Error>> {
127 use xz2::write::XzEncoder;
128
129 let mut encoder = XzEncoder::new(Vec::new(), level);
130 encoder.write_all(data)?;
131 Ok(encoder.finish()?)
132}
133
134fn decompress_lzma(data: &[u8]) -> Result<Vec<u8>, Box<dyn std::error::Error>> {
135 use xz2::read::XzDecoder;
136
137 let mut decoder = XzDecoder::new(data);
138 let mut result = Vec::new();
139 decoder.read_to_end(&mut result)?;
140 Ok(result)
141}
142
143#[cfg(test)]
144mod tests {
145 use super::*;
146
147 #[test]
148 fn test_gzip_roundtrip() {
149 let data = b"Hello, world! This is a test of gzip compression.";
150 let compressed = compress(data, CompressionAlgorithm::Gzip, 6).unwrap();
151 let decompressed = decompress(&compressed, CompressionAlgorithm::Gzip).unwrap();
152 assert_eq!(data.as_ref(), decompressed.as_slice());
153 }
154
155 #[test]
156 fn test_zstd_roundtrip() {
157 let data = b"Hello, world! This is a test of zstd compression.";
158 let compressed = compress(data, CompressionAlgorithm::Zstd, 3).unwrap();
159 let decompressed = decompress(&compressed, CompressionAlgorithm::Zstd).unwrap();
160 assert_eq!(data.as_ref(), decompressed.as_slice());
161 }
162
163 #[test]
164 fn test_brotli_roundtrip() {
165 let data = b"Hello, world! This is a test of brotli compression.";
166 let compressed = compress(data, CompressionAlgorithm::Brotli, 6).unwrap();
167 let decompressed = decompress(&compressed, CompressionAlgorithm::Brotli).unwrap();
168 assert_eq!(data.as_ref(), decompressed.as_slice());
169 }
170
171 #[test]
172 fn test_lz4_roundtrip() {
173 let data = b"Hello, world! This is a test of lz4 compression.";
174 let compressed = compress(data, CompressionAlgorithm::Lz4, 0).unwrap();
175 let decompressed = decompress(&compressed, CompressionAlgorithm::Lz4).unwrap();
176 assert_eq!(data.as_ref(), decompressed.as_slice());
177 }
178
179 #[test]
180 fn test_snappy_roundtrip() {
181 let data = b"Hello, world! This is a test of snappy compression.";
182 let compressed = compress(data, CompressionAlgorithm::Snappy, 0).unwrap();
183 let decompressed = decompress(&compressed, CompressionAlgorithm::Snappy).unwrap();
184 assert_eq!(data.as_ref(), decompressed.as_slice());
185 }
186
187 #[test]
188 fn test_lzma_roundtrip() {
189 let data = b"Hello, world! This is a test of lzma compression.";
190 let compressed = compress(data, CompressionAlgorithm::Lzma, 6).unwrap();
191 let decompressed = decompress(&compressed, CompressionAlgorithm::Lzma).unwrap();
192 assert_eq!(data.as_ref(), decompressed.as_slice());
193 }
194}