multiboot2_common/
iter.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
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
//! Iterator over Multiboot2 structures. Technically, the process for iterating
//! Multiboot2 information tags and iterating Multiboot2 header tags is the
//! same.

use crate::{ALIGNMENT, DynSizedStructure, Header, increase_to_alignment};
use core::marker::PhantomData;
use core::mem;

/// Iterates over the tags (modelled by [`DynSizedStructure`]) of the underlying
/// byte slice. Each tag is expected to have the same common [`Header`] with
/// the corresponding ABI guarantees.
///
/// As the iterator emits elements of type [`DynSizedStructure`], users should
/// cast them to specific [`Tag`]s using [`DynSizedStructure::cast`] following
/// a user-specific policy. This can for example happen on the basis of some ID.
///
/// This iterator also emits end tags and doesn't treat them separately.
///
/// This type ensures the memory safety guarantees promised by this crates
/// documentation.
///
/// [`Tag`]: crate::Tag
#[derive(Clone, Debug)]
pub struct TagIter<'a, H: Header> {
    /// Absolute offset to next tag and updated in each iteration.
    next_tag_offset: usize,
    buffer: &'a [u8],
    // Ensure that all instances are bound to a specific `Header`.
    // Otherwise, UB can happen.
    _t: PhantomData<H>,
}

impl<'a, H: Header> TagIter<'a, H> {
    /// Creates a new iterator.
    #[must_use]
    // TODO we could take a BytesRef here, but the surrounding code should be
    //  bullet-proof enough.
    pub fn new(mem: &'a [u8]) -> Self {
        // Assert alignment.
        assert_eq!(mem.as_ptr().align_offset(ALIGNMENT), 0);

        TagIter {
            next_tag_offset: 0,
            buffer: mem,
            _t: PhantomData,
        }
    }
}

impl<'a, H: Header + 'a> Iterator for TagIter<'a, H> {
    type Item = &'a DynSizedStructure<H>;

    fn next(&mut self) -> Option<Self::Item> {
        if self.next_tag_offset == self.buffer.len() {
            return None;
        }
        assert!(self.next_tag_offset < self.buffer.len());

        let ptr = unsafe { self.buffer.as_ptr().add(self.next_tag_offset) }.cast::<H>();
        let tag_hdr = unsafe { &*ptr };

        // Get relevant byte portion for the next tag. This includes padding
        // bytes to fulfill Rust memory guarantees. Otherwise, Miri complains.
        // See <https://doc.rust-lang.org/reference/type-layout.html>.
        let slice = {
            let from = self.next_tag_offset;
            let len = mem::size_of::<H>() + tag_hdr.payload_len();
            let to = from + len;

            // The size of (the allocation for) a value is always a multiple of
            // its alignment.
            // https://doc.rust-lang.org/reference/type-layout.html
            let to = increase_to_alignment(to);

            // Update ptr for next iteration.
            self.next_tag_offset += to - from;

            &self.buffer[from..to]
        };

        // unwrap: We should not fail at this point.
        let tag = DynSizedStructure::ref_from_slice(slice).unwrap();
        Some(tag)
    }
}

#[cfg(test)]
mod tests {
    use crate::TagIter;
    use crate::test_utils::{AlignedBytes, DummyTestHeader};
    use core::borrow::Borrow;

    #[test]
    fn test_tag_iter() {
        #[rustfmt::skip]
        let bytes = AlignedBytes::new(
            [
                /* Some minimal tag.  */
                0xff, 0, 0, 0,
                8, 0, 0, 0,
                /* Some tag with payload.  */
                0xfe, 0, 0, 0,
                12, 0, 0, 0,
                1, 2, 3, 4,
                // Padding
                0, 0, 0, 0,
                /* End tag */
                0, 0, 0, 0,
                8, 0, 0, 0,
            ],
        );
        let mut iter = TagIter::<DummyTestHeader>::new(bytes.borrow());
        let first = iter.next().unwrap();
        assert_eq!(first.header().typ(), 0xff);
        assert_eq!(first.header().size(), 8);
        assert!(first.payload().is_empty());

        let second = iter.next().unwrap();
        assert_eq!(second.header().typ(), 0xfe);
        assert_eq!(second.header().size(), 12);
        assert_eq!(&second.payload(), &[1, 2, 3, 4]);

        let third = iter.next().unwrap();
        assert_eq!(third.header().typ(), 0);
        assert_eq!(third.header().size(), 8);
        assert!(first.payload().is_empty());

        assert_eq!(iter.next(), None);
    }
}