multiboot2-common 0.7.0

Common helpers for the `multiboot2` and `multiboot2-header` crates.
Documentation
//! Various test utilities.

#![allow(missing_docs)]

use crate::{Header, MaybeDynSized, Tag};
use core::borrow::Borrow;
use core::ops::Deref;

/// Helper to 8-byte align the underlying bytes, as mandated in the Multiboot2
/// spec.
///
/// With this type, one can create manual and raw Multiboot2 boot information or
/// just the bytes for simple tags, in a manual and raw approach.
#[derive(Debug)]
#[repr(C, align(8))]
pub struct AlignedBytes<const N: usize>(pub [u8; N]);

const _: () = assert!(align_of::<AlignedBytes<0>>() == crate::ALIGNMENT);

impl<const N: usize> AlignedBytes<N> {
    /// Creates a new type.
    #[must_use]
    pub const fn new(bytes: [u8; N]) -> Self {
        Self(bytes)
    }
}

impl<const N: usize> Borrow<[u8; N]> for AlignedBytes<N> {
    fn borrow(&self) -> &[u8; N] {
        &self.0
    }
}

impl<const N: usize> Borrow<[u8]> for AlignedBytes<N> {
    fn borrow(&self) -> &[u8] {
        &self.0
    }
}

impl<const N: usize> Deref for AlignedBytes<N> {
    type Target = [u8; N];

    fn deref(&self) -> &Self::Target {
        &self.0
    }
}

/// Dummy test header.
#[derive(Clone, Debug, PartialEq, Eq)]
#[repr(C, align(8))]
pub struct DummyTestHeader {
    typ: u32,
    size: u32,
}

impl DummyTestHeader {
    #[must_use]
    pub const fn new(typ: u32, size: u32) -> Self {
        Self { typ, size }
    }

    #[must_use]
    pub const fn typ(&self) -> u32 {
        self.typ
    }

    #[must_use]
    pub const fn size(&self) -> u32 {
        self.size
    }
}

// SAFETY: The header is a padding-free repr(C) struct of raw integers, and
// any bit pattern is valid for it.
unsafe impl Header for DummyTestHeader {
    fn total_size(&self) -> usize {
        self.size as usize
    }

    fn set_size(&mut self, total_size: usize) {
        self.size = u32::try_from(total_size).unwrap();
    }
}

#[derive(Debug, PartialEq, Eq, ptr_meta::Pointee)]
#[repr(C, align(8))]
pub struct DummyDstTag {
    header: DummyTestHeader,
    payload: [u8],
}

impl DummyDstTag {
    const BASE_SIZE: usize = size_of::<DummyTestHeader>();

    #[must_use]
    pub const fn header(&self) -> &DummyTestHeader {
        &self.header
    }

    #[must_use]
    pub const fn payload(&self) -> &[u8] {
        &self.payload
    }
}

// SAFETY: The tag is repr(C) with the header as first field, any bit
// pattern is valid, and `BASE_SIZE`/`dst_len` match the ABI.
unsafe impl MaybeDynSized for DummyDstTag {
    type Header = DummyTestHeader;

    const BASE_SIZE: usize = size_of::<DummyTestHeader>();

    fn dst_len(header: &Self::Header) -> Self::Metadata {
        assert!(header.size as usize >= Self::BASE_SIZE);
        header.size as usize - Self::BASE_SIZE
    }
}

impl Tag for DummyDstTag {
    type IDType = u32;
    const ID: Self::IDType = 42;
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn abi() {
        let bytes = AlignedBytes([0]);
        assert_eq!(bytes.as_ptr().align_offset(8), 0);
        assert_eq!((&raw const bytes[0]).align_offset(8), 0);

        let bytes = AlignedBytes([0, 1, 2, 3, 4, 5, 6, 7]);
        assert_eq!(bytes.as_ptr().align_offset(8), 0);
        assert_eq!((&raw const bytes[0]).align_offset(8), 0);

        let bytes = AlignedBytes([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]);
        assert_eq!(bytes.as_ptr().align_offset(8), 0);
        assert_eq!((&raw const bytes[0]).align_offset(8), 0);
        assert_eq!((&raw const bytes[7]).align_offset(8), 1);
        assert_eq!((&raw const bytes[8]).align_offset(8), 0);
        assert_eq!((&raw const bytes[9]).align_offset(8), 7);
    }
}