#![cfg(feature = "gzip")]
use crate::{Chunk, ChunkIncr, ToChunkIncr};
use std::collections::VecDeque;
use std::num::Wrapping;
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct GzipRsyncable {
window_len: usize,
modulus: u64,
}
impl GzipRsyncable {
pub fn with_window_and_modulus(window: usize, modulus: u64) -> GzipRsyncable {
Self {
window_len: window,
modulus,
}
}
}
impl Default for GzipRsyncable {
fn default() -> Self {
Self::with_window_and_modulus(8192, 4096)
}
}
impl Chunk for GzipRsyncable {
type SearchState = GzipRsyncableSearchState;
fn to_search_state(&self) -> Self::SearchState {
Self::SearchState::default()
}
fn find_chunk_edge(
&self,
state: &mut Self::SearchState,
data: &[u8],
) -> (Option<usize>, usize) {
for i in state.offset..data.len() {
let v = data[i];
if state.state.add(data, self, i, v) {
state.reset();
return (Some(i + 1), i + 1);
}
}
let discard_ct = data.len().saturating_sub(self.window_len);
state.offset = data.len() - discard_ct;
(None, discard_ct)
}
}
impl From<&GzipRsyncable> for GzipRsyncableIncr {
fn from(src: &GzipRsyncable) -> Self {
src.clone().into()
}
}
impl ToChunkIncr for GzipRsyncable {
type Incr = GzipRsyncableIncr;
fn to_chunk_incr(&self) -> Self::Incr {
self.into()
}
}
#[derive(Debug, Default, Clone)]
struct GzipRsyncableState {
accum: Wrapping<u64>,
}
impl GzipRsyncableState {
fn reset(&mut self) {
self.accum.0 = 0;
}
}
#[derive(Debug, Default, Clone)]
pub struct GzipRsyncableSearchState {
offset: usize,
state: GzipRsyncableState,
}
impl GzipRsyncableSearchState {
fn reset(&mut self) {
self.offset = 0;
self.state.reset();
}
}
#[derive(Debug, Clone)]
pub struct GzipRsyncableIncr {
params: GzipRsyncable,
accum: Wrapping<u64>,
window: VecDeque<u8>,
}
impl GzipRsyncableIncr {
fn reset(&mut self) {
self.window.clear();
self.accum = Wrapping(0);
}
}
impl From<GzipRsyncable> for GzipRsyncableIncr {
fn from(params: GzipRsyncable) -> Self {
let window = VecDeque::with_capacity(params.window_len);
GzipRsyncableIncr {
params,
accum: Wrapping(0),
window,
}
}
}
impl GzipRsyncableState {
fn add(&mut self, data: &[u8], parent: &GzipRsyncable, i: usize, v: u8) -> bool {
if i >= parent.window_len {
self.accum -= Wrapping(data[i - parent.window_len] as u64);
}
self.accum += Wrapping(v as u64);
(self.accum % Wrapping(parent.modulus)).0 == 0
}
}
impl ChunkIncr for GzipRsyncableIncr {
fn push(&mut self, data: &[u8]) -> Option<usize> {
for (i, &v) in data.iter().enumerate() {
if self.window.len() >= self.params.window_len {
self.accum -= Wrapping(self.window.pop_front().unwrap() as u64);
}
self.accum += Wrapping(v as u64);
self.window.push_back(v);
if (self.accum % Wrapping(self.params.modulus)).0 == 0 {
self.reset();
return Some(i + 1);
}
}
None
}
}