use std::io;
pub(crate) fn compress_zlib(data: &[u8]) -> io::Result<Vec<u8>> {
noflate::zlib::compress(data).map_err(io::Error::other)
}
pub(crate) fn decompress_zlib(data: &[u8], max_size: usize) -> io::Result<Vec<u8>> {
const CHUNK_SIZE: usize = 4 * 1024;
let mut decoder = noflate::zlib::Decoder::new();
let mut output = Vec::new();
let mut total_output = 0usize;
for chunk in data.chunks(CHUNK_SIZE) {
decoder.feed(chunk).map_err(io::Error::other)?;
let produced = decoder.output();
let new_total = total_output.checked_add(produced.len()).ok_or_else(|| {
io::Error::new(io::ErrorKind::InvalidData, "decompressed size overflow")
})?;
if new_total > max_size {
return Err(io::Error::new(
io::ErrorKind::InvalidData,
"decompressed size exceeds limit",
));
}
output.extend_from_slice(produced);
total_output = new_total;
decoder.advance(produced.len());
}
if !decoder.is_finished() {
return Err(io::Error::new(
io::ErrorKind::InvalidData,
"zlib stream ended before the trailer",
));
}
Ok(output)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn decompress_zlib_within_limit() {
let original = b"Hello, zlib!";
let compressed = compress_zlib(original).expect("テストデータの圧縮に失敗しました");
let decompressed = decompress_zlib(&compressed, 1024).expect("展開に失敗しました");
assert_eq!(decompressed, original);
}
#[test]
fn decompress_zlib_at_limit() {
let original = vec![b'a'; 1024];
let compressed = compress_zlib(&original).expect("テストデータの圧縮に失敗しました");
let decompressed = decompress_zlib(&compressed, 1024).expect("展開に失敗しました");
assert_eq!(decompressed, original);
}
#[test]
fn decompress_zlib_over_limit() {
let original = vec![b'a'; 1025];
let compressed = compress_zlib(&original).expect("テストデータの圧縮に失敗しました");
let err = decompress_zlib(&compressed, 1024)
.expect_err("上限超過の展開はエラーになる必要があります");
assert_eq!(err.kind(), io::ErrorKind::InvalidData);
}
#[test]
fn decompress_zlib_high_compression_ratio() {
let original = vec![b'a'; 1024 * 1024];
let compressed = compress_zlib(&original).expect("テストデータの圧縮に失敗しました");
assert!(compressed.len() < 1024 * 1024);
let err = decompress_zlib(&compressed, 1024)
.expect_err("上限超過の展開はエラーになる必要があります");
assert_eq!(err.kind(), io::ErrorKind::InvalidData);
}
#[test]
fn decompress_zlib_empty_payload_with_limit_zero() {
let compressed = compress_zlib(b"").expect("空ペイロードの圧縮に失敗しました");
let decompressed =
decompress_zlib(&compressed, 0).expect("空ペイロードの展開に失敗しました");
assert!(decompressed.is_empty());
}
#[test]
fn decompress_zlib_truncated_stream() {
let original = b"Hello, zlib!";
let compressed = compress_zlib(original).expect("テストデータの圧縮に失敗しました");
let truncated = &compressed[..compressed.len() - 1];
let err = decompress_zlib(truncated, 1024)
.expect_err("切り詰められたストリームの展開はエラーになる必要があります");
assert_eq!(err.kind(), io::ErrorKind::InvalidData);
}
#[test]
fn decompress_zlib_adler32_mismatch() {
let original = b"Hello, zlib!";
let mut compressed = compress_zlib(original).expect("テストデータの圧縮に失敗しました");
let last = compressed.len() - 1;
compressed[last] ^= 0xFF;
let err = decompress_zlib(&compressed, 1024)
.expect_err("Adler-32 が一致しない展開はエラーになる必要があります");
assert_eq!(err.kind(), io::ErrorKind::Other);
}
}