mod compress;
mod compress_in_place;
mod decompress;
use crate::{LzssError, Read, Write};
pub struct Lzss<const EI: usize, const EJ: usize, const C: u8, const N: usize, const N2: usize>(());
macro_rules! assert_parameters {
() => {
if EJ == 0 {
panic!("LZSS: Invalid EJ, must be larger than 0")
}
if EJ >= EI {
panic!("LZSS: Invalid EI, must be larger than EJ")
}
if EI + EJ < 8 {
panic!("LZSS: Invalid EI, EJ, both together must be 8 or more")
}
if EI + EJ > 24 {
panic!("LZSS: Invalid EI, EJ, both together must be 24 or less")
}
if (N as u32) != (1u32 << EI) {
panic!("LZSS: Invalid N, must be exactly 1<<EI")
}
if (N2 as u32) != 2 * (N as u32) {
panic!("LZSS: Invalid N2, must be exactly 2*N")
}
};
}
impl<const EI: usize, const EJ: usize, const C: u8, const N: usize, const N2: usize>
Lzss<EI, EJ, C, N, N2>
{
pub fn compress<R: Read, W: Write>(
mut reader: R,
mut writer: W,
) -> Result<W::Output, LzssError<R::Error, W::Error>> {
#[cfg(not(feature = "const_panic"))]
Self::assert_parameters();
let mut buffer = [C; N2];
Self::compress_internal(&mut reader, &mut writer, &mut buffer)?;
writer.finish().map_err(LzssError::WriteError)
}
#[cfg(feature = "alloc")]
pub fn compress_heap<R: Read, W: Write>(
mut reader: R,
mut writer: W,
) -> Result<W::Output, LzssError<R::Error, W::Error>> {
#[cfg(not(feature = "const_panic"))]
Self::assert_parameters();
let mut buffer = vec![C; N2];
Self::compress_internal(&mut reader, &mut writer, unsafe {
&mut *(buffer.as_mut_ptr() as *mut [u8; N2])
})?;
writer.finish().map_err(LzssError::WriteError)
}
pub fn compress_with_buffer<R: Read, W: Write>(
mut reader: R,
mut writer: W,
buffer: &mut [u8; N2],
) -> Result<W::Output, LzssError<R::Error, W::Error>> {
#[cfg(not(feature = "const_panic"))]
Self::assert_parameters();
unsafe { ::core::ptr::write_bytes(buffer.as_mut_ptr(), C, Self::N - Self::F) };
Self::compress_internal(&mut reader, &mut writer, buffer)?;
writer.finish().map_err(LzssError::WriteError)
}
pub fn decompress<R: Read, W: Write>(
mut reader: R,
mut writer: W,
) -> Result<W::Output, LzssError<R::Error, W::Error>> {
#[cfg(not(feature = "const_panic"))]
Self::assert_parameters();
let mut buffer: [u8; N] = [C; N];
Self::decompress_internal(&mut reader, &mut writer, &mut buffer)?;
writer.finish().map_err(LzssError::WriteError)
}
#[cfg(feature = "alloc")]
pub fn decompress_heap<R: Read, W: Write>(
mut reader: R,
mut writer: W,
) -> Result<W::Output, LzssError<R::Error, W::Error>> {
#[cfg(not(feature = "const_panic"))]
Self::assert_parameters();
let mut buffer = vec![C; N];
Self::decompress_internal(&mut reader, &mut writer, unsafe {
&mut *(buffer.as_mut_ptr() as *mut [u8; N])
})?;
writer.finish().map_err(LzssError::WriteError)
}
pub fn decompress_with_buffer<R: Read, W: Write>(
mut reader: R,
mut writer: W,
buffer: &mut [u8; N],
) -> Result<W::Output, LzssError<R::Error, W::Error>> {
#[cfg(not(feature = "const_panic"))]
Self::assert_parameters();
unsafe { ::core::ptr::write_bytes(buffer.as_mut_ptr(), C, Self::N) };
Self::decompress_internal(&mut reader, &mut writer, buffer)?;
writer.finish().map_err(LzssError::WriteError)
}
}
impl<const EI: usize, const EJ: usize, const C: u8, const N: usize, const N2: usize>
Lzss<EI, EJ, C, N, N2>
{
pub fn compress_in_place(io: &mut [u8], offset: usize) -> (usize, Option<usize>) {
Self::compress_in_place_internal(io, offset)
}
pub const MIN_OFFSET: usize = (Self::N - Self::F) + Self::MIN_GAP_SIZE;
}
impl<const EI: usize, const EJ: usize, const C: u8, const N: usize, const N2: usize>
Lzss<EI, EJ, C, N, N2>
{
pub(crate) const N: usize = {
#[cfg(feature = "const_panic")]
assert_parameters!();
1 << EI
};
pub(crate) const P: usize = (1 + EI + EJ) / 9;
pub(crate) const F: usize = (1 << EJ) + Self::P;
pub(crate) const MIN_GAP_SIZE: usize = Self::P + 4;
#[cfg(not(feature = "const_panic"))]
#[inline(always)]
pub(crate) fn assert_parameters() {
assert_parameters!();
}
}
#[cfg(test)]
mod tests {
use crate::generic::Lzss;
use crate::slice::SliceReader;
use crate::vec::VecWriter;
use crate::ResultLzssErrorVoidExt;
type TestLZSS = Lzss<10, 4, 0x20, { 1 << 10 }, { 2 << 10 }>;
const TEST_DATA: &[u8; 27] = b"Sample Data 11221233123";
const COMPRESSED_DATA: [u8; 26] = [
169, 216, 109, 183, 11, 101, 149, 246, 13, 18, 195, 116, 176, 191, 81, 152, 204, 102, 83, 32,
0, 19, 57, 152, 3, 16,
];
#[test]
fn test_decompress() {
let output = TestLZSS::decompress(
SliceReader::new(&COMPRESSED_DATA),
VecWriter::with_capacity(TEST_DATA.len()),
)
.void_unwrap();
assert_eq!(output.as_slice(), TEST_DATA);
}
#[test]
fn test_decompress_with_buffer() {
let mut buffer = [123; 1024];
let output = TestLZSS::decompress_with_buffer(
SliceReader::new(&COMPRESSED_DATA),
VecWriter::with_capacity(TEST_DATA.len()),
&mut buffer,
)
.void_unwrap();
assert_eq!(output.as_slice(), TEST_DATA);
}
#[test]
fn test_compress() {
let output = TestLZSS::compress(
SliceReader::new(TEST_DATA),
VecWriter::with_capacity(COMPRESSED_DATA.len()),
)
.void_unwrap();
assert_eq!(output.as_slice(), COMPRESSED_DATA);
}
#[test]
fn test_compress_with_buffer() {
let mut buffer = [123; 2048];
let output = TestLZSS::compress_with_buffer(
SliceReader::new(TEST_DATA),
VecWriter::with_capacity(COMPRESSED_DATA.len()),
&mut buffer,
)
.void_unwrap();
assert_eq!(output.as_slice(), COMPRESSED_DATA);
}
#[test]
fn test_compress_in_place() {
const OFFSET: usize = TestLZSS::MIN_OFFSET + TEST_DATA.len() / 8;
let mut io = [0u8; OFFSET + TEST_DATA.len()];
io[OFFSET..].copy_from_slice(TEST_DATA);
let (c, u) = TestLZSS::compress_in_place(&mut io, OFFSET);
assert_eq!(c, COMPRESSED_DATA.len());
assert_eq!(u, None);
assert_eq!(io[0..c], COMPRESSED_DATA);
}
#[test]
fn test_compress_big() {
let big_test_data = include_bytes!("mod.rs");
let output1 = TestLZSS::compress(
SliceReader::new(big_test_data),
VecWriter::with_capacity(big_test_data.len()),
)
.void_unwrap();
let offset: usize = TestLZSS::MIN_OFFSET + big_test_data.len() / 8;
let mut io = Vec::new();
io.resize(offset + big_test_data.len(), 0);
let mut io = io.as_mut_slice();
io[offset..].copy_from_slice(big_test_data);
let (c, u) = TestLZSS::compress_in_place(&mut io, offset);
assert_eq!(u, None);
assert_eq!(output1.as_slice(), &io[0..c]);
let output2 = TestLZSS::decompress(
SliceReader::new(&io[0..c]),
VecWriter::with_capacity(big_test_data.len()),
)
.void_unwrap();
assert_eq!(output2.as_slice(), big_test_data);
}
}