#![deny(
missing_copy_implementations,
missing_docs,
trivial_casts,
trivial_numeric_casts,
unstable_features,
unused_import_braces,
unused_qualifications
)]
#![cfg_attr(feature = "nightly", allow(unstable_features))]
extern crate libc;
extern crate librsync_sys as raw;
#[cfg(feature = "log")]
#[macro_use]
extern crate log;
mod job;
mod logfwd;
mod macros;
pub mod whole;
use crate::job::{Job, JobDriver};
use std::cell::{RefCell, RefMut};
use std::error;
use std::fmt::{self, Display, Formatter};
use std::io::{self, BufRead, BufReader, Read, Seek};
use std::mem::{self, ManuallyDrop};
use std::ops::Deref;
use std::ptr;
use std::rc::Rc;
use std::slice;
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum SignatureType {
MD4,
Blake2,
}
#[derive(Debug)]
pub enum Error {
Io(io::Error),
Mem,
BadMagic,
Unimplemented,
Internal,
Unknown(i32),
}
pub type Result<T> = std::result::Result<T, Error>;
pub struct Signature<R> {
driver: JobDriver<R>,
}
pub struct Delta<R> {
driver: JobDriver<R>,
_sumset: Sumset,
}
pub struct Patch<'a, B: 'a, D> {
driver: JobDriver<D>,
base: Rc<RefCell<B>>,
raw: *mut Rc<RefCell<dyn ReadAndSeek + 'a>>,
}
struct Sumset(*mut raw::rs_signature_t);
trait ReadAndSeek: Read + Seek {}
impl<T: Read + Seek> ReadAndSeek for T {}
impl<R: Read> Signature<BufReader<R>> {
pub fn new(input: R) -> Result<Self> {
Self::with_options(input, raw::RS_DEFAULT_BLOCK_LEN, 0, SignatureType::Blake2)
}
pub fn with_options(
input: R,
block_len: usize,
strong_len: usize,
sig_magic: SignatureType,
) -> Result<Self> {
Self::with_buf_read(BufReader::new(input), block_len, strong_len, sig_magic)
}
}
impl<R: BufRead> Signature<R> {
pub fn with_buf_read(
input: R,
block_len: usize,
strong_len: usize,
sig_magic: SignatureType,
) -> Result<Self> {
logfwd::init();
let job = unsafe { raw::rs_sig_begin(block_len, strong_len, sig_magic.as_raw()) };
if job.is_null() {
return Err(Error::BadMagic);
}
Ok(Signature {
driver: JobDriver::new(input, Job(job)),
})
}
pub fn into_inner(self) -> R {
self.driver.into_inner()
}
}
impl<R: BufRead> Read for Signature<R> {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
self.driver.read(buf)
}
}
impl<R: Read> Delta<BufReader<R>> {
pub fn new<S: Read + ?Sized>(new: R, base_sig: &mut S) -> Result<Self> {
Self::with_buf_read(BufReader::new(new), base_sig)
}
}
impl<R: BufRead> Delta<R> {
pub fn with_buf_read<S: Read + ?Sized>(new: R, base_sig: &mut S) -> Result<Self> {
logfwd::init();
let sumset = unsafe {
let mut sumset = ptr::null_mut();
let job = raw::rs_loadsig_begin(&mut sumset);
assert!(!job.is_null());
let mut job = JobDriver::new(BufReader::new(base_sig), Job(job));
job.consume_input()?;
let sumset = Sumset(sumset);
let res = raw::rs_build_hash_table(*sumset);
if res != raw::RS_DONE {
return Err(Error::from(res));
}
sumset
};
let job = unsafe { raw::rs_delta_begin(*sumset) };
if job.is_null() {
return Err(io_err(
io::ErrorKind::InvalidData,
"invalid signature given",
));
}
Ok(Delta {
driver: JobDriver::new(new, Job(job)),
_sumset: sumset,
})
}
pub fn into_inner(self) -> R {
self.driver.into_inner()
}
}
impl<R: BufRead> Read for Delta<R> {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
self.driver.read(buf)
}
}
impl<'a, B: Read + Seek + 'a, D: Read> Patch<'a, B, BufReader<D>> {
pub fn new(base: B, delta: D) -> Result<Self> {
Self::with_buf_read(base, BufReader::new(delta))
}
}
impl<'a, B: Read + Seek + 'a, D: BufRead> Patch<'a, B, D> {
pub fn with_buf_read(base: B, delta: D) -> Result<Self> {
logfwd::init();
let base = Rc::new(RefCell::new(base));
let cb_data: Box<Rc<RefCell<dyn ReadAndSeek>>> = Box::new(base.clone());
let raw_ptr = Box::into_raw(cb_data);
let job = unsafe { raw::rs_patch_begin(patch_copy_cb, raw_ptr as *mut libc::c_void) };
assert!(!job.is_null());
Ok(Patch {
driver: JobDriver::new(delta, Job(job)),
base,
raw: raw_ptr,
})
}
pub fn into_inner(self) -> (B, D) {
let mut this = ManuallyDrop::new(self);
unsafe {
if !this.raw.is_null() {
let _ = Box::from_raw(this.raw);
this.raw = ptr::null_mut();
}
}
let base_rc = unsafe { ptr::read(&this.base) };
let base = match Rc::try_unwrap(base_rc) {
Ok(base) => base,
_ => unreachable!(),
};
let driver = unsafe { ptr::read(&this.driver) };
(base.into_inner(), driver.into_inner())
}
}
impl<'a, B, D: BufRead> Read for Patch<'a, B, D> {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
self.driver.read(buf)
}
}
impl<'a, B, D> Drop for Patch<'a, B, D> {
fn drop(&mut self) {
unsafe {
if !self.raw.is_null() {
let _ = Box::from_raw(self.raw);
}
}
}
}
unsafe impl<'a, B: 'a, D> Send for Patch<'a, B, D>
where
B: Send,
D: Send,
{
}
impl error::Error for Error {}
impl Display for Error {
fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
match *self {
Error::Io(ref e) => write!(fmt, "{}", e),
Error::Mem => write!(fmt, "out of memory"),
Error::BadMagic => write!(fmt, "bad magic number given"),
Error::Unimplemented => write!(fmt, "unimplemented feature"),
Error::Internal => write!(fmt, "internal error"),
Error::Unknown(n) => write!(fmt, "unknown error {} from native library", n),
}
}
}
impl From<io::Error> for Error {
fn from(err: io::Error) -> Error {
Error::Io(err)
}
}
impl From<raw::rs_result> for Error {
fn from(err: raw::rs_result) -> Error {
match err {
raw::RS_BLOCKED => io_err(io::ErrorKind::WouldBlock, "blocked waiting for more data"),
raw::RS_IO_ERROR => io_err(io::ErrorKind::Other, "unknown IO error from librsync"),
raw::RS_MEM_ERROR => Error::Mem,
raw::RS_INPUT_ENDED => {
io_err(io::ErrorKind::UnexpectedEof, "unexpected end of input file")
}
raw::RS_BAD_MAGIC => Error::BadMagic,
raw::RS_UNIMPLEMENTED => Error::Unimplemented,
raw::RS_CORRUPT => io_err(io::ErrorKind::InvalidData, "unbelievable value in stream"),
raw::RS_INTERNAL_ERROR => Error::Internal,
raw::RS_PARAM_ERROR => io_err(io::ErrorKind::InvalidInput, "bad parameter"),
n => Error::Unknown(n),
}
}
}
impl SignatureType {
fn as_raw(self) -> raw::rs_magic_number {
match self {
SignatureType::MD4 => raw::RS_MD4_SIG_MAGIC,
SignatureType::Blake2 => raw::RS_BLAKE2_SIG_MAGIC,
}
}
}
impl Drop for Sumset {
fn drop(&mut self) {
unsafe {
if !self.0.is_null() {
raw::rs_free_sumset(self.0);
}
}
}
}
impl Deref for Sumset {
type Target = *mut raw::rs_signature_t;
fn deref(&self) -> &Self::Target {
&self.0
}
}
unsafe impl Send for Sumset {}
extern "C" fn patch_copy_cb(
opaque: *mut libc::c_void,
pos: raw::rs_long_t,
len: *mut libc::size_t,
buf: *mut *mut libc::c_void,
) -> raw::rs_result {
let mut input: RefMut<dyn ReadAndSeek> = unsafe {
let h: *mut Rc<RefCell<dyn ReadAndSeek>> = mem::transmute(opaque);
(*h).borrow_mut()
};
let output = unsafe {
let buf: *mut u8 = mem::transmute(*buf);
slice::from_raw_parts_mut(buf, *len)
};
try_or_rs_error!(input.seek(io::SeekFrom::Start(pos as u64)));
try_or_rs_error!(input.read(output));
raw::RS_DONE
}
fn io_err<E>(kind: io::ErrorKind, e: E) -> Error
where
E: Into<Box<dyn error::Error + Send + Sync>>,
{
Error::Io(io::Error::new(kind, e))
}
#[cfg(test)]
mod test {
use super::*;
use std::io::{Cursor, Read};
use std::thread;
const DATA: &str = "this is a string to be tested";
const DATA2: &str = "this is another string to be tested";
fn data_signature() -> Vec<u8> {
vec![
0x72, 0x73, 0x01, 0x36, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x05, 0x1b, 0x21,
0x04, 0x8b, 0xad, 0x3c, 0xbd, 0x19, 0x09, 0x1d, 0x1b, 0x04, 0xf0, 0x9d, 0x1f, 0x64,
0x31, 0xde, 0x15, 0xf4, 0x04, 0x87, 0x60, 0x96, 0x19, 0x50, 0x39,
]
}
fn data2_delta() -> Vec<u8> {
vec![
0x72, 0x73, 0x02, 0x36, 0x10, 0x74, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x61,
0x6e, 0x6f, 0x74, 0x68, 0x65, 0x72, 0x20, 0x45, 0x0a, 0x13, 0x00,
]
}
#[test]
fn signature() {
let cursor = Cursor::new(DATA);
let mut sig = Signature::with_options(cursor, 10, 5, SignatureType::MD4).unwrap();
let mut signature = Vec::new();
let read = sig.read_to_end(&mut signature).unwrap();
assert_eq!(read, signature.len());
assert_eq!(signature, data_signature());
sig.into_inner();
}
#[test]
fn delta() {
let sig = data_signature();
let input = Cursor::new(DATA2);
let mut job = Delta::new(input, &mut Cursor::new(sig)).unwrap();
let mut delta = Vec::new();
let read = job.read_to_end(&mut delta).unwrap();
assert_eq!(read, delta.len());
assert_eq!(delta, data2_delta());
job.into_inner();
}
#[test]
fn patch() {
let base = Cursor::new(DATA);
let delta = data2_delta();
let delta = Cursor::new(delta);
let mut patch = Patch::new(base, delta).unwrap();
let mut computed_new = String::new();
patch.read_to_string(&mut computed_new).unwrap();
assert_eq!(computed_new, DATA2);
patch.into_inner();
}
#[test]
fn integration() {
let base = Cursor::new(DATA);
let new = Cursor::new(DATA2);
let mut sig = Signature::with_options(base, 10, 5, SignatureType::MD4).unwrap();
let delta = Delta::new(new, &mut sig).unwrap();
let base = Cursor::new(DATA);
let mut patch = Patch::new(base, delta).unwrap();
let mut computed_new = String::new();
patch.read_to_string(&mut computed_new).unwrap();
assert_eq!(computed_new, DATA2);
}
#[test]
fn send_sig() {
let cursor = Cursor::new(DATA);
let mut sig = Signature::new(cursor).unwrap();
let t = thread::spawn(move || {
let mut signature = Vec::new();
sig.read_to_end(&mut signature).unwrap();
});
t.join().unwrap();
}
#[test]
fn send_delta() {
let sig = data_signature();
let input = Cursor::new(DATA2);
let mut job = Delta::new(input, &mut Cursor::new(sig)).unwrap();
let t = thread::spawn(move || {
let mut delta = Vec::new();
job.read_to_end(&mut delta).unwrap();
});
t.join().unwrap();
}
#[test]
fn send_patch() {
let base = Cursor::new(DATA);
let delta = data2_delta();
let delta = Cursor::new(delta);
let mut patch = Patch::new(base, delta).unwrap();
let t = thread::spawn(move || {
let mut computed_new = String::new();
patch.read_to_string(&mut computed_new).unwrap();
});
t.join().unwrap();
}
#[test]
fn trivial_large_file() {
let data = vec![0; 65536];
let mut sig =
Signature::with_options(Cursor::new(&data), 16384, 5, SignatureType::MD4).unwrap();
let delta = Delta::new(Cursor::new(&data), &mut sig).unwrap();
let mut computed_new = vec![];
Patch::new(Cursor::new(&data), delta)
.unwrap()
.read_to_end(&mut computed_new)
.unwrap();
assert_eq!(computed_new, data);
}
}