multiboot2_common/
tag.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
//! Module for the traits [`MaybeDynSized`] and [`Tag`].

use crate::{BytesRef, DynSizedStructure, Header};
use core::mem;
use core::slice;
use ptr_meta::Pointee;

/// A trait to abstract sized and unsized structures (DSTs). It enables
/// casting a [`DynSizedStructure`] to sized or unsized structures using
/// [`DynSizedStructure::cast`].
///
/// Structs that are a DST must provide a **correct** [`MaybeDynSized::dst_len`]
/// implementation.
///
/// # ABI
/// Implementors **must** use `#[repr(C)]`. As there might be padding necessary
/// for the proper Rust layout, `size_of_val(&self)` might report additional
/// padding bytes that are not reflected by the actual payload. These additional
/// padding bytes however will be reflected in corresponding [`BytesRef`]
/// instances.
///
/// [`ID`]: Tag::ID
/// [`DynSizedStructure`]: crate::DynSizedStructure
pub trait MaybeDynSized: Pointee {
    /// The associated [`Header`] of this tag.
    type Header: Header;

    /// The true base size of the struct without any implicit or additional
    /// padding. Note that `size_of::<T>()` isn't sufficient, as for example
    /// the type could have three `u32` fields, which would add an implicit
    /// `u32` padding. However, this constant **must always** fulfill
    /// `BASE_SIZE >= size_of::<Self::Header>()`.
    ///
    /// The main purpose of this constant is to create awareness when you
    /// implement [`Self::dst_len`], where you should use this. If this value
    /// is correct, we prevent situations where we read uninitialized bytes,
    /// especially when creating tags in builders.
    const BASE_SIZE: usize;

    /// Returns the amount of items in the dynamically sized portion of the
    /// DST. Note that this is not the amount of bytes. So if the dynamically
    /// sized portion is 16 bytes in size and each element is 4 bytes big, then
    /// this function must return 4.
    ///
    /// For sized tags, this just returns `()`. For DSTs, this returns an
    /// `usize`.
    fn dst_len(header: &Self::Header) -> Self::Metadata;

    /// Returns the corresponding [`Header`].
    fn header(&self) -> &Self::Header {
        let ptr = core::ptr::addr_of!(*self);
        unsafe { &*ptr.cast::<Self::Header>() }
    }

    /// Returns the payload, i.e., all memory that is not occupied by the
    /// [`Header`] of the type.
    fn payload(&self) -> &[u8] {
        let from = mem::size_of::<Self::Header>();
        &self.as_bytes()[from..]
    }

    /// Returns the whole allocated bytes for this structure encapsulated in
    /// [`BytesRef`]. This includes padding bytes. To only get the "true" tag
    /// data, read the tag size from [`Self::header`] and create a sub slice.
    fn as_bytes(&self) -> BytesRef<Self::Header> {
        let ptr = core::ptr::addr_of!(*self);
        // Actual tag size with optional terminating padding.
        let size = mem::size_of_val(self);
        let slice = unsafe { slice::from_raw_parts(ptr.cast::<u8>(), size) };
        // Unwrap is fine as this type can't exist without the underlying memory
        // guarantees.
        BytesRef::try_from(slice).unwrap()
    }

    /// Returns a pointer to this structure.
    fn as_ptr(&self) -> *const Self::Header {
        self.as_bytes().as_ptr().cast()
    }
}

/// Extension of [`MaybeDynSized`] for Tags.
pub trait Tag: MaybeDynSized {
    /// The ID type that identifies the tag.
    type IDType: PartialEq + Eq;

    /// The ID of this tag. This should be unique across all implementors.
    ///
    /// Although the ID is not yet used in `multiboot2-common`, it ensures
    /// a consistent API in consumer crates.
    const ID: Self::IDType;
}

// This implementation is not needed for parsing but for creation, when
// downstream types just wrap this type.
impl<H: Header> MaybeDynSized for DynSizedStructure<H> {
    type Header = H;

    const BASE_SIZE: usize = mem::size_of::<H>();

    fn dst_len(header: &Self::Header) -> Self::Metadata {
        header.payload_len()
    }
}