1pub mod fast_deflate;
13
14pub fn deflate_decompress(data: &[u8], max_output_size: usize) -> Result<Vec<u8>, String> {
19 fast_deflate::decompress(data, max_output_size)
20}
21
22pub fn deflate_compress(data: &[u8], level: u32) -> Result<Vec<u8>, String> {
26 fast_deflate::compress(data, level)
27}
28
29pub fn deflate_decompress_miniz(data: &[u8]) -> Result<Vec<u8>, String> {
32 let result = miniz_oxide::inflate::decompress_to_vec_zlib(data)
33 .map_err(|e| format!("miniz_oxide decompress error: {e:?}"))?;
34 Ok(result)
35}
36
37pub fn deflate_compress_miniz(data: &[u8], level: u32) -> Result<Vec<u8>, String> {
40 let level = level.min(10) as u8;
41 let result = miniz_oxide::deflate::compress_to_vec_zlib(data, level);
42 Ok(result)
43}
44
45pub fn deflate_backend() -> &'static str {
47 fast_deflate::active_backend()
48}
49
50#[cfg(test)]
51mod tests {
52 use super::*;
53
54 #[test]
55 fn compress_decompress_roundtrip() {
56 let data: Vec<u8> = (0..1000).map(|i| (i % 256) as u8).collect();
57 let compressed = deflate_compress(&data, 6).unwrap();
58 let decompressed = deflate_decompress(&compressed, data.len()).unwrap();
59 assert_eq!(decompressed, data);
60 }
61
62 #[test]
63 fn decompress_python_zlib() {
64 let compressed: Vec<u8> = vec![
66 120, 156, 99, 96, 100, 98, 102, 97, 101, 99, 231, 224, 4, 0, 0, 175, 0, 46,
67 ];
68 let decompressed = deflate_decompress(&compressed, 10).unwrap();
69 assert_eq!(decompressed, vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9]);
70 }
71
72 #[test]
73 fn miniz_always_available() {
74 let data = vec![42u8; 100];
75 let compressed = deflate_compress_miniz(&data, 6).unwrap();
76 let decompressed = deflate_decompress_miniz(&compressed).unwrap();
77 assert_eq!(decompressed, data);
78 }
79
80 #[test]
81 fn cross_backend_compatibility() {
82 let data: Vec<u8> = (0..500).map(|i| (i * 7 % 256) as u8).collect();
84 let compressed = deflate_compress_miniz(&data, 6).unwrap();
85 let decompressed = deflate_decompress(&compressed, data.len()).unwrap();
86 assert_eq!(decompressed, data);
87 }
88
89 #[test]
90 fn cross_backend_reverse() {
91 let data: Vec<u8> = (0..500).map(|i| (i * 13 % 256) as u8).collect();
93 let compressed = deflate_compress(&data, 6).unwrap();
94 let decompressed = deflate_decompress_miniz(&compressed).unwrap();
95 assert_eq!(decompressed, data);
96 }
97
98 #[test]
99 fn empty_data() {
100 let compressed = deflate_compress(&[], 6).unwrap();
101 let decompressed = deflate_decompress(&compressed, 0).unwrap();
102 assert!(decompressed.is_empty());
103 }
104
105 #[test]
106 fn large_data_roundtrip() {
107 let data: Vec<u8> = (0..100_000).map(|i| (i % 256) as u8).collect();
108 let compressed = deflate_compress(&data, 6).unwrap();
109 assert!(compressed.len() < data.len()); let decompressed = deflate_decompress(&compressed, data.len()).unwrap();
111 assert_eq!(decompressed, data);
112 }
113
114 #[test]
115 fn backend_reports_name() {
116 let name = deflate_backend();
117 assert!(
118 ["miniz_oxide", "zlib-ng", "apple-compression"].contains(&name),
119 "unexpected backend: {name}"
120 );
121 }
122
123 #[test]
124 fn all_backends_produce_identical_output() {
125 let data: Vec<u8> = (0..10_000).map(|i| (i * 31 % 256) as u8).collect();
126
127 let compressed_current = deflate_compress(&data, 6).unwrap();
129 let compressed_miniz = deflate_compress_miniz(&data, 6).unwrap();
131
132 let dec_current = deflate_decompress(&compressed_current, data.len()).unwrap();
134 let dec_miniz = deflate_decompress_miniz(&compressed_miniz).unwrap();
135 let dec_cross = deflate_decompress_miniz(&compressed_current).unwrap();
136 let dec_cross2 = deflate_decompress(&compressed_miniz, data.len()).unwrap();
137
138 assert_eq!(dec_current, data);
139 assert_eq!(dec_miniz, data);
140 assert_eq!(dec_cross, data);
141 assert_eq!(dec_cross2, data);
142 }
143}