Skip to main content

multiboot2_common/
tag.rs

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