use super::lz77_matcher::{
lz77_get_longest_match_fast, lz77_get_longest_match_slow, Lz77Match, Lz77Parameters,
};
use crate::impls::comp::comp_dict::CompDict;
use crate::prelude::Allocator;
use core::{ptr::write_unaligned, slice};
const WINDOW_SIZE: usize = u16::MAX as usize;
const MAX_OFFSET: usize = 0x1FFF;
const COPY_MAX_LENGTH: isize = 0x100;
const SHORT_COPY_MAX_OFFSET: isize = 0x100;
const SHORT_COPY_MAX_LEN: usize = 5;
const SHORT_COPY_MIN_LEN: usize = 2;
pub unsafe fn prs_compress<L: Allocator + Copy, S: Allocator + Copy>(
source: *const u8,
mut dest: *mut u8,
source_len: usize,
long_lived_allocator: L,
short_lived_allocator: S,
) -> usize {
let orig_dest = dest as usize;
let mut last_init_covered_all = false;
let mut control_byte_ptr = reserve_control_byte(&mut dest);
let mut control_bit_position = 0;
let mut source_ofs = 0;
let mut dict = CompDict::new_in(WINDOW_SIZE, long_lived_allocator, short_lived_allocator);
if source_len > 0 {
append_control_bit(
1,
&mut dest,
&mut control_bit_position,
&mut control_byte_ptr,
);
append_byte(*source, &mut dest);
source_ofs += 1;
}
let fast_processing_end = source_len.saturating_sub(COPY_MAX_LENGTH as usize);
while source_ofs < fast_processing_end {
let window_start = source_ofs.saturating_sub(MAX_OFFSET);
let window_end = window_start + WINDOW_SIZE;
let window_end = if window_end >= source_len {
last_init_covered_all = true;
source_len
} else {
window_end
};
let window_slice =
slice::from_raw_parts(source.add(window_start), window_end - window_start);
dict.init(window_slice, window_start);
while source_ofs < window_end.min(fast_processing_end) {
let result = lz77_get_longest_match_fast::<CompressParameters, L, S>(
&mut dict, source, source_ofs,
);
encode_lz77_match(
result,
&mut dest,
&mut control_bit_position,
&mut control_byte_ptr,
&mut source_ofs,
source,
);
}
}
if !last_init_covered_all {
let window_start = source_ofs.saturating_sub(MAX_OFFSET);
let window_slice =
slice::from_raw_parts(source.add(window_start), source_len - window_start);
dict.init(window_slice, window_start);
}
while source_ofs < source_len.saturating_sub(1) {
let result = lz77_get_longest_match_slow::<CompressParameters, L, S>(
&mut dict, source, source_len, source_ofs,
);
encode_lz77_match(
result,
&mut dest,
&mut control_bit_position,
&mut control_byte_ptr,
&mut source_ofs,
source,
);
}
if source_ofs == source_len.wrapping_sub(1) {
append_control_bit(
1,
&mut dest,
&mut control_bit_position,
&mut control_byte_ptr,
);
append_byte(*source.add(source_ofs), &mut dest);
}
append_control_bit(
0,
&mut dest,
&mut control_bit_position,
&mut control_byte_ptr,
);
append_control_bit(
1,
&mut dest,
&mut control_bit_position,
&mut control_byte_ptr,
);
append_byte(0x00, &mut dest);
append_byte(0x00, &mut dest);
dest as usize - orig_dest
}
#[inline(always)]
unsafe fn encode_lz77_match(
result: Lz77Match,
dest: &mut *mut u8,
control_bit_position: &mut usize,
control_byte_ptr: &mut *mut u8,
source_ofs: &mut usize,
source: *const u8,
) {
if result.offset >= -SHORT_COPY_MAX_OFFSET
&& result.length >= SHORT_COPY_MIN_LEN
&& result.length <= SHORT_COPY_MAX_LEN
{
write_short_copy(dest, &result, control_bit_position, control_byte_ptr);
*source_ofs += result.length;
} else if result.length <= 2 {
append_control_bit(1, dest, control_bit_position, control_byte_ptr);
append_byte(*source.add(*source_ofs), dest);
*source_ofs += 1;
} else {
if result.length <= 9 {
write_long_copy_small(dest, &result, control_bit_position, control_byte_ptr);
*source_ofs += result.length;
} else {
write_long_copy_large(dest, &result, control_bit_position, control_byte_ptr);
*source_ofs += result.length;
}
}
}
#[inline(always)]
unsafe fn write_short_copy(
dest: &mut *mut u8,
result: &Lz77Match,
control_bit_position: &mut usize,
control_byte_ptr: &mut *mut u8,
) {
let encoded_len = result.length - 2;
append_control_bit(0, dest, control_bit_position, control_byte_ptr);
append_control_bit(0, dest, control_bit_position, control_byte_ptr);
append_control_bit(
((encoded_len >> 1) & 1) as u8,
dest,
control_bit_position,
control_byte_ptr,
);
append_control_bit(
(encoded_len & 1) as u8,
dest,
control_bit_position,
control_byte_ptr,
);
append_byte((result.offset & 0xFF) as u8, dest);
}
#[inline(always)]
unsafe fn write_long_copy_small(
dest: &mut *mut u8,
result: &Lz77Match,
control_bit_position: &mut usize,
control_byte_ptr: &mut *mut u8,
) {
append_control_bit(0, dest, control_bit_position, control_byte_ptr);
append_control_bit(1, dest, control_bit_position, control_byte_ptr);
let len = (result.length - 2) as isize;
let ofs = result.offset << 3 & 0xFFF8;
let packed = (ofs | len) as u16;
append_u16_le(packed, dest);
}
#[inline(always)]
unsafe fn write_long_copy_large(
dest: &mut *mut u8,
result: &Lz77Match,
control_bit_position: &mut usize,
control_byte_ptr: &mut *mut u8,
) {
append_control_bit(0, dest, control_bit_position, control_byte_ptr);
append_control_bit(1, dest, control_bit_position, control_byte_ptr);
let packed = (result.offset << 3 & 0xFFF8) as u16;
append_u16_le(packed, dest);
append_byte((result.length - 1) as u8, dest);
}
#[inline]
unsafe fn append_control_bit(
bit: u8,
dest: &mut *mut u8,
control_bit_position: &mut usize,
control_byte_ptr: &mut *mut u8,
) {
if *control_bit_position >= 8 {
*control_byte_ptr = reserve_control_byte(dest);
*control_bit_position = 0;
}
**control_byte_ptr |= bit << *control_bit_position;
*control_bit_position += 1;
}
#[inline]
fn reserve_control_byte(dest: &mut *mut u8) -> *mut u8 {
unsafe {
**dest = 0; let result = *dest;
*dest = dest.add(1);
result
}
}
#[inline]
unsafe fn append_byte(value: u8, dest: &mut *mut u8) {
**dest = value;
*dest = dest.add(1);
}
#[inline]
unsafe fn append_u16_le(value: u16, dest: &mut *mut u8) {
write_unaligned((*dest) as *mut u16, value.to_le());
*dest = dest.add(2);
}
struct CompressParameters;
impl Lz77Parameters for CompressParameters {
const MAX_OFFSET: usize = MAX_OFFSET;
const MAX_LENGTH: usize = COPY_MAX_LENGTH as usize;
}