use std::io;
use crate::error::{Error, ErrorContext, Result};
pub(crate) fn zstd(stored: &[u8], expected_length: usize) -> Result<Vec<u8>> {
let mut decoded = Vec::new();
decoded.try_reserve_exact(expected_length).map_err(|_| {
Error::resource_limit("unable to allocate the decompressed stream", None)
.with_context(ErrorContext::Payload)
})?;
zstd::bulk::Decompressor::new()
.and_then(|mut decompressor| decompressor.decompress_to_buffer(stored, &mut decoded))
.map_err(invalid_stream)?;
if decoded.len() != expected_length {
return Err(Error::corruption(
format!(
"decompressed stream length {} does not match {expected_length}",
decoded.len()
),
None,
)
.with_context(ErrorContext::Payload));
}
Ok(decoded)
}
fn invalid_stream(error: io::Error) -> Error {
Error::corruption(format!("invalid Zstandard stream: {error}"), None)
.with_context(ErrorContext::Payload)
}
#[cfg(test)]
mod tests {
use super::zstd;
#[test]
fn zstandard_round_trip_honors_the_declared_length() {
let source = b"acta stream data";
let compressed = zstd::bulk::compress(source, 1).expect("compression should succeed");
assert_eq!(
zstd(&compressed, source.len()).expect("decompression should succeed"),
source
);
}
#[test]
fn a_stream_longer_than_its_declaration_is_rejected() {
let source = b"acta stream data";
let compressed = zstd::bulk::compress(source, 1).expect("compression should succeed");
let error = zstd(&compressed, source.len() - 1).expect_err("the buffer is too small");
assert_eq!(error.kind(), crate::ErrorKind::Corruption);
}
}