#![cfg_attr(not(feature = "std"), no_std)]
#![cfg_attr(docsrs, feature(doc_cfg))]
extern crate alloc;
use alloc::vec::Vec;
pub const MAX_ZERO_RUN: usize = 128; pub const MAX_FF_RUN: usize = 32;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Error {
RunMarkerWithoutControl,
InvalidRunLength { len: usize },
}
pub fn compress(input: &[u8]) -> Vec<u8> {
let mut out = Vec::with_capacity(input.len()); let mut zero = 0usize;
let mut ff = 0usize;
#[inline]
fn emit_run(out: &mut Vec<u8>, is_ff: bool, n: usize) {
debug_assert!(n >= 1);
debug_assert!((!is_ff && n <= MAX_ZERO_RUN) || (is_ff && n <= MAX_FF_RUN));
let mut ctrl = ((n as u8) - 1) & 0x7f;
if is_ff { ctrl |= 0x80; }
out.push(0x00);
out.push(ctrl);
}
#[inline]
fn flush(out: &mut Vec<u8>, zero: &mut usize, ff: &mut usize) {
if *ff != 0 { emit_run(out, true, *ff); *ff = 0; }
if *zero != 0 { emit_run(out, false, *zero); *zero = 0; }
}
for &b in input {
match b {
0x00 => {
if ff != 0 { emit_run(&mut out, true, ff); ff = 0; }
zero += 1;
if zero == MAX_ZERO_RUN { emit_run(&mut out, false, MAX_ZERO_RUN); zero = 0; }
}
0xFF => {
if zero != 0 { emit_run(&mut out, false, zero); zero = 0; }
ff += 1;
if ff == MAX_FF_RUN { emit_run(&mut out, true, MAX_FF_RUN); ff = 0; }
}
_ => { flush(&mut out, &mut zero, &mut ff); out.push(b); }
}
}
flush(&mut out, &mut zero, &mut ff);
let lim = core::cmp::min(4, out.len());
for i in 0..lim { out[i] ^= 0xFF; }
out
}
pub fn decompress(comp: &[u8]) -> Result<Vec<u8>, Error> {
let mut out = Vec::with_capacity(comp.len()); let mut i = 0usize;
#[inline]
fn read_unneg(comp: &[u8], i: &mut usize) -> u8 {
let mut b = comp[*i];
if *i < 4 { b ^= 0xFF; }
*i += 1;
b
}
while i < comp.len() {
let b = read_unneg(comp, &mut i);
if b != 0x00 {
out.push(b);
continue;
}
if i >= comp.len() {
return Err(Error::RunMarkerWithoutControl);
}
let c = read_unneg(comp, &mut i);
let is_ff = (c & 0x80) != 0;
let len = (c & 0x7F) as usize + 1;
if is_ff && len > MAX_FF_RUN {
return Err(Error::InvalidRunLength { len });
}
let fill = if is_ff { 0xFF } else { 0x00 };
let base = out.len();
out.resize(base + len, fill);
}
Ok(out)
}
#[cfg(test)]
mod tests {
use super::*;
use alloc::vec;
fn rt(v: &[u8]) {
let c = compress(v);
let d = decompress(&c).unwrap();
assert_eq!(v, d.as_slice(), "in:{:x?} cmp:{:x?} dec:{:x?}", v, c, d);
}
#[test] fn empty() { rt(&[]); }
#[test] fn literals() { rt(&[1,2,3,4,5]); }
#[test] fn zeros() {
rt(&vec![0x00; 1]);
rt(&vec![0x00; 127]);
rt(&vec![0x00; 128]);
rt(&vec![0x00; 129]); }
#[test] fn ffs() {
rt(&vec![0xFF; 1]);
rt(&vec![0xFF; 31]);
rt(&vec![0xFF; 32]);
rt(&vec![0xFF; 33]); }
#[test] fn mixed() {
rt(&[0,0,0,0, 42, 0xFF,0xFF,0xFF, 1,2,3, 0, 0xFF, 0, 0xAA,0xBB, 0, 0xFF]);
}
#[test] fn err_run_marker_without_control() {
let mut c = vec![0x00];
for i in 0..c.len().min(4) { c[i] ^= 0xFF; }
assert_eq!(decompress(&c), Err(Error::RunMarkerWithoutControl));
}
#[test] fn err_invalid_ff_run_len() {
let mut c = vec![0x00, 0xA0]; for i in 0..c.len().min(4) { c[i] ^= 0xFF; }
assert_eq!(decompress(&c), Err(Error::InvalidRunLength { len: 33 }));
}
}