use crate::control::Aehobak as AehobakControl;
use crate::control::Bsdiff as BsdiffControl;
use std::hint::assert_unchecked;
use std::io;
use std::io::ErrorKind::{InvalidData, UnexpectedEof};
use streamvbyte64::{Coder, Coder0124};
#[allow(clippy::ptr_arg)]
pub fn patch(old: &[u8], mut patch: &[u8], new: &mut Vec<u8>) -> io::Result<()> {
let prefix_tag = patch.get(..1).ok_or(io::Error::from(UnexpectedEof))?;
patch = &patch[1..];
let coder = Coder0124::new();
let prefix_len = coder.data_len(prefix_tag);
if patch.len() < prefix_len {
return Err(io::Error::from(UnexpectedEof));
}
let (controls_len, deltas_len, control_data_len, literals_len) = {
let mut v = [0u32; 4];
coder.decode(prefix_tag, patch, &mut v);
(v[0] as usize, v[1] as usize, v[2] as usize, v[3] as usize)
};
patch = &patch[prefix_len..];
let control_tags_len = controls_len
.checked_mul(3)
.ok_or(io::Error::from(InvalidData))?
.div_ceil(4);
let delta_tags_len = deltas_len.div_ceil(4);
let control_tags = patch
.get(..control_tags_len)
.ok_or(io::Error::from(UnexpectedEof))?;
patch = &patch[control_tags_len..];
let delta_tags = patch
.get(..delta_tags_len)
.ok_or(io::Error::from(UnexpectedEof))?;
patch = &patch[delta_tags_len..];
if patch.len() < control_data_len {
return Err(io::Error::from(UnexpectedEof));
}
let control_data = patch;
patch = &patch[control_data_len..];
let mut literals = patch
.get(..literals_len)
.ok_or(io::Error::from(UnexpectedEof))?;
patch = &patch[literals_len..];
let delta_data_len = coder.data_len(delta_tags);
if patch.len() < delta_data_len {
return Err(io::Error::from(UnexpectedEof));
}
let delta_data = patch;
patch = &patch[delta_data_len..];
let mut delta_diffs = patch
.get(..deltas_len)
.ok_or(io::Error::from(UnexpectedEof))?;
let buf_len = control_tags_len
.checked_add(delta_tags_len)
.ok_or(io::Error::from(InvalidData))?
.checked_mul(4)
.ok_or(io::Error::from(InvalidData))?;
let mut u32_buf = vec![0; buf_len];
unsafe {
assert_unchecked(u32_buf.len() >= 4 * control_tags_len);
}
let (controls, delta_skips) = u32_buf.split_at_mut(4 * control_tags_len);
unsafe {
assert_unchecked(controls.len() >= controls_len * 3);
assert_unchecked(delta_skips.len() >= deltas_len);
}
let _ = coder.decode(control_tags, control_data, controls);
let controls = &controls[..controls_len * 3];
let _ = coder.decode(delta_tags, delta_data, delta_skips);
let mut delta_skips = &delta_skips[..deltas_len];
let mut old_cursor: usize = 0;
let mut delta_cursor: usize = 0;
let mut stream_cursor: usize = 0;
for buffer in controls.chunks_exact(3) {
let control: BsdiffControl = (&AehobakControl::try_from(buffer).unwrap()).into();
let (add, copy) = (control.add as usize, control.copy as usize);
let new_stream_cursor = stream_cursor
.checked_add(add)
.ok_or(io::Error::from(InvalidData))?;
let old_slice = old
.get(old_cursor..)
.ok_or(io::Error::from(UnexpectedEof))?
.get(..add)
.ok_or(io::Error::from(UnexpectedEof))?;
let new_cursor = new.len();
new.extend_from_slice(old_slice);
while !delta_skips.is_empty() && !delta_diffs.is_empty() {
let Some(new_delta_cursor) = delta_cursor.checked_add(delta_skips[0] as usize) else {
break;
};
if new_delta_cursor >= new_stream_cursor {
break;
}
unsafe {
assert_unchecked(new.len() > new_delta_cursor - stream_cursor + new_cursor);
}
let new_byte = &mut new[new_delta_cursor - stream_cursor + new_cursor];
*new_byte = new_byte.wrapping_add(delta_diffs[0]);
delta_cursor = new_delta_cursor
.checked_add(1)
.ok_or(io::Error::from(InvalidData))?;
delta_skips = &delta_skips[1..];
delta_diffs = &delta_diffs[1..];
}
new.extend_from_slice(literals.get(..copy).ok_or(io::Error::from(UnexpectedEof))?);
literals = &literals[copy..];
stream_cursor = new_stream_cursor;
old_cursor = usize::try_from(
i64::try_from(
old_cursor
.checked_add(add)
.ok_or(io::Error::from(InvalidData))?,
)
.map_err(|_| io::Error::from(InvalidData))?
.checked_add(control.seek)
.ok_or(io::Error::from(InvalidData))?,
)
.map_err(|_| io::Error::from(InvalidData))?;
}
Ok(())
}