@@ -8,37 +8,100 @@ pub use information::InformationBuilder;
8
8
use alloc:: alloc:: alloc;
9
9
use alloc:: boxed:: Box ;
10
10
use core:: alloc:: Layout ;
11
- use core:: mem:: size_of;
11
+ use core:: mem:: { align_of_val, size_of, size_of_val} ;
12
+ use core:: ops:: Deref ;
12
13
13
- use crate :: { TagTrait , TagTypeId } ;
14
+ use crate :: { Tag , TagTrait , TagTypeId } ;
14
15
15
16
/// Create a boxed tag with the given content.
17
+ ///
18
+ /// # Parameters
19
+ /// - `typ` - The given [`TagTypeId`]
20
+ /// - `content` - All payload bytes of the DST tag without the tag type or the
21
+ /// size. The memory is only read and can be discarded afterwards.
16
22
pub ( super ) fn boxed_dst_tag < T : TagTrait < Metadata = usize > + ?Sized > (
17
23
typ : impl Into < TagTypeId > ,
18
24
content : & [ u8 ] ,
19
25
) -> Box < T > {
20
- // based on https://stackoverflow.com/a/64121094/2192464
21
- let ( layout, size_offset) = Layout :: new :: < TagTypeId > ( )
22
- . extend ( Layout :: new :: < u32 > ( ) )
23
- . unwrap ( ) ;
24
- let ( layout, inner_offset) = layout
25
- . extend ( Layout :: array :: < usize > ( content. len ( ) ) . unwrap ( ) )
26
- . unwrap ( ) ;
26
+ // Currently, I do not find a nice way of making this dynamic so that also
27
+ // miri is happy. But it seems that 4 is fine.
28
+ const ALIGN : usize = 4 ;
29
+
30
+ let tag_size = size_of :: < TagTypeId > ( ) + size_of :: < u32 > ( ) + content. len ( ) ;
31
+ // round up to the next multiple of 8
32
+ // Rust uses this convention for all types. I found out so by checking
33
+ // miris output of the corresponding unit test.
34
+ let alloc_size = ( tag_size + 7 ) & !0b111 ;
35
+ let layout = Layout :: from_size_align ( alloc_size, ALIGN ) . unwrap ( ) ;
27
36
let ptr = unsafe { alloc ( layout) } ;
28
37
assert ! ( !ptr. is_null( ) ) ;
38
+
39
+ // write tag content to memory
29
40
unsafe {
30
- // initialize the content as good as we can
31
- ptr. cast :: < TagTypeId > ( ) . write ( typ. into ( ) ) ;
32
- ptr. add ( size_offset) . cast :: < u32 > ( ) . write (
33
- ( content. len ( ) + size_of :: < TagTypeId > ( ) + size_of :: < u32 > ( ) )
34
- . try_into ( )
35
- . unwrap ( ) ,
36
- ) ;
37
- // initialize body
38
- let content_ptr = ptr. add ( inner_offset) ;
39
- for ( idx, val) in content. iter ( ) . enumerate ( ) {
40
- content_ptr. add ( idx) . write ( * val) ;
41
+ // write tag type
42
+ let ptrx = ptr. cast :: < TagTypeId > ( ) ;
43
+ ptrx. write ( typ. into ( ) ) ;
44
+
45
+ // write tag size
46
+ let ptrx = ptrx. add ( 1 ) . cast :: < u32 > ( ) ;
47
+ ptrx. write ( tag_size as u32 ) ;
48
+
49
+ // write rest of content
50
+ let ptrx = ptrx. add ( 1 ) . cast :: < u8 > ( ) ;
51
+ let tag_content_slice = core:: slice:: from_raw_parts_mut ( ptrx, content. len ( ) ) ;
52
+ for ( i, & byte) in content. iter ( ) . enumerate ( ) {
53
+ tag_content_slice[ i] = byte;
54
+ }
55
+ }
56
+
57
+ let base_tag = unsafe { & * ptr. cast :: < Tag > ( ) } ;
58
+ let raw: * mut T = ptr_meta:: from_raw_parts_mut ( ptr. cast ( ) , T :: dst_size ( base_tag) ) ;
59
+
60
+ unsafe {
61
+ let boxed = Box :: from_raw ( raw) ;
62
+ let reference: & T = boxed. deref ( ) ;
63
+ // If this panics, please create an issue on GitHub.
64
+ assert_eq ! ( size_of_val( reference) , alloc_size) ;
65
+ assert_eq ! ( align_of_val( reference) , ALIGN ) ;
66
+ boxed
67
+ }
68
+ }
69
+
70
+ #[ cfg( test) ]
71
+ mod tests {
72
+ use super :: * ;
73
+
74
+ const METADATA_SIZE : usize = 8 ;
75
+
76
+ #[ derive( ptr_meta:: Pointee ) ]
77
+ #[ repr( C ) ]
78
+ struct CustomTag {
79
+ typ : TagTypeId ,
80
+ size : u32 ,
81
+ string : [ u8 ] ,
82
+ }
83
+
84
+ impl CustomTag {
85
+ fn string ( & self ) -> Result < & str , core:: str:: Utf8Error > {
86
+ Tag :: get_dst_str_slice ( & self . string )
41
87
}
42
- Box :: from_raw ( ptr_meta:: from_raw_parts_mut ( ptr as * mut ( ) , content. len ( ) ) )
88
+ }
89
+
90
+ impl TagTrait for CustomTag {
91
+ fn dst_size ( base_tag : & Tag ) -> usize {
92
+ assert ! ( base_tag. size as usize >= METADATA_SIZE ) ;
93
+ base_tag. size as usize - METADATA_SIZE
94
+ }
95
+ }
96
+
97
+ #[ test]
98
+ fn test_boxed_dst_tag ( ) {
99
+ let tag_type_id = 1337_u32 ;
100
+ let content = "hallo" ;
101
+
102
+ let tag = boxed_dst_tag :: < CustomTag > ( tag_type_id, content. as_bytes ( ) ) ;
103
+ assert_eq ! ( tag. typ, tag_type_id) ;
104
+ assert_eq ! ( tag. size as usize , METADATA_SIZE + content. len( ) ) ;
105
+ assert_eq ! ( tag. string( ) , Ok ( content) ) ;
43
106
}
44
107
}
0 commit comments