use std::io;
use std::io::Read;
pub fn patch<T: Read>(old: &[u8], patch: &mut T, new: &mut Vec<u8>) -> io::Result<()> {
let mut oldpos: usize = 0;
loop {
let mut buf = [0; 24];
if read_or_eof(patch, &mut buf)? {
return Ok(());
}
let mix_len = usize::try_from(u64::from_le_bytes(buf[0..8].try_into().unwrap())).map_err(|_| io::ErrorKind::InvalidData)?;
let copy_len = usize::try_from(u64::from_le_bytes(buf[8..16].try_into().unwrap())).map_err(|_| io::ErrorKind::InvalidData)?;
let seek_len = offtin(buf[16..24].try_into().unwrap());
let to_read = copy_len
.checked_add(mix_len)
.ok_or(io::ErrorKind::InvalidData)?;
let mix_start = new.len();
let has_read = patch.take(to_read as u64).read_to_end(new)?;
if has_read != to_read {
return Err(io::ErrorKind::UnexpectedEof.into());
}
let mix_end = mix_start.checked_add(mix_len).ok_or(io::ErrorKind::InvalidData)?;
let mix_slice = new.get_mut(mix_start..mix_end).ok_or(io::ErrorKind::UnexpectedEof)?;
let oldpos_end = oldpos.checked_add(mix_len).ok_or(io::ErrorKind::InvalidData)?;
let old_slice = old.get(oldpos ..oldpos_end).ok_or(io::ErrorKind::UnexpectedEof)?;
for (n, o) in mix_slice.iter_mut().zip(old_slice.iter().copied()) {
*n = n.wrapping_add(o);
}
oldpos += mix_len;
oldpos = (oldpos as i64)
.checked_add(seek_len)
.and_then(|n| usize::try_from(n).ok())
.ok_or(io::ErrorKind::InvalidData)?;
}
}
fn read_or_eof<T: Read>(reader: &mut T, buf: &mut [u8; 24]) -> io::Result<bool> {
let mut tmp = &mut buf[..];
loop {
match reader.read(tmp) {
Ok(0) => {
return if tmp.len() == 24 {
Ok(true)
} else {
Err(io::ErrorKind::UnexpectedEof.into())
}
}
Ok(n) => {
if n >= tmp.len() {
return Ok(false);
}
tmp = &mut tmp[n..];
}
Err(ref e) if e.kind() == io::ErrorKind::Interrupted => {}
Err(e) => return Err(e),
}
}
}
#[inline]
fn offtin(buf: [u8; 8]) -> i64 {
let y = i64::from_le_bytes(buf);
if 0 == y & (1 << 63) {
y
} else {
-(y & !(1 << 63))
}
}