use crate::compression_chirho::CompressorChirho;
use crate::error_chirho::ResultChirho;
const RING_BUFFER_SIZE_CHIRHO: usize = 4096;
const MAX_MATCH_LENGTH_CHIRHO: usize = 18;
const MIN_MATCH_LENGTH_CHIRHO: usize = 3;
const RING_BUFFER_MASK_CHIRHO: usize = RING_BUFFER_SIZE_CHIRHO - 1;
#[derive(Debug, Clone, Default)]
pub struct LzssCompressorChirho;
impl LzssCompressorChirho {
pub fn new_chirho() -> Self {
Self
}
fn decompress_internal_chirho(&self, data_chirho: &[u8]) -> ResultChirho<Vec<u8>> {
if data_chirho.is_empty() {
return Ok(Vec::new());
}
let mut output_chirho = Vec::with_capacity(data_chirho.len() * 2);
let mut ring_buffer_chirho = vec![b' '; RING_BUFFER_SIZE_CHIRHO];
let mut ring_pos_chirho = RING_BUFFER_SIZE_CHIRHO - MAX_MATCH_LENGTH_CHIRHO;
let mut input_pos_chirho = 0;
while input_pos_chirho < data_chirho.len() {
let flags_chirho = data_chirho[input_pos_chirho];
input_pos_chirho += 1;
for bit_chirho in 0..8 {
if input_pos_chirho >= data_chirho.len() {
break;
}
if (flags_chirho >> bit_chirho) & 1 != 0 {
let byte_chirho = data_chirho[input_pos_chirho];
input_pos_chirho += 1;
output_chirho.push(byte_chirho);
ring_buffer_chirho[ring_pos_chirho] = byte_chirho;
ring_pos_chirho = (ring_pos_chirho + 1) & RING_BUFFER_MASK_CHIRHO;
} else {
if input_pos_chirho + 1 >= data_chirho.len() {
break;
}
let byte1_chirho = data_chirho[input_pos_chirho] as usize;
let byte2_chirho = data_chirho[input_pos_chirho + 1] as usize;
input_pos_chirho += 2;
let position_chirho = byte1_chirho | ((byte2_chirho & 0xF0) << 4);
let length_chirho = (byte2_chirho & 0x0F) + MIN_MATCH_LENGTH_CHIRHO;
for i_chirho in 0..length_chirho {
let src_pos_chirho = (position_chirho + i_chirho) & RING_BUFFER_MASK_CHIRHO;
let byte_chirho = ring_buffer_chirho[src_pos_chirho];
output_chirho.push(byte_chirho);
ring_buffer_chirho[ring_pos_chirho] = byte_chirho;
ring_pos_chirho = (ring_pos_chirho + 1) & RING_BUFFER_MASK_CHIRHO;
}
}
}
}
Ok(output_chirho)
}
fn compress_internal_chirho(&self, data_chirho: &[u8]) -> ResultChirho<Vec<u8>> {
if data_chirho.is_empty() {
return Ok(Vec::new());
}
let mut output_chirho = Vec::with_capacity(data_chirho.len());
let mut ring_buffer_chirho = vec![b' '; RING_BUFFER_SIZE_CHIRHO];
let mut ring_pos_chirho = RING_BUFFER_SIZE_CHIRHO - MAX_MATCH_LENGTH_CHIRHO;
let mut input_pos_chirho = 0;
let mut code_buf_chirho: Vec<u8> = Vec::with_capacity(17); let mut mask_chirho: u8 = 1;
let mut flags_chirho: u8 = 0;
code_buf_chirho.push(0);
while input_pos_chirho < data_chirho.len() {
let (match_pos_chirho, match_len_chirho) =
self.find_longest_match_chirho(&ring_buffer_chirho, ring_pos_chirho, data_chirho, input_pos_chirho);
if match_len_chirho >= MIN_MATCH_LENGTH_CHIRHO {
let byte1_chirho = (match_pos_chirho & 0xFF) as u8;
let byte2_chirho = (((match_pos_chirho >> 4) & 0xF0) | ((match_len_chirho - MIN_MATCH_LENGTH_CHIRHO) & 0x0F)) as u8;
code_buf_chirho.push(byte1_chirho);
code_buf_chirho.push(byte2_chirho);
for i_chirho in 0..match_len_chirho {
ring_buffer_chirho[ring_pos_chirho] = data_chirho[input_pos_chirho + i_chirho];
ring_pos_chirho = (ring_pos_chirho + 1) & RING_BUFFER_MASK_CHIRHO;
}
input_pos_chirho += match_len_chirho;
} else {
flags_chirho |= mask_chirho;
let byte_chirho = data_chirho[input_pos_chirho];
code_buf_chirho.push(byte_chirho);
ring_buffer_chirho[ring_pos_chirho] = byte_chirho;
ring_pos_chirho = (ring_pos_chirho + 1) & RING_BUFFER_MASK_CHIRHO;
input_pos_chirho += 1;
}
mask_chirho <<= 1;
if mask_chirho == 0 {
code_buf_chirho[0] = flags_chirho;
output_chirho.extend_from_slice(&code_buf_chirho);
code_buf_chirho.clear();
code_buf_chirho.push(0);
flags_chirho = 0;
mask_chirho = 1;
}
}
if code_buf_chirho.len() > 1 {
code_buf_chirho[0] = flags_chirho;
output_chirho.extend_from_slice(&code_buf_chirho);
}
Ok(output_chirho)
}
fn find_longest_match_chirho(
&self,
ring_buffer_chirho: &[u8],
ring_pos_chirho: usize,
data_chirho: &[u8],
input_pos_chirho: usize,
) -> (usize, usize) {
let mut best_pos_chirho = 0;
let mut best_len_chirho = 0;
let remaining_chirho = data_chirho.len() - input_pos_chirho;
let max_len_chirho = remaining_chirho.min(MAX_MATCH_LENGTH_CHIRHO);
if max_len_chirho < MIN_MATCH_LENGTH_CHIRHO {
return (0, 0);
}
for offset_chirho in 1..RING_BUFFER_SIZE_CHIRHO {
let search_pos_chirho = (ring_pos_chirho + RING_BUFFER_SIZE_CHIRHO - offset_chirho) & RING_BUFFER_MASK_CHIRHO;
let mut match_len_chirho = 0;
while match_len_chirho < max_len_chirho {
let ring_idx_chirho = (search_pos_chirho + match_len_chirho) & RING_BUFFER_MASK_CHIRHO;
if ring_buffer_chirho[ring_idx_chirho] != data_chirho[input_pos_chirho + match_len_chirho] {
break;
}
match_len_chirho += 1;
}
if match_len_chirho > best_len_chirho {
best_pos_chirho = search_pos_chirho;
best_len_chirho = match_len_chirho;
if best_len_chirho == max_len_chirho {
break;
}
}
}
(best_pos_chirho, best_len_chirho)
}
}
impl CompressorChirho for LzssCompressorChirho {
fn compress_chirho(&self, data_chirho: &[u8]) -> ResultChirho<Vec<u8>> {
self.compress_internal_chirho(data_chirho)
}
fn decompress_chirho(&self, data_chirho: &[u8]) -> ResultChirho<Vec<u8>> {
self.decompress_internal_chirho(data_chirho)
}
fn name_chirho(&self) -> &str {
"LZSS"
}
}
#[cfg(test)]
mod tests_chirho {
use super::*;
#[test]
fn test_lzss_empty_chirho() {
let compressor_chirho = LzssCompressorChirho::new_chirho();
let compressed_chirho = compressor_chirho.compress_chirho(&[]).unwrap();
assert!(compressed_chirho.is_empty());
let decompressed_chirho = compressor_chirho.decompress_chirho(&[]).unwrap();
assert!(decompressed_chirho.is_empty());
}
#[test]
fn test_lzss_roundtrip_simple_chirho() {
let compressor_chirho = LzssCompressorChirho::new_chirho();
let original_chirho = b"Hello, World!";
let compressed_chirho = compressor_chirho.compress_chirho(original_chirho).unwrap();
let decompressed_chirho = compressor_chirho.decompress_chirho(&compressed_chirho).unwrap();
assert_eq!(decompressed_chirho, original_chirho);
}
#[test]
fn test_lzss_roundtrip_repetitive_chirho() {
let compressor_chirho = LzssCompressorChirho::new_chirho();
let original_chirho = b"ABCABCABCABCABCABCABCABC";
let compressed_chirho = compressor_chirho.compress_chirho(original_chirho).unwrap();
let decompressed_chirho = compressor_chirho.decompress_chirho(&compressed_chirho).unwrap();
assert_eq!(decompressed_chirho, original_chirho);
assert!(compressed_chirho.len() < original_chirho.len());
}
#[test]
fn test_lzss_roundtrip_bible_text_chirho() {
let compressor_chirho = LzssCompressorChirho::new_chirho();
let original_chirho = b"In the beginning God created the heaven and the earth. \
And the earth was without form, and void; and darkness was upon the face of the deep. \
And the Spirit of God moved upon the face of the waters.";
let compressed_chirho = compressor_chirho.compress_chirho(original_chirho).unwrap();
let decompressed_chirho = compressor_chirho.decompress_chirho(&compressed_chirho).unwrap();
assert_eq!(decompressed_chirho, original_chirho);
}
#[test]
fn test_lzss_roundtrip_random_chirho() {
let compressor_chirho = LzssCompressorChirho::new_chirho();
let original_chirho: Vec<u8> = (0..256).map(|i| ((i * 17 + 31) % 256) as u8).collect();
let compressed_chirho = compressor_chirho.compress_chirho(&original_chirho).unwrap();
let decompressed_chirho = compressor_chirho.decompress_chirho(&compressed_chirho).unwrap();
assert_eq!(decompressed_chirho, original_chirho);
}
#[test]
fn test_lzss_name_chirho() {
let compressor_chirho = LzssCompressorChirho::new_chirho();
assert_eq!(compressor_chirho.name_chirho(), "LZSS");
}
}