use alloc::vec::Vec;
use core::fmt;
pub fn apply(original: &[u8], delta: &[u8]) -> Result<Vec<u8>, DeltaError> {
let mut cursor = DeltaCursor::new(delta);
let header_orig_size = cursor.read_size()?;
let header_mod_size = cursor.read_size()?;
if original.len() as u64 != header_orig_size {
return Err(DeltaError::OriginalSizeMismatch {
expected: header_orig_size,
actual: original.len() as u64,
});
}
let mut result = Vec::with_capacity(header_mod_size.min(super::MAX_PREALLOC) as usize);
while !cursor.is_empty() {
let control = cursor.read_byte()?;
if control & 0x80 != 0 {
let (src_offset, copy_len) = cursor.read_copy_params(control)?;
let src_end = src_offset
.checked_add(copy_len)
.ok_or(DeltaError::InvalidCopyRange)?;
if src_end > original.len() {
return Err(DeltaError::CopyOutOfBounds {
offset: src_offset,
length: copy_len,
original_size: original.len(),
});
}
result.extend_from_slice(&original[src_offset..src_end]);
} else if control == 0 {
return Err(DeltaError::UnexpectedOpcode);
} else {
let add_len = control as usize;
let data = cursor.read_bytes(add_len)?;
result.extend_from_slice(data);
}
}
if result.len() as u64 != header_mod_size {
return Err(DeltaError::ModifiedSizeMismatch {
expected: header_mod_size,
actual: result.len() as u64,
});
}
Ok(result)
}
struct DeltaCursor<'a> {
data: &'a [u8],
offset: usize,
}
impl<'a> DeltaCursor<'a> {
fn new(data: &'a [u8]) -> Self {
Self { data, offset: 0 }
}
fn is_empty(&self) -> bool {
self.offset >= self.data.len()
}
fn read_byte(&mut self) -> Result<u8, DeltaError> {
if self.offset >= self.data.len() {
return Err(DeltaError::UnexpectedEof);
}
let byte = self.data[self.offset];
self.offset += 1;
Ok(byte)
}
fn read_bytes(&mut self, len: usize) -> Result<&'a [u8], DeltaError> {
let end = self
.offset
.checked_add(len)
.ok_or(DeltaError::UnexpectedEof)?;
if end > self.data.len() {
return Err(DeltaError::UnexpectedEof);
}
let bytes = &self.data[self.offset..end];
self.offset = end;
Ok(bytes)
}
fn read_size(&mut self) -> Result<u64, DeltaError> {
let mut file_len: u64 = 0;
let mut shift: u32 = 0;
loop {
let byte = self.read_byte()?;
let value = (byte & 0x7F) as u64;
file_len |= value.checked_shl(shift).ok_or(DeltaError::SizeOverflow)?;
if byte & 0x80 == 0 {
break;
}
shift += 7;
}
Ok(file_len)
}
fn read_copy_params(&mut self, control: u8) -> Result<(usize, usize), DeltaError> {
let mut src_offset: u32 = 0;
for (mask, shift) in [(0x01, 0), (0x02, 8), (0x04, 16), (0x08, 24)] {
if control & mask != 0 {
let byte = self.read_byte()? as u32;
src_offset |= byte.checked_shl(shift).ok_or(DeltaError::SizeOverflow)?;
}
}
let mut copy_len: u32 = 0;
for (mask, shift) in [(0x10, 0), (0x20, 8), (0x40, 16)] {
if control & mask != 0 {
let byte = self.read_byte()? as u32;
copy_len |= byte.checked_shl(shift).ok_or(DeltaError::SizeOverflow)?;
}
}
if copy_len == 0 {
copy_len = 0x10000;
}
Ok((src_offset as usize, copy_len as usize))
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum DeltaError {
UnexpectedEof,
SizeOverflow,
OriginalSizeMismatch { expected: u64, actual: u64 },
ModifiedSizeMismatch { expected: u64, actual: u64 },
CopyOutOfBounds {
offset: usize,
length: usize,
original_size: usize,
},
InvalidCopyRange,
UnexpectedOpcode,
}
impl fmt::Display for DeltaError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
DeltaError::UnexpectedEof => write!(f, "unexpected end of delta data"),
DeltaError::SizeOverflow => write!(f, "size value overflow"),
DeltaError::OriginalSizeMismatch { expected, actual } => {
write!(
f,
"original size mismatch: expected {expected}, got {actual}"
)
}
DeltaError::ModifiedSizeMismatch { expected, actual } => {
write!(
f,
"modified size mismatch: expected {expected}, got {actual}"
)
}
DeltaError::CopyOutOfBounds {
offset,
length,
original_size,
} => {
write!(
f,
"copy out of bounds: offset={offset}, length={length}, original_size={original_size}"
)
}
DeltaError::InvalidCopyRange => write!(f, "copy range calculation overflow"),
DeltaError::UnexpectedOpcode => write!(f, "unexpected delta opcode 0"),
}
}
}
impl core::error::Error for DeltaError {}
#[cfg(test)]
mod tests {
use super::*;
use alloc::vec;
#[test]
fn read_size_single_byte() {
let data = [0x0A];
let mut cursor = DeltaCursor::new(&data);
assert_eq!(cursor.read_size().unwrap(), 10);
}
#[test]
fn read_size_multi_byte() {
let data = [0x81, 0x02];
let mut cursor = DeltaCursor::new(&data);
assert_eq!(cursor.read_size().unwrap(), 1 + (2 << 7));
}
#[test]
fn apply_add_only() {
let delta = [
0x00, 0x05, 0x05, b'h', b'e', b'l', b'l', b'o',
];
let result = apply(&[], &delta).unwrap();
assert_eq!(result, b"hello");
}
#[test]
fn apply_copy_only() {
let delta = [
0x05, 0x05, 0x90, 0x05, ];
let original = b"hello";
let result = apply(original, &delta).unwrap();
assert_eq!(result, b"hello");
}
#[test]
fn apply_copy_with_offset() {
let delta = [
0x0A, 0x05, 0x91, 0x05, 0x05, ];
let original = b"helloworld";
let result = apply(original, &delta).unwrap();
assert_eq!(result, b"world");
}
#[test]
fn apply_mixed_instructions() {
let delta = [
0x0B, 0x0B, 0x05, b'H', b'E', b'L', b'L', b'O', 0x91, 0x05, 0x06, ];
let original = b"hello world";
let result = apply(original, &delta).unwrap();
assert_eq!(result, b"HELLO world");
}
#[test]
fn apply_copy_size_zero_means_65536() {
let original = vec![0xAB; 65536];
let delta = [
0x80, 0x80, 0x04, 0x80, 0x80, 0x04, 0x80, ];
let result = apply(&original, &delta).unwrap();
assert_eq!(result.len(), 65536);
assert_eq!(result, original);
}
#[test]
fn error_original_size_mismatch() {
let delta = [
0x0A, 0x05, ];
let original = b"short"; let err = apply(original, &delta).unwrap_err();
assert!(matches!(
err,
DeltaError::OriginalSizeMismatch {
expected: 10,
actual: 5
}
));
}
#[test]
fn error_copy_out_of_bounds() {
let delta = [
0x05, 0x05, 0x91, 0x0A, 0x05, ];
let original = b"hello";
let err = apply(original, &delta).unwrap_err();
assert!(matches!(err, DeltaError::CopyOutOfBounds { .. }));
}
#[test]
fn error_unexpected_eof_in_add() {
let delta = [
0x00, 0x05, 0x05, b'h', b'i', ];
let err = apply(&[], &delta).unwrap_err();
assert_eq!(err, DeltaError::UnexpectedEof);
}
#[test]
fn error_unexpected_opcode() {
let delta = [
0x05, 0x05, 0x91, 0x00, 0x04, 0x00, 0x01, b'X', ];
let original = b"hello";
let err = apply(original, &delta).unwrap_err();
assert_eq!(err, DeltaError::UnexpectedOpcode);
}
}