1use crate::DCPError;
7use blake3;
8
9#[derive(Debug, Clone, PartialEq, Eq)]
11pub struct XorDelta {
12 pub prev_hash: [u8; 32],
14 pub new_hash: [u8; 32],
16 pub patch: Vec<u8>,
18 pub new_len: usize,
20}
21
22impl XorDelta {
23 pub fn compute(prev: &[u8], new: &[u8]) -> Self {
25 let max_len = prev.len().max(new.len());
27 let mut xor = Vec::with_capacity(max_len);
28
29 for i in 0..max_len {
30 let prev_byte = prev.get(i).copied().unwrap_or(0);
31 let new_byte = new.get(i).copied().unwrap_or(0);
32 xor.push(prev_byte ^ new_byte);
33 }
34
35 Self {
36 prev_hash: *blake3::hash(prev).as_bytes(),
37 new_hash: *blake3::hash(new).as_bytes(),
38 patch: rle_compress(&xor),
39 new_len: new.len(),
40 }
41 }
42
43 pub fn apply(&self, state: &[u8]) -> Result<Vec<u8>, DCPError> {
46 let current_hash = *blake3::hash(state).as_bytes();
48 if current_hash != self.prev_hash {
49 return Err(DCPError::HashMismatch);
50 }
51
52 let xor = rle_decompress(&self.patch);
54
55 let mut new_state = Vec::with_capacity(self.new_len);
57 for i in 0..self.new_len {
58 let state_byte = state.get(i).copied().unwrap_or(0);
59 let xor_byte = xor.get(i).copied().unwrap_or(0);
60 new_state.push(state_byte ^ xor_byte);
61 }
62
63 let result_hash = *blake3::hash(&new_state).as_bytes();
65 if result_hash != self.new_hash {
66 return Err(DCPError::HashMismatch);
67 }
68
69 Ok(new_state)
70 }
71
72 pub fn patch_size(&self) -> usize {
74 self.patch.len()
75 }
76
77 pub fn is_sparse(&self, full_state_size: usize) -> bool {
80 self.patch.len() < full_state_size
81 }
82
83 pub fn verify_prev_hash(&self, state: &[u8]) -> bool {
85 let current_hash = *blake3::hash(state).as_bytes();
86 current_hash == self.prev_hash
87 }
88}
89
90fn rle_compress(data: &[u8]) -> Vec<u8> {
94 if data.is_empty() {
95 return Vec::new();
96 }
97
98 let mut result = Vec::new();
99 let mut i = 0;
100
101 while i < data.len() {
102 let value = data[i];
103 let mut count = 1u8;
104
105 while i + (count as usize) < data.len()
107 && data[i + (count as usize)] == value
108 && count < 255
109 {
110 count += 1;
111 }
112
113 result.push(count);
114 result.push(value);
115 i += count as usize;
116 }
117
118 result
119}
120
121fn rle_decompress(data: &[u8]) -> Vec<u8> {
123 let mut result = Vec::new();
124 let mut i = 0;
125
126 while i + 1 < data.len() {
127 let count = data[i] as usize;
128 let value = data[i + 1];
129
130 for _ in 0..count {
131 result.push(value);
132 }
133
134 i += 2;
135 }
136
137 result
138}
139
140#[cfg(test)]
141mod tests {
142 use super::*;
143
144 #[test]
145 fn test_rle_compress_decompress() {
146 let data = vec![0, 0, 0, 1, 1, 2, 2, 2, 2, 0];
147 let compressed = rle_compress(&data);
148 let decompressed = rle_decompress(&compressed);
149 assert_eq!(decompressed, data);
150 }
151
152 #[test]
153 fn test_rle_empty() {
154 let data: Vec<u8> = vec![];
155 let compressed = rle_compress(&data);
156 let decompressed = rle_decompress(&compressed);
157 assert!(compressed.is_empty());
158 assert!(decompressed.is_empty());
159 }
160
161 #[test]
162 fn test_rle_single_byte() {
163 let data = vec![42];
164 let compressed = rle_compress(&data);
165 let decompressed = rle_decompress(&compressed);
166 assert_eq!(decompressed, data);
167 }
168
169 #[test]
170 fn test_rle_long_run() {
171 let data = vec![0u8; 300];
173 let compressed = rle_compress(&data);
174 let decompressed = rle_decompress(&compressed);
175 assert_eq!(decompressed, data);
176 assert_eq!(compressed.len(), 4);
178 }
179
180 #[test]
181 fn test_delta_basic() {
182 let prev = b"hello world";
183 let new = b"hello rust!";
184
185 let delta = XorDelta::compute(prev, new);
186 let result = delta.apply(prev).unwrap();
187
188 assert_eq!(result, new);
189 }
190
191 #[test]
192 fn test_delta_sparse_change() {
193 let mut prev = vec![0u8; 1000];
195 let mut new = prev.clone();
196 new[500] = 1; let delta = XorDelta::compute(&prev, &new);
199
200 assert!(delta.is_sparse(new.len()));
202
203 let result = delta.apply(&prev).unwrap();
204 assert_eq!(result, new);
205 }
206
207 #[test]
208 fn test_delta_hash_mismatch() {
209 let prev = b"hello";
210 let new = b"world";
211
212 let delta = XorDelta::compute(prev, new);
213
214 let wrong_state = b"wrong";
216 let result = delta.apply(wrong_state);
217
218 assert_eq!(result, Err(DCPError::HashMismatch));
219 }
220
221 #[test]
222 fn test_delta_different_lengths() {
223 let prev = b"short";
224 let new = b"much longer string";
225
226 let delta = XorDelta::compute(prev, new);
227 let result = delta.apply(prev).unwrap();
228
229 assert_eq!(result, new);
230 }
231
232 #[test]
233 fn test_delta_shrinking() {
234 let prev = b"much longer string";
235 let new = b"short";
236
237 let delta = XorDelta::compute(prev, new);
238 let result = delta.apply(prev).unwrap();
239
240 assert_eq!(result, new);
241 }
242
243 #[test]
244 fn test_delta_empty_states() {
245 let prev: &[u8] = b"";
246 let new = b"new data";
247
248 let delta = XorDelta::compute(prev, new);
249 let result = delta.apply(prev).unwrap();
250
251 assert_eq!(result, new);
252 }
253
254 #[test]
255 fn test_delta_to_empty() {
256 let prev = b"old data";
257 let new: &[u8] = b"";
258
259 let delta = XorDelta::compute(prev, new);
260 let result = delta.apply(prev).unwrap();
261
262 assert_eq!(result, new);
263 }
264
265 #[test]
266 fn test_verify_prev_hash() {
267 let prev = b"hello";
268 let new = b"world";
269
270 let delta = XorDelta::compute(prev, new);
271
272 assert!(delta.verify_prev_hash(prev));
273 assert!(!delta.verify_prev_hash(new));
274 }
275}