driven/streaming/
xor_patcher.rs1use crate::{DrivenError, Result};
7
8#[derive(Debug, Clone)]
10pub struct XorPatch {
11 pub block_size: u32,
13 pub target_len: u32,
15 pub block_count: u32,
17 pub blocks: Vec<(u32, Vec<u8>)>,
19}
20
21impl XorPatch {
22 pub fn empty() -> Self {
24 Self {
25 block_size: 64,
26 target_len: 0,
27 block_count: 0,
28 blocks: Vec::new(),
29 }
30 }
31
32 pub fn apply(&self, original: &[u8]) -> Result<Vec<u8>> {
34 let mut result = original.to_vec();
35
36 for (block_idx, xor_data) in &self.blocks {
37 let start = (*block_idx as usize) * (self.block_size as usize);
38 let _end = (start + self.block_size as usize).min(result.len());
39
40 if start >= result.len() {
41 result.resize(start + xor_data.len(), 0);
43 }
44
45 for (i, &xor_byte) in xor_data.iter().enumerate() {
47 if start + i < result.len() {
48 result[start + i] ^= xor_byte;
49 }
50 }
51 }
52
53 result.resize(self.target_len as usize, 0);
55
56 Ok(result)
57 }
58
59 pub fn serialize(&self) -> Vec<u8> {
61 let mut output = Vec::new();
62
63 output.extend_from_slice(&self.block_size.to_le_bytes());
64 output.extend_from_slice(&self.target_len.to_le_bytes());
65 output.extend_from_slice(&self.block_count.to_le_bytes());
66
67 for (idx, data) in &self.blocks {
68 output.extend_from_slice(&idx.to_le_bytes());
69 output.extend_from_slice(&(data.len() as u32).to_le_bytes());
70 output.extend_from_slice(data);
71 }
72
73 output
74 }
75
76 pub fn deserialize(data: &[u8]) -> Result<Self> {
78 if data.len() < 12 {
79 return Err(DrivenError::InvalidBinary("XOR patch too small".into()));
80 }
81
82 let block_size = u32::from_le_bytes([data[0], data[1], data[2], data[3]]);
83 let target_len = u32::from_le_bytes([data[4], data[5], data[6], data[7]]);
84 let block_count = u32::from_le_bytes([data[8], data[9], data[10], data[11]]);
85
86 let mut blocks = Vec::with_capacity(block_count as usize);
87 let mut pos = 12;
88
89 for _ in 0..block_count {
90 if pos + 8 > data.len() {
91 return Err(DrivenError::InvalidBinary("Truncated XOR patch".into()));
92 }
93
94 let idx = u32::from_le_bytes([data[pos], data[pos + 1], data[pos + 2], data[pos + 3]]);
95 let len =
96 u32::from_le_bytes([data[pos + 4], data[pos + 5], data[pos + 6], data[pos + 7]]);
97 pos += 8;
98
99 if pos + len as usize > data.len() {
100 return Err(DrivenError::InvalidBinary("Truncated block data".into()));
101 }
102
103 blocks.push((idx, data[pos..pos + len as usize].to_vec()));
104 pos += len as usize;
105 }
106
107 Ok(Self {
108 block_size,
109 target_len,
110 block_count,
111 blocks,
112 })
113 }
114
115 pub fn is_empty(&self) -> bool {
117 self.blocks.is_empty()
118 }
119
120 pub fn savings(&self, original_size: usize) -> f32 {
122 let patch_size = self.serialize().len();
123 if original_size == 0 {
124 return 0.0;
125 }
126 1.0 - (patch_size as f32 / original_size as f32)
127 }
128}
129
130#[derive(Debug)]
132pub struct XorPatcher {
133 block_size: u32,
135}
136
137impl XorPatcher {
138 pub fn new(block_size: u32) -> Self {
140 Self { block_size }
141 }
142
143 pub fn compute(&self, old: &[u8], new: &[u8]) -> XorPatch {
145 let block_size = self.block_size as usize;
146 let max_len = old.len().max(new.len());
147 let num_blocks = max_len.div_ceil(block_size);
148
149 let mut blocks = Vec::new();
150
151 for block_idx in 0..num_blocks {
152 let start = block_idx * block_size;
153 let old_end = (start + block_size).min(old.len());
154 let new_end = (start + block_size).min(new.len());
155
156 let old_block = if start < old.len() {
157 &old[start..old_end]
158 } else {
159 &[]
160 };
161
162 let new_block = if start < new.len() {
163 &new[start..new_end]
164 } else {
165 &[]
166 };
167
168 if old_block != new_block {
170 let mut xor_data = vec![0u8; block_size];
172 for (i, &new_byte) in new_block.iter().enumerate() {
173 let old_byte = old_block.get(i).copied().unwrap_or(0);
174 xor_data[i] = old_byte ^ new_byte;
175 }
176
177 for i in old_block.len()..new_block.len() {
179 xor_data[i] = new_block[i];
180 }
181
182 while xor_data.last() == Some(&0) {
184 xor_data.pop();
185 }
186
187 if !xor_data.is_empty() {
188 blocks.push((block_idx as u32, xor_data));
189 }
190 }
191 }
192
193 XorPatch {
194 block_size: self.block_size,
195 target_len: new.len() as u32,
196 block_count: blocks.len() as u32,
197 blocks,
198 }
199 }
200
201 pub fn is_identical(&self, old: &[u8], new: &[u8]) -> bool {
203 old == new
204 }
205}
206
207impl Default for XorPatcher {
208 fn default() -> Self {
209 Self::new(64)
210 }
211}
212
213#[cfg(test)]
214mod tests {
215 use super::*;
216
217 #[test]
218 fn test_xor_patch_roundtrip() {
219 let old = b"Hello, World! This is a test.";
220 let new = b"Hello, Rust! This is a test.";
221
222 let patcher = XorPatcher::new(8);
223 let patch = patcher.compute(old, new);
224
225 assert!(!patch.is_empty());
227
228 let result = patch.apply(old).unwrap();
230 assert_eq!(result, new);
231 }
232
233 #[test]
234 fn test_identical_files() {
235 let data = b"Same content";
236 let patcher = XorPatcher::new(8);
237
238 assert!(patcher.is_identical(data, data));
239
240 let patch = patcher.compute(data, data);
241 assert!(patch.is_empty());
242 }
243
244 #[test]
245 fn test_savings() {
246 let old = vec![0u8; 1000];
247 let mut new = old.clone();
248 new[500] = 1; let patcher = XorPatcher::new(64);
251 let patch = patcher.compute(&old, &new);
252
253 let savings = patch.savings(old.len());
255 assert!(savings > 0.9); }
257}