gdeflate 0.1.0

A library for compressing and decompressing the GDeflate format
Documentation
use thiserror::Error;

#[cxx::bridge(namespace = "GDeflate")]
mod ffi {
    unsafe extern "C++" {
        include!("GDeflate.h");

        fn CompressBound(size: usize) -> usize;
        unsafe fn Compress(
            output: *mut u8,
            output_size: *mut usize,
            in_: *const u8,
            in_size: usize,
            level: u32,
            flags: u32,
        ) -> bool;
        unsafe fn Decompress(
            output: *mut u8,
            output_size: usize,
            in_: *const u8,
            in_size: usize,
            num_workers: u32,
        ) -> bool;
    }
}

#[repr(i32)]
#[derive(Copy, Clone, Debug)]
pub enum CompressionLevel {
    None = 0,
    Level1 = 1,
    Level2 = 2,
    Level3 = 3,
    Level4 = 4,
    Level5 = 5,
    Level6 = 6,
    Level7 = 7,
    Level8 = 8,
    Level9 = 9,
    Level10 = 10,
    Level11 = 11,
    Level12 = 12,
}

#[derive(Error, Debug)]
pub enum Error {
    #[error("Compression failed")]
    CompressionFailed,
    #[error("Decompression failed")]
    DecompressionFailed,
}

const COMPRESS_SINGLE_THREAD: u32 = 0x200;

pub fn compress(bytes: &[u8], level: CompressionLevel) -> Result<Vec<u8>, Error> {
    let mut size = ffi::CompressBound(bytes.len());

    let mut buffer = vec![0; size];

    let result = unsafe {
        ffi::Compress(
            buffer.as_mut_ptr(),
            &mut size,
            bytes.as_ptr(),
            bytes.len(),
            level as _,
            COMPRESS_SINGLE_THREAD,
        )
    };
    buffer.resize(size, 0);

    if !result {
        return Err(Error::CompressionFailed)
    }

    Ok(buffer)
}

pub fn decompress(bytes: &[u8], decompressed_size: usize) -> Result<Vec<u8>, Error> {
    let mut buffer = vec![0; decompressed_size];

    let result = unsafe {
        ffi::Decompress(
            buffer.as_mut_ptr(),
            decompressed_size,
            bytes.as_ptr(),
            bytes.len(),
            1,
        )
    };

    if !result {
        return Err(Error::DecompressionFailed)
    }

    Ok(buffer)
}

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

    #[test]
    fn test_compress() {
        let bytes = b"Hello, world!";
        let compressed = compress(bytes, CompressionLevel::Level1).unwrap();
        let decompressed = decompress(&compressed, bytes.len()).unwrap();
        assert_eq!(bytes, &decompressed);
    }
}