Skip to main content

multiboot2_common/
bytes_ref.rs

1//! Module for [`BytesRef`].
2
3use crate::{ALIGNMENT, Header, MemoryError};
4use core::marker::PhantomData;
5use core::ops::Deref;
6
7/// Wraps a byte slice representing a Multiboot2 structure including an optional
8/// terminating padding, if necessary.
9///
10/// This type helps that casts to a specific tag from the underlying bytes are
11/// either same-size casts or down-size casts, but never upsize-casts, which are
12/// illegal and UB! Instances of this type guarantee that the memory
13/// requirements promised in the crates description are respected.
14#[derive(Clone, Debug, PartialEq, Eq)]
15#[repr(transparent)]
16pub struct BytesRef<'a, H: Header> {
17    bytes: &'a [u8],
18    // Ensure that consumers can rely on the size properties for `H` that
19    // already have been verified when this type was constructed.
20    _h: PhantomData<H>,
21}
22
23impl<'a, H: Header> TryFrom<&'a [u8]> for BytesRef<'a, H> {
24    type Error = MemoryError;
25
26    fn try_from(bytes: &'a [u8]) -> Result<Self, Self::Error> {
27        if bytes.len() < size_of::<H>() {
28            return Err(MemoryError::ShorterThanHeader);
29        }
30        // Doesn't work as expected: if align_of_val(&value[0]) < ALIGNMENT {
31        if bytes.as_ptr().align_offset(ALIGNMENT) != 0 {
32            return Err(MemoryError::WrongAlignment);
33        }
34        let padding_bytes = bytes.len() % ALIGNMENT;
35        if padding_bytes != 0 {
36            return Err(MemoryError::MissingPadding);
37        }
38        Ok(Self {
39            bytes,
40            _h: PhantomData,
41        })
42    }
43}
44
45impl<'a, H: Header> Deref for BytesRef<'a, H> {
46    type Target = &'a [u8];
47
48    fn deref(&self) -> &Self::Target {
49        &self.bytes
50    }
51}
52
53#[cfg(test)]
54mod tests {
55    use super::*;
56    use crate::test_utils::{AlignedBytes, DummyTestHeader};
57
58    #[test]
59    fn test_bytes_ref() {
60        let empty: &[u8] = &[];
61        assert_eq!(
62            BytesRef::<'_, DummyTestHeader>::try_from(empty),
63            Err(MemoryError::ShorterThanHeader)
64        );
65
66        let slice = &[0_u8, 1, 2, 3, 4, 5, 6];
67        assert_eq!(
68            BytesRef::<'_, DummyTestHeader>::try_from(&slice[..]),
69            Err(MemoryError::ShorterThanHeader)
70        );
71
72        let slice = AlignedBytes([0_u8, 1, 2, 3, 4, 5, 6, 7, 0, 0, 0]);
73        // Guaranteed wrong alignment
74        let unaligned_slice = &slice[3..];
75        assert_eq!(
76            BytesRef::<'_, DummyTestHeader>::try_from(unaligned_slice),
77            Err(MemoryError::WrongAlignment)
78        );
79
80        let slice = AlignedBytes([0_u8, 1, 2, 3, 4, 5, 6, 7]);
81        let slice = &slice[..];
82        assert_eq!(
83            BytesRef::try_from(slice),
84            Ok(BytesRef {
85                bytes: slice,
86                _h: PhantomData::<DummyTestHeader>
87            })
88        );
89    }
90}