diff_in_place/
lib.rs

1#![no_std]
2
3pub trait DiffInPlace<T, const N: usize>
4where
5    T: PartialEq,
6{
7    /// Fallible version of `diff_in_place` for propagating errors.
8    /// Perform an in-place diff between two const-size arrays, invoking the given function
9    /// for each run of different elements, with the index into the array and
10    /// the slice of different elements from the other array.
11    ///
12    ///
13    /// # Arguments
14    /// * `other`   - The other array to compare against.
15    /// * `func`    - The function to call for each run of different elements.
16    ///
17    /// # Example
18    /// ```
19    ///     use diff_in_place::DiffInPlace;
20    ///     let a = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
21    ///     let mut b = [0, 0, 1, 2, 0, 0, 0, 3, 4, 5];
22    ///
23    ///     a.try_diff_in_place(&b, |idx, diff| -> Result<(), ()> {
24    ///         // println!("{}: {:?}", idx, diff);
25    ///         // Prints:
26    ///         // 2: [1, 2]
27    ///         // 7: [3, 4, 5]
28    ///         Ok(())
29    ///     }).unwrap();
30    /// ```
31    fn try_diff_in_place<F, R>(&self, other: &[T; N], func: F) -> Result<(), R>
32    where
33        F: FnMut(usize, &[T]) -> Result<(), R>;
34
35    /// Perform an in-place diff between two const-size arrays, invoking the given function
36    /// for each run of different elements, with the index into the array and
37    /// the slice of different elements from the other array.
38    ///
39    /// # Arguments
40    /// * `other`   - The other array to compare against.
41    /// * `func`    - The function to call for each run of different elements.
42    ///
43    /// # Example
44    /// ```
45    ///     use diff_in_place::DiffInPlace;
46    ///     let a = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
47    ///     let mut b = [0, 0, 1, 2, 0, 0, 0, 3, 4, 5];
48    ///
49    ///     a.diff_in_place(&b, |idx, diff| {
50    ///         // println!("{}: {:?}", idx, diff);
51    ///         // Prints:
52    ///         // 2: [1, 2]
53    ///         // 7: [3, 4, 5]
54    ///     });
55    /// ```
56    fn diff_in_place<F>(&self, other: &[T; N], mut func: F)
57    where
58        F: FnMut(usize, &[T]),
59    {
60        self.try_diff_in_place(other, |idx, diff| -> Result<(), ()> {
61            func(idx, diff);
62            Ok(())
63        })
64        .unwrap();
65    }
66}
67
68#[derive(Copy, Clone)]
69enum DiffState {
70    Same,
71    Different(usize),
72}
73
74impl<T, const N: usize> DiffInPlace<T, N> for [T; N]
75where
76    T: PartialEq + Copy,
77{
78    fn try_diff_in_place<F, R>(&self, other: &[T; N], mut func: F) -> Result<(), R>
79    where
80        F: FnMut(usize, &[T]) -> Result<(), R>,
81    {
82        // Go over the bytes in both arrays, comparing them.
83        // For all different runs within the two buffers, call the diff function
84        // with the index into the buffers and the bytes that need to be changed.
85        let byte_for_byte = self.iter().zip(other.iter());
86        let mut run_state = DiffState::Same;
87        for (current, (left, right)) in byte_for_byte.enumerate() {
88            match (run_state, left == right) {
89                (DiffState::Same, false) => {
90                    // We are starting an unequal run, preserve the current index
91                    run_state = DiffState::Different(current);
92                }
93                (DiffState::Different(run_start), true) => {
94                    // We are ending an unequal run, call the diff function
95                    func(run_start, &other[run_start..current])?;
96                    run_state = DiffState::Same;
97                }
98                _ => {
99                    // Run state is unchanged
100                }
101            }
102        }
103
104        // If we are still in a different run, call the diff function
105        if let DiffState::Different(run_start) = run_state {
106            func(run_start, &other[run_start..])?;
107        }
108
109        Ok(())
110    }
111}
112
113#[cfg(test)]
114mod tests {
115    use super::*;
116
117    #[test]
118    fn test_fully_same() {
119        let a = [0u8; 40];
120        let b = [0u8; 40];
121        a.diff_in_place(&b, |_, _| panic!("Should not be called"))
122    }
123
124    #[test]
125    fn test_fully_different() {
126        let a = [0u8; 40];
127        let b = [1u8; 40];
128
129        let mut called = false;
130        a.diff_in_place(&b, |idx, diff| {
131            assert_eq!(idx, 0);
132            assert_eq!(diff, &[1u8; 40]);
133            called = true;
134        });
135        assert!(called);
136    }
137
138    #[test]
139    fn test_start_different() {
140        let a = [0u8; 40];
141        let mut b = [0u8; 40];
142
143        b[..10].copy_from_slice(&[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
144
145        const EXPECTED_CALLS: [(usize, &[u8]); 1] = [(0usize, &[1, 2, 3, 4, 5, 6, 7, 8, 9, 10])];
146
147        let mut called = false;
148        a.diff_in_place(&b, |idx, diff| {
149            let (expected_idx, expected_diff) = EXPECTED_CALLS[0];
150            assert_eq!(idx, expected_idx);
151            assert_eq!(diff, expected_diff);
152            called = true;
153        });
154        assert!(called);
155    }
156
157    #[test]
158    fn test_middle_different() {
159        let a = [0u8; 40];
160        let mut b = [0u8; 40];
161
162        b[10..20].copy_from_slice(&[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
163
164        const EXPECTED_CALLS: [(usize, &[u8]); 1] = [(10usize, &[1, 2, 3, 4, 5, 6, 7, 8, 9, 10])];
165
166        let mut called = false;
167        a.diff_in_place(&b, |idx, diff| {
168            let (expected_idx, expected_diff) = EXPECTED_CALLS[0];
169            assert_eq!(idx, expected_idx);
170            assert_eq!(diff, expected_diff);
171            called = true;
172        });
173        assert!(called);
174    }
175
176    #[test]
177    fn test_end_different() {
178        let a = [0u8; 40];
179        let mut b = [0u8; 40];
180
181        b[30..].copy_from_slice(&[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
182
183        const EXPECTED_CALLS: [(usize, &[u8]); 1] = [(30usize, &[1, 2, 3, 4, 5, 6, 7, 8, 9, 10])];
184
185        let mut called = false;
186        a.diff_in_place(&b, |idx, diff| {
187            let (expected_idx, expected_diff) = EXPECTED_CALLS[0];
188            assert_eq!(idx, expected_idx);
189            assert_eq!(diff, expected_diff);
190            called = true;
191        });
192        assert!(called);
193    }
194
195    #[test]
196    fn test_multiple_different() {
197        let a = [0u8; 40];
198        let mut b = [0u8; 40];
199
200        b[..10].copy_from_slice(&[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
201        b[20..25].copy_from_slice(&[11, 12, 13, 14, 15]);
202        b[39] = 20;
203
204        const EXPECTED_CALLS: [(usize, &[u8]); 3] = [
205            (0usize, &[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]),
206            (20usize, &[11, 12, 13, 14, 15]),
207            (39usize, &[20]),
208        ];
209
210        let mut call_idx = 0;
211        let mut called = false;
212        a.diff_in_place(&b, |idx, diff| {
213            let (expected_idx, expected_diff) = EXPECTED_CALLS[call_idx];
214            assert_eq!(idx, expected_idx);
215            assert_eq!(diff, expected_diff);
216            call_idx += 1;
217            called = true;
218        });
219        assert!(called);
220    }
221
222    #[test]
223    fn test_other_types() {
224        let a = [0.0f32; 40];
225        let mut b = [0.0f32; 40];
226
227        b[..10].copy_from_slice(&[1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7., 8., 9., 10.]);
228
229        const EXPECTED_CALLS: [(usize, &[f32]); 1] =
230            [(0usize, &[1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7., 8., 9., 10.])];
231
232        let mut called = false;
233        a.diff_in_place(&b, |idx, diff| {
234            let (expected_idx, expected_diff) = EXPECTED_CALLS[0];
235            assert_eq!(idx, expected_idx);
236            assert_eq!(diff, expected_diff);
237            called = true;
238        });
239        assert!(called);
240    }
241
242    #[test]
243    #[should_panic]
244    fn test_fallible_diff() {
245        let a = [0u8; 40];
246        let mut b = [0u8; 40];
247
248        b[..10].copy_from_slice(&[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
249
250        a.try_diff_in_place(&b, |_idx, _diff| -> Result<(), ()> { Err(()) })
251            .unwrap();
252    }
253}