bebop/types/
slice.rs

1use std::iter::FusedIterator;
2use std::mem::size_of;
3use std::ops::Deref;
4use std::ptr::slice_from_raw_parts;
5
6use crate::{FixedSized, SubRecord};
7
8/// This allows us to either wrap an existing &[T] slice to serialize it OR to store a raw byte
9/// slice from an encoding and access its potentially unaligned values.
10///
11/// **Warning:** Creating Raw arrays manually may lead to undefined behavior, use `from_raw`.
12#[derive(Copy, Clone, Debug, PartialEq)]
13pub enum SliceWrapper<'a, T: FixedSized> {
14    Raw(&'a [u8]),
15    Cooked(&'a [T]),
16}
17
18impl<'a, T, A> From<A> for SliceWrapper<'a, T>
19where
20    T: FixedSized,
21    A: AsRef<&'a [T]>,
22{
23    #[inline]
24    fn from(array: A) -> Self {
25        SliceWrapper::from_cooked(array.as_ref())
26    }
27}
28
29impl<'a> Deref for SliceWrapper<'a, u8> {
30    type Target = &'a [u8];
31
32    #[inline]
33    fn deref(&self) -> &Self::Target {
34        match self {
35            SliceWrapper::Raw(d) => d,
36            SliceWrapper::Cooked(d) => d,
37        }
38    }
39}
40
41impl<'a> AsRef<[u8]> for SliceWrapper<'a, u8> {
42    #[inline]
43    fn as_ref(&self) -> &'a [u8] {
44        **self
45    }
46}
47
48impl<'a, T> SliceWrapper<'a, T>
49where
50    T: FixedSized,
51{
52    /// Take in a little-endian array. Most not include size bytes since the slice already has that
53    /// info.
54    pub fn from_raw(bytes: &'a [u8]) -> Self {
55        assert_eq!(bytes.len() % size_of::<T>(), 0);
56        Self::Raw(bytes)
57    }
58
59    pub fn from_cooked(array: &'a [T]) -> Self {
60        SliceWrapper::Cooked(array)
61    }
62}
63
64impl<'a, T> SliceWrapper<'a, T>
65where
66    T: FixedSized + SubRecord<'a>,
67{
68    /// Retrieve a value at a given index
69    pub fn get(&self, i: usize) -> Option<T> {
70        match *self {
71            SliceWrapper::Raw(raw) => {
72                if i * size_of::<T>() + size_of::<T>() > raw.len() {
73                    None
74                } else {
75                    let raw: &'a [u8] = unsafe {
76                        &*slice_from_raw_parts(
77                            raw.as_ptr().add(i * size_of::<T>()),
78                            size_of::<T>(),
79                        )
80                    };
81                    Some(T::_deserialize_chained(raw).map(|(_, v)| v).unwrap())
82                }
83            }
84            SliceWrapper::Cooked(ary) => ary.get(i).copied(),
85        }
86    }
87
88    pub fn iter(&self) -> Iter<'a, T> {
89        self.into_iter()
90    }
91}
92
93impl<'a, T> SliceWrapper<'a, T>
94where
95    T: FixedSized,
96{
97    pub fn is_raw(&self) -> bool {
98        matches!(self, SliceWrapper::Raw(_))
99    }
100}
101
102impl<'a, T> SliceWrapper<'a, T>
103where
104    T: FixedSized,
105{
106    /// Retrieve the number of items
107    #[inline]
108    pub fn len(&self) -> usize {
109        match *self {
110            SliceWrapper::Raw(raw) => raw.len() / size_of::<T>(),
111            SliceWrapper::Cooked(ary) => ary.len(),
112        }
113    }
114
115    #[inline]
116    pub fn is_empty(&self) -> bool {
117        match *self {
118            SliceWrapper::Raw(raw) => raw.is_empty(),
119            SliceWrapper::Cooked(ary) => ary.is_empty(),
120        }
121    }
122
123    /// Retrieve the total size of this slice's data in bytes. Does not include any extra bytes to
124    /// define the length of the array so it is not the same as the serialized size.
125    #[inline]
126    pub fn size(&self) -> usize {
127        match *self {
128            SliceWrapper::Raw(raw) => raw.len(),
129            SliceWrapper::Cooked(ary) => ary.len() * size_of::<T>(),
130        }
131    }
132}
133
134impl<'a, T> IntoIterator for SliceWrapper<'a, T>
135where
136    T: FixedSized + SubRecord<'a>,
137{
138    type Item = T;
139    type IntoIter = Iter<'a, T>;
140
141    #[inline]
142    fn into_iter(self) -> Self::IntoIter {
143        Iter(self, 0)
144    }
145}
146
147pub struct Iter<'a, T: FixedSized>(SliceWrapper<'a, T>, usize);
148
149impl<'a, T> Iterator for Iter<'a, T>
150where
151    T: FixedSized + SubRecord<'a>,
152{
153    type Item = T;
154
155    fn next(&mut self) -> Option<T> {
156        let r = self.0.get(self.1);
157        self.1 += 1;
158        r
159    }
160}
161
162impl<'a, T> FusedIterator for Iter<'a, T> where T: FixedSized + SubRecord<'a> {}
163impl<'a, T> ExactSizeIterator for Iter<'a, T>
164where
165    T: FixedSized + SubRecord<'a>,
166{
167    fn len(&self) -> usize {
168        debug_assert!(self.1 <= self.0.len());
169        self.0.len() - self.1
170    }
171}
172
173#[cfg(test)]
174mod test {
175    use std::convert::TryInto;
176
177    use crate::{
178        define_serialize_chained, packed_read, DeResult, FixedSized, SliceWrapper,
179        SubRecord,
180    };
181
182    #[repr(packed)]
183    #[derive(Debug, Eq, PartialEq, Copy, Clone)]
184    struct Fixed {
185        a: u8,
186        b: u64,
187    }
188
189    impl FixedSized for Fixed {}
190
191    fn cooked_array() -> &'static [Fixed] {
192        &[
193            Fixed { a: 23, b: 98072396 },
194            Fixed {
195                a: 134,
196                b: 2389502334,
197            },
198            Fixed { a: 73, b: 98273 },
199            Fixed { a: 1, b: 59125 },
200        ]
201    }
202
203    impl<'raw> SubRecord<'raw> for Fixed {
204        const MIN_SERIALIZED_SIZE: usize = Self::SERIALIZED_SIZE;
205        const EXACT_SERIALIZED_SIZE: Option<usize> = Some(Self::SERIALIZED_SIZE);
206
207        fn serialized_size(&self) -> usize {
208            Self::SERIALIZED_SIZE
209        }
210
211        define_serialize_chained!(*Fixed => |zelf, dest| {
212            Ok(zelf.a._serialize_chained(dest)? + packed_read!(zelf.b)._serialize_chained(dest)?)
213        });
214
215        fn _deserialize_chained(raw: &'raw [u8]) -> DeResult<(usize, Self)> {
216            Ok((
217                9,
218                Self {
219                    a: raw[0],
220                    b: u64::from_le_bytes((raw[1..9]).try_into().unwrap()),
221                },
222            ))
223        }
224    }
225
226    #[test]
227    fn from_raw() {
228        // make sure it checks the size of the array is correct
229        let s = <SliceWrapper<u16>>::from_raw(&[0x00, 0x00, 0x00, 0x00]);
230        assert!(!s.is_empty());
231    }
232
233    #[test]
234    #[should_panic]
235    fn from_raw_err() {
236        // make sure it checks the size of the array is correct
237        let s = <SliceWrapper<u16>>::from_raw(&[0x00, 0x00, 0x00]);
238        assert!(!s.is_empty());
239    }
240
241    #[test]
242    fn get_cooked_fixed_struct() {
243        let array = cooked_array();
244        let s = SliceWrapper::Cooked(array);
245        for i in 0..array.len() {
246            assert_eq!(s.get(i).unwrap(), array[i]);
247        }
248    }
249
250    #[test]
251    fn get_cooked_primitive() {
252        let array: &'static [u16] = &[0x0000, 0x1243, 0x8f90, 0x097a];
253        let s = SliceWrapper::Cooked(array);
254        for i in 0..array.len() {
255            assert_eq!(s.get(i).unwrap(), array[i]);
256        }
257    }
258
259    #[test]
260    fn get_raw_fixed_struct() {
261        // only happens for big-endian systems or systems where `repr(packed)` is not supported
262        let s = <SliceWrapper<Fixed>>::Raw(&[0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]);
263        let s0 = s.get(0).unwrap();
264        assert_eq!(packed_read!(s0.a), 1);
265        assert_eq!(packed_read!(s0.b), 2);
266    }
267
268    #[test]
269    fn get_raw_primitive() {
270        // only happens for multi-byte values
271        let s = <SliceWrapper<u16>>::Raw(&[0x01, 0x00, 0x02, 0x00]);
272        assert_eq!(s.get(0).unwrap(), 1);
273        assert_eq!(s.get(1).unwrap(), 2);
274    }
275
276    #[test]
277    fn iter_cooked() {
278        let array = cooked_array();
279        let s = SliceWrapper::Cooked(array);
280        for (i, v) in s.iter().enumerate() {
281            assert_eq!(v, array[i]);
282        }
283    }
284
285    #[test]
286    fn iter_raw() {
287        let s = <SliceWrapper<Fixed>>::Raw(&[
288            0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x04, 0x00, 0x00, 0x00,
289            0x00, 0x00, 0x00, 0x00,
290        ]);
291        for (i, v) in s.iter().enumerate() {
292            assert_eq!(packed_read!(v.a), (i * 2 + 1) as u8);
293            assert_eq!(packed_read!(v.b), (i * 2 + 2) as u64);
294        }
295    }
296
297    #[test]
298    fn size_cooked_struct() {
299        let s = SliceWrapper::Cooked(cooked_array());
300        assert_eq!(s.len(), 4);
301        assert_eq!(s.size(), 9 * 4);
302    }
303
304    #[test]
305    fn size_raw_struct() {
306        let s = <SliceWrapper<Fixed>>::Raw(&[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]);
307        assert_eq!(s.len(), 1);
308        assert_eq!(s.size(), 9);
309    }
310
311    #[test]
312    fn size_cooked_primitive() {
313        let s = <SliceWrapper<u16>>::Cooked(&[0x0000, 0x0000, 0x0000]);
314        assert_eq!(s.len(), 3);
315        assert_eq!(s.size(), 6);
316    }
317
318    #[test]
319    fn size_raw_primitive() {
320        let s = <SliceWrapper<u16>>::Raw(&[0x00, 0x00, 0x00, 0x00, 0x00, 0x00]);
321        assert_eq!(s.len(), 3);
322        assert_eq!(s.size(), 6);
323    }
324
325    #[test]
326    fn deref_u8_raw() {
327        let s = <SliceWrapper<u8>>::Raw(&[0x00, 0x01, 0x04, 0x06]);
328        let s2: &[u8] = *s;
329        assert_eq!(s2[0], 0);
330        assert_eq!(s2[1], 1);
331        assert_eq!(s2[2], 4);
332        assert_eq!(s2[3], 6);
333    }
334
335    #[test]
336    fn deref_u8_cooked() {
337        let s = <SliceWrapper<u8>>::Cooked(&[0x00, 0x01, 0x04, 0x06]);
338        let s2: &[u8] = *s;
339        assert_eq!(s2[0], 0);
340        assert_eq!(s2[1], 1);
341        assert_eq!(s2[2], 4);
342        assert_eq!(s2[3], 6);
343    }
344}