base_d/encoders/algorithms/schema/
compression.rs1use super::types::SchemaError;
2use crate::features::compression::{CompressionAlgorithm, compress, decompress};
3
4const ALGO_NONE: u8 = 0x00;
6const ALGO_BROTLI: u8 = 0x01;
7const ALGO_LZ4: u8 = 0x02;
8const ALGO_ZSTD: u8 = 0x03;
9
10const DEFAULT_LEVEL: u32 = 6;
12
13#[derive(Clone, Copy, Debug)]
43pub enum SchemaCompressionAlgo {
44 Brotli,
45 Lz4,
46 Zstd,
47}
48
49pub fn compress_with_prefix(
94 binary: &[u8],
95 algo: Option<SchemaCompressionAlgo>,
96) -> Result<Vec<u8>, SchemaError> {
97 let (algo_byte, compressed) = match algo {
98 None => (ALGO_NONE, binary.to_vec()),
99 Some(SchemaCompressionAlgo::Brotli) => {
100 let c = compress(binary, CompressionAlgorithm::Brotli, DEFAULT_LEVEL)
101 .map_err(|e| SchemaError::Compression(e.to_string()))?;
102 (ALGO_BROTLI, c)
103 }
104 Some(SchemaCompressionAlgo::Lz4) => {
105 let c = compress(binary, CompressionAlgorithm::Lz4, DEFAULT_LEVEL)
106 .map_err(|e| SchemaError::Compression(e.to_string()))?;
107 (ALGO_LZ4, c)
108 }
109 Some(SchemaCompressionAlgo::Zstd) => {
110 let c = compress(binary, CompressionAlgorithm::Zstd, DEFAULT_LEVEL)
111 .map_err(|e| SchemaError::Compression(e.to_string()))?;
112 (ALGO_ZSTD, c)
113 }
114 };
115
116 let mut result = Vec::with_capacity(1 + compressed.len());
118 result.push(algo_byte);
119 result.extend_from_slice(&compressed);
120 Ok(result)
121}
122
123pub fn decompress_with_prefix(binary: &[u8]) -> Result<Vec<u8>, SchemaError> {
157 if binary.is_empty() {
158 return Err(SchemaError::UnexpectedEndOfData {
159 context: "compression prefix".to_string(),
160 position: 0,
161 });
162 }
163
164 let algo_byte = binary[0];
165 let payload = &binary[1..];
166
167 match algo_byte {
168 ALGO_NONE => Ok(payload.to_vec()),
169 ALGO_BROTLI => decompress(payload, CompressionAlgorithm::Brotli)
170 .map_err(|e| SchemaError::Decompression(e.to_string())),
171 ALGO_LZ4 => decompress(payload, CompressionAlgorithm::Lz4)
172 .map_err(|e| SchemaError::Decompression(e.to_string())),
173 ALGO_ZSTD => decompress(payload, CompressionAlgorithm::Zstd)
174 .map_err(|e| SchemaError::Decompression(e.to_string())),
175 _ => Err(SchemaError::InvalidCompressionAlgorithm(algo_byte)),
176 }
177}
178
179#[cfg(test)]
180mod tests {
181 use super::*;
182
183 #[test]
184 fn test_no_compression() {
185 let data = b"Hello, world!";
186 let compressed = compress_with_prefix(data, None).unwrap();
187
188 assert_eq!(compressed[0], ALGO_NONE);
190 assert_eq!(&compressed[1..], data);
191
192 let decompressed = decompress_with_prefix(&compressed).unwrap();
193 assert_eq!(decompressed, data);
194 }
195
196 #[test]
197 fn test_brotli_roundtrip() {
198 let data = b"Hello, world! This is a test of brotli compression in schema encoding.";
199 let compressed = compress_with_prefix(data, Some(SchemaCompressionAlgo::Brotli)).unwrap();
200
201 assert_eq!(compressed[0], ALGO_BROTLI);
202
203 let decompressed = decompress_with_prefix(&compressed).unwrap();
204 assert_eq!(decompressed, data);
205 }
206
207 #[test]
208 fn test_lz4_roundtrip() {
209 let data = b"Hello, world! This is a test of lz4 compression in schema encoding.";
210 let compressed = compress_with_prefix(data, Some(SchemaCompressionAlgo::Lz4)).unwrap();
211
212 assert_eq!(compressed[0], ALGO_LZ4);
213
214 let decompressed = decompress_with_prefix(&compressed).unwrap();
215 assert_eq!(decompressed, data);
216 }
217
218 #[test]
219 fn test_zstd_roundtrip() {
220 let data = b"Hello, world! This is a test of zstd compression in schema encoding.";
221 let compressed = compress_with_prefix(data, Some(SchemaCompressionAlgo::Zstd)).unwrap();
222
223 assert_eq!(compressed[0], ALGO_ZSTD);
224
225 let decompressed = decompress_with_prefix(&compressed).unwrap();
226 assert_eq!(decompressed, data);
227 }
228
229 #[test]
230 fn test_small_payload() {
231 let data = b"Hi";
233 let compressed = compress_with_prefix(data, Some(SchemaCompressionAlgo::Brotli)).unwrap();
234 let decompressed = decompress_with_prefix(&compressed).unwrap();
235 assert_eq!(decompressed, data);
236 }
237
238 #[test]
239 fn test_empty_payload() {
240 let data = b"";
241 let compressed = compress_with_prefix(data, None).unwrap();
242 assert_eq!(compressed, vec![ALGO_NONE]);
243
244 let decompressed = decompress_with_prefix(&compressed).unwrap();
245 assert_eq!(decompressed, data);
246 }
247
248 #[test]
249 fn test_invalid_algorithm() {
250 let invalid = vec![0xFF, 0x01, 0x02, 0x03];
251 let result = decompress_with_prefix(&invalid);
252 assert!(matches!(
253 result,
254 Err(SchemaError::InvalidCompressionAlgorithm(0xFF))
255 ));
256 }
257
258 #[test]
259 fn test_missing_prefix() {
260 let result = decompress_with_prefix(&[]);
261 assert!(matches!(
262 result,
263 Err(SchemaError::UnexpectedEndOfData { .. })
264 ));
265 }
266}