paracletics-hypercube 0.1.0

General-purpose paracletic hyper cube compression toolkit.
Documentation
use super::{CompressionError, Compressor};

const ESCAPE_LITERAL: u8 = 0x80;

pub struct DeltaPulse;

impl Compressor for DeltaPulse {
    fn name(&self) -> &'static str {
        "delta-pulse"
    }

    fn compress(&self, input: &[u8]) -> Vec<u8> {
        if input.is_empty() {
            return Vec::new();
        }

        let mut out = Vec::with_capacity(input.len());
        out.push(input[0]);
        let mut prev = input[0];

        for &current in &input[1..] {
            let delta = current as i16 - prev as i16;
            if (-127..=127).contains(&delta) && delta != -128 {
                out.push((delta as i8) as u8);
            } else {
                out.push(ESCAPE_LITERAL);
                out.push(current);
            }
            prev = current;
        }
        out
    }

    fn decompress(&self, input: &[u8]) -> Result<Vec<u8>, CompressionError> {
        if input.is_empty() {
            return Ok(Vec::new());
        }

        let mut out = Vec::with_capacity(input.len());
        out.push(input[0]);
        let mut prev = input[0];
        let mut i = 1usize;

        while i < input.len() {
            let token = input[i];
            i += 1;
            if token == ESCAPE_LITERAL {
                if i >= input.len() {
                    return Err(CompressionError::CorruptStream(
                        "delta-pulse escape token missing literal byte",
                    ));
                }
                let literal = input[i];
                i += 1;
                out.push(literal);
                prev = literal;
                continue;
            }

            let delta = (token as i8) as i16;
            let next = prev as i16 + delta;
            if !(0..=255).contains(&next) {
                return Err(CompressionError::ValueOverflow);
            }
            let value = next as u8;
            out.push(value);
            prev = value;
        }
        Ok(out)
    }
}