bt_diff/
core.rs

1use std::io::Write;
2
3use enum_to_string::EnumToString;
4use flate2::write::{DeflateDecoder, DeflateEncoder};
5use flate2::Compression;
6use serde::{Deserialize, Serialize};
7
8use crate::Result;
9
10pub const DEFAULT_AXIS_BOUNDARY_LEN: usize = 36;
11
12#[derive(EnumToString, Clone, Copy, Deserialize, Serialize)]
13pub enum DiffType {
14    Binary,
15    Text,
16}
17#[derive(Debug, Clone, Copy, Deserialize, Serialize, Hash, PartialEq, PartialOrd, Eq, Ord)]
18pub enum LineTermination {
19    CR,
20    LF,
21    CRLF,
22}
23impl Default for LineTermination {
24    fn default() -> LineTermination {
25        LineTermination::CRLF
26    }
27}
28
29#[derive(Debug, Clone, Deserialize, Serialize, Hash, PartialEq, PartialOrd, Ord, Eq)]
30pub enum AxisBoundary {
31    Len(usize),
32    // LineTermination(LineTermination),
33    // Byte(u8),
34    // Pair([u8; 2]),
35    // Sequence(Vec<u8>),
36}
37impl Default for AxisBoundary {
38    fn default() -> AxisBoundary {
39        AxisBoundary::Len(DEFAULT_AXIS_BOUNDARY_LEN)
40    }
41}
42
43#[derive(Debug, Clone, Deserialize, Serialize, Hash, PartialEq, PartialOrd, Eq, Ord)]
44pub struct DiffSettings {
45    pub axis_boundary: AxisBoundary,
46}
47impl Default for DiffSettings {
48    fn default() -> DiffSettings {
49        DiffSettings {
50            axis_boundary: AxisBoundary::default(),
51        }
52    }
53}
54
55#[derive(Debug, Clone, Deserialize, Serialize, Hash, PartialEq, PartialOrd, Eq, Ord)]
56pub struct DiffUnit {
57    pub x: usize,
58    pub y: usize,
59    pub anterior: Vec<u8>,
60    pub current: Vec<u8>,
61}
62impl DiffUnit {
63    pub fn new(anterior: Option<u8>, current: Option<u8>, x: usize, y: usize) -> DiffUnit {
64        DiffUnit {
65            x,
66            y,
67            anterior: anterior.map(|byte| vec![byte]).unwrap_or_default(),
68            current: current.map(|byte| vec![byte]).unwrap_or_default(),
69        }
70    }
71
72    pub fn anterior(&self) -> Option<u8> {
73        self.anterior.get(0).copied()
74    }
75
76    pub fn current(&self) -> Option<u8> {
77        self.current.get(0).copied()
78    }
79}
80
81#[derive(Clone, Deserialize, Serialize, Hash, PartialEq, PartialOrd, Eq, Ord, Debug)]
82pub struct Diff {
83    pub sequence: Vec<DiffUnit>,
84    pub settings: DiffSettings,
85}
86impl Default for Diff {
87    fn default() -> Diff {
88        Diff {
89            sequence: Vec::new(),
90            settings: DiffSettings::default(),
91        }
92    }
93}
94impl Diff {
95    pub fn new(axis_boundary: AxisBoundary) -> Diff {
96        Diff {
97            sequence: Vec::new(),
98            settings: DiffSettings { axis_boundary },
99        }
100    }
101
102    pub fn to_bytes(&self) -> Result<Vec<u8>> {
103        let bytes = bincode::serialize(self)?;
104        Ok(bytes)
105    }
106
107    pub fn from_bytes(bytes: &[u8]) -> Result<Diff> {
108        let diff: Diff = bincode::deserialize(bytes)?;
109        Ok(diff)
110    }
111
112    pub fn to_flate_bytes(&self) -> Result<Vec<u8>> {
113        let mut e = DeflateEncoder::new(Vec::new(), Compression::best());
114        e.write(&self.to_bytes()?)?;
115        Ok(e.finish()?)
116    }
117
118    pub fn from_deflate_bytes(bytes: &[u8]) -> Result<Diff> {
119        let mut d = DeflateDecoder::new(Vec::new());
120        d.write(bytes)?;
121        let deflated = d.finish()?;
122        Ok(Diff::from_bytes(&deflated)?)
123    }
124
125    pub fn anterior_version(&self) -> Vec<u8> {
126        self.sequence
127            .iter()
128            .filter(|unit| unit.anterior().is_some())
129            .map(|unit| unit.anterior().unwrap())
130            .collect()
131    }
132
133    pub fn current_version(&self) -> Vec<u8> {
134        self.sequence
135            .iter()
136            .filter(|unit| unit.current().is_some())
137            .map(|unit| unit.current().unwrap())
138            .collect()
139    }
140
141    pub fn update(&mut self, data: &[u8]) -> Result<Vec<u8>> {
142        let anterior = self.anterior_version();
143        let current = self.current_version();
144        let diff = diff(&current, data, self.axis_boundary())?;
145        self.sequence = diff.sequence.clone();
146        Ok(anterior)
147    }
148
149    pub fn axis_boundary(&self) -> AxisBoundary {
150        self.settings.axis_boundary.clone()
151    }
152
153    pub fn digest_anterior_version<A: digest::Digest>(&self) -> Vec<u8> {
154        let mut sha3 = A::new();
155        sha3.update(&self.anterior_version());
156        sha3.finalize().to_vec()
157    }
158
159    pub fn digest_current_version<A: digest::Digest>(&self) -> Vec<u8> {
160        let mut sha3 = A::new();
161        sha3.update(&self.current_version());
162        sha3.finalize().to_vec()
163    }
164
165    pub fn digest<A: digest::Digest>(&self) -> Vec<u8> {
166        xor(
167            &self.digest_anterior_version::<A>().to_vec(),
168            &self.digest_current_version::<A>().to_vec(),
169        )
170    }
171
172    pub fn render(&self) -> String {
173        let mut chunks = Vec::<String>::new();
174
175        for (index, chunk) in match self.settings.axis_boundary {
176            AxisBoundary::Len(length) => self
177                .sequence
178                .clone()
179                .chunks(length)
180                .map(|units| {
181                    units
182                        .iter()
183                        .map(|unit| unit.clone())
184                        .collect::<Vec<DiffUnit>>()
185                })
186                .collect::<Vec<Vec<DiffUnit>>>(),
187        }
188        .iter()
189        .enumerate()
190        {
191            let mut temp_chunks = Vec::<Vec<String>>::new();
192            let mut changes = false;
193            for unit in chunk {
194                let anterior = unit.anterior();
195                let current = unit.current();
196                if anterior != current {
197                    changes = true;
198                    temp_chunks.push(vec![
199                        format!("\x1b[0;31m0x{:02x}\x1b[0m", anterior.unwrap_or_default()),
200                        format!("\x1b[0;32m0x{:02x}\x1b[0m", current.unwrap_or_default()),
201                    ]);
202                } else {
203                    temp_chunks.push(vec![format!(
204                        "0x{:02x}",
205                        anterior.or(current).unwrap_or_default()
206                    )]);
207                }
208            }
209            if changes {
210                chunks.push(format!(
211                    "\x1b[0;31m-0x{:07x}\x1b[0m {}",
212                    index,
213                    temp_chunks
214                        .iter()
215                        .map(|chunk| chunk[0].to_string())
216                        .collect::<Vec<String>>()
217                        .join(" ")
218                ));
219                chunks.push(format!(
220                    "\x1b[0;32m+0x{:07x}\x1b[0m {}",
221                    index,
222                    temp_chunks
223                        .iter()
224                        .map(|chunk| if chunk.len() > 1 {
225                            chunk[1].to_string()
226                        } else {
227                            chunk[0].to_string()
228                        }
229                        .to_string())
230                        .collect::<Vec<String>>()
231                        .join(" ")
232                ));
233            } else {
234                chunks.push(format!(
235                    " 0x{:07x} {}",
236                    index,
237                    temp_chunks
238                        .iter()
239                        .map(|chunk| chunk.join(""))
240                        .collect::<Vec<String>>()
241                        .join(" ")
242                ));
243            }
244        }
245        chunks.join("\n")
246    }
247}
248
249pub fn diff(anterior: &[u8], current: &[u8], axis_boundary: AxisBoundary) -> Result<Diff> {
250    match axis_boundary {
251        AxisBoundary::Len(len) => diff_len(anterior, current, len),
252        // AxisBoundary::LineTermination(_) => Err(Error::DecodingError(format!("not implemented"))),
253    }
254}
255
256pub fn diff_len(anterior: &[u8], current: &[u8], axis_boundary_len: usize) -> Result<Diff> {
257    let mut diff = Vec::<DiffUnit>::new();
258    for (y, chunk) in zip_chunked(anterior, current, axis_boundary_len)
259        .iter()
260        .enumerate()
261    {
262        for (x, (anterior, current)) in chunk.iter().enumerate() {
263            let anterior = *anterior;
264            let current = *current;
265            diff.push(DiffUnit::new(anterior, current, x, y));
266        }
267    }
268    Ok(Diff {
269        sequence: diff,
270        settings: DiffSettings {
271            axis_boundary: AxisBoundary::Len(axis_boundary_len),
272        },
273    })
274}
275
276fn optional_max_bytes(items: &[u8], chunk_size: usize) -> Vec<Option<u8>> {
277    let rem = rem(items, chunk_size);
278    let mut items = items
279        .iter()
280        .map(|byte| Some(*byte))
281        .collect::<Vec<Option<u8>>>();
282    for _ in 0..rem {
283        items.push(None)
284    }
285    items
286}
287
288fn zip_max_modulus(x: &[u8], y: &[u8], chunk_size: usize) -> Vec<(Option<u8>, Option<u8>)> {
289    let max_len = [x.len(), y.len()].iter().max().map(|n| *n).unwrap_or(0);
290    let mut x = optional_max_bytes(x, chunk_size);
291    if x.len() < max_len {
292        for _ in 0..(max_len - x.len()) {
293            x.push(None)
294        }
295    }
296    let mut y = optional_max_bytes(y, chunk_size);
297    if y.len() < max_len {
298        for _ in 0..(max_len - y.len()) {
299            y.push(None)
300        }
301    }
302    x.iter()
303        .zip(y)
304        .map(|(x, y)| (*x, y))
305        .collect::<Vec<(Option<u8>, Option<u8>)>>()
306}
307
308fn zip_chunked(x: &[u8], y: &[u8], chunk_size: usize) -> Vec<Vec<(Option<u8>, Option<u8>)>> {
309    let mut chunked = Vec::<Vec<(Option<u8>, Option<u8>)>>::new();
310    for chunk in zip_max_modulus(x, y, chunk_size).chunks(chunk_size) {
311        let tchunks = chunk
312            .iter()
313            .map(|possible| *possible)
314            .collect::<Vec<(Option<u8>, Option<u8>)>>();
315        chunked.push(tchunks);
316    }
317    chunked
318}
319pub(crate) fn rem(items: &[u8], chunk_size: usize) -> usize {
320    if items.len() > chunk_size {
321        items.len() % chunk_size
322    } else if items.len() > 0 && chunk_size > items.len() {
323        chunk_size % items.len()
324    } else {
325        0
326    }
327}
328
329#[cfg(test)]
330mod tests {
331    use super::*;
332
333    #[test]
334    fn test_rem() {
335        let data = vec![0x01, 0x10, 0xF1, 0x61];
336        assert_eq!(rem(&data, 6), 2);
337        let data = vec![0x01, 0x10, 0xF1, 0x61, 0x01, 0x10, 0xF1, 0x61];
338        assert_eq!(rem(&data, 6), 2);
339        let data = vec![];
340        assert_eq!(rem(&data, 6), 0);
341    }
342
343    #[test]
344    fn test_optional_max_bytes() {
345        let anterior = vec![
346            0x00, 0x01, 0x02, 0x03, 0x10, 0x11, 0x12, 0x13, 0x20, 0x21, 0x23, 0x30, 0x31, 0x32,
347            0x33,
348        ];
349
350        assert_eq!(
351            optional_max_bytes(&anterior, 4),
352            vec![
353                Some(0x00),
354                Some(0x01),
355                Some(0x02),
356                Some(0x03),
357                Some(0x10),
358                Some(0x11),
359                Some(0x12),
360                Some(0x13),
361                Some(0x20),
362                Some(0x21),
363                Some(0x23),
364                Some(0x30),
365                Some(0x31),
366                Some(0x32),
367                Some(0x33),
368                None,
369                None,
370                None,
371            ],
372        )
373    }
374
375    #[test]
376    fn test_zip_max_modulus() {
377        let anterior = vec![0x00, 0x01, 0x02, 0x03, 0x10, 0x11, 0x12];
378        let current = vec![0x13, 0x20, 0x21, 0x23, 0x30, 0x31, 0x32, 0x33];
379
380        assert_eq!(
381            zip_max_modulus(&anterior, &current, 4),
382            vec![
383                (Some(0x00), Some(0x13)),
384                (Some(0x01), Some(0x20)),
385                (Some(0x02), Some(0x21)),
386                (Some(0x03), Some(0x23)),
387                (Some(0x10), Some(0x30)),
388                (Some(0x11), Some(0x31)),
389                (Some(0x12), Some(0x32)),
390                (None, Some(0x33)),
391            ]
392        )
393    }
394    #[test]
395    fn test_zip_chunked_max_len() {
396        let anterior = vec![0x00, 0x00, 0x00, 0x00, 0x00, 0x00];
397        let current = vec![0x00, 0x00, 0x02];
398        assert_eq!(
399            zip_chunked(&anterior, &current, 2),
400            vec![
401                vec![(Some(0x00), Some(0x00)), (Some(0x00), Some(0x00))],
402                vec![(Some(0x00), Some(0x02)), (Some(0x00), None)],
403                vec![(Some(0x00), None), (Some(0x00), None)],
404            ]
405        );
406    }
407    #[test]
408    fn test_zip_chunked() {
409        let anterior = vec![0x00, 0x01, 0x02, 0x03, 0x10, 0x11, 0x12];
410        let current = vec![0x13, 0x20, 0x21, 0x23, 0x30, 0x31, 0x32, 0x33];
411
412        assert_eq!(
413            zip_chunked(&anterior, &current, 4),
414            vec![
415                vec![
416                    (Some(0x00), Some(0x13)),
417                    (Some(0x01), Some(0x20)),
418                    (Some(0x02), Some(0x21)),
419                    (Some(0x03), Some(0x23)),
420                ],
421                vec![
422                    (Some(0x10), Some(0x30)),
423                    (Some(0x11), Some(0x31)),
424                    (Some(0x12), Some(0x32)),
425                    (None, Some(0x33))
426                ]
427            ]
428        )
429    }
430}
431
432pub fn xor(a: &Vec<u8>, b: &Vec<u8>) -> Vec<u8> {
433    a.into_iter().zip(b.iter()).map(|(a, b)| a ^ b).collect()
434}