use alloc::vec::Vec;
use crate::delta::{Delta, Op};
use crate::error::{Error, Result};
pub struct DeltaApplier {
buffer_size: usize,
}
impl Default for DeltaApplier {
fn default() -> Self {
Self {
buffer_size: 4096,
}
}
}
impl DeltaApplier {
pub fn new() -> Self {
Self::default()
}
pub fn buffer_size(mut self, size: usize) -> Self {
self.buffer_size = size.max(256);
self
}
pub fn apply(&self, source: &[u8], delta: &Delta) -> Result<Vec<u8>> {
if source.len() as u64 != delta.source_size {
return Err(Error::delta(format!(
"source size mismatch: expected {}, got {}",
delta.source_size, source.len()
)));
}
let capacity = (self.buffer_size * 10).min(delta.target_size as usize);
let mut result = Vec::with_capacity(capacity);
for op in &delta.ops {
match op {
Op::Copy { src_offset, length } => {
let start = *src_offset as usize;
let len = *length as usize;
if start.saturating_add(len) > source.len() {
return Err(Error::delta(format!(
"copy out of bounds: offset={}, len={}, source={}",
start, len, source.len()
)));
}
result.extend_from_slice(&source[start..start + len]);
}
Op::Insert { data } => {
result.extend_from_slice(data);
}
}
if result.len() > delta.target_size as usize {
return Err(Error::delta("output exceeded expected size"));
}
}
if result.len() as u64 != delta.target_size {
return Err(Error::delta(format!(
"size mismatch: expected {}, got {}",
delta.target_size, result.len()
)));
}
Ok(result)
}
pub fn verify(&self, source_len: usize, delta: &Delta) -> Result<()> {
if source_len as u64 != delta.source_size {
return Err(Error::delta("source length mismatch"));
}
let mut output_size: u64 = 0;
for op in &delta.ops {
match op {
Op::Copy { src_offset, length } => {
if src_offset.saturating_add(*length) > source_len as u64 {
return Err(Error::delta("copy extends past source"));
}
output_size += length;
}
Op::Insert { data } => {
output_size += data.len() as u64;
}
}
if output_size > delta.target_size {
return Err(Error::delta("output would exceed target size"));
}
}
if output_size != delta.target_size {
return Err(Error::delta("final size mismatch"));
}
Ok(())
}
}