use crate::tag::TagHeader;
use crate::{StringError, TagIter, TagType, parse_slice_as_string};
use core::fmt::{Debug, Formatter};
use core::mem;
use multiboot2_common::{MaybeDynSized, Tag};
#[cfg(feature = "builder")]
use {alloc::boxed::Box, multiboot2_common::new_boxed};
#[derive(ptr_meta::Pointee, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[repr(C, align(8))]
pub struct ModuleTag {
header: TagHeader,
mod_start: u32,
mod_end: u32,
cmdline: [u8],
}
impl ModuleTag {
#[cfg(feature = "builder")]
#[must_use]
pub fn new(start: u32, end: u32, cmdline: &str) -> Box<Self> {
let header = TagHeader::new(Self::ID, 0);
assert!(end > start, "must have a size");
let start = start.to_ne_bytes();
let end = end.to_ne_bytes();
let cmdline = cmdline.as_bytes();
if cmdline.ends_with(&[0]) {
new_boxed(header, &[&start, &end, cmdline])
} else {
new_boxed(header, &[&start, &end, cmdline, &[0]])
}
}
pub fn cmdline(&self) -> Result<&str, StringError> {
parse_slice_as_string(&self.cmdline)
}
#[must_use]
pub const fn start_address(&self) -> u32 {
self.mod_start
}
#[must_use]
pub const fn end_address(&self) -> u32 {
self.mod_end
}
#[must_use]
pub const fn module_size(&self) -> u32 {
self.mod_end - self.mod_start
}
}
impl MaybeDynSized for ModuleTag {
type Header = TagHeader;
const BASE_SIZE: usize = mem::size_of::<TagHeader>() + 2 * mem::size_of::<u32>();
fn dst_len(header: &TagHeader) -> usize {
assert!(header.size as usize >= Self::BASE_SIZE);
header.size as usize - Self::BASE_SIZE
}
}
impl Tag for ModuleTag {
type IDType = TagType;
const ID: TagType = TagType::Module;
}
impl Debug for ModuleTag {
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
f.debug_struct("ModuleTag")
.field("type", &self.header.typ)
.field("size", &self.header.size)
.field("mod_start", &self.mod_start)
.field("mod_end", &self.mod_end)
.field("mod_size", &self.module_size())
.field("cmdline", &self.cmdline())
.finish()
}
}
pub const fn module_iter(iter: TagIter) -> ModuleIter {
ModuleIter { iter }
}
#[derive(Clone)]
pub struct ModuleIter<'a> {
iter: TagIter<'a>,
}
impl<'a> Iterator for ModuleIter<'a> {
type Item = &'a ModuleTag;
fn next(&mut self) -> Option<&'a ModuleTag> {
self.iter
.find(|tag| tag.header().typ == TagType::Module)
.map(|tag| tag.cast())
}
}
impl Debug for ModuleIter<'_> {
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
let mut list = f.debug_list();
self.clone().for_each(|tag| {
list.entry(&tag);
});
list.finish()
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::GenericInfoTag;
use core::borrow::Borrow;
use multiboot2_common::test_utils::AlignedBytes;
#[rustfmt::skip]
fn get_bytes() -> AlignedBytes<24> {
AlignedBytes::new([
TagType::Module.val() as u8, 0, 0, 0,
22, 0, 0, 0,
0x00, 0xff, 0, 0,
0xff, 0xff, 0, 0,
b'h', b'e', b'l', b'l', b'o', b'\0',
0, 0,
])
}
#[test]
fn test_parse_str() {
let bytes = get_bytes();
let tag = GenericInfoTag::ref_from_slice(bytes.borrow()).unwrap();
let tag = tag.cast::<ModuleTag>();
assert_eq!(tag.header.typ, TagType::Module);
assert_eq!(tag.cmdline(), Ok("hello"));
}
#[test]
#[cfg(feature = "builder")]
fn test_build_str() {
let tag = ModuleTag::new(0xff00, 0xffff, "hello");
let bytes = tag.as_bytes().as_ref();
let bytes = &bytes[..tag.header.size as usize];
assert_eq!(bytes, &get_bytes()[..tag.header().size as usize]);
assert_eq!(tag.cmdline(), Ok("hello"));
let tag = ModuleTag::new(0xff00, 0xffff, "hello\0");
let bytes = tag.as_bytes().as_ref();
let bytes = &bytes[..tag.header.size as usize];
assert_eq!(bytes, &get_bytes()[..tag.header().size as usize]);
assert_eq!(tag.cmdline(), Ok("hello"));
let tag = ModuleTag::new(0, 1, "AbCdEfGhUjK YEAH");
assert_eq!(tag.cmdline(), Ok("AbCdEfGhUjK YEAH"));
let tag = ModuleTag::new(0, 1, "AbCdEfGhUjK YEAH".repeat(42).as_str());
assert_eq!(tag.cmdline(), Ok("AbCdEfGhUjK YEAH".repeat(42).as_str()));
}
}