use crate::error::{LazippyError, LazippyResult};
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Properties {
pub lc: u8,
pub lp: u8,
pub pb: u8,
pub dict_size: u32,
pub uncompressed_size: Option<u64>,
}
pub fn parse_header(input: &[u8]) -> LazippyResult<Properties> {
if input.len() < 13 {
return Err(LazippyError::invalid_header(format!(
"header too short: need 13 bytes, got {}",
input.len()
)));
}
let props_byte = input[0];
if props_byte >= (4 * 5 + 4) * 9 + 9 {
return Err(LazippyError::InvalidProperties(props_byte));
}
let lc = props_byte % 9;
let remainder = props_byte / 9;
let lp = remainder % 5;
let pb = remainder / 5;
let dict_size = u32::from_le_bytes([input[1], input[2], input[3], input[4]]);
let raw_size = u64::from_le_bytes([
input[5], input[6], input[7], input[8], input[9], input[10], input[11], input[12],
]);
let uncompressed_size = if raw_size == u64::MAX {
None
} else {
Some(raw_size)
};
Ok(Properties {
lc,
lp,
pb,
dict_size,
uncompressed_size,
})
}
pub fn parse_7z_props(input: &[u8]) -> LazippyResult<(u8, u32)> {
if input.len() < 5 {
return Err(LazippyError::invalid_header(format!(
"7z LZMA props too short: need 5 bytes, got {}",
input.len()
)));
}
let props_byte = input[0];
let dict_size = u32::from_le_bytes([input[1], input[2], input[3], input[4]]);
Ok((props_byte, dict_size))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn parse_header_parses_13_bytes() {
let mut header = [0u8; 13];
header[0] = 93; header[1..5].copy_from_slice(&(1u32 << 16).to_le_bytes()); header[5..13].copy_from_slice(&12345u64.to_le_bytes()); let props = parse_header(&header).unwrap();
assert_eq!(props.lc, 3);
assert_eq!(props.lp, 0);
assert_eq!(props.pb, 2);
assert_eq!(props.dict_size, 1 << 16);
assert_eq!(props.uncompressed_size, Some(12345));
}
#[test]
fn parse_header_too_short() {
let result = parse_header(&[0u8; 5]);
assert!(matches!(result, Err(LazippyError::InvalidHeader(_))));
}
#[test]
fn parse_header_unknown_size() {
let mut header = [0u8; 13];
header[0] = 93;
header[1..5].copy_from_slice(&(1u32 << 16).to_le_bytes());
header[5..13].copy_from_slice(&u64::MAX.to_le_bytes());
let props = parse_header(&header).unwrap();
assert_eq!(props.uncompressed_size, None);
}
}