|  | @@ -1,9 +1,13 @@
 | 
	
		
			
				|  |  |  //! Various utilities.
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -use crate::ALIGNMENT;
 | 
	
		
			
				|  |  | +use crate::tag::GenericTag;
 | 
	
		
			
				|  |  | +use crate::{TagHeader, TagTrait, TagType, ALIGNMENT};
 | 
	
		
			
				|  |  |  use core::fmt;
 | 
	
		
			
				|  |  |  use core::fmt::{Display, Formatter};
 | 
	
		
			
				|  |  |  use core::str::Utf8Error;
 | 
	
		
			
				|  |  | +use core::{ptr, slice};
 | 
	
		
			
				|  |  | +#[cfg(feature = "builder")]
 | 
	
		
			
				|  |  | +use {alloc::alloc::Layout, alloc::boxed::Box};
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  /// Error type describing failures when parsing the string from a tag.
 | 
	
		
			
				|  |  |  #[derive(Debug, PartialEq, Eq, Clone)]
 | 
	
	
		
			
				|  | @@ -31,6 +35,38 @@ impl core::error::Error for StringError {
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +/// Creates a new tag implementing [`TagTrait`] on the heap. This works for
 | 
	
		
			
				|  |  | +/// sized and unsized tags. However, it only makes sense to use this for tags
 | 
	
		
			
				|  |  | +/// that are DSTs (unsized), as for the sized ones, you can call a regular
 | 
	
		
			
				|  |  | +/// constructor and box the result.
 | 
	
		
			
				|  |  | +///
 | 
	
		
			
				|  |  | +/// # Parameters
 | 
	
		
			
				|  |  | +/// - `additional_bytes`: All bytes apart from the default [`TagHeader`] that
 | 
	
		
			
				|  |  | +///                       are included into the tag.
 | 
	
		
			
				|  |  | +#[cfg(feature = "alloc")]
 | 
	
		
			
				|  |  | +pub fn new_boxed<T: TagTrait + ?Sized>(additional_bytes: &[u8]) -> Box<T> {
 | 
	
		
			
				|  |  | +    let size = size_of::<TagHeader>() + additional_bytes.iter().len();
 | 
	
		
			
				|  |  | +    let alloc_size = increase_to_alignment(size);
 | 
	
		
			
				|  |  | +    let layout = Layout::from_size_align(alloc_size, ALIGNMENT).unwrap();
 | 
	
		
			
				|  |  | +    let heap_ptr = unsafe { alloc::alloc::alloc(layout) };
 | 
	
		
			
				|  |  | +    assert!(!heap_ptr.is_null());
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    unsafe {
 | 
	
		
			
				|  |  | +        heap_ptr.cast::<u32>().write(T::ID.val());
 | 
	
		
			
				|  |  | +        heap_ptr.cast::<u32>().add(1).write(size as u32);
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +    unsafe {
 | 
	
		
			
				|  |  | +        let ptr = heap_ptr.add(size_of::<TagHeader>());
 | 
	
		
			
				|  |  | +        ptr::copy_nonoverlapping(additional_bytes.as_ptr(), ptr, additional_bytes.len());
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    let header = unsafe { heap_ptr.cast::<TagHeader>().as_ref() }.unwrap();
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    let ptr = ptr_meta::from_raw_parts_mut(heap_ptr.cast(), T::dst_len(header));
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    unsafe { Box::from_raw(ptr) }
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  /// Parses the provided byte sequence as Multiboot string, which maps to a
 | 
	
		
			
				|  |  |  /// [`str`].
 | 
	
		
			
				|  |  |  pub fn parse_slice_as_string(bytes: &[u8]) -> Result<&str, StringError> {
 | 
	
	
		
			
				|  | @@ -52,6 +88,8 @@ pub const fn increase_to_alignment(size: usize) -> usize {
 | 
	
		
			
				|  |  |  #[cfg(test)]
 | 
	
		
			
				|  |  |  mod tests {
 | 
	
		
			
				|  |  |      use super::*;
 | 
	
		
			
				|  |  | +    use crate::tag::GenericTag;
 | 
	
		
			
				|  |  | +    use crate::CommandLineTag;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      #[test]
 | 
	
		
			
				|  |  |      fn test_parse_slice_as_string() {
 | 
	
	
		
			
				|  | @@ -87,4 +125,13 @@ mod tests {
 | 
	
		
			
				|  |  |          assert_eq!(increase_to_alignment(8), 8);
 | 
	
		
			
				|  |  |          assert_eq!(increase_to_alignment(9), 16);
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    #[test]
 | 
	
		
			
				|  |  | +    fn test_new_boxed() {
 | 
	
		
			
				|  |  | +        let tag = new_boxed::<GenericTag>(&[0, 1, 2, 3]);
 | 
	
		
			
				|  |  | +        assert_eq!(tag.header().typ, GenericTag::ID);
 | 
	
		
			
				|  |  | +        {}
 | 
	
		
			
				|  |  | +        let tag = new_boxed::<CommandLineTag>("hello\0".as_bytes());
 | 
	
		
			
				|  |  | +        assert_eq!(tag.cmdline(), Ok("hello"));
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  |  }
 |