multiboot2_common/
boxed.rsuse crate::{ALIGNMENT, Header, MaybeDynSized, increase_to_alignment};
use alloc::boxed::Box;
use core::alloc::Layout;
use core::mem;
use core::ops::Deref;
use core::ptr;
#[must_use]
pub fn new_boxed<T: MaybeDynSized<Metadata = usize> + ?Sized>(
mut header: T::Header,
additional_bytes_slices: &[&[u8]],
) -> Box<T> {
let additional_size = additional_bytes_slices
.iter()
.map(|b| b.len())
.sum::<usize>();
let tag_size = mem::size_of::<T::Header>() + additional_size;
header.set_size(tag_size);
let alloc_size = increase_to_alignment(tag_size);
let layout = Layout::from_size_align(alloc_size, ALIGNMENT).unwrap();
let heap_ptr = unsafe { alloc::alloc::alloc(layout) };
assert!(!heap_ptr.is_null());
{
let len = mem::size_of::<T::Header>();
let ptr = core::ptr::addr_of!(header);
unsafe {
ptr::copy_nonoverlapping(ptr.cast::<u8>(), heap_ptr, len);
}
}
{
let mut write_offset = mem::size_of::<T::Header>();
for &bytes in additional_bytes_slices {
let len = bytes.len();
let src = bytes.as_ptr();
unsafe {
let dst = heap_ptr.add(write_offset);
ptr::copy_nonoverlapping(src, dst, len);
write_offset += len;
}
}
}
let ptr: *mut T = ptr_meta::from_raw_parts_mut(heap_ptr.cast(), T::dst_len(&header));
let reference = unsafe { Box::from_raw(ptr) };
assert_eq!(
mem::size_of_val(reference.deref()),
alloc_size,
"Allocation should match Rusts expectation"
);
reference
}
#[must_use]
pub fn clone_dyn<T: MaybeDynSized<Metadata = usize> + ?Sized>(tag: &T) -> Box<T> {
new_boxed(tag.header().clone(), &[tag.payload()])
}
#[cfg(test)]
mod tests {
use super::*;
use crate::Tag;
use crate::test_utils::{DummyDstTag, DummyTestHeader};
#[test]
fn test_new_boxed() {
let header = DummyTestHeader::new(DummyDstTag::ID, 0);
let tag = new_boxed::<DummyDstTag>(header, &[&[0, 1, 2, 3]]);
assert_eq!(tag.header().typ(), 42);
assert_eq!(tag.payload(), &[0, 1, 2, 3]);
let header = DummyTestHeader::new(0xdead_beef, 0);
let tag = new_boxed::<DummyDstTag>(header, &[&[0], &[1], &[2, 3]]);
assert_eq!(tag.header().typ(), 0xdead_beef);
assert_eq!(tag.payload(), &[0, 1, 2, 3]);
}
#[test]
fn test_clone_tag() {
let header = DummyTestHeader::new(DummyDstTag::ID, 0);
let tag = new_boxed::<DummyDstTag>(header, &[&[0, 1, 2, 3]]);
assert_eq!(tag.header().typ(), 42);
assert_eq!(tag.payload(), &[0, 1, 2, 3]);
let _cloned = clone_dyn(tag.as_ref());
}
}