1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
use byteorder::{ByteOrder, LittleEndian, WriteBytesExt};
use mozlz4_sys::*;


const MAGIC_NUMBER: &[u8] = b"mozLz40\0";

pub fn decompress(ibuffer: Vec<u8>) -> Result<Vec<u8>, String> {
    let magic_number_len = MAGIC_NUMBER.len();
    if ibuffer.len() < (magic_number_len + 4) || !ibuffer.starts_with(MAGIC_NUMBER) {
        return Err("Unrecognized input file".into())
    }

    let decompressed_size = ibuffer
        .get(magic_number_len..(magic_number_len + 4))
        .unwrap();
    let decompressed_size = LittleEndian::read_u32(decompressed_size) as usize;

    let block = ibuffer.get(magic_number_len + 4..).unwrap();
    let mut obuffer: Vec<u8> = Vec::with_capacity(decompressed_size);

    unsafe {
        let bytes_decompressed = LZ4_decompress_safe(
            block.as_ptr() as *const _,
            obuffer.as_mut_ptr() as *mut _,
            block.len() as i32,
            decompressed_size as i32,
        );
        if bytes_decompressed < 0 {
            return Err("Malformed input file".into())
        }
        obuffer.set_len(bytes_decompressed as usize);
    }
    Ok(obuffer)
}

pub fn compress(ibuffer: Vec<u8>) -> Result<Vec<u8>, String> {
    let uncompressed_size = ibuffer.len();
    let compress_bound = unsafe { LZ4_compressBound(uncompressed_size as i32) as usize };

    let magic_number_len = MAGIC_NUMBER.len();
    let mut obuffer: Vec<u8> = Vec::with_capacity(magic_number_len + 4 + compress_bound);
    obuffer.extend(MAGIC_NUMBER);
    obuffer
        .write_u32::<LittleEndian>(uncompressed_size as u32)
        .unwrap();

    unsafe {
        let bytes_compressed = LZ4_compress_default(
            ibuffer.as_ptr() as *const _,
            obuffer[(magic_number_len + 4)..].as_mut_ptr() as *mut _,
            uncompressed_size as i32,
            compress_bound as i32,
        );
        if bytes_compressed <= 0 {
            return Err("Compression failed".into())
        }
        obuffer.set_len(magic_number_len + 4 + bytes_compressed as usize);
    }
    Ok(obuffer)
}