@@ -8,37 +8,100 @@ pub use information::InformationBuilder;
88use alloc:: alloc:: alloc;
99use alloc:: boxed:: Box ;
1010use 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 ;
1213
13- use crate :: { TagTrait , TagTypeId } ;
14+ use crate :: { Tag , TagTrait , TagTypeId } ;
1415
1516/// 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.
1622pub ( super ) fn boxed_dst_tag < T : TagTrait < Metadata = usize > + ?Sized > (
1723 typ : impl Into < TagTypeId > ,
1824 content : & [ u8 ] ,
1925) -> 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 ( ) ;
2736 let ptr = unsafe { alloc ( layout) } ;
2837 assert ! ( !ptr. is_null( ) ) ;
38+
39+ // write tag content to memory
2940 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 )
4187 }
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) ) ;
43106 }
44107}
0 commit comments