kacrab_protocol/compression/
gzip.rs1use std::io::Write;
4
5use super::{Compression, CompressionError, CompressionErrorKind, Result};
6
7const DEFAULT_GZIP_LEVEL: u32 = 6;
8
9pub fn compress_with_level(payload: &[u8], level: Option<i32>) -> Result<Vec<u8>> {
12 let lvl = level.map_or(DEFAULT_GZIP_LEVEL, |l| l.max(0).cast_unsigned());
13 let mut encoder = flate2::write::GzEncoder::new(Vec::new(), flate2::Compression::new(lvl));
14 encoder
15 .write_all(payload)
16 .map_err(|e| encode_err(e.to_string()))?;
17 encoder.finish().map_err(|e| encode_err(e.to_string()))
18}
19
20pub fn decompress(payload: &[u8]) -> Result<Vec<u8>> {
22 decompress_bounded(payload, super::MAX_DECOMPRESSED_LEN)
23}
24
25pub fn decompress_bounded(payload: &[u8], max_len: usize) -> Result<Vec<u8>> {
28 let decoder = flate2::read::GzDecoder::new(payload);
29 super::read_to_end_bounded(decoder, max_len, Compression::Gzip)
30}
31
32const fn encode_err(message: String) -> CompressionError {
33 CompressionError::new(
34 Compression::Gzip,
35 CompressionErrorKind::EncodeFailed { message },
36 )
37}
38
39#[cfg(test)]
40mod tests {
41 use super::{super::CompressionErrorKind, compress_with_level, decompress, decompress_bounded};
42
43 #[test]
44 fn decompress_bounded_rejects_a_decompression_bomb() {
45 let payload = vec![0u8; 4096];
48 let compressed = compress_with_level(&payload, None).unwrap();
49
50 let err = decompress_bounded(&compressed, 64).unwrap_err();
51 assert!(
52 matches!(
53 err.kind,
54 CompressionErrorKind::DecompressedTooLarge { limit: 64 }
55 ),
56 "expected DecompressedTooLarge, got {:?}",
57 err.kind
58 );
59 }
60
61 #[test]
62 fn decompress_bounded_allows_output_at_exactly_the_limit() {
63 let payload = vec![0u8; 4096];
64 let compressed = compress_with_level(&payload, None).unwrap();
65
66 assert_eq!(decompress_bounded(&compressed, 4096).unwrap(), payload);
67 assert_eq!(decompress(&compressed).unwrap(), payload);
68 }
69}