pigeon/
unaligned_slice.rs

1use core::cmp::{Eq, PartialEq};
2
3/// A byte slice which is not aligned, but does contain a whole number of bytes.
4#[derive(Debug, Clone, Copy)]
5pub struct UnalignedSlice<'a> {
6    bytes: &'a [u8],
7    bit_offset: u32,
8}
9
10impl<'a> Eq for UnalignedSlice<'a> {}
11
12impl<'a> PartialEq for UnalignedSlice<'a> {
13    fn eq(&self, other: &UnalignedSlice<'a>) -> bool {
14        let mut iter_self = self.iter();
15        let mut iter_other = other.iter();
16        loop {
17            let byte_self = iter_self.next();
18            let byte_other = iter_other.next();
19            if byte_self != byte_other {
20                return false;
21            }
22            if byte_self.is_none() {
23                return true;
24            }
25        }
26    }
27}
28
29impl<'a> PartialEq<&'a [u8]> for UnalignedSlice<'a> {
30    fn eq(&self, other: &&'a [u8]) -> bool {
31        self.eq(&UnalignedSlice::new(other, 0))
32    }
33}
34
35impl<'a> UnalignedSlice<'a> {
36    /// Create a new `UnalignedSlice` from a byte slice and a bit offset.
37    pub fn new(bytes: &'a [u8], bit_offset: u32) -> Self {
38        Self { bytes, bit_offset }
39    }
40
41    /// Get the length of this unaligned slice.
42    pub fn len(&self) -> usize {
43        let extra_byte = if self.bit_offset == 0 { 0 } else { 1 };
44        self.bytes.len() - extra_byte
45    }
46
47    /// Copy the contents to an aligned slice.
48    pub fn copy_to_slice(&self, buf: &mut [u8]) {
49        assert_eq!(self.len(), buf.len());
50        for (idx, byte) in self.iter().enumerate() {
51            buf[idx] = byte;
52        }
53    }
54
55    /// Get an iterator over the bytes.
56    pub fn iter(&self) -> Iter<'a> {
57        let mut iter = self.bytes.iter();
58        let (mask_first, mask_second, last) = if self.bit_offset == 0 {
59            (0, 0xFF, 0)
60        } else {
61            let mask_first = 0xFF >> (8 - self.bit_offset);
62            let mask_second = 0xFF << self.bit_offset;
63            if let Some(&byte) = iter.next() {
64                (mask_first, mask_second, byte)
65            } else {
66                (mask_first, mask_second, 0)
67            }
68        };
69        Iter {
70            iter,
71            bit_offset: self.bit_offset,
72            mask_first,
73            mask_second,
74            last,
75        }
76    }
77}
78
79/// An iterator over the bytes in an `UnalignedSlice`.
80pub struct Iter<'a> {
81    iter: core::slice::Iter<'a, u8>,
82    bit_offset: u32,
83    mask_first: u8,
84    mask_second: u8,
85    last: u8,
86}
87
88impl<'a> Iterator for Iter<'a> {
89    type Item = u8;
90
91    fn next(&mut self) -> Option<Self::Item> {
92        if let Some(&current) = self.iter.next() {
93            if self.bit_offset == 0 {
94                Some(current)
95            } else {
96                let fst = self.last & self.mask_first;
97                let snd = current & self.mask_second;
98                let byte = (fst | snd).rotate_right(self.bit_offset);
99                self.last = current;
100                Some(byte)
101            }
102        } else {
103            None
104        }
105    }
106}
107
108#[cfg(test)]
109mod tests {
110    use super::*;
111
112    #[test]
113    fn test_unaligned_slice() {
114        let data = [0x12, 0x34, 0x56, 0x78];
115        let slice_a = UnalignedSlice::new(&data[1..3], 4);
116        assert_eq!(slice_a.len(), 1);
117        assert_eq!(slice_a, &[0x45][..]);
118        let slice_b = UnalignedSlice::new(&data[1..4], 4);
119        assert_eq!(slice_b.len(), 2);
120        assert_eq!(slice_b, &[0x45, 0x67][..]);
121    }
122
123    #[test]
124    fn test_unaligned_slice_iter() {
125        let data = [0x12, 0x34, 0x56, 0x78];
126        let slice = UnalignedSlice::new(&data[0..3], 4);
127        let mut iter = slice.iter();
128        assert_eq!(iter.next(), Some(0x23));
129        assert_eq!(iter.next(), Some(0x45));
130        assert_eq!(iter.next(), None);
131    }
132
133    #[test]
134    fn test_unaliged_slice_copy_to() {
135        let data = [0x12, 0x34, 0x56, 0x78];
136        let slice = UnalignedSlice::new(&data[0..3], 4);
137        let mut buf = [0; 2];
138        slice.copy_to_slice(&mut buf[..]);
139        assert_eq!(&buf[..2], &[0x23, 0x45]);
140    }
141}