#![no_std]
#![forbid(unsafe_code)]
#[cfg(feature = "alloc")]
extern crate alloc;
use core::fmt;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Error {
InputOverrun,
OutputOverrun,
LookbehindOverrun,
InputNotConsumed,
Malformed,
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(match self {
Error::InputOverrun => "lzo: compressed input ended prematurely",
Error::OutputOverrun => "lzo: output buffer too small",
Error::LookbehindOverrun => "lzo: back-reference before output start",
Error::InputNotConsumed => "lzo: trailing bytes after end-of-stream",
Error::Malformed => "lzo: malformed compressed block",
})
}
}
impl core::error::Error for Error {}
fn rd(src: &[u8], ip: &mut usize) -> Result<u8, Error> {
let b = *src.get(*ip).ok_or(Error::InputOverrun)?;
*ip += 1;
Ok(b)
}
fn rd_le16(src: &[u8], ip: &mut usize) -> Result<usize, Error> {
let lo = rd(src, ip)? as usize;
let hi = rd(src, ip)? as usize;
Ok(lo | (hi << 8))
}
fn length_ext(src: &[u8], ip: &mut usize) -> Result<usize, Error> {
let mut zeros = 0usize;
while *src.get(*ip).ok_or(Error::InputOverrun)? == 0 {
*ip += 1;
zeros += 1;
}
let term = rd(src, ip)? as usize;
Ok(zeros.saturating_mul(255).saturating_add(term))
}
fn copy_literals(
src: &[u8],
ip: &mut usize,
dst: &mut [u8],
op: &mut usize,
n: usize,
) -> Result<(), Error> {
if n > dst.len() - *op {
return Err(Error::OutputOverrun);
}
if n > src.len() - *ip {
return Err(Error::InputOverrun);
}
dst[*op..*op + n].copy_from_slice(&src[*ip..*ip + n]);
*op += n;
*ip += n;
Ok(())
}
fn copy_match(dst: &mut [u8], op: &mut usize, distance: usize, length: usize) -> Result<(), Error> {
if distance == 0 || distance > *op {
return Err(Error::LookbehindOverrun);
}
if length > dst.len() - *op {
return Err(Error::OutputOverrun);
}
let s = *op - distance;
#[allow(clippy::needless_range_loop)]
for i in 0..length {
dst[*op + i] = dst[s + i];
}
*op += length;
Ok(())
}
pub fn decompress_into(src: &[u8], dst: &mut [u8]) -> Result<usize, Error> {
if src.len() < 3 {
return Err(Error::InputOverrun);
}
let mut ip = 0usize;
let mut op = 0usize;
let mut state: usize;
let mut t: usize;
let first = src[0];
if first > 17 {
ip = 1;
t = first as usize - 17;
copy_literals(src, &mut ip, dst, &mut op, t)?;
state = if t < 4 { t } else { 4 };
} else {
state = 0; }
loop {
t = rd(src, &mut ip)? as usize;
let (distance, length, next);
if t < 16 {
if state == 0 {
let len = if t == 0 {
length_ext(src, &mut ip)?.saturating_add(18)
} else {
t + 3
};
copy_literals(src, &mut ip, dst, &mut op, len)?;
state = 4;
continue;
}
next = t & 3;
if state == 4 {
distance = 1 + 2048 + (t >> 2) + ((rd(src, &mut ip)? as usize) << 2);
length = 3;
} else {
distance = 1 + (t >> 2) + ((rd(src, &mut ip)? as usize) << 2);
length = 2;
}
} else if t >= 64 {
next = t & 3;
distance = 1 + ((t >> 2) & 7) + ((rd(src, &mut ip)? as usize) << 3);
length = (t >> 5) + 1;
} else if t >= 32 {
length = if (t & 31) == 0 {
length_ext(src, &mut ip)?.saturating_add(33)
} else {
(t & 31) + 2
};
let d = rd_le16(src, &mut ip)?;
distance = 1 + (d >> 2);
next = d & 3;
} else {
let hi = (t & 8) << 11;
length = if (t & 7) == 0 {
length_ext(src, &mut ip)?.saturating_add(9)
} else {
(t & 7) + 2
};
let d = rd_le16(src, &mut ip)?;
let dist_part = d >> 2;
next = d & 3;
if hi == 0 && dist_part == 0 {
if length != 3 {
return Err(Error::Malformed);
}
if ip < src.len() {
return Err(Error::InputNotConsumed);
}
return Ok(op);
}
distance = hi + dist_part + 0x4000;
}
copy_match(dst, &mut op, distance, length)?;
copy_literals(src, &mut ip, dst, &mut op, next)?;
state = next;
}
}
#[cfg(feature = "alloc")]
pub fn decompress(src: &[u8], max_len: usize) -> Result<alloc::vec::Vec<u8>, Error> {
let mut dst = alloc::vec![0u8; max_len];
let n = decompress_into(src, &mut dst)?;
dst.truncate(n);
Ok(dst)
}