|
1 | 1 | //! Module for the builder-feature. |
2 | 2 |
|
| 3 | +mod boxed_dst; |
3 | 4 | mod information; |
4 | 5 |
|
5 | | -pub(crate) use information::AsBytes; |
| 6 | +// This must by public to support external people to create boxed DSTs. |
| 7 | +pub use boxed_dst::BoxedDst; |
6 | 8 | pub use information::InformationBuilder; |
7 | 9 |
|
8 | | -use alloc::alloc::alloc; |
9 | | -use core::alloc::Layout; |
10 | | -use core::marker::PhantomData; |
11 | | -use core::mem::size_of; |
12 | | -use core::ops::Deref; |
13 | | -use core::ptr::NonNull; |
14 | | - |
15 | | -use crate::{Tag, TagTrait, TagTypeId}; |
16 | | - |
17 | | -/// A helper type to create boxed DST, i.e., tags with a dynamic size for the |
18 | | -/// builder. This is tricky in Rust. This type behaves similar to the regular |
19 | | -/// `Box` type except that it ensure the same layout is used for the (explicit) |
20 | | -/// allocation and the (implicit) deallocation of memory. Otherwise, I didn't |
21 | | -/// found any way to figure out the right layout for a DST. Miri always reported |
22 | | -/// issues that the deallocation used a wrong layout. |
23 | | -/// |
24 | | -/// Technically, I'm certain this code is memory safe. But with this type, I |
25 | | -/// also can convince miri that it is. |
26 | | -#[derive(Debug, Eq)] |
27 | | -pub struct BoxedDst<T: ?Sized> { |
28 | | - ptr: core::ptr::NonNull<T>, |
29 | | - layout: Layout, |
30 | | - // marker: I used this only as the regular Box impl also does it. |
31 | | - _marker: PhantomData<T>, |
32 | | -} |
33 | | - |
34 | | -impl<T: TagTrait<Metadata = usize> + ?Sized> BoxedDst<T> { |
35 | | - /// Create a boxed tag with the given content. |
36 | | - /// |
37 | | - /// # Parameters |
38 | | - /// - `content` - All payload bytes of the DST tag without the tag type or |
39 | | - /// the size. The memory is only read and can be discarded |
40 | | - /// afterwards. |
41 | | - pub(crate) fn new(content: &[u8]) -> Self { |
42 | | - // Currently, I do not find a nice way of making this dynamic so that |
43 | | - // also miri is guaranteed to be happy. But it seems that 4 is fine |
44 | | - // here. I do have control over allocation and deallocation. |
45 | | - const ALIGN: usize = 4; |
46 | | - |
47 | | - let tag_size = size_of::<TagTypeId>() + size_of::<u32>() + content.len(); |
48 | | - |
49 | | - // By using miri, I could figure out that there often are problems where |
50 | | - // miri thinks an allocation is smaller then necessary. Most probably |
51 | | - // due to not packed structs. Using packed structs however |
52 | | - // (especially with DSTs), is a crazy ass pain and unusable :/ Therefore, |
53 | | - // the best solution I can think of is to allocate a few byte more than |
54 | | - // necessary. I think that during runtime, everything works fine and |
55 | | - // that no memory issues are present. |
56 | | - let alloc_size = (tag_size + 7) & !7; // align to next 8 byte boundary |
57 | | - let layout = Layout::from_size_align(alloc_size, ALIGN).unwrap(); |
58 | | - let ptr = unsafe { alloc(layout) }; |
59 | | - assert!(!ptr.is_null()); |
60 | | - |
61 | | - // write tag content to memory |
62 | | - unsafe { |
63 | | - // write tag type |
64 | | - let ptrx = ptr.cast::<TagTypeId>(); |
65 | | - ptrx.write(T::ID.into()); |
66 | | - |
67 | | - // write tag size |
68 | | - let ptrx = ptrx.add(1).cast::<u32>(); |
69 | | - ptrx.write(tag_size as u32); |
70 | | - |
71 | | - // write rest of content |
72 | | - let ptrx = ptrx.add(1).cast::<u8>(); |
73 | | - let tag_content_slice = core::slice::from_raw_parts_mut(ptrx, content.len()); |
74 | | - for (i, &byte) in content.iter().enumerate() { |
75 | | - tag_content_slice[i] = byte; |
76 | | - } |
77 | | - } |
78 | | - |
79 | | - let base_tag = unsafe { &*ptr.cast::<Tag>() }; |
80 | | - let raw: *mut T = ptr_meta::from_raw_parts_mut(ptr.cast(), T::dst_size(base_tag)); |
81 | | - |
82 | | - Self { |
83 | | - ptr: NonNull::new(raw).unwrap(), |
84 | | - layout, |
85 | | - _marker: PhantomData, |
86 | | - } |
87 | | - } |
88 | | -} |
89 | | - |
90 | | -impl<T: ?Sized> Drop for BoxedDst<T> { |
91 | | - fn drop(&mut self) { |
92 | | - unsafe { alloc::alloc::dealloc(self.ptr.as_ptr().cast(), self.layout) } |
93 | | - } |
94 | | -} |
95 | | - |
96 | | -impl<T: ?Sized> Deref for BoxedDst<T> { |
97 | | - type Target = T; |
98 | | - fn deref(&self) -> &Self::Target { |
99 | | - unsafe { self.ptr.as_ref() } |
100 | | - } |
101 | | -} |
102 | | - |
103 | | -impl<T: ?Sized + PartialEq> PartialEq for BoxedDst<T> { |
104 | | - fn eq(&self, other: &Self) -> bool { |
105 | | - self.deref().eq(other.deref()) |
106 | | - } |
107 | | -} |
108 | | - |
109 | | -#[cfg(test)] |
110 | | -mod tests { |
111 | | - use super::*; |
112 | | - use crate::TagType; |
113 | | - |
114 | | - const METADATA_SIZE: usize = 8; |
115 | | - |
116 | | - #[derive(ptr_meta::Pointee)] |
117 | | - #[repr(C)] |
118 | | - struct CustomTag { |
119 | | - typ: TagTypeId, |
120 | | - size: u32, |
121 | | - string: [u8], |
122 | | - } |
123 | | - |
124 | | - impl CustomTag { |
125 | | - fn string(&self) -> Result<&str, core::str::Utf8Error> { |
126 | | - Tag::get_dst_str_slice(&self.string) |
127 | | - } |
128 | | - } |
129 | | - |
130 | | - impl TagTrait for CustomTag { |
131 | | - const ID: TagType = TagType::Custom(0x1337); |
132 | | - |
133 | | - fn dst_size(base_tag: &Tag) -> usize { |
134 | | - assert!(base_tag.size as usize >= METADATA_SIZE); |
135 | | - base_tag.size as usize - METADATA_SIZE |
136 | | - } |
137 | | - } |
138 | | - |
139 | | - #[test] |
140 | | - fn test_boxed_dst_tag() { |
141 | | - let content = "hallo"; |
142 | | - |
143 | | - let tag = BoxedDst::<CustomTag>::new(content.as_bytes()); |
144 | | - assert_eq!(tag.typ, CustomTag::ID); |
145 | | - assert_eq!(tag.size as usize, METADATA_SIZE + content.len()); |
146 | | - assert_eq!(tag.string(), Ok(content)); |
| 10 | +/// Helper trait for all structs that need to be serialized that do not |
| 11 | +/// implement `TagTrait`. |
| 12 | +pub trait AsBytes: Sized { |
| 13 | + fn as_bytes(&self) -> &[u8] { |
| 14 | + let ptr = core::ptr::addr_of!(*self); |
| 15 | + let size = core::mem::size_of::<Self>(); |
| 16 | + unsafe { core::slice::from_raw_parts(ptr.cast(), size) } |
147 | 17 | } |
148 | 18 | } |
0 commit comments