lzrw 0.1.0

Rust bindings for the LZRW family of lossless data-compression algorithms
//! Safe Rust API for the LZRW family of lossless data-compression algorithms.
//!
//! This crate wraps the raw FFI bindings in the [`lzrw-sys`](https://crates.io/crates/lzrw-sys),
//! providing a safe, ergonomic API for the LZRW family of algorithms.
//!
//! # Examples
//!
//! ```
//! use lzrw::{self, LzrwAlgorithm};
//!
//! let input = b"Hello world! Hello world! Hello world!";
//!
//! let compressed = lzrw::compress(
//!     LzrwAlgorithm::Lzrw3a,
//!     input,
//! );
//!
//! println!("compressed {} -> {} bytes", input.len(), compressed.len());
//!
//! // decompress
//! let decompressed = lzrw::decompress(
//!     LzrwAlgorithm::Lzrw3a,
//!     &compressed,
//! );
//!
//! assert_eq!(&decompressed, input);
//! ```
//!
//! # Algorithms
//!
//! The LZRW family trades compression ratio for speed differently across several variants.
//! Both sides of a compress/decompress operation must use the same algorithm.

use lzrw_sys::*;

/// The compression algorithm to use.
/// Each variant corresponds to a different algorithm in the LZRW family.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum LzrwAlgorithm {
    /// The simplest variant, fast and minimal memory usage.
    Lzrw1,
    /// Optimized version of LZRW1.
    Lzrw1a,
    /// Adds adaptive encoding for improved ratios.
    Lzrw2,
    /// Uses a hash table for faster matching.
    Lzrw3,
    /// Optimized version of LZRW3, best compression ratio of the family.
    Lzrw3a,
    /// LZW-based (dictionary, not sliding window), different than all other variants.
    Lzrw5,
}

/// Resolves an [`LzrwAlgorithm`] to the corresponding compress function.
fn get_compress_fn(algorithm: LzrwAlgorithm) -> CompressFn {
    match algorithm {
        LzrwAlgorithm::Lzrw1 => lzrw1_compress,
        LzrwAlgorithm::Lzrw1a => lzrw1a_compress,
        LzrwAlgorithm::Lzrw2 => lzrw2_compress,
        LzrwAlgorithm::Lzrw3 => lzrw3_compress,
        LzrwAlgorithm::Lzrw3a => lzrw3a_compress,
        LzrwAlgorithm::Lzrw5 => lzrw5_compress,
    }
}

/// Compresses `src` using the given algorithm and returns the compressed data.
///
/// # Examples
///
/// ```
/// use lzrw::{self, LzrwAlgorithm};
///
/// let compressed = lzrw::compress(
///     LzrwAlgorithm::Lzrw3a,
///     b"Hello world! Hello world! Hello world!",
/// );
///
/// assert!(!compressed.is_empty());
/// ```
pub fn compress(algorithm: LzrwAlgorithm, src: &[u8]) -> Vec<u8> {
    let compress_fn = get_compress_fn(algorithm);
    let wrk_mem_size = get_wrk_mem_size(compress_fn);
    let mut working_mem = vec![0u8; wrk_mem_size];
    let mut out = vec![0u8; src.len() + COMPRESS_OVERRUN];
    let mut out_len = 0;

    unsafe {
        compress_fn(
            COMPRESS_ACTION_COMPRESS,
            working_mem.as_mut_ptr(),
            src.as_ptr(),
            src.len() as u32,
            out.as_mut_ptr(),
            &mut out_len,
        );
    }

    out.truncate(out_len as usize);
    out
}

/// Decompresses `src` using the given algorithm and returns the decompressed data.
///
/// # Examples
///
/// ```
/// use lzrw::{self, LzrwAlgorithm};
///
/// let input = b"Hello world! Hello world! Hello world!";
/// let compressed = lzrw::compress(
///     LzrwAlgorithm::Lzrw3a,
///     input,
/// );
///
/// let decompressed = lzrw::decompress(
///     LzrwAlgorithm::Lzrw3a,
///     &compressed,
/// );
///
/// assert_eq!(&decompressed, input);
/// ```
pub fn decompress(algorithm: LzrwAlgorithm, src: &[u8]) -> Vec<u8> {
    let compress_fn = get_compress_fn(algorithm);
    let wrk_mem_size = get_wrk_mem_size(compress_fn);
    let mut working_mem = vec![0u8; wrk_mem_size];
    let mut out = vec![0u8; src.len() * 10];
    let mut out_len = 0;

    unsafe {
        compress_fn(
            COMPRESS_ACTION_DECOMPRESS,
            working_mem.as_mut_ptr(),
            src.as_ptr() as *mut u8,
            src.len() as u32,
            out.as_mut_ptr(),
            &mut out_len,
        );
    }

    out.truncate(out_len as usize);
    out
}