Skip to main content

fastlanes/
rle.rs

1pub trait RLE: Sized {
2    /// Encode an array using Run-Length Encoding
3    ///
4    /// Creates a dictionary of run values (`rle_vals`) and an index array
5    /// (`rle_idxs`) that maps each input position to a dictionary entry.
6    ///
7    /// # Returns
8    /// The number of run values in the dictionary
9    ///
10    /// # Safety
11    ///
12    /// - All three arguments must be valid for 1024 elements, as their types already
13    ///   guarantee.
14    ///
15    /// Implementations perform unchecked buffer accesses that rely on these bounds; they are
16    /// checked only with `debug_assert` (i.e., not checked on release builds).
17    unsafe fn encode_unchecked(
18        input: &[Self; 1024],
19        rle_vals: &mut [Self; 1024],
20        rle_idxs: &mut [u16; 1024],
21    ) -> usize;
22
23    /// Decode RLE-encoded data back to original values
24    ///
25    /// Takes the dictionary of run values and indices, reconstructing the original array
26    ///
27    /// # Safety
28    ///
29    /// - Every element of `rle_idxs`, converted via `Into<usize>`, must be less than
30    ///   `rle_vals.len()`.
31    ///
32    /// This is checked only with `debug_assert` (i.e., not checked on release builds).
33    unsafe fn decode_unchecked<I>(
34        rle_vals: &[Self],
35        rle_idxs: &[I; 1024],
36        output: &mut [Self; 1024],
37    ) where
38        I: Copy + Into<usize>;
39}
40
41impl<T: PartialEq + Copy> RLE for T {
42    #[inline(never)]
43    unsafe fn encode_unchecked(
44        input: &[Self; 1024],
45        rle_vals: &mut [Self; 1024],
46        rle_idxs: &mut [u16; 1024],
47    ) -> usize {
48        let mut pos_val = 0u16;
49        let mut rle_val_idx = 0usize;
50
51        let mut prev_val = unsafe { *input.get_unchecked(0) };
52        unsafe { *rle_vals.get_unchecked_mut(rle_val_idx) = prev_val };
53        rle_val_idx += 1;
54        unsafe { *rle_idxs.get_unchecked_mut(0) = pos_val };
55
56        for i in 1..1024 {
57            let cur_val = unsafe { *input.get_unchecked(i) };
58            if cur_val != prev_val {
59                // SAFETY: `rle_val_idx` increments at most once per element, so it stays
60                // below 1024.
61                debug_assert!(rle_val_idx < rle_vals.len());
62                unsafe { *rle_vals.get_unchecked_mut(rle_val_idx) = cur_val };
63                rle_val_idx += 1;
64                pos_val += 1;
65                prev_val = cur_val;
66            }
67            unsafe { *rle_idxs.get_unchecked_mut(i) = pos_val };
68        }
69
70        rle_val_idx
71    }
72
73    #[inline(never)]
74    unsafe fn decode_unchecked<I>(
75        rle_vals: &[Self],
76        rle_idxs: &[I; 1024],
77        output: &mut [Self; 1024],
78    ) where
79        I: Copy + Into<usize>,
80    {
81        for (idx, output) in rle_idxs.iter().zip(output.iter_mut()) {
82            debug_assert!((*idx).into() < rle_vals.len());
83            // SAFETY: the caller guarantees every index is less than `rle_vals.len()`.
84            *output = unsafe { *rle_vals.get_unchecked((*idx).into()) };
85        }
86    }
87}
88
89#[cfg(test)]
90mod test {
91    use super::*;
92
93    #[test]
94    fn test_rle_encode_unique_count() {
95        let input: [u32; 1024] = core::array::from_fn(|i| (i / 100 + 1) as u32);
96        let mut rle_vals = [0u32; 1024];
97        let mut rle_idxs = [0u16; 1024];
98
99        // SAFETY: all arguments are 1024-element arrays.
100        let unique_count = unsafe { u32::encode_unchecked(&input, &mut rle_vals, &mut rle_idxs) };
101
102        assert_eq!(unique_count, 11);
103    }
104
105    #[test]
106    fn test_rle_encode_values() {
107        let input: [u32; 1024] = core::array::from_fn(|i| (i / 100 + 1) as u32);
108        let mut rle_vals = [0u32; 1024];
109        let mut rle_idxs = [0u16; 1024];
110
111        // SAFETY: all arguments are 1024-element arrays.
112        let unique_count = unsafe { u32::encode_unchecked(&input, &mut rle_vals, &mut rle_idxs) };
113
114        // Check that RLE values are 1, 2, 3, ..., 11
115        for i in 0..unique_count {
116            assert_eq!(rle_vals[i], i as u32 + 1);
117        }
118    }
119
120    #[test]
121    fn test_rle_encode_index_groups() {
122        let input: [u32; 1024] = core::array::from_fn(|i| (i / 100 + 1) as u32);
123        let mut rle_vals = [0u32; 1024];
124        let mut rle_idxs = [0u16; 1024];
125
126        // SAFETY: all arguments are 1024-element arrays.
127        unsafe { u32::encode_unchecked(&input, &mut rle_vals, &mut rle_idxs) };
128
129        for i in 0..100 {
130            assert_eq!(rle_idxs[i], 0);
131        }
132
133        for i in 100..200 {
134            assert_eq!(rle_idxs[i], 1);
135        }
136
137        for i in 1000..1024 {
138            assert_eq!(rle_idxs[i], 10);
139        }
140    }
141
142    #[test]
143    fn test_rle_encode_single_value() {
144        let input = [42u16; 1024];
145        let mut rle_vals = [0u16; 1024];
146        let mut rle_idxs = [0u16; 1024];
147
148        // SAFETY: all arguments are 1024-element arrays.
149        let unique_count = unsafe { u16::encode_unchecked(&input, &mut rle_vals, &mut rle_idxs) };
150
151        assert_eq!(unique_count, 1);
152        assert_eq!(rle_vals[0], 42);
153
154        for &idx in &rle_idxs {
155            assert_eq!(idx, 0);
156        }
157    }
158
159    #[test]
160    fn test_rle_encode_all_different() {
161        let input: [u8; 1024] = core::array::from_fn(|i| (i % 256) as u8);
162
163        let mut rle_vals = [0u8; 1024];
164        let mut rle_idxs = [0u16; 1024];
165
166        // SAFETY: all arguments are 1024-element arrays.
167        let unique_count = unsafe { u8::encode_unchecked(&input, &mut rle_vals, &mut rle_idxs) };
168
169        // RLE creates a new dictionary entry every time the value changes,
170        // not when we encounter a new unique value.
171        assert_eq!(unique_count, 1024);
172    }
173
174    #[test]
175    fn test_rle_round_trip() {
176        let input: [u8; 1024] = core::array::from_fn(|i| (i % 256) as u8);
177
178        let mut rle_vals = [0u8; 1024];
179        let mut rle_idxs = [0u16; 1024];
180        // SAFETY: all arguments are 1024-element arrays.
181        let unique_count = unsafe { u8::encode_unchecked(&input, &mut rle_vals, &mut rle_idxs) };
182
183        let mut decoded = [0u8; 1024];
184        // SAFETY: `encode_unchecked` only writes indices below the returned `unique_count`.
185        unsafe { u8::decode_unchecked(&rle_vals[..unique_count], &rle_idxs, &mut decoded) };
186        assert_eq!(input, decoded);
187    }
188}