mediagit_compression/
zlib_compressor.rs1use crate::error::{CompressionError, CompressionResult};
20use crate::{CompressionLevel, Compressor};
21use flate2::read::{ZlibDecoder, ZlibEncoder};
22use flate2::Compression;
23use std::fmt;
24use std::io::Read;
25
26#[derive(Clone)]
31pub struct ZlibCompressor {
32 level: CompressionLevel,
33}
34
35impl ZlibCompressor {
36 pub fn new(level: CompressionLevel) -> Self {
38 ZlibCompressor { level }
39 }
40
41 pub fn fast() -> Self {
43 ZlibCompressor::new(CompressionLevel::Fast)
44 }
45
46 pub fn default_level() -> Self {
48 ZlibCompressor::new(CompressionLevel::Default)
49 }
50
51 pub fn best() -> Self {
53 ZlibCompressor::new(CompressionLevel::Best)
54 }
55
56 fn get_compression(&self) -> Compression {
58 match self.level {
59 CompressionLevel::Fast => Compression::fast(), CompressionLevel::Default => Compression::new(6), CompressionLevel::Best => Compression::best(), }
63 }
64}
65
66impl fmt::Debug for ZlibCompressor {
67 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
68 f.debug_struct("ZlibCompressor")
69 .field("level", &self.level)
70 .finish()
71 }
72}
73
74impl Compressor for ZlibCompressor {
75 fn compress(&self, data: &[u8]) -> CompressionResult<Vec<u8>> {
76 if data.is_empty() {
77 return Ok(Vec::new());
78 }
79
80 let mut encoder = ZlibEncoder::new(data, self.get_compression());
81 let mut compressed = Vec::new();
82
83 encoder
84 .read_to_end(&mut compressed)
85 .map_err(|e| CompressionError::zstd_error(format!("zlib compression failed: {}", e)))?;
86
87 Ok(compressed)
88 }
89
90 fn decompress(&self, data: &[u8]) -> CompressionResult<Vec<u8>> {
91 if data.is_empty() {
92 return Ok(Vec::new());
93 }
94
95 let is_zlib = data.len() >= 2 && data[0] == 0x78 && {
99 let cmf = data[0] as u16;
100 let flg = data[1] as u16;
101 (cmf * 256 + flg).is_multiple_of(31)
102 };
103
104 if is_zlib {
105 let mut decoder = ZlibDecoder::new(data);
106 let mut decompressed = Vec::new();
107
108 decoder.read_to_end(&mut decompressed).map_err(|e| {
109 CompressionError::decompression_failed(format!("zlib decompression failed: {}", e))
110 })?;
111
112 Ok(decompressed)
113 } else {
114 Ok(data.to_vec())
117 }
118 }
119}
120
121#[cfg(test)]
122#[allow(clippy::unwrap_used)]
123mod tests {
124 use super::*;
125
126 #[test]
127 fn test_zlib_compress_decompress() {
128 let compressor = ZlibCompressor::new(CompressionLevel::Default);
129 let original = b"Hello, World! This is a test of zlib compression.";
130
131 let compressed = compressor.compress(original).unwrap();
132 let decompressed = compressor.decompress(&compressed).unwrap();
133
134 assert_eq!(original, &decompressed[..]);
135 }
136
137 #[test]
138 fn test_zlib_compress_empty() {
139 let compressor = ZlibCompressor::new(CompressionLevel::Default);
140 let compressed = compressor.compress(b"").unwrap();
141 assert_eq!(compressed.len(), 0);
142 }
143
144 #[test]
145 fn test_zlib_decompress_empty() {
146 let compressor = ZlibCompressor::new(CompressionLevel::Default);
147 let decompressed = compressor.decompress(b"").unwrap();
148 assert_eq!(decompressed.len(), 0);
149 }
150
151 #[test]
152 fn test_zlib_decompress_uncompressed_data() {
153 let compressor = ZlibCompressor::new(CompressionLevel::Default);
154 let data = b"This is not compressed";
155 let result = compressor.decompress(data).unwrap();
156 assert_eq!(result, data);
158 }
159
160 #[test]
161 fn test_zlib_compression_levels() {
162 let data = b"This is test data that should compress. ".repeat(100);
163
164 let fast = ZlibCompressor::fast().compress(&data).unwrap();
165 let default = ZlibCompressor::default_level().compress(&data).unwrap();
166 let best = ZlibCompressor::best().compress(&data).unwrap();
167
168 assert!(best.len() <= default.len());
170 assert!(default.len() <= fast.len() + 50); let decompressor = ZlibCompressor::default_level();
174 let fast_dec = decompressor.decompress(&fast).unwrap();
175 let default_dec = decompressor.decompress(&default).unwrap();
176 let best_dec = decompressor.decompress(&best).unwrap();
177
178 assert_eq!(fast_dec, data);
179 assert_eq!(default_dec, data);
180 assert_eq!(best_dec, data);
181 }
182
183 #[test]
184 fn test_zlib_large_data() {
185 let compressor = ZlibCompressor::new(CompressionLevel::Default);
186 let original = vec![0x42u8; 1024 * 1024]; let compressed = compressor.compress(&original).unwrap();
189 let decompressed = compressor.decompress(&compressed).unwrap();
190
191 assert_eq!(original, decompressed);
192 assert!(compressed.len() < original.len() / 100);
194 }
195
196 #[test]
197 fn test_zlib_random_data() {
198 let compressor = ZlibCompressor::new(CompressionLevel::Default);
199 let original = (0..1000).map(|i| (i ^ 0xAA) as u8).collect::<Vec<_>>();
201
202 let compressed = compressor.compress(&original).unwrap();
203 let decompressed = compressor.decompress(&compressed).unwrap();
204
205 assert_eq!(original, decompressed);
206 }
207
208 #[test]
209 fn test_zlib_debug_format() {
210 let compressor = ZlibCompressor::new(CompressionLevel::Default);
211 let debug_str = format!("{:?}", compressor);
212 assert!(debug_str.contains("ZlibCompressor"));
213 assert!(debug_str.contains("Default"));
214 }
215
216 #[test]
217 fn test_zlib_git_compatibility() {
218 let compressor = ZlibCompressor::default_level();
220 let git_blob = b"blob 13\0Hello, World!";
221
222 let compressed = compressor.compress(git_blob).unwrap();
223
224 assert_eq!(compressed[0], 0x78);
226
227 let decompressed = compressor.decompress(&compressed).unwrap();
229 assert_eq!(decompressed, git_blob);
230 }
231}