mod minilzo;
use std::mem::{size_of, MaybeUninit};
use std::os::raw::{c_int, c_long, c_short};
type LZOResult<T> = Result<T, Error>;
#[derive(Debug, PartialEq)]
pub enum Error {
Error,
OutOfMemory,
NotCompressible,
InputOverrun,
OutputOverrun,
LookbehindOverrun,
EOFNotFound,
InputNotConsumed,
NotYetImplemented,
InvalidArgument,
InvalidAlignment,
OutputNotConsumed,
InternalError,
}
impl std::fmt::Display for Error {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "{:?}", self)
}
}
impl std::error::Error for Error {
fn description(&self) -> &str {
match *self {
Error::OutOfMemory => "out of memory",
Error::NotCompressible => "not compressible",
Error::InputOverrun => "input overrun",
Error::OutputOverrun => "output overrun",
Error::LookbehindOverrun => "lookbehind overrun",
Error::EOFNotFound => "EOF not found",
Error::InputNotConsumed => "input not consumed",
Error::NotYetImplemented => "not yet implemented",
Error::InvalidArgument => "invalid argument",
Error::InvalidAlignment => "invalid alignment",
Error::OutputNotConsumed => "output not consumed",
Error::InternalError => "internal error",
Error::Error => "error",
}
}
}
fn lzo_err_code_to_result<T>(code: i32, value: T) -> LZOResult<T> {
let error = match code {
0 => return Ok(value),
-1 => Error::Error,
-2 => Error::OutOfMemory,
-3 => Error::NotCompressible,
-4 => Error::InputOverrun,
-5 => Error::OutputOverrun,
-6 => Error::LookbehindOverrun,
-7 => Error::EOFNotFound,
-8 => Error::InputNotConsumed,
-9 => Error::NotYetImplemented,
-10 => Error::InvalidArgument,
-11 => Error::InvalidArgument,
-12 => Error::OutputNotConsumed,
-99 => Error::InternalError,
_ => Error::Error,
};
Err(error)
}
pub struct LZO {
wrkmem: [u8; minilzo::LZO1X_1_MEM_COMPRESS],
}
impl LZO {
pub fn init() -> LZOResult<Self> {
match Self::lzo_init() {
Ok(_) => {
let x = MaybeUninit::<[u8; 131072]>::uninit();
Ok(LZO {
wrkmem: unsafe { x.assume_init() },
})
}
Err(e) => Err(e),
}
}
fn lzo_init() -> LZOResult<()> {
let code = unsafe {
minilzo::__lzo_init_v2(
minilzo::lzo_version(),
size_of::<c_short>() as c_int,
size_of::<c_int>() as c_int,
size_of::<c_long>() as c_int,
size_of::<u32>() as c_int,
size_of::<minilzo::lzo_uint>() as c_int,
size_of::<usize>() as c_int,
size_of::<usize>() as c_int,
size_of::<usize>() as c_int,
size_of::<minilzo::lzo_callback_t>() as c_int,
)
};
lzo_err_code_to_result(code, ())
}
pub fn compress(&mut self, src: &[u8]) -> LZOResult<Vec<u8>> {
let mut out_len = (src.len() + src.len() / 16 + 64 + 3) as u64;
let mut out: Vec<u8> = vec![0u8; out_len as usize];
let code = unsafe {
minilzo::lzo1x_1_compress(
src.as_ptr(),
src.len() as u64,
out.as_mut_ptr(),
&mut out_len,
self.wrkmem.as_mut_ptr() as *mut _,
)
};
out.resize(out_len as usize, 0);
lzo_err_code_to_result(code, out)
}
pub fn decompress(&self, src: &[u8], dst_len: usize) -> LZOResult<Vec<u8>> {
let mut dst = vec![0u8; dst_len];
let code = unsafe {
minilzo::lzo1x_decompress(
src.as_ptr(),
src.len() as u64,
dst.as_mut_ptr(),
&dst_len as *const _ as *mut _,
std::ptr::null_mut(),
)
};
if code == 0 && dst.len() < dst_len {
dst.resize(dst_len, 0);
}
lzo_err_code_to_result(code, dst)
}
pub fn decompress_safe(&self, src: &[u8], dst_len: usize) -> LZOResult<Vec<u8>> {
let mut dst = vec![0u8; dst_len];
let code = unsafe {
minilzo::lzo1x_decompress_safe(
src.as_ptr(),
src.len() as minilzo::lzo_uint,
dst.as_mut_ptr(),
&dst_len as *const _ as *mut _,
std::ptr::null_mut(),
)
};
if code == 0 && dst.len() < dst_len {
dst.resize(dst_len, 0);
}
lzo_err_code_to_result(code, dst)
}
}
pub fn adler32(buf: &[u8]) -> u32 {
let checksum = 1u32;
unsafe { minilzo::lzo_adler32(checksum, buf.as_ptr(), buf.len() as u64) }
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_lzo_cmpress() {
let mut lzo = LZO::init().unwrap();
let input = [0x00u8; 1024];
let out = lzo.compress(&input).unwrap();
let input_o = input.to_owned();
let input = lzo.decompress_safe(&out[..], 1024);
let input = input.unwrap();
assert_eq!(&input_o[..], &input[..]);
}
#[test]
fn test_adler32() {
let buff = [0x09u8; 1024];
let checksum = adler32(&buff);
assert_eq!(checksum, 439886849);
}
}