pub fn decompress(input: &[u8], expected_len: usize) -> Option<Vec<u8>> {
let mut out: Vec<u8> = Vec::with_capacity(expected_len);
let mut i = 0usize;
while i < input.len() {
let ctrl = input[i] as usize;
i += 1;
if ctrl < 32 {
let run = ctrl + 1;
let end = i.checked_add(run)?;
if end > input.len() {
return None;
}
out.extend_from_slice(&input[i..end]);
i = end;
} else {
let mut len = ctrl >> 5;
if len == 7 {
len += *input.get(i)? as usize;
i += 1;
}
let dist = ((ctrl & 0x1f) << 8) | *input.get(i)? as usize;
i += 1;
let mut src = out.len().checked_sub(dist + 1)?;
for _ in 0..len + 2 {
let b = *out.get(src)?;
out.push(b);
src += 1;
}
}
}
if out.len() == expected_len {
Some(out)
} else {
None
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn literal_run_only() {
assert_eq!(
decompress(&[4, b'h', b'e', b'l', b'l', b'o'], 5).unwrap(),
b"hello"
);
}
#[test]
fn overlapping_back_reference_expands_a_run() {
let out = decompress(&[0, b'a', (3 << 5), 0], 6).unwrap();
assert_eq!(out, b"aaaaaa");
}
#[test]
fn truncated_input_fails_instead_of_panicking() {
assert!(decompress(&[10, b'a'], 11).is_none());
assert!(decompress(&[(3 << 5)], 5).is_none());
}
#[test]
fn length_mismatch_is_rejected() {
assert!(decompress(&[4, b'h', b'e', b'l', b'l', b'o'], 99).is_none());
}
#[test]
fn reference_before_start_is_rejected() {
assert!(decompress(&[(3 << 5), 200], 5).is_none());
}
}