extern crate alloc;
use alloc::vec::Vec;
use crate::error::Error;
pub(crate) const WINDOW_SIZE: usize = 0x10000;
pub(crate) fn emit_literal(out: &mut Vec<u8>, byte: u8) {
out.push(byte);
}
pub(crate) fn emit_match(out: &mut Vec<u8>, distance: usize, length: usize) -> Result<(), Error> {
if distance == 0 || distance > WINDOW_SIZE || distance > out.len() {
return Err(Error::InvalidDistance);
}
if distance == 1 {
let b = out[out.len() - 1];
out.resize(out.len() + length, b);
} else {
let mut rem = length;
let mut src = out.len() - distance;
while rem > 0 {
let n = rem.min(out.len() - src);
out.extend_from_within(src..src + n);
src += n;
rem -= n;
}
}
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
use alloc::vec;
#[test]
fn literal_then_match() {
let mut out = vec![];
for &b in b"hello" {
emit_literal(&mut out, b);
}
emit_match(&mut out, 5, 5).unwrap();
assert_eq!(&out, b"hellohello");
}
#[test]
fn overlap_run() {
let mut out = vec![];
emit_literal(&mut out, b'A');
emit_match(&mut out, 1, 8).unwrap();
assert_eq!(&out, b"AAAAAAAAA");
}
#[test]
fn overlap_two_byte_period() {
let mut out = vec![];
emit_literal(&mut out, b'A');
emit_literal(&mut out, b'B');
emit_match(&mut out, 2, 6).unwrap();
assert_eq!(&out, b"ABABABAB");
}
#[test]
fn rejects_zero_distance() {
let mut out = vec![1u8];
assert_eq!(emit_match(&mut out, 0, 1), Err(Error::InvalidDistance));
}
#[test]
fn rejects_distance_before_start() {
let mut out = vec![1u8];
assert_eq!(emit_match(&mut out, 2, 1), Err(Error::InvalidDistance));
}
#[test]
fn rejects_distance_over_window() {
let mut out = vec![0u8; WINDOW_SIZE + 10];
assert_eq!(
emit_match(&mut out, WINDOW_SIZE + 1, 1),
Err(Error::InvalidDistance)
);
}
}