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);
}
}