use crate::dynamic::LzssDyn;
use crate::error::LzssError;
use crate::read_write::{Read, Write};
use core::convert::Infallible;
#[cfg(feature = "safe")]
use core::convert::TryInto;
mod compress;
mod compress_in_place;
mod decompress;
pub struct Lzss<const EI: usize, const EJ: usize, const C: u8, const N: usize, const N2: usize>(
Infallible,
);
impl<const EI: usize, const EJ: usize, const C: u8, const N: usize, const N2: usize>
Lzss<EI, EJ, C, N, N2>
{
#[must_use]
pub const fn as_dyn() -> LzssDyn {
let _: Result<(), ()> = Self::ASSERT_PARAMETERS;
LzssDyn {
ei: EI,
ej: EJ,
c: C,
}
}
#[inline(always)]
#[deprecated(since = "0.9.0", note = "renamed to compress_stack")]
pub fn compress<R: Read, W: Write>(
reader: R,
writer: W,
) -> Result<W::Output, LzssError<R::Error, W::Error>> {
Self::compress_stack(reader, writer)
}
pub fn compress_stack<R: Read, W: Write>(
mut reader: R,
mut writer: W,
) -> Result<W::Output, LzssError<R::Error, W::Error>> {
let _: Result<(), ()> = Self::ASSERT_PARAMETERS;
let mut buffer = [C; N2];
Self::compress_internal(&mut reader, &mut writer, &mut buffer)?;
writer.finish().map_err(LzssError::WriteError)
}
#[cfg_attr(docsrs, doc(cfg(any(feature = "alloc", feature = "std"))))]
#[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>> {
let _: Result<(), ()> = Self::ASSERT_PARAMETERS;
let mut buffer = vec![C; N2];
#[cfg(not(feature = "safe"))]
let buffer = unsafe { &mut *(buffer.as_mut_ptr().cast::<[u8; N2]>()) };
#[cfg(feature = "safe")]
let buffer: &mut [u8; N2] = (&mut buffer[..]).try_into().unwrap();
Self::compress_internal(&mut reader, &mut writer, buffer)?;
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>> {
let _: Result<(), ()> = Self::ASSERT_PARAMETERS;
buffer[..N - Self::F].fill(C);
Self::compress_internal(&mut reader, &mut writer, buffer)?;
writer.finish().map_err(LzssError::WriteError)
}
#[inline(always)]
#[deprecated(since = "0.9.0", note = "renamed to decompress_stack")]
pub fn decompress<R: Read, W: Write>(
reader: R,
writer: W,
) -> Result<W::Output, LzssError<R::Error, W::Error>> {
Self::decompress_stack(reader, writer)
}
pub fn decompress_stack<R: Read, W: Write>(
mut reader: R,
mut writer: W,
) -> Result<W::Output, LzssError<R::Error, W::Error>> {
let _: Result<(), ()> = 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_attr(docsrs, doc(cfg(any(feature = "alloc", feature = "std"))))]
#[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>> {
let _: Result<(), ()> = Self::ASSERT_PARAMETERS;
let mut buffer = vec![C; N];
#[cfg(not(feature = "safe"))]
let buffer = unsafe { &mut *(buffer.as_mut_ptr().cast::<[u8; N]>()) };
#[cfg(feature = "safe")]
let buffer: &mut [u8; N] = (&mut buffer[..]).try_into().unwrap();
Self::decompress_internal(&mut reader, &mut writer, buffer)?;
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>> {
let _: Result<(), ()> = Self::ASSERT_PARAMETERS;
buffer[..N].fill(C);
Self::decompress_internal(&mut reader, &mut writer, buffer)?;
writer.finish().map_err(LzssError::WriteError)
}
pub fn compress_in_place(io: &mut [u8], offset: usize) -> (usize, Option<usize>) {
let _: Result<(), ()> = Self::ASSERT_PARAMETERS;
Self::compress_in_place_internal(io, offset)
}
pub const MIN_OFFSET: usize = (N - Self::F) + Self::MIN_GAP_SIZE;
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;
const ASSERT_PARAMETERS: Result<(), ()> = {
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 (EI as u32) + 1 >= usize::BITS {
panic!("LZSS: Invalid EI, too large for usize")
}
if N != 1usize << EI {
panic!("LZSS: Invalid N, must be exactly 1<<EI")
}
if N2 != 2 * N {
panic!("LZSS: Invalid N2, must be exactly 2*N")
}
Ok(())
};
}
#[cfg(test)]
mod tests {
use crate::generic::Lzss;
use crate::slice::SliceReader;
use crate::vec::VecWriter;
use crate::void::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_stack(
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_stack(
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_stack(
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 io = io.as_mut_slice();
io[offset..].copy_from_slice(big_test_data);
let (c, u) = TestLZSS::compress_in_place(io, offset);
assert_eq!(u, None);
assert_eq!(output1.as_slice(), &io[0..c]);
let output2 = TestLZSS::decompress_stack(
SliceReader::new(&io[0..c]),
VecWriter::with_capacity(big_test_data.len()),
)
.void_unwrap();
assert_eq!(output2.as_slice(), big_test_data);
}
}