use std::io::{Read, Seek, Write};
use smdiff_common::MAX_INST_SIZE;
pub mod zstd{
pub use ruzstd::streaming_decoder::StreamingDecoder;
}
pub mod brotli{
pub use brotlic::DecompressorReader;
}
pub mod reader;
pub fn apply_patch<P:Read+Seek,R:Read+Seek,W:Write+Read+Seek>(patch:&mut P,mut src:Option<&mut R>,sink:&mut W) -> std::io::Result<()> {
let mut cur_o_pos: usize = 0;
let mut reader = crate::reader::SectionIterator::new(patch);
while let Some(res) = reader.next_borrowed(){
let (ops,_header) = res?;
apply_ops(ops, &mut src, sink, &mut cur_o_pos)?;
}
Ok(())
}
fn apply_no_sec_comp<P:Read,R:Read+Seek,W:Write+Read+Seek>(patch:&mut P,mut src:Option<&mut R>,sink:&mut W) -> std::io::Result<()> {
let mut cur_o_pos = 0;
let mut reader = smdiff_reader::SectionIterator::new(patch);
while let Some(res) = reader.next_borrowed(){
let (ops,_header) = res?;
apply_ops(ops, &mut src, sink, &mut cur_o_pos)?;
}
Ok(())
}
fn apply_ops<R:Read+Seek,W:Write+Read+Seek>(ops:&[smdiff_reader::Op],src:&mut Option<&mut R>,cur_o:&mut W, cur_o_pos: &mut usize) -> std::io::Result<()> {
cur_o.seek(std::io::SeekFrom::Start(*cur_o_pos as u64))?;
let mut copy_buffer = vec![0u8;MAX_INST_SIZE];
for op in ops {
match op {
smdiff_common::Op::Add(add) => {
cur_o.write_all(&add.bytes)?;
*cur_o_pos += add.bytes.len();
},
smdiff_common::Op::Copy(copy) => {
match copy.src{
smdiff_common::CopySrc::Dict => {
let src = match src.as_mut(){
Some(s) => s,
None => panic!("Copy operation without source data"),
};
src.seek(std::io::SeekFrom::Start(copy.addr))?;
let len = copy.len as usize;
src.read_exact(&mut copy_buffer[..len])?;
cur_o.write_all(©_buffer[..len])?;
*cur_o_pos += len;
},
smdiff_common::CopySrc::Output => {
let start_pos = *cur_o_pos;
cur_o.seek(std::io::SeekFrom::Start(copy.addr as u64))?;
let len = copy.len as usize;
cur_o.read_exact(&mut copy_buffer[..len])?;
cur_o.seek(std::io::SeekFrom::Start(start_pos as u64))?;
cur_o.write_all(©_buffer[..len])?;
*cur_o_pos += len;
},
}
},
smdiff_common::Op::Run(run) => {
let len = run.len as usize;
copy_buffer[..len].fill(run.byte);
cur_o.write_all(©_buffer[..len])?;
*cur_o_pos += len;
},
}
}
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
use std::io::Cursor;
#[test]
fn test_src_apply(){
let mut src = Cursor::new("hello".as_bytes().to_vec());
let patch = vec![
0, 4, 13, 129, 72, 4, 2, 130, 33, 32, 70, 0, ];
let mut patch = Cursor::new(patch);
let mut sink = Cursor::new(Vec::new());
apply_patch(&mut patch,Some(&mut src),&mut sink).unwrap();
assert_eq!(sink.into_inner(), "Hello! Hello!".as_bytes());
}
#[test]
fn test_complex_apply(){
let mut src = Cursor::new("hello".as_bytes().to_vec());
let patch = vec![
192, 1, 1, 0, 129, 72,
192, 1, 0, 4, 4, 2,
192, 1, 2, 0, 130, 33, 32,
64, 1, 0, 6, 70, 0, ];
let mut patch = Cursor::new(patch);
let mut sink = Cursor::new(Vec::new());
apply_patch(&mut patch,Some(&mut src),&mut sink).unwrap();
assert_eq!(sink.into_inner(), "Hello! Hello!".as_bytes());
}
#[test]
fn test_kitchen_sink(){
let mut src = Cursor::new("hello".as_bytes().to_vec());
let patch = vec![
192, 1, 1, 0, 129, 72,
192, 1, 0, 4, 4, 2,
192, 1, 2, 0, 130, 33, 32,
192, 1, 0, 6, 70, 0,
192, 1, 0, 5, 69, 12,
64, 1, 0, 3, 195, 46, ];
let mut patch = Cursor::new(patch);
let mut sink = Cursor::new(Vec::new());
apply_patch(&mut patch,Some(&mut src),&mut sink).unwrap();
assert_eq!(sink.into_inner(), "Hello! Hello! Hell...".as_bytes());
}
}