#![feature(nll)]
extern crate crc;
extern crate adler32;
use std::collections::HashMap;
use adler32::RollingAdler32;
use crc::crc32::{Digest, IEEE};
use crc::Hasher32;
pub struct RSync{
size: usize
}
impl RSync{
pub fn new(block_size: usize) -> RSync{
RSync{
size: match block_size{
0 => 64,
_ => block_size
}
}
}
pub fn check_sum(&self, data: &[u8]) -> Vec<CheckSum>{
let data_len = data.len();
let block_size = self.size; let mut block_index: usize = 0;
let mut start = 0;
let mut end = block_size;
if end > data_len {
end = data_len;
}
let mut results: Vec<CheckSum> = Vec::new(); let mut result;
let mut d = Digest::new(IEEE);
while start < data_len{
let chunk = &data[start..end];
d.reset();
d.write(chunk);
let weak = RollingAdler32::from_buffer(chunk).hash(); let strong = d.sum32();
result = CheckSum{weak: weak, strong: strong, index: block_index};
results.push(result);
block_index += 1;
start += block_size;
end += block_size;
if end > data_len {
end = data_len;
}
}
return results;
}
pub fn diff(&self, new_data: &[u8], old_check_sums: Vec<CheckSum>) -> Vec<Diff>{
let mut results = Vec::new();
let block_size = self.size; let len = new_data.len();
let mut start = 0;
let mut end = block_size;
if end > len {
end = len;
}
let mut hashtable = create_hashtable(old_check_sums);
let mut crc = Digest::new(IEEE);
let mut last_matched_end = 0;
let mut prev_rolling_weak: Option<RollingAdler32> = None;
let mut weak;
while end <= len {
match &mut prev_rolling_weak {
&mut Some(ref mut v) => {
v.remove(block_size, new_data[start - 1]);
v.update(new_data[end]);
weak = v.hash();
},
None => {let v = RollingAdler32::from_buffer(&new_data[start..end]);
weak = v.hash();
},
};
let check_sums = hashtable.get_mut(&weak);
let mut match_check_sum: Option<&CheckSum> = None;
let mut strong_number: Option<u32> = None;
if let Some(check_sums) = check_sums {
for check_sum in check_sums.iter() {
let might_match = check_sum; let strong = match strong_number {
Some(r) => r,
None => {
let chunk = &new_data[start..end];
crc.reset();
crc.write(chunk);
let r = crc.sum32();
strong_number = Some(r);
r
}
};
if might_match.strong == strong {
match_check_sum = Some(&might_match);
break;
};
}
}
match match_check_sum {
Some(r) => {
let d = match start > last_matched_end {
true => Some(Vec::from(&new_data[last_matched_end..start])),
false => None,
};
results.push(Diff{index: Some(r.index), data: d});
last_matched_end = end;
start = end;
end += block_size;
prev_rolling_weak = None;
},
None => {
start += 1;
end += 1;
}
}
}
if last_matched_end < len {
results.push(Diff{
index: None,
data: Some(Vec::from(&new_data[last_matched_end..len]))
});
}
results
}
pub fn sync(&self, old_data: &[u8], diffs: Vec<Diff>) -> Vec<u8>{
let mut synced = Vec::new(); for chunk in diffs.into_iter() {
match chunk.data {
Some(v) => {
synced.extend_from_slice(&v);
if let Some(i) = chunk.index {
synced.extend_from_slice(rawslice(old_data, i, self.size));
}
},
None => synced.extend_from_slice(rawslice(old_data, chunk.index.unwrap(), self.size))
}
}
return synced;
}
}
pub struct CheckSum{
index: usize,
weak: u32,
strong: u32,
}
pub struct Diff{
index: Option<usize>,
data: Option<Vec<u8>>,
}
fn create_hashtable(check_sums: Vec<CheckSum>) -> HashMap<u32, Vec<CheckSum>> {
let mut map: HashMap<u32, Vec<CheckSum>> = HashMap::new();
for check_sum in check_sums.into_iter() {
let weak = check_sum.weak;
match map.get_mut(&weak) {
Some(arr) => arr.push(check_sum),
None => {map.insert(weak, vec![check_sum]);},
};
}
map
}
fn rawslice(raw: &[u8], index: usize, chunk_size: usize) -> &[u8] {
let start = index*chunk_size;
let len = raw.len();
let end = match start + chunk_size > len{
true => len,
false => start + chunk_size,
};
&raw[start..end]
}
#[test]
fn test(){
let str1 = "qwertyuiopasdfghjklzxcvbnmrrrrrrrrrrrefregrtytfessfrer";
let str2 = "qzetyuioasdfgzjklzxcwbkmrerrrrrrrrrrrtghgtrrrrrfgsfrer";
let bytes1 = String::from(str1).into_bytes();
let bytes2 = String::from(str2).into_bytes();
let rsync = RSync::new(5);
let check_sums = rsync.check_sum(&bytes1);
let diffs = rsync.diff(&bytes2, check_sums);
let r = rsync.sync(&bytes1, diffs);
let s = String::from_utf8(r).expect("-----------------------------");
assert_eq!(&s, str2);
}