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