view utils/src/lzss.rs @ 792:11bc22bad1bf default tip

python: Replace the image crate with png We weren’t using any of its features anyway, so the png crate is exactly what we need, without the many heavy dependencies of image. https://github.com/image-rs/image-png/pull/670 will eventually make it even faster to build.
author Link Mauve <linkmauve@linkmauve.fr>
date Sat, 17 Jan 2026 22:22:25 +0100
parents daa23a4ff24d
children
line wrap: on
line source

//! LZSS implementation.

use std::io;
use crate::bitstream::BitStream;

/// Decompresses a LZSS-compressed file.
pub fn decompress<R: io::Read + io::Seek>(bitstream: &mut BitStream<R>, size: usize, dictionary_size: usize, offset_size: usize, length_size: usize, minimum_match_length: usize) -> io::Result<Vec<u8>> {
    let mut data = vec![0; size];
    let mut dictionary = vec![0; dictionary_size];
    let mut dictionary_head = 1;
    let mut ptr = 0;

    while ptr < size {
        if bitstream.read_bit()? {
            // The `flag` bit is set, indicating the upcoming chunk of data is a literal.
            // Add it to the uncompressed file, and store it in the dictionary.
            let byte = bitstream.read(8)? as u8;
            dictionary[dictionary_head] = byte;
            dictionary_head = (dictionary_head + 1) % dictionary_size;
            data[ptr] = byte;
            ptr += 1;
        } else {
            // The `flag` bit is not set, the upcoming chunk is a (offset, length) tuple.
            let offset = bitstream.read(offset_size)?;
            let length = bitstream.read(length_size)? + minimum_match_length;
            if ptr + length > size {
                return Err(io::Error::new(io::ErrorKind::Other, "Oh no!"));
            }
            if offset == 0 && length == 0 {
                break;
            }
            for i in offset..offset + length {
                data[ptr] = dictionary[i % dictionary_size];
                dictionary[dictionary_head] = dictionary[i % dictionary_size];
                dictionary_head = (dictionary_head + 1) % dictionary_size;
                ptr += 1;
            }
        }
    }

    Ok(data)
}

#[cfg(test)]
mod tests {
    use super::*;
    use std::io::Cursor;

    #[test]
    #[ignore]
    fn bit_by_bit() {
        // TODO: find actual lzss data.
        let data = Cursor::new(vec![0, 0, 0]);
        let mut bitstream = BitStream::new(data);
        decompress(&mut bitstream, 3, 0x2000, 13, 4, 3).unwrap();
    }
}