#![cfg_attr(docsrs, doc(cfg(feature = "rle90")))]
use alloc::vec::Vec;
use crate::error::Error;
use crate::traits::{Algorithm, RawDecoder, RawEncoder, RawProgress};
const FLAG: u8 = 0x90;
const MIN_RUN: usize = 3;
const MAX_RUN: usize = 255;
#[derive(Debug, Clone, Copy, Default)]
pub struct Rle90;
impl Algorithm for Rle90 {
const NAME: &'static str = "rle90";
type Encoder = Encoder;
type Decoder = Decoder;
type EncoderConfig = ();
type DecoderConfig = ();
fn encoder_with(_: ()) -> Encoder {
Encoder::new()
}
fn decoder_with(_: ()) -> Decoder {
Decoder::new()
}
}
#[derive(Debug, Default)]
pub struct Encoder {
run: Option<(u8, usize)>,
pending: Vec<u8>,
head: usize,
finished: bool,
}
impl Encoder {
pub const fn new() -> Self {
Self {
run: None,
pending: Vec::new(),
head: 0,
finished: false,
}
}
fn drain_pending(&mut self, out: &mut [u8]) -> usize {
let avail = self.pending.len() - self.head;
let n = avail.min(out.len());
out[..n].copy_from_slice(&self.pending[self.head..self.head + n]);
self.head += n;
if self.head == self.pending.len() {
self.pending.clear();
self.head = 0;
}
n
}
fn emit_literal(&mut self, b: u8) {
self.pending.push(b);
if b == FLAG {
self.pending.push(0);
}
}
fn flush_run(&mut self) {
let Some((byte, count)) = self.run.take() else {
return;
};
if byte == FLAG || count < MIN_RUN {
for _ in 0..count {
self.emit_literal(byte);
}
return;
}
self.pending.push(byte);
let mut emitted = 1usize;
while count - emitted >= 1 {
let chunk = (count - emitted + 1).min(MAX_RUN);
self.pending.push(FLAG);
self.pending.push(chunk as u8);
emitted += chunk - 1;
}
}
fn feed(&mut self, b: u8) {
match self.run {
Some((byte, count)) if byte == b && count < MAX_RUN => {
self.run = Some((byte, count + 1));
}
Some(_) => {
self.flush_run();
self.run = Some((b, 1));
}
None => {
self.run = Some((b, 1));
}
}
}
fn finalize(&mut self) {
self.flush_run();
}
}
impl RawEncoder for Encoder {
fn raw_encode(&mut self, input: &[u8], output: &mut [u8]) -> Result<RawProgress, Error> {
let mut consumed = 0usize;
let mut written = 0usize;
if self.head < self.pending.len() {
written += self.drain_pending(&mut output[written..]);
}
while consumed < input.len() {
if self.pending.len().saturating_sub(self.head) >= output.len().saturating_sub(written)
{
if written < output.len() {
written += self.drain_pending(&mut output[written..]);
}
if self.head < self.pending.len() {
break;
}
}
self.feed(input[consumed]);
consumed += 1;
if self.head < self.pending.len() && written < output.len() {
written += self.drain_pending(&mut output[written..]);
}
}
if self.head < self.pending.len() && written < output.len() {
written += self.drain_pending(&mut output[written..]);
}
Ok(RawProgress {
consumed,
written,
done: false,
})
}
fn raw_finish(&mut self, output: &mut [u8]) -> Result<RawProgress, Error> {
if self.finished && self.head >= self.pending.len() {
return Ok(RawProgress {
consumed: 0,
written: 0,
done: true,
});
}
if !self.finished {
self.finalize();
self.finished = true;
}
let mut written = 0usize;
if self.head < self.pending.len() {
written += self.drain_pending(&mut output[written..]);
}
let done = self.head >= self.pending.len();
Ok(RawProgress {
consumed: 0,
written,
done,
})
}
fn raw_reset(&mut self) {
self.run = None;
self.pending.clear();
self.head = 0;
self.finished = false;
}
}
#[derive(Debug, Clone, Copy)]
enum DecState {
Normal,
AwaitCount,
Run { byte: u8, remaining: u8 },
}
#[derive(Debug, Clone, Copy)]
pub struct Decoder {
state: DecState,
last: u8,
have_last: bool,
poisoned: bool,
}
impl Decoder {
pub const fn new() -> Self {
Self {
state: DecState::Normal,
last: 0,
have_last: false,
poisoned: false,
}
}
}
impl Default for Decoder {
fn default() -> Self {
Self::new()
}
}
impl RawDecoder for Decoder {
fn raw_decode(&mut self, input: &[u8], output: &mut [u8]) -> Result<RawProgress, Error> {
if self.poisoned {
return Err(Error::Corrupt);
}
let mut consumed = 0usize;
let mut written = 0usize;
loop {
match self.state {
DecState::Normal => {
if consumed == input.len() {
return Ok(RawProgress {
consumed,
written,
done: false,
});
}
let b = input[consumed];
if b == FLAG {
consumed += 1;
self.state = DecState::AwaitCount;
} else {
if written == output.len() {
return Ok(RawProgress {
consumed,
written,
done: false,
});
}
output[written] = b;
written += 1;
consumed += 1;
self.last = b;
self.have_last = true;
}
}
DecState::AwaitCount => {
if consumed == input.len() {
return Ok(RawProgress {
consumed,
written,
done: false,
});
}
let n = input[consumed];
consumed += 1;
if n == 0 {
if written == output.len() {
self.state = DecState::Run {
byte: FLAG,
remaining: 1,
};
self.last = FLAG;
self.have_last = true;
return Ok(RawProgress {
consumed,
written,
done: false,
});
}
output[written] = FLAG;
written += 1;
self.last = FLAG;
self.have_last = true;
self.state = DecState::Normal;
} else {
if !self.have_last {
self.poisoned = true;
return Err(Error::Corrupt);
}
self.state = DecState::Run {
byte: self.last,
remaining: n - 1,
};
}
}
DecState::Run { byte, remaining } => {
if remaining == 0 {
self.state = DecState::Normal;
continue;
}
if written == output.len() {
return Ok(RawProgress {
consumed,
written,
done: false,
});
}
let out_avail = output.len() - written;
let n = (remaining as usize).min(out_avail);
for slot in &mut output[written..written + n] {
*slot = byte;
}
written += n;
let new_remaining = remaining - n as u8;
self.state = if new_remaining == 0 {
DecState::Normal
} else {
DecState::Run {
byte,
remaining: new_remaining,
}
};
}
}
}
}
fn raw_finish(&mut self, output: &mut [u8]) -> Result<RawProgress, Error> {
if self.poisoned {
return Err(Error::Corrupt);
}
let mut written = 0usize;
if let DecState::Run { byte, remaining } = self.state {
let out_avail = output.len() - written;
let n = (remaining as usize).min(out_avail);
for slot in &mut output[written..written + n] {
*slot = byte;
}
written += n;
let new_remaining = remaining - n as u8;
self.state = if new_remaining == 0 {
DecState::Normal
} else {
DecState::Run {
byte,
remaining: new_remaining,
}
};
}
match self.state {
DecState::Normal => Ok(RawProgress {
consumed: 0,
written,
done: true,
}),
DecState::Run { .. } => Ok(RawProgress {
consumed: 0,
written,
done: false,
}),
DecState::AwaitCount => {
self.poisoned = true;
Err(Error::UnexpectedEnd)
}
}
}
fn raw_reset(&mut self) {
self.state = DecState::Normal;
self.last = 0;
self.have_last = false;
self.poisoned = false;
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::traits::{Decoder as _, Encoder as _, Status};
use alloc::vec;
fn encode_all(input: &[u8]) -> Vec<u8> {
let mut enc = Rle90::encoder();
let mut out = Vec::new();
let mut buf = [0u8; 64];
let mut consumed = 0;
while consumed < input.len() {
let (p, status) = enc.encode(&input[consumed..], &mut buf).unwrap();
out.extend_from_slice(&buf[..p.written]);
consumed += p.consumed;
match status {
Status::OutputFull => continue,
Status::InputEmpty => break,
Status::StreamEnd => break,
}
}
loop {
let (p, status) = enc.finish(&mut buf).unwrap();
out.extend_from_slice(&buf[..p.written]);
if matches!(status, Status::StreamEnd) {
break;
}
}
out
}
fn decode_all(input: &[u8]) -> Result<Vec<u8>, Error> {
let mut dec = Rle90::decoder();
let mut out = Vec::new();
let mut buf = [0u8; 64];
let mut consumed = 0;
loop {
let (p, status) = dec.decode(&input[consumed..], &mut buf)?;
out.extend_from_slice(&buf[..p.written]);
consumed += p.consumed;
match status {
Status::OutputFull => continue,
Status::InputEmpty => break,
Status::StreamEnd => break,
}
}
loop {
let (p, status) = dec.finish(&mut buf)?;
out.extend_from_slice(&buf[..p.written]);
if matches!(status, Status::StreamEnd) {
break;
}
}
Ok(out)
}
fn decode_byte_by_byte(input: &[u8]) -> Result<Vec<u8>, Error> {
let mut dec = Rle90::decoder();
let mut out = Vec::new();
let mut buf = [0u8; 1];
for i in 0..input.len() {
let mut chunk = &input[i..i + 1];
loop {
let (p, _status) = dec.decode(chunk, &mut buf)?;
out.extend_from_slice(&buf[..p.written]);
if p.consumed > 0 {
chunk = &input[i + 1..i + 1];
}
if p.written == 0 && p.consumed == 0 {
break;
}
}
}
loop {
let (p, status) = dec.finish(&mut buf)?;
out.extend_from_slice(&buf[..p.written]);
if matches!(status, Status::StreamEnd) {
break;
}
}
Ok(out)
}
fn round_trip(input: &[u8]) {
let encoded = encode_all(input);
let decoded = decode_all(&encoded).expect("decode");
assert_eq!(decoded, input, "round-trip mismatch");
let decoded_bb = decode_byte_by_byte(&encoded).expect("byte-by-byte decode");
assert_eq!(decoded_bb, input, "byte-by-byte round-trip mismatch");
}
#[test]
fn empty() {
round_trip(&[]);
}
#[test]
fn no_marker_data() {
round_trip(b"hello, world!");
round_trip(&[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
}
#[test]
fn literal_markers() {
round_trip(&[FLAG]);
round_trip(&[FLAG, FLAG, FLAG]);
round_trip(&[1, FLAG, 2, FLAG, FLAG, 3]);
round_trip(&[FLAG; 10]);
}
#[test]
fn short_runs() {
round_trip(&[7, 7]);
round_trip(&[7, 7, 7]);
round_trip(b"aabbccdd");
}
#[test]
fn long_run() {
round_trip(&[0x41; 100]);
round_trip(&[0x00; 200]);
}
#[test]
fn run_over_255_split() {
round_trip(&[0x5a; 255]);
round_trip(&[0x5a; 256]);
round_trip(&[0x5a; 300]);
round_trip(&[0x5a; 1000]);
round_trip(&[FLAG; 600]);
}
#[test]
fn all_256_bytes() {
let data: Vec<u8> = (0..=255u8).collect();
round_trip(&data);
let mut data2 = Vec::new();
for b in 0..=255u8 {
data2.extend_from_slice(&[b, b, b, b]);
}
round_trip(&data2);
}
#[test]
fn pseudo_random() {
let mut state = 0x1234_5678u32;
let mut data = Vec::with_capacity(4096);
for _ in 0..4096 {
state = state.wrapping_mul(1_103_515_245).wrapping_add(12345);
data.push((state >> 16) as u8);
}
round_trip(&data);
}
#[test]
fn mixed_runs_and_literals() {
let mut data = Vec::new();
data.extend_from_slice(b"abc");
data.extend_from_slice(&[0xff; 50]);
data.extend_from_slice(b"def");
data.extend_from_slice(&[FLAG; 7]);
data.extend_from_slice(&[0x00; 300]);
data.push(0x90);
round_trip(&data);
}
#[test]
fn known_encoding() {
let encoded = encode_all(b"aaaa");
assert_eq!(encoded, vec![b'a', FLAG, 0x04]);
assert_eq!(encode_all(&[FLAG]), vec![FLAG, 0x00]);
}
#[test]
fn decode_known_sequences() {
assert_eq!(decode_all(&[b'a', FLAG, 0x04]).unwrap(), b"aaaa");
assert_eq!(decode_all(&[FLAG, 0x00]).unwrap(), vec![FLAG]);
assert_eq!(decode_all(&[b'a', FLAG, 0x01]).unwrap(), b"a");
}
#[test]
fn malformed_run_no_literal() {
let r = decode_all(&[FLAG, 0x05]);
assert_eq!(r, Err(Error::Corrupt));
}
#[test]
fn malformed_dangling_flag() {
let r = decode_all(&[b'a', FLAG]);
assert_eq!(r, Err(Error::UnexpectedEnd));
}
#[test]
fn poisoned_after_error() {
let mut dec = Rle90::decoder();
let mut buf = [0u8; 16];
let r = dec.decode(&[FLAG, 0x05], &mut buf);
assert_eq!(r, Err(Error::Corrupt));
let r2 = dec.decode(b"a", &mut buf);
assert_eq!(r2, Err(Error::Corrupt));
dec.reset();
let (p, _s) = dec.decode(b"a", &mut buf).unwrap();
assert_eq!(p.written, 1);
}
#[test]
fn cross_check_arc_squeeze_convention() {
assert_eq!(encode_all(b"xxxxx"), vec![b'x', FLAG, 0x05]);
assert_eq!(decode_all(&[b'x', FLAG, 0x05]).unwrap(), b"xxxxx");
}
}