use std::io::{self, Write};
use byteorder::{LittleEndian, WriteBytesExt};
use integer_encoding::VarIntWriter;
use zstd::Encoder;
use crate::{
bsdiff::ControlProducer,
header::{DATA_OFFSET, MAGIC, VERSION_MAJOR, VERSION_MINOR},
};
pub fn diff<W>(old: &[u8], new: &[u8], patch: &mut W) -> io::Result<()>
where
W: Write + ?Sized,
{
diff_with_config(old, new, patch, &DiffConfig::default())
}
pub fn diff_with_config<W>(
old: &[u8],
new: &[u8],
mut patch: &mut W,
options: &DiffConfig,
) -> io::Result<()>
where
W: Write + ?Sized,
{
patch.write_u32::<LittleEndian>(MAGIC)?;
patch.write_u16::<LittleEndian>(VERSION_MAJOR)?;
patch.write_u16::<LittleEndian>(VERSION_MINOR)?;
patch.write_varint(DATA_OFFSET)?;
let mut patch_encoder = Encoder::new(patch, options.compression_level)?;
patch_encoder.multithread(options.compression_threads)?;
for control in ControlProducer::new(old, new) {
patch_encoder.write_varint(control.add().len())?;
patch_encoder.write_all(control.add())?;
patch_encoder.write_varint(control.copy().len())?;
patch_encoder.write_all(control.copy())?;
patch_encoder.write_varint(control.seek())?;
}
patch_encoder.finish()?;
Ok(())
}
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)]
pub struct DiffConfig {
compression_threads: u32,
compression_level: i32,
}
impl DiffConfig {
pub const fn new() -> Self {
Self {
compression_threads: Self::DEFAULT_COMPRESSION_THREADS,
compression_level: Self::DEFAULT_COMPRESSION_LEVEL,
}
}
pub fn compression_threads(&mut self, threads: u32) -> &mut Self {
self.compression_threads = threads;
self
}
pub fn compression_level(&mut self, level: i32) -> &mut Self {
self.compression_level = level;
self
}
pub const DEFAULT_COMPRESSION_THREADS: u32 = 1;
pub const DEFAULT_COMPRESSION_LEVEL: i32 = 19;
}
impl Default for DiffConfig {
fn default() -> Self {
Self::new()
}
}