use super::{DeltaLimits, decode::variable_byte};
use crate::{Error, Result};
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum DeltaStatus<'input> {
NeedInput,
Header { base_size: u64, result_size: u64 },
Copy { offset: u64, length: u64 },
Insert(&'input [u8]),
Finished,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct DeltaProgress<'input> {
pub consumed: usize,
pub status: DeltaStatus<'input>,
}
#[derive(Clone, Copy)]
enum State {
Base,
Size,
Opcode,
Copy(CopyFields),
Insert(u8),
Done,
Failed(Error),
}
#[derive(Clone, Copy)]
struct CopyFields {
flags: u8,
index: u8,
offset: u32,
length: u32,
}
impl CopyFields {
fn read(&mut self, input: &[u8]) -> (usize, bool) {
let mut consumed = 0;
while self.index < 7 {
if self.flags & (1 << self.index) != 0 {
let Some(&byte) = input.get(consumed) else {
return (consumed, false);
};
consumed += 1;
if self.index < 4 {
self.offset |= u32::from(byte) << (self.index * 8);
} else {
self.length |= u32::from(byte) << ((self.index - 4) * 8);
}
}
self.index += 1;
}
(consumed, true)
}
}
pub struct DeltaDecoder {
state: State,
limits: DeltaLimits,
base_size: u64,
result_size: u64,
position: u64,
instructions: u64,
variable: u64,
shift: u32,
depth: u32,
}
impl DeltaDecoder {
pub fn new(base_size: u64, base_depth: u32, limits: DeltaLimits) -> Result<Self> {
if base_depth >= limits.max_depth {
return Err(Error::LimitExceeded);
}
Ok(Self {
state: State::Base,
limits,
base_size,
result_size: 0,
position: 0,
instructions: 0,
variable: 0,
shift: 0,
depth: base_depth + 1,
})
}
pub fn position(&self) -> u64 {
self.position
}
pub fn depth(&self) -> u32 {
self.depth
}
pub fn update<'input>(
&mut self,
input: &'input [u8],
end_of_input: bool,
) -> Result<DeltaProgress<'input>> {
let result = self.advance(input, end_of_input);
if let Err(error) = result {
self.state = State::Failed(error);
}
result
}
pub fn finish(&self) -> Result<()> {
match self.state {
State::Done => Ok(()),
State::Failed(error) => Err(error),
_ => Err(Error::CorruptPack),
}
}
fn advance<'input>(&mut self, input: &'input [u8], eof: bool) -> Result<DeltaProgress<'input>> {
let mut consumed = 0;
loop {
let status = match self.state {
State::Failed(error) => return Err(error),
State::Done => {
if !input.is_empty() {
return Err(Error::CorruptPack);
}
Some(DeltaStatus::Finished)
}
State::Opcode if self.position == self.result_size => {
if consumed != input.len() {
return Err(Error::CorruptPack);
}
if eof {
self.state = State::Done;
Some(DeltaStatus::Finished)
} else {
Some(DeltaStatus::NeedInput)
}
}
State::Copy(mut fields) => {
let (read, complete) = fields.read(&input[consumed..]);
consumed += read;
if complete {
Some(self.copy(fields.offset, fields.length)?)
} else {
self.state = State::Copy(fields);
if eof {
return Err(Error::CorruptPack);
}
Some(DeltaStatus::NeedInput)
}
}
_ if consumed == input.len() => {
if eof {
return Err(Error::CorruptPack);
}
Some(DeltaStatus::NeedInput)
}
State::Base | State::Size => {
let byte = input[consumed];
consumed += 1;
self.size_byte(byte)?
}
State::Opcode => {
self.opcode(input[consumed])?;
consumed += 1;
None
}
State::Insert(remaining) => {
let length = usize::from(remaining).min(input.len() - consumed);
let bytes = &input[consumed..consumed + length];
consumed += length;
self.emit(length as u64)?;
self.state = if length == usize::from(remaining) {
State::Opcode
} else {
State::Insert(remaining - length as u8)
};
Some(DeltaStatus::Insert(bytes))
}
};
if let Some(status) = status {
return Ok(DeltaProgress { consumed, status });
}
}
}
fn size_byte(&mut self, byte: u8) -> Result<Option<DeltaStatus<'static>>> {
let Some(value) = variable_byte(&mut self.variable, &mut self.shift, byte)? else {
return Ok(None);
};
self.variable = 0;
self.shift = 0;
if matches!(self.state, State::Base) {
if value != self.base_size {
return Err(Error::CorruptPack);
}
self.state = State::Size;
return Ok(None);
}
if value > self.limits.max_output {
return Err(Error::LimitExceeded);
}
self.result_size = value;
self.state = State::Opcode;
Ok(Some(DeltaStatus::Header {
base_size: self.base_size,
result_size: value,
}))
}
fn opcode(&mut self, opcode: u8) -> Result<()> {
if self.instructions == self.limits.max_instructions {
return Err(Error::LimitExceeded);
}
self.instructions += 1;
self.state = match opcode {
0 => return Err(Error::CorruptPack),
1..=127 => {
if u64::from(opcode) > self.result_size - self.position {
return Err(Error::CorruptPack);
}
State::Insert(opcode)
}
_ => State::Copy(CopyFields {
flags: opcode,
index: 0,
offset: 0,
length: 0,
}),
};
Ok(())
}
fn copy(&mut self, offset: u32, length: u32) -> Result<DeltaStatus<'static>> {
let length = if length == 0 {
65536
} else {
u64::from(length)
};
let offset = u64::from(offset);
if offset + length > self.base_size {
return Err(Error::CorruptPack);
}
self.emit(length)?;
self.state = State::Opcode;
Ok(DeltaStatus::Copy { offset, length })
}
fn emit(&mut self, length: u64) -> Result<()> {
if length > self.result_size - self.position {
return Err(Error::CorruptPack);
}
self.position += length;
Ok(())
}
}