use alloc::{vec, vec::Vec};
use super::MAX_DECOMPRESSED_EXTENT_BYTES;
use crate::error::{Error, Result};
const LZO_SECTOR_PLAINTEXT_BYTES: usize = 4096;
pub(super) fn decode(src: &[u8], dst: &mut Vec<u8>) -> Result<()> {
if src.len() < 4 {
return Err(Error::BadCompression {
algorithm: "comp_lzo",
});
}
let total_compressed = u32::from_le_bytes([src[0], src[1], src[2], src[3]]) as usize;
if total_compressed > src.len() || total_compressed < 4 {
return Err(Error::BadCompression {
algorithm: "comp_lzo",
});
}
let mut p = 4usize;
while p < total_compressed {
if p + 4 > src.len() {
return Err(Error::BadCompression {
algorithm: "comp_lzo",
});
}
let sector_compressed_size =
u32::from_le_bytes([src[p], src[p + 1], src[p + 2], src[p + 3]]) as usize;
p += 4;
if sector_compressed_size == 0 {
break;
}
if p + sector_compressed_size > src.len() {
return Err(Error::BadCompression {
algorithm: "comp_lzo",
});
}
let sector = &src[p..p + sector_compressed_size];
p += sector_compressed_size;
if dst.len() + LZO_SECTOR_PLAINTEXT_BYTES > MAX_DECOMPRESSED_EXTENT_BYTES {
return Err(Error::BadCompression {
algorithm: "comp_lzo",
});
}
let mut scratch = vec![0u8; LZO_SECTOR_PLAINTEXT_BYTES];
let n = lzokay::decompress::decompress(sector, &mut scratch).map_err(|_| {
Error::BadCompression {
algorithm: "comp_lzo",
}
})?;
dst.extend_from_slice(&scratch[..n]);
while p < total_compressed && p % 4 != 0 && src[p] == 0 {
p += 1;
}
}
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn rejects_short_input() {
let mut dst = Vec::new();
assert!(decode(b"", &mut dst).is_err());
assert!(decode(&[0, 0, 0], &mut dst).is_err());
}
#[test]
fn rejects_oversized_total() {
let mut dst = Vec::new();
let buf = [0, 4, 0, 0];
assert!(decode(&buf, &mut dst).is_err());
}
#[test]
fn rejects_truncated_sector() {
let mut dst = Vec::new();
let mut buf = vec![16, 0, 0, 0]; buf.extend_from_slice(&1024u32.to_le_bytes()); buf.extend_from_slice(&[0u8; 8]); assert!(decode(&buf, &mut dst).is_err());
}
}