|
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