mod literals;
mod sequences;
pub use literals::{LiteralsBlockType, LiteralsSection};
pub use sequences::{Sequence, SequencesSection, LITERAL_LENGTH_BASELINE, MATCH_LENGTH_BASELINE};
use haagenti_core::{Error, Result};
pub fn decode_raw_block(input: &[u8], output: &mut Vec<u8>) -> Result<()> {
output.extend_from_slice(input);
Ok(())
}
pub fn decode_rle_block(input: &[u8], size: usize, output: &mut Vec<u8>) -> Result<()> {
if input.is_empty() {
return Err(Error::corrupted("RLE block missing byte"));
}
let byte = input[0];
output.reserve(size);
for _ in 0..size {
output.push(byte);
}
Ok(())
}
pub fn decode_compressed_block(input: &[u8], output: &mut Vec<u8>, window: &[u8]) -> Result<()> {
if input.is_empty() {
return Err(Error::corrupted("Empty compressed block"));
}
let (literals, literals_size) = LiteralsSection::parse(input)?;
let sequences_data = &input[literals_size..];
let sequences = SequencesSection::parse(sequences_data, &literals)?;
execute_sequences(&literals, &sequences.sequences, output, window)?;
Ok(())
}
fn execute_sequences(
literals: &LiteralsSection,
sequences: &[Sequence],
output: &mut Vec<u8>,
_window: &[u8],
) -> Result<()> {
let literal_bytes = literals.data();
let mut literal_pos = 0;
for seq in sequences {
let literal_end = literal_pos + seq.literal_length as usize;
if literal_end > literal_bytes.len() {
return Err(Error::corrupted(
"Literal length exceeds available literals",
));
}
output.extend_from_slice(&literal_bytes[literal_pos..literal_end]);
literal_pos = literal_end;
if seq.match_length > 0 {
let out_len = output.len();
if seq.offset as usize > out_len {
return Err(Error::corrupted(format!(
"Match offset {} exceeds output size {}",
seq.offset, out_len
)));
}
let match_start = out_len - seq.offset as usize;
for i in 0..seq.match_length as usize {
let byte = output[match_start + (i % seq.offset as usize)];
output.push(byte);
}
}
}
if literal_pos < literal_bytes.len() {
output.extend_from_slice(&literal_bytes[literal_pos..]);
}
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_decode_raw_block() {
let input = b"Hello, World!";
let mut output = Vec::new();
decode_raw_block(input, &mut output).unwrap();
assert_eq!(output, input);
}
#[test]
fn test_decode_rle_block() {
let input = [b'A'];
let mut output = Vec::new();
decode_rle_block(&input, 5, &mut output).unwrap();
assert_eq!(output, b"AAAAA");
}
#[test]
fn test_decode_rle_block_empty_error() {
let input = [];
let mut output = Vec::new();
let result = decode_rle_block(&input, 5, &mut output);
assert!(result.is_err());
}
#[test]
fn test_decode_rle_block_large() {
let input = [0xFF];
let mut output = Vec::new();
decode_rle_block(&input, 1000, &mut output).unwrap();
assert_eq!(output.len(), 1000);
assert!(output.iter().all(|&b| b == 0xFF));
}
#[test]
fn test_execute_sequences_literals_only() {
let literals = LiteralsSection::new_raw(b"Hello".to_vec());
let sequences = vec![];
let mut output = Vec::new();
execute_sequences(&literals, &sequences, &mut output, &[]).unwrap();
assert_eq!(output, b"Hello");
}
#[test]
fn test_execute_sequences_with_match() {
let literals = LiteralsSection::new_raw(b"ab".to_vec());
let sequences = vec![Sequence {
literal_length: 2,
offset: 2,
match_length: 4,
}];
let mut output = Vec::new();
execute_sequences(&literals, &sequences, &mut output, &[]).unwrap();
assert_eq!(output, b"ababab");
}
#[test]
fn test_execute_sequences_rle_pattern() {
let literals = LiteralsSection::new_raw(b"a".to_vec());
let sequences = vec![Sequence {
literal_length: 1,
offset: 1,
match_length: 4,
}];
let mut output = Vec::new();
execute_sequences(&literals, &sequences, &mut output, &[]).unwrap();
assert_eq!(output, b"aaaaa");
}
#[test]
fn test_execute_sequences_multiple() {
let literals = LiteralsSection::new_raw(b"abcXYZ".to_vec());
let sequences = vec![
Sequence {
literal_length: 3, offset: 3,
match_length: 3, },
Sequence {
literal_length: 3, offset: 0,
match_length: 0,
},
];
let mut output = Vec::new();
execute_sequences(&literals, &sequences, &mut output, &[]).unwrap();
assert_eq!(output, b"abcabcXYZ");
}
}