1#![cfg(feature = "gzip")]
2
3use crate::{Chunk, ChunkIncr, ToChunkIncr};
50use std::collections::VecDeque;
51use std::num::Wrapping;
52
53#[derive(Clone, Debug, PartialEq, Eq)]
55pub struct GzipRsyncable {
56 window_len: usize,
60 modulus: u64,
61}
62
63impl GzipRsyncable {
64 pub fn with_window_and_modulus(window: usize, modulus: u64) -> GzipRsyncable {
65 Self {
66 window_len: window,
67 modulus,
68 }
69 }
70}
71
72impl Default for GzipRsyncable {
73 fn default() -> Self {
74 Self::with_window_and_modulus(8192, 4096)
75 }
76}
77
78impl Chunk for GzipRsyncable {
79 type SearchState = GzipRsyncableSearchState;
80
81 fn to_search_state(&self) -> Self::SearchState {
82 Self::SearchState::default()
83 }
84
85 fn find_chunk_edge(
86 &self,
87 state: &mut Self::SearchState,
88 data: &[u8],
89 ) -> (Option<usize>, usize) {
90 for i in state.offset..data.len() {
91 let v = data[i];
92
93 if state.state.add(data, self, i, v) {
94 state.reset();
95 return (Some(i + 1), i + 1);
96 }
97 }
98
99 let discard_ct = data.len().saturating_sub(self.window_len);
101 state.offset = data.len() - discard_ct;
102 (None, discard_ct)
103 }
104}
105
106impl From<&GzipRsyncable> for GzipRsyncableIncr {
107 fn from(src: &GzipRsyncable) -> Self {
108 src.clone().into()
109 }
110}
111
112impl ToChunkIncr for GzipRsyncable {
113 type Incr = GzipRsyncableIncr;
114 fn to_chunk_incr(&self) -> Self::Incr {
115 self.into()
116 }
117}
118
119#[derive(Debug, Default, Clone)]
120struct GzipRsyncableState {
121 accum: Wrapping<u64>,
122}
123
124impl GzipRsyncableState {
125 fn reset(&mut self) {
126 self.accum.0 = 0;
127 }
128}
129
130#[derive(Debug, Default, Clone)]
134pub struct GzipRsyncableSearchState {
135 offset: usize,
136 state: GzipRsyncableState,
137}
138
139impl GzipRsyncableSearchState {
140 fn reset(&mut self) {
141 self.offset = 0;
142 self.state.reset();
143 }
144}
145
146#[derive(Debug, Clone)]
154pub struct GzipRsyncableIncr {
155 params: GzipRsyncable,
156
157 accum: Wrapping<u64>,
158 window: VecDeque<u8>,
160}
161
162impl GzipRsyncableIncr {
163 fn reset(&mut self) {
164 self.window.clear();
165 self.accum = Wrapping(0);
166 }
167}
168
169impl From<GzipRsyncable> for GzipRsyncableIncr {
170 fn from(params: GzipRsyncable) -> Self {
171 let window = VecDeque::with_capacity(params.window_len);
172 GzipRsyncableIncr {
173 params,
174 accum: Wrapping(0),
175 window,
176 }
177 }
178}
179
180impl GzipRsyncableState {
181 fn add(&mut self, data: &[u8], parent: &GzipRsyncable, i: usize, v: u8) -> bool {
182 if i >= parent.window_len {
183 self.accum -= Wrapping(data[i - parent.window_len] as u64);
184 }
185 self.accum += Wrapping(v as u64);
186 (self.accum % Wrapping(parent.modulus)).0 == 0
187 }
188}
189
190impl ChunkIncr for GzipRsyncableIncr {
191 fn push(&mut self, data: &[u8]) -> Option<usize> {
192 for (i, &v) in data.iter().enumerate() {
193 if self.window.len() >= self.params.window_len {
194 self.accum -= Wrapping(self.window.pop_front().unwrap() as u64);
195 }
196
197 self.accum += Wrapping(v as u64);
198 self.window.push_back(v);
199
200 if (self.accum % Wrapping(self.params.modulus)).0 == 0 {
201 self.reset();
202 return Some(i + 1);
203 }
204 }
205
206 None
207 }
208}