binary_layout/utils/
data.rs

1use core::fmt::Debug;
2use core::ops::{Bound, Deref, DerefMut, Range, RangeBounds};
3
4/// An instance of data owns a block of data. It implements `AsRef<[u8]>` and `AsMut<[u8]>` to allow
5/// borrowing that data, and it has a [Data::into_subregion] function that cuts away bytes at either
6/// end of the block and returns a [Data] instance that (semantically) owns a subrange of the original
7/// [Data] instance. This works without copying. Implementation wise, the new instance still owns and
8/// holds all of the data, just the accessors got limited to a smaller subrange.
9///
10/// That means this struct is great if you need to handle data blocks, cut away headers and pass ownership
11/// of the remaining data on to something else without having to copy it. The downside is that the
12/// header data isn't freed up - as long as any subregion of the original data exists somewhere,
13/// the whole data has to be kept in memory.
14#[derive(Clone)]
15pub struct Data<S> {
16    storage: S,
17    // region stores the subregion in the vector that we care for.
18    // TODO We're probably safer with an invariant that 0 <= range.start <= range.end <= storage.len(). Otherwise we'd have to think about the other case everywhere.
19    region: Range<usize>,
20}
21
22// TODO region should be a type parameter so that binary-layout can guarantee it's evaluated at compile time
23
24impl<S> Data<S> {
25    /// Return the length of the [Data] instance (or if it is a subregion, length of the subregion)
26    #[inline(always)]
27    pub fn len(&self) -> usize {
28        self.region.len()
29    }
30
31    /// Returns true if the [Data] instance contains data and false if it has a zero length.
32    #[inline(always)]
33    pub fn is_empty(&self) -> bool {
34        self.region.is_empty()
35    }
36
37    /// Return a [Data] instance that semantically only represents a subregion of the original instance.
38    /// Using any data accessors like `AsRef<[u8]>` or `AsMut<[u8]>` on the new instance will behave
39    /// as if the instance only owned the subregion.
40    ///
41    /// Creating subregions is super fast and does not incur a copy.
42    /// Note, however, that this is implemented by keeping all of the original data in memory and just
43    /// changing the behavior of the accessors. The memory will only be freed once the subregion instance
44    /// gets dropped.
45    #[inline]
46    pub fn into_subregion(self, range: impl RangeBounds<usize> + Debug) -> Self {
47        let start_bound_diff = match range.start_bound() {
48            Bound::Unbounded => 0,
49            Bound::Included(&x) => x,
50            Bound::Excluded(&x) => x + 1,
51        };
52        let panic_end_out_of_bounds = || {
53            panic!(
54                "Range end out of bounds. Tried to access subregion {:?} for a Data instance of length {}",
55                range,
56                self.region.len(),
57            );
58        };
59        let end_bound_diff = match range.end_bound() {
60            Bound::Unbounded => 0,
61            Bound::Included(&x) => self
62                .region
63                .len()
64                .checked_sub(x + 1)
65                .unwrap_or_else(panic_end_out_of_bounds),
66            Bound::Excluded(&x) => self
67                .region
68                .len()
69                .checked_sub(x)
70                .unwrap_or_else(panic_end_out_of_bounds),
71        };
72        Self {
73            storage: self.storage,
74            region: Range {
75                start: self.region.start + start_bound_diff,
76                end: self.region.end - end_bound_diff,
77            },
78        }
79    }
80}
81
82impl<S> From<S> for Data<S>
83where
84    S: AsRef<[u8]>,
85{
86    /// Create a new [Data] object from a given `Vec<[u8]>` allocation.
87    #[inline(always)]
88    fn from(data: S) -> Data<S> {
89        let len = data.as_ref().len();
90        Self {
91            storage: data,
92            region: 0..len,
93        }
94    }
95}
96
97impl<S> AsRef<[u8]> for Data<S>
98where
99    S: AsRef<[u8]>,
100{
101    #[inline(always)]
102    fn as_ref(&self) -> &[u8] {
103        &self.storage.as_ref()[self.region.clone()]
104    }
105}
106
107impl<S> AsMut<[u8]> for Data<S>
108where
109    S: AsMut<[u8]>,
110{
111    #[inline(always)]
112    fn as_mut(&mut self) -> &mut [u8] {
113        &mut self.storage.as_mut()[self.region.clone()]
114    }
115}
116
117// TODO Test
118impl<S> Deref for Data<S>
119where
120    S: AsRef<[u8]>,
121{
122    type Target = [u8];
123
124    #[inline(always)]
125    fn deref(&self) -> &[u8] {
126        self.as_ref()
127    }
128}
129
130// TODO Test
131impl<S> DerefMut for Data<S>
132where
133    S: AsRef<[u8]> + AsMut<[u8]>,
134{
135    #[inline(always)]
136    fn deref_mut(&mut self) -> &mut [u8] {
137        self.as_mut()
138    }
139}
140
141impl<'a> Data<&'a [u8]> {
142    /// Transform the [Data] object into a slice for the data pointed to.
143    /// This also extracts the lifetime and can be useful to get an object
144    /// whose lifetime isn't bound to the view or any local object anymore,
145    /// but instead bound to the original storage of the View.
146    ///
147    /// Example:
148    /// ---------------
149    /// ```
150    /// use binary_layout::binary_layout;
151    ///
152    /// binary_layout!(my_layout, LittleEndian, {
153    ///   field: u16,
154    ///   data: [u8],
155    /// });
156    ///
157    /// fn func(input: &[u8]) -> &[u8] {
158    ///   let view = my_layout::View::new(input);
159    ///   // Returning `view.data_mut()` doesn't work because its lifetime is bound to the local `view` object.
160    ///   // But we can return the following
161    ///   view.into_data().into_slice()
162    /// }
163    ///
164    /// let data = vec![0; 1024];
165    /// assert_eq!(0, func(&data)[0]);
166    /// ```
167    pub fn into_slice(self) -> &'a [u8] {
168        &self.storage[self.region]
169    }
170}
171
172impl<'a> Data<&'a mut [u8]> {
173    /// Transform the [Data] object into a slice for the data pointed to.
174    /// This also extracts the lifetime and can be useful to get an object
175    /// whose lifetime isn't bound to the view or any local object anymore,
176    /// but instead bound to the original storage of the View.
177    ///
178    /// Example:
179    /// ---------------
180    /// ```
181    /// use binary_layout::binary_layout;
182    ///
183    /// binary_layout!(my_layout, LittleEndian, {
184    ///   field: u16,
185    ///   data: [u8],
186    /// });
187    ///
188    /// fn func(input: &mut [u8]) -> &mut [u8] {
189    ///   let view = my_layout::View::new(input);
190    ///   // Returning `view.data_mut()` doesn't work because its lifetime is bound to the local `view` object.
191    ///   // But we can return the following
192    ///   view.into_data().into_slice()
193    /// }
194    ///
195    /// let mut data = vec![0; 1024];
196    /// func(&mut data)[0] = 5;
197    /// ```
198    pub fn into_slice(self) -> &'a mut [u8] {
199        &mut self.storage[self.region]
200    }
201}
202
203#[cfg(test)]
204#[cfg(feature = "std")] // TODO add no-std tests?
205mod tests {
206    use super::*;
207    use rand::{rngs::StdRng, RngCore, SeedableRng};
208
209    fn data_region(size: usize, seed: u64) -> Vec<u8> {
210        let mut rng = StdRng::seed_from_u64(seed);
211        let mut res = vec![0; size];
212        rng.fill_bytes(&mut res);
213        res
214    }
215
216    #[test]
217    fn given_fullrangedata_when_callingasref() {
218        let data: Data<_> = data_region(1024, 0).into();
219        assert_eq!(data.as_ref(), &data_region(1024, 0));
220    }
221
222    #[test]
223    fn given_fullrangedata_when_callingasmut() {
224        let mut data: Data<_> = data_region(1024, 0).into();
225        assert_eq!(data.as_mut(), &data_region(1024, 0));
226    }
227
228    #[test]
229    fn given_fullsubregiondata_when_callingasref() {
230        let data: Data<_> = data_region(1024, 0).into();
231        let subdata = data.into_subregion(..);
232        assert_eq!(subdata.as_ref(), &data_region(1024, 0));
233    }
234
235    #[test]
236    fn given_fullsubregiondata_when_callingasmut() {
237        let data: Data<_> = data_region(1024, 0).into();
238        let mut subdata = data.into_subregion(..);
239        assert_eq!(subdata.as_mut(), &data_region(1024, 0));
240    }
241
242    #[test]
243    fn given_openendsubregiondata_when_callingasref() {
244        let data: Data<_> = data_region(1024, 0).into();
245        let subdata = data.into_subregion(5..);
246        assert_eq!(subdata.as_ref(), &data_region(1024, 0)[5..]);
247    }
248
249    #[test]
250    fn given_openendsubregiondata_when_callingasmut() {
251        let data: Data<_> = data_region(1024, 0).into();
252        let mut subdata = data.into_subregion(5..);
253        assert_eq!(subdata.as_mut(), &data_region(1024, 0)[5..]);
254    }
255
256    #[test]
257    fn given_openbeginningexclusivesubregiondata_when_callingasref() {
258        let data: Data<_> = data_region(1024, 0).into();
259        let subdata = data.into_subregion(..1000);
260        assert_eq!(subdata.as_ref(), &data_region(1024, 0)[..1000]);
261    }
262
263    #[test]
264    fn given_openbeginningexclusivesubregiondata_when_callingasmut() {
265        let data: Data<_> = data_region(1024, 0).into();
266        let mut subdata = data.into_subregion(..1000);
267        assert_eq!(subdata.as_mut(), &data_region(1024, 0)[..1000]);
268    }
269
270    #[test]
271    fn given_openbeginninginclusivesubregiondata_when_callingasref() {
272        let data: Data<_> = data_region(1024, 0).into();
273        let subdata = data.into_subregion(..=1000);
274        assert_eq!(subdata.as_ref(), &data_region(1024, 0)[..=1000]);
275    }
276
277    #[test]
278    fn given_openbeginninginclusivesubregiondata_when_callingasmut() {
279        let data: Data<_> = data_region(1024, 0).into();
280        let mut subdata = data.into_subregion(..=1000);
281        assert_eq!(subdata.as_mut(), &data_region(1024, 0)[..=1000]);
282    }
283
284    #[test]
285    fn given_exclusivesubregiondata_when_callingasref() {
286        let data: Data<_> = data_region(1024, 0).into();
287        let subdata = data.into_subregion(5..1000);
288        assert_eq!(subdata.as_ref(), &data_region(1024, 0)[5..1000]);
289    }
290
291    #[test]
292    fn given_exclusivesubregiondata_when_callingasmut() {
293        let data: Data<_> = data_region(1024, 0).into();
294        let mut subdata = data.into_subregion(5..1000);
295        assert_eq!(subdata.as_mut(), &data_region(1024, 0)[5..1000]);
296    }
297
298    #[test]
299    fn given_inclusivesubregiondata_when_callingasref() {
300        let data: Data<_> = data_region(1024, 0).into();
301        let subdata = data.into_subregion(5..=1000);
302        assert_eq!(subdata.as_ref(), &data_region(1024, 0)[5..=1000]);
303    }
304
305    #[test]
306    fn given_inclusivesubregiondata_when_callingasmut() {
307        let data: Data<_> = data_region(1024, 0).into();
308        let mut subdata = data.into_subregion(5..=1000);
309        assert_eq!(subdata.as_mut(), &data_region(1024, 0)[5..=1000]);
310    }
311
312    #[test]
313    fn nested_subregions_still_do_the_right_thing() {
314        let data: Data<_> = data_region(1024, 0).into();
315        let subdata = data
316            .into_subregion(..)
317            .into_subregion(5..)
318            .into_subregion(..1000)
319            .into_subregion(..=950)
320            .into_subregion(10..900)
321            .into_subregion(3..=800)
322            // and all types of ranges again, just in case they don't work if a certain other range happens beforehand
323            .into_subregion(..)
324            .into_subregion(5..)
325            .into_subregion(..700)
326            .into_subregion(..=650)
327            .into_subregion(10..600)
328            .into_subregion(3..=500);
329        assert_eq!(
330            subdata.as_ref(),
331            &data_region(1024, 0)[..][5..][..1000][..=950][10..900][3..=800][..][5..][..700]
332                [..=650][10..600][3..=500]
333        );
334    }
335
336    #[test]
337    #[should_panic(
338        expected = "Range end out of bounds. Tried to access subregion ..=1024 for a Data instance of length 1024"
339    )]
340    fn given_fullrangedata_when_tryingtogrowendbeyondlength_with_inclusiverange_then_panics() {
341        let data: Data<_> = data_region(1024, 0).into();
342        data.into_subregion(..=1024);
343    }
344
345    #[test]
346    #[should_panic(
347        expected = "Range end out of bounds. Tried to access subregion ..=100 for a Data instance of length 100"
348    )]
349    fn given_subrangedata_when_tryingtogrowendbeyondlength_with_inclusiverange_then_panics() {
350        let data: Data<_> = data_region(1024, 0).into();
351        let data = data.into_subregion(0..100);
352        data.into_subregion(..=100);
353    }
354
355    #[test]
356    #[should_panic(
357        expected = "Range end out of bounds. Tried to access subregion ..1025 for a Data instance of length 1024"
358    )]
359    fn given_fullrangedata_when_tryingtogrowendbeyondlength_with_exclusiverange_then_panics() {
360        let data: Data<_> = data_region(1024, 0).into();
361        data.into_subregion(..1025);
362    }
363
364    #[test]
365    #[should_panic(
366        expected = "Range end out of bounds. Tried to access subregion ..101 for a Data instance of length 100"
367    )]
368    fn given_subrangedata_when_tryingtogrowendbeyondlength_with_exclusiverange_then_panics() {
369        let data: Data<_> = data_region(1024, 0).into();
370        let data = data.into_subregion(0..100);
371        data.into_subregion(..101);
372    }
373
374    #[test]
375    fn given_fullrangedata_when_tryingtogrowstartbeyondend_then_returnszerolengthrange() {
376        let data: Data<_> = data_region(1024, 0).into();
377        #[allow(clippy::reversed_empty_ranges)]
378        let data = data.into_subregion(5000..400);
379        assert_eq!(0, data.len());
380    }
381}