multiboot2_common/
tag.rs

1//! Module for the traits [`MaybeDynSized`] and [`Tag`].
2
3use crate::{BytesRef, DynSizedStructure, Header};
4use core::mem;
5use core::slice;
6use ptr_meta::Pointee;
7
8/// A trait to abstract sized and unsized structures (DSTs). It enables
9/// casting a [`DynSizedStructure`] to sized or unsized structures using
10/// [`DynSizedStructure::cast`].
11///
12/// Structs that are a DST must provide a **correct** [`MaybeDynSized::dst_len`]
13/// implementation.
14///
15/// # ABI
16/// Implementors **must** use `#[repr(C)]`. As there might be padding necessary
17/// for the proper Rust layout, `size_of_val(&self)` might report additional
18/// padding bytes that are not reflected by the actual payload. These additional
19/// padding bytes however will be reflected in corresponding [`BytesRef`]
20/// instances.
21///
22/// [`ID`]: Tag::ID
23/// [`DynSizedStructure`]: crate::DynSizedStructure
24pub trait MaybeDynSized: Pointee {
25    /// The associated [`Header`] of this tag.
26    type Header: Header;
27
28    /// The true base size of the struct without any implicit or additional
29    /// padding. Note that `size_of::<T>()` isn't sufficient, as for example
30    /// the type could have three `u32` fields, which would add an implicit
31    /// `u32` padding. However, this constant **must always** fulfill
32    /// `BASE_SIZE >= size_of::<Self::Header>()`.
33    ///
34    /// The main purpose of this constant is to create awareness when you
35    /// implement [`Self::dst_len`], where you should use this. If this value
36    /// is correct, we prevent situations where we read uninitialized bytes,
37    /// especially when creating tags in builders.
38    const BASE_SIZE: usize;
39
40    /// Returns the amount of items in the dynamically sized portion of the
41    /// DST. Note that this is not the amount of bytes. So if the dynamically
42    /// sized portion is 16 bytes in size and each element is 4 bytes big, then
43    /// this function must return 4.
44    ///
45    /// For sized tags, this just returns `()`. For DSTs, this returns an
46    /// `usize`.
47    fn dst_len(header: &Self::Header) -> Self::Metadata;
48
49    /// Returns the corresponding [`Header`].
50    fn header(&self) -> &Self::Header {
51        let ptr = core::ptr::addr_of!(*self);
52        unsafe { &*ptr.cast::<Self::Header>() }
53    }
54
55    /// Returns the payload, i.e., all memory that is not occupied by the
56    /// [`Header`] of the type.
57    fn payload(&self) -> &[u8] {
58        let from = mem::size_of::<Self::Header>();
59        &self.as_bytes()[from..]
60    }
61
62    /// Returns the whole allocated bytes for this structure encapsulated in
63    /// [`BytesRef`]. This includes padding bytes. To only get the "true" tag
64    /// data, read the tag size from [`Self::header`] and create a sub slice.
65    fn as_bytes(&self) -> BytesRef<Self::Header> {
66        let ptr = core::ptr::addr_of!(*self);
67        // Actual tag size with optional terminating padding.
68        let size = mem::size_of_val(self);
69        let slice = unsafe { slice::from_raw_parts(ptr.cast::<u8>(), size) };
70        // Unwrap is fine as this type can't exist without the underlying memory
71        // guarantees.
72        BytesRef::try_from(slice).unwrap()
73    }
74
75    /// Returns a pointer to this structure.
76    fn as_ptr(&self) -> *const Self::Header {
77        self.as_bytes().as_ptr().cast()
78    }
79}
80
81/// Extension of [`MaybeDynSized`] for Tags.
82pub trait Tag: MaybeDynSized {
83    /// The ID type that identifies the tag.
84    type IDType: PartialEq + Eq;
85
86    /// The ID of this tag. This should be unique across all implementors.
87    ///
88    /// Although the ID is not yet used in `multiboot2-common`, it ensures
89    /// a consistent API in consumer crates.
90    const ID: Self::IDType;
91}
92
93// This implementation is not needed for parsing but for creation, when
94// downstream types just wrap this type.
95impl<H: Header> MaybeDynSized for DynSizedStructure<H> {
96    type Header = H;
97
98    const BASE_SIZE: usize = mem::size_of::<H>();
99
100    fn dst_len(header: &Self::Header) -> Self::Metadata {
101        header.payload_len()
102    }
103}