use alloc::vec::Vec;
use crate::error::{LercError, Result};
const END_MARKER: i16 = -32768;
const MIN_RUN_EVEN: usize = 5;
pub fn compress(input: &[u8]) -> Vec<u8> {
let mut out = Vec::new();
let len = input.len();
let mut i = 0;
while i < len {
let mut run_len = 1usize;
while i + run_len < len && input[i + run_len] == input[i] && run_len < 32767 {
run_len += 1;
}
if run_len >= MIN_RUN_EVEN {
let count = -(run_len as i16);
out.extend_from_slice(&count.to_le_bytes());
out.push(input[i]);
i += run_len;
} else {
let start = i;
while i < len {
let mut next_run = 1usize;
while i + next_run < len && input[i + next_run] == input[i] && next_run < 32767 {
next_run += 1;
}
if next_run >= MIN_RUN_EVEN {
break;
}
i += 1;
if (i - start) >= 32767 {
break;
}
}
let count = (i - start) as i16;
out.extend_from_slice(&count.to_le_bytes());
out.extend_from_slice(&input[start..i]);
}
}
out.extend_from_slice(&END_MARKER.to_le_bytes());
out
}
pub fn decompress(input: &[u8], expected_size: usize) -> Result<Vec<u8>> {
let mut out = Vec::with_capacity(expected_size);
let mut pos = 0;
loop {
if pos + 2 > input.len() {
return Err(LercError::InvalidData("RLE: unexpected end of data".into()));
}
let count = i16::from_le_bytes(input[pos..pos + 2].try_into().unwrap());
pos += 2;
if count == END_MARKER {
break;
}
if count > 0 {
let n = count as usize;
if pos + n > input.len() {
return Err(LercError::InvalidData("RLE: literal run overflow".into()));
}
out.extend_from_slice(&input[pos..pos + n]);
pos += n;
} else {
let n = (-count) as usize;
if pos >= input.len() {
return Err(LercError::InvalidData("RLE: missing repeat byte".into()));
}
let byte = input[pos];
pos += 1;
out.resize(out.len() + n, byte);
}
}
if out.len() != expected_size {
return Err(LercError::InvalidData(alloc::format!(
"RLE: expected {expected_size} bytes, got {}",
out.len()
)));
}
Ok(out)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn round_trip_empty() {
let compressed = compress(&[]);
let decompressed = decompress(&compressed, 0).unwrap();
assert!(decompressed.is_empty());
}
#[test]
fn round_trip_literals() {
let data: Vec<u8> = (0..100).collect();
let compressed = compress(&data);
let decompressed = decompress(&compressed, data.len()).unwrap();
assert_eq!(decompressed, data);
}
#[test]
fn round_trip_runs() {
let mut data = Vec::new();
data.resize(50, 0xAA);
data.resize(100, 0xBB);
data.resize(150, 0xCC);
let compressed = compress(&data);
let decompressed = decompress(&compressed, data.len()).unwrap();
assert_eq!(decompressed, data);
}
#[test]
fn round_trip_mixed() {
let mut data = Vec::new();
data.extend_from_slice(&[1, 2, 3, 4]);
data.resize(data.len() + 20, 0xFF);
data.extend_from_slice(&[10, 20, 30]);
data.resize(data.len() + 10, 0x00);
let compressed = compress(&data);
let decompressed = decompress(&compressed, data.len()).unwrap();
assert_eq!(decompressed, data);
}
#[test]
fn compression_effective_for_runs() {
let data = vec![0xAA; 1000];
let compressed = compress(&data);
assert!(compressed.len() < 10);
}
}