lazippy 0.0.1

Pure-Rust LZMA (Lempel-Ziv-Markov chain Algorithm), part of the 8z umbrella
Documentation
//! LZMA decoder — Phase 1 wrapper around `lzma-rust2`.

pub mod dict;
pub mod header;
pub mod literal;
pub mod r#match;

use std::io::Read;

use lzma_rust2::LzmaReader;

use crate::error::{LazippyError, LazippyResult};

/// Decompress LZMA data that has a full 13-byte `.lzma` file header.
///
/// The header is: [props_byte(1)] [dict_size_le(4)] [uncompressed_size_le(8)]
///
/// # Errors
/// Returns an error if the header is malformed or the stream is corrupt.
pub fn decode(input: &[u8]) -> LazippyResult<Vec<u8>> {
    let props = header::parse_header(input)?;
    let uncompressed_size = props.uncompressed_size.unwrap_or(u64::MAX);
    let compressed = &input[13..];
    decode_raw(
        compressed,
        props.lc as u32,
        props.lp as u32,
        props.pb as u32,
        props.dict_size,
        uncompressed_size,
    )
}

/// Decompress raw LZMA data (no `.lzma` header) using a 7z-style 5-byte props blob.
///
/// `props_bytes` must be exactly 5 bytes: [props_byte(1)] [dict_size_le(4)].
/// `uncompressed_size` is the expected output length (read from the 7z folder metadata).
///
/// # Errors
/// Returns an error if `props_bytes` is wrong length, props byte is invalid,
/// or the stream is corrupt.
pub fn decode_7z(
    input: &[u8],
    props_bytes: &[u8],
    uncompressed_size: u64,
) -> LazippyResult<Vec<u8>> {
    let (props_byte, dict_size) = header::parse_7z_props(props_bytes)?;
    let mut reader =
        LzmaReader::new_with_props(input, uncompressed_size, props_byte, dict_size, None)
            .map_err(|e| LazippyError::Backend(e.to_string()))?;
    let mut out = Vec::with_capacity(uncompressed_size.min(256 * 1024 * 1024) as usize);
    reader
        .read_to_end(&mut out)
        .map_err(|e| LazippyError::Backend(e.to_string()))?;
    Ok(out)
}

/// Decompress raw LZMA data (no header) using explicit LZMA parameters.
fn decode_raw(
    input: &[u8],
    lc: u32,
    lp: u32,
    pb: u32,
    dict_size: u32,
    uncompressed_size: u64,
) -> LazippyResult<Vec<u8>> {
    let mut reader = LzmaReader::new(input, uncompressed_size, lc, lp, pb, dict_size, None)
        .map_err(|e| LazippyError::Backend(e.to_string()))?;
    let mut out = Vec::with_capacity(uncompressed_size.min(256 * 1024 * 1024) as usize);
    reader
        .read_to_end(&mut out)
        .map_err(|e| LazippyError::Backend(e.to_string()))?;
    Ok(out)
}

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

    #[test]
    fn decode_empty_returns_error() {
        // Empty input has no valid header.
        let result = decode(&[]);
        assert!(result.is_err());
    }
}