use core::cmp;
use core::result::Result;
use crate::compression::{Compression, CompressionError, Decompression, DecompressionError};
const LENGTH_BITS: u32 = 6;
const OFFSET_BITS: u32 = 10;
const LENGTH_SHIFT: u32 = OFFSET_BITS;
const OFFSET_MASK: u16 = (1 << OFFSET_BITS) - 1;
const LENGTH_MIN: usize = 3;
const LENGTH_MAX: usize = ((1 << LENGTH_BITS) - 1) + LENGTH_MIN;
const COPY_MASK_NEXT: u8 = 0;
const TABLE_LENGTH: usize = 1024;
pub struct LzjbEncoder {
table: [u16; TABLE_LENGTH],
}
impl Default for LzjbEncoder {
fn default() -> Self {
Self::new()
}
}
impl LzjbEncoder {
pub fn new() -> LzjbEncoder {
LzjbEncoder {
table: [0; TABLE_LENGTH],
}
}
pub fn compress_pre_v21(
&mut self,
dst: &mut [u8],
src: &[u8],
_level: u32,
) -> Result<usize, CompressionError> {
self.table[0..256].fill(0);
let mut src_idx: usize = 0;
let mut dst_idx: usize = 0;
let mut copy_mask: u8 = COPY_MASK_NEXT;
let mut copy_map_idx = 0;
while src_idx < src.len() {
copy_mask <<= 1;
if copy_mask == COPY_MASK_NEXT {
copy_mask = 1;
if dst.len() - dst_idx < 1 + (u8::BITS * 2) as usize {
return Err(CompressionError::NotCompressable {});
}
copy_map_idx = dst_idx;
dst[copy_map_idx] = 0;
dst_idx += 1;
}
if src.len() - src_idx < LENGTH_MAX {
dst[dst_idx] = src[src_idx];
dst_idx += 1;
src_idx += 1;
continue;
}
let hash: u8 = src[src_idx].wrapping_add(13)
^ src[src_idx + 1].wrapping_sub(13)
^ src[src_idx + 2];
let cpy_idx_modulo = &mut self.table[usize::from(hash)];
let old_offset = (src_idx - (*cpy_idx_modulo as usize)) & usize::from(OFFSET_MASK);
*cpy_idx_modulo = src_idx as u16;
let cpy_idx = src_idx - old_offset;
if cpy_idx != src_idx
&& src[src_idx] == src[cpy_idx]
&& src[src_idx + 1] == src[cpy_idx + 1]
&& src[src_idx + 2] == src[cpy_idx + 2]
{
dst[copy_map_idx] |= copy_mask;
let mut length = LENGTH_MIN;
while length < LENGTH_MAX {
if src[src_idx + length] == src[cpy_idx + length] {
length += 1;
} else {
break;
}
}
src_idx += length;
let length_offset = (((length - LENGTH_MIN) << LENGTH_SHIFT) | old_offset) as u16;
dst[dst_idx..dst_idx + 2].copy_from_slice(&u16::to_be_bytes(length_offset));
dst_idx += 2;
} else {
dst[dst_idx] = src[src_idx];
dst_idx += 1;
src_idx += 1;
}
}
Ok(dst_idx)
}
}
impl Compression for LzjbEncoder {
fn compress(
&mut self,
dst: &mut [u8],
src: &[u8],
_level: u32,
) -> Result<usize, CompressionError> {
self.table.fill(0);
let mut src_idx: usize = 0;
let mut dst_idx: usize = 0;
let mut copy_mask: u8 = COPY_MASK_NEXT;
let mut copy_map_idx = 0;
while src_idx < src.len() {
copy_mask <<= 1;
if copy_mask == COPY_MASK_NEXT {
copy_mask = 1;
if dst.len() - dst_idx < 1 + (u8::BITS * 2) as usize {
return Err(CompressionError::NotCompressable {});
}
copy_map_idx = dst_idx;
dst[copy_map_idx] = 0;
dst_idx += 1;
}
if src.len() - src_idx < LENGTH_MAX {
dst[dst_idx] = src[src_idx];
dst_idx += 1;
src_idx += 1;
continue;
}
let mut hash = (u32::from(src[src_idx]) << 16)
| (u32::from(src[src_idx + 1]) << 8)
| u32::from(src[src_idx + 2]);
hash += hash >> 9;
hash += hash >> 5;
let cpy_idx_modulo = &mut self.table[(hash as usize) % self.table.len()];
let old_offset = (src_idx - (*cpy_idx_modulo as usize)) & usize::from(OFFSET_MASK);
*cpy_idx_modulo = src_idx as u16;
let cpy_idx = src_idx - old_offset;
if cpy_idx != src_idx
&& src[src_idx] == src[cpy_idx]
&& src[src_idx + 1] == src[cpy_idx + 1]
&& src[src_idx + 2] == src[cpy_idx + 2]
{
dst[copy_map_idx] |= copy_mask;
let mut length = LENGTH_MIN;
while length < LENGTH_MAX {
if src[src_idx + length] == src[cpy_idx + length] {
length += 1;
} else {
break;
}
}
src_idx += length;
let length_offset = (((length - LENGTH_MIN) << LENGTH_SHIFT) | old_offset) as u16;
dst[dst_idx..dst_idx + 2].copy_from_slice(&u16::to_be_bytes(length_offset));
dst_idx += 2;
} else {
dst[dst_idx] = src[src_idx];
dst_idx += 1;
src_idx += 1;
}
}
Ok(dst_idx)
}
}
pub struct LzjbDecoder {}
impl Decompression for LzjbDecoder {
fn decompress(
&mut self,
dst: &mut [u8],
src: &[u8],
_level: u32,
) -> Result<(), DecompressionError> {
let mut src_idx: usize = 0;
let mut dst_idx: usize = 0;
let mut copy_mask: u8 = COPY_MASK_NEXT;
let mut copy_map: u8 = 0;
while dst_idx < dst.len() {
copy_mask <<= 1;
if copy_mask == COPY_MASK_NEXT {
copy_mask = 1;
copy_map = match src.get(src_idx) {
Some(v) => *v,
None => {
return Err(DecompressionError::EndOfInput {
offset: src_idx,
capacity: src.len(),
count: 1,
})
}
};
src_idx += 1;
}
if (copy_map & copy_mask) != 0 {
if src.len() - src_idx < 2 {
return Err(DecompressionError::EndOfInput {
offset: src_idx,
capacity: src.len(),
count: 2,
});
}
let length_offset =
u16::from_be_bytes(src[src_idx..src_idx + 2].try_into().unwrap());
src_idx += 2;
let length = usize::from(length_offset >> LENGTH_SHIFT) + LENGTH_MIN;
let offset = usize::from(length_offset & OFFSET_MASK);
let mut cpy_idx = match dst_idx.checked_sub(offset) {
Some(v) => v,
None => {
return Err(DecompressionError::InvalidInput {
offset: src_idx - 2,
})
}
};
let length = cmp::min(length, dst.len() - dst_idx);
for _ in 0..length {
dst[dst_idx] = dst[cpy_idx];
dst_idx += 1;
cpy_idx += 1;
}
} else {
dst[dst_idx] = match src.get(src_idx) {
Some(v) => *v,
None => {
return Err(DecompressionError::EndOfInput {
offset: src_idx,
capacity: src.len(),
count: 1,
})
}
};
dst_idx += 1;
src_idx += 1;
}
}
Ok(())
}
}