|
@@ -8,37 +8,100 @@ pub use information::InformationBuilder;
|
|
|
use alloc::alloc::alloc;
|
|
|
use alloc::boxed::Box;
|
|
|
use core::alloc::Layout;
|
|
|
-use core::mem::size_of;
|
|
|
+use core::mem::{align_of_val, size_of, size_of_val};
|
|
|
+use core::ops::Deref;
|
|
|
|
|
|
-use crate::{TagTrait, TagTypeId};
|
|
|
+use crate::{Tag, TagTrait, TagTypeId};
|
|
|
|
|
|
/// Create a boxed tag with the given content.
|
|
|
+///
|
|
|
+/// # Parameters
|
|
|
+/// - `typ` - The given [`TagTypeId`]
|
|
|
+/// - `content` - All payload bytes of the DST tag without the tag type or the
|
|
|
+/// size. The memory is only read and can be discarded afterwards.
|
|
|
pub(super) fn boxed_dst_tag<T: TagTrait<Metadata = usize> + ?Sized>(
|
|
|
typ: impl Into<TagTypeId>,
|
|
|
content: &[u8],
|
|
|
) -> Box<T> {
|
|
|
- // based on https://stackoverflow.com/a/64121094/2192464
|
|
|
- let (layout, size_offset) = Layout::new::<TagTypeId>()
|
|
|
- .extend(Layout::new::<u32>())
|
|
|
- .unwrap();
|
|
|
- let (layout, inner_offset) = layout
|
|
|
- .extend(Layout::array::<usize>(content.len()).unwrap())
|
|
|
- .unwrap();
|
|
|
+ // Currently, I do not find a nice way of making this dynamic so that also
|
|
|
+ // miri is happy. But it seems that 4 is fine.
|
|
|
+ const ALIGN: usize = 4;
|
|
|
+
|
|
|
+ let tag_size = size_of::<TagTypeId>() + size_of::<u32>() + content.len();
|
|
|
+ // round up to the next multiple of 8
|
|
|
+ // Rust uses this convention for all types. I found out so by checking
|
|
|
+ // miris output of the corresponding unit test.
|
|
|
+ let alloc_size = (tag_size + 7) & !0b111;
|
|
|
+ let layout = Layout::from_size_align(alloc_size, ALIGN).unwrap();
|
|
|
let ptr = unsafe { alloc(layout) };
|
|
|
assert!(!ptr.is_null());
|
|
|
+
|
|
|
+ // write tag content to memory
|
|
|
unsafe {
|
|
|
- // initialize the content as good as we can
|
|
|
- ptr.cast::<TagTypeId>().write(typ.into());
|
|
|
- ptr.add(size_offset).cast::<u32>().write(
|
|
|
- (content.len() + size_of::<TagTypeId>() + size_of::<u32>())
|
|
|
- .try_into()
|
|
|
- .unwrap(),
|
|
|
- );
|
|
|
- // initialize body
|
|
|
- let content_ptr = ptr.add(inner_offset);
|
|
|
- for (idx, val) in content.iter().enumerate() {
|
|
|
- content_ptr.add(idx).write(*val);
|
|
|
+ // write tag type
|
|
|
+ let ptrx = ptr.cast::<TagTypeId>();
|
|
|
+ ptrx.write(typ.into());
|
|
|
+
|
|
|
+ // write tag size
|
|
|
+ let ptrx = ptrx.add(1).cast::<u32>();
|
|
|
+ ptrx.write(tag_size as u32);
|
|
|
+
|
|
|
+ // write rest of content
|
|
|
+ let ptrx = ptrx.add(1).cast::<u8>();
|
|
|
+ let tag_content_slice = core::slice::from_raw_parts_mut(ptrx, content.len());
|
|
|
+ for (i, &byte) in content.iter().enumerate() {
|
|
|
+ tag_content_slice[i] = byte;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ let base_tag = unsafe { &*ptr.cast::<Tag>() };
|
|
|
+ let raw: *mut T = ptr_meta::from_raw_parts_mut(ptr.cast(), T::dst_size(base_tag));
|
|
|
+
|
|
|
+ unsafe {
|
|
|
+ let boxed = Box::from_raw(raw);
|
|
|
+ let reference: &T = boxed.deref();
|
|
|
+ // If this panics, please create an issue on GitHub.
|
|
|
+ assert_eq!(size_of_val(reference), alloc_size);
|
|
|
+ assert_eq!(align_of_val(reference), ALIGN);
|
|
|
+ boxed
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+#[cfg(test)]
|
|
|
+mod tests {
|
|
|
+ use super::*;
|
|
|
+
|
|
|
+ const METADATA_SIZE: usize = 8;
|
|
|
+
|
|
|
+ #[derive(ptr_meta::Pointee)]
|
|
|
+ #[repr(C)]
|
|
|
+ struct CustomTag {
|
|
|
+ typ: TagTypeId,
|
|
|
+ size: u32,
|
|
|
+ string: [u8],
|
|
|
+ }
|
|
|
+
|
|
|
+ impl CustomTag {
|
|
|
+ fn string(&self) -> Result<&str, core::str::Utf8Error> {
|
|
|
+ Tag::get_dst_str_slice(&self.string)
|
|
|
}
|
|
|
- Box::from_raw(ptr_meta::from_raw_parts_mut(ptr as *mut (), content.len()))
|
|
|
+ }
|
|
|
+
|
|
|
+ impl TagTrait for CustomTag {
|
|
|
+ fn dst_size(base_tag: &Tag) -> usize {
|
|
|
+ assert!(base_tag.size as usize >= METADATA_SIZE);
|
|
|
+ base_tag.size as usize - METADATA_SIZE
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ #[test]
|
|
|
+ fn test_boxed_dst_tag() {
|
|
|
+ let tag_type_id = 1337_u32;
|
|
|
+ let content = "hallo";
|
|
|
+
|
|
|
+ let tag = boxed_dst_tag::<CustomTag>(tag_type_id, content.as_bytes());
|
|
|
+ assert_eq!(tag.typ, tag_type_id);
|
|
|
+ assert_eq!(tag.size as usize, METADATA_SIZE + content.len());
|
|
|
+ assert_eq!(tag.string(), Ok(content));
|
|
|
}
|
|
|
}
|