use alloc::vec::Vec;
use crate::error::Error;
use crate::traits::{RawDecoder, RawProgress};
use super::{HEADER_LEN, MAGIC, MAX_DICT_LOG2, MIN_DICT_LOG2};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum State {
Header,
EmptyDone,
PayloadUnsupported,
Done,
}
#[derive(Debug)]
pub struct Decoder {
state: State,
header_buf: Vec<u8>,
poisoned: bool,
}
impl Default for Decoder {
fn default() -> Self {
Self::new()
}
}
impl Decoder {
pub const fn new() -> Self {
Self {
state: State::Header,
header_buf: Vec::new(),
poisoned: false,
}
}
fn fill_header(&mut self, input: &[u8]) -> usize {
let need = HEADER_LEN - self.header_buf.len();
let take = need.min(input.len());
self.header_buf.extend_from_slice(&input[..take]);
take
}
fn finalise_header(&mut self) -> Result<(), Error> {
debug_assert_eq!(self.header_buf.len(), HEADER_LEN);
if self.header_buf[..4] != MAGIC {
return Err(Error::BadHeader);
}
let dict_log2 = self.header_buf[4];
if !(MIN_DICT_LOG2..=MAX_DICT_LOG2).contains(&dict_log2) {
return Err(Error::BadHeader);
}
let uncompressed_size = u64::from_le_bytes([
self.header_buf[5],
self.header_buf[6],
self.header_buf[7],
self.header_buf[8],
self.header_buf[9],
self.header_buf[10],
self.header_buf[11],
self.header_buf[12],
]);
if uncompressed_size == 0 {
self.state = State::EmptyDone;
} else {
self.state = State::PayloadUnsupported;
}
Ok(())
}
}
impl RawDecoder for Decoder {
fn raw_decode(&mut self, input: &[u8], _output: &mut [u8]) -> Result<RawProgress, Error> {
if self.poisoned {
return Err(Error::Corrupt);
}
match self.state {
State::Header => {
let consumed = self.fill_header(input);
if self.header_buf.len() == HEADER_LEN
&& let Err(e) = self.finalise_header()
{
self.poisoned = true;
return Err(e);
}
if matches!(self.state, State::PayloadUnsupported) {
self.poisoned = true;
return Err(Error::Unsupported);
}
Ok(RawProgress {
consumed,
written: 0,
done: matches!(self.state, State::EmptyDone),
})
}
State::EmptyDone => {
Ok(RawProgress {
consumed: 0,
written: 0,
done: true,
})
}
State::PayloadUnsupported => {
self.poisoned = true;
Err(Error::Unsupported)
}
State::Done => Ok(RawProgress {
consumed: 0,
written: 0,
done: true,
}),
}
}
fn raw_finish(&mut self, _output: &mut [u8]) -> Result<RawProgress, Error> {
if self.poisoned {
return Err(Error::Corrupt);
}
match self.state {
State::Header => {
self.poisoned = true;
Err(Error::UnexpectedEnd)
}
State::EmptyDone => {
self.state = State::Done;
Ok(RawProgress {
consumed: 0,
written: 0,
done: true,
})
}
State::PayloadUnsupported => {
self.poisoned = true;
Err(Error::Unsupported)
}
State::Done => Ok(RawProgress {
consumed: 0,
written: 0,
done: true,
}),
}
}
fn raw_reset(&mut self) {
self.state = State::Header;
self.header_buf.clear();
self.poisoned = false;
}
}