linflate/tests/overwrite_headroom.rs
//! The output buffer contract: nothing is ever written past the slice handed
//! to the decoder, even when the output does not fit (`OutputOverflow`, a size
//! guess or a header that was too small), whatever SIMD match copy the CPU
//! picks (AVX-512 copies 64 bytes at a time). Guard bytes behind the slice
//! must stay untouched.
use linflate::{OVERWRITE_HEADROOM, inflate_into};
const GUARD: usize = 4096;
#[test]
fn no_byte_is_written_past_expected_plus_headroom() {
let mut x: u64 = 0x1234_5678_9abc_def1;
for case in 0..200 {
// random bytes, then long matches at distances ≥ 64 up to the very end
let mut data = Vec::new();
let n = 2000 + case * 37;
for _ in 0..n {
x ^= x << 13;
x ^= x >> 7;
x ^= x << 17;
data.push(x as u8);
}
let reps = 3 + case % 11;
for r in 0..reps {
let d = 64 + (r * 97 + case) % 900;
let start = data.len() - d;
for i in 0..258 {
let b = data[start + i % d];
data.push(b);
}
}
let deflate = miniz_oxide::deflate::compress_to_vec(&data, 9);
// exact, then too small by a little and by a lot
for short in [0usize, 1, 31, 64, 200, 290, 777, data.len() / 3] {
let total = (data.len() + OVERWRITE_HEADROOM).saturating_sub(short);
let mut buf = vec![0xA5u8; total + GUARD];
let r = inflate_into(&deflate, &mut buf[..total]);
if short == 0 {
let n_out = r.expect("decode");
assert_eq!(&buf[..n_out], &data[..], "case {case}: output");
}
if let Some(p) = buf[total..].iter().rposition(|&b| b != 0xA5) {
panic!(
"case {case} short {short}: {} bytes written past the slice",
p + 1
);
}
}
}
}