1#![doc = include_str!("../README.md")]
2
3#![feature(ptr_metadata)]
4#![feature(decl_macro)]
5#![feature(coerce_unsized)]
6#![feature(unsize)]
7
8mod dyn_arg;
9
10pub use dyn_arg::*;
11
12#[cfg(feature = "derive")]
13pub use dyn_struct_derive2::DynStruct;
14
15use std::mem::{align_of, size_of};
16use std::ptr::{addr_of_mut, null_mut, Pointee};
17use transmute::transmute;
18
19#[repr(C)]
20#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
21pub struct DynStruct<Header, Tail: ?Sized> {
22 pub header: Header,
23 pub tail: Tail,
24}
25
26impl<Header, Tail: ?Sized> DynStruct<Header, Tail> {
27 #[inline]
29 pub fn new(header: Header, tail: DynArg<Tail>) -> Box<Self> {
30 let size = Self::size(&tail);
31 let align = Self::align(&tail);
32 let metadata = tail.metadata();
34
35 let thin_ptr = if size == 0 {
37 null_mut() as *mut ()
39 } else {
40 unsafe {
41 let layout = std::alloc::Layout::from_size_align(size, align).unwrap();
43 let thin_ptr = std::alloc::alloc(layout) as *mut ();
44
45 if thin_ptr.is_null() {
47 std::alloc::handle_alloc_error(layout)
48 }
49
50 thin_ptr
51 }
52 };
53
54 let ptr: *mut Self = unsafe { transmute((thin_ptr, metadata)) };
56
57 unsafe {
58 let header_ptr = addr_of_mut!((*ptr).header);
60 let tail_ptr = addr_of_mut!((*ptr).tail);
61
62 header_ptr.write(header);
64 tail.write_into(tail_ptr);
65 };
66
67
68 unsafe { Box::from_raw(ptr) }
69 }
70
71
72 pub unsafe fn transmute<T: Pointee<Metadata = <Tail as Pointee>::Metadata> + ?Sized>(self: Box<Self>) -> Box<T> {
78 self.more_unsafe_transmute()
79 }
80
81 pub unsafe fn more_unsafe_transmute<T: ?Sized>(self: Box<Self>) -> Box<T> {
87 let ptr = Box::into_raw(self);
88 let ptr: *mut T = transmute(ptr);
89 Box::from_raw(ptr)
90 }
91
92 #[inline]
93 fn align(tail: &DynArg<Tail>) -> usize {
94 usize::max(align_of::<Header>(), tail.align())
95 }
96
97 #[inline]
100 fn size(tail: &DynArg<Tail>) -> usize {
101 let header = size_of::<Header>();
102 let tail_size = tail.size();
103 let tail_align = tail.align();
104
105 let padding = if header % tail_align == 0 {
106 0
107 } else {
108 tail_align - header % tail_align
109 };
110
111 header + padding + tail_size
112 }
113}
114
115#[cfg(test)]
116mod tests {
117 use std::fmt::Display;
118 use std::rc::Rc;
119 use crate::dyn_arg;
120 use super::*;
121
122 #[test]
123 fn sized_types() {
124 let tail = [1u64, 2, 3, 4];
125 let mixed = DynStruct::new((true, 32u16), dyn_arg!(tail));
126 assert_eq!(mixed.header, (true, 32u16));
127 assert_eq!(&mixed.tail, &[1, 2, 3, 4]);
128 }
129
130 #[test]
131 fn unsized_types() {
132 let tail = [1u64, 2, 3, 4];
133 let mixed = DynStruct::new((true, 32u16), dyn_arg!(tail) as DynArg<[u64]>);
134 assert_eq!(mixed.header, (true, 32u16));
135 assert_eq!(&mixed.tail, &[1, 2, 3, 4]);
136 }
137
138 #[test]
139 fn zero_sized_types() {
140 let tail = [(), ()];
141 let zero = DynStruct::new((), dyn_arg!(tail));
142 assert_eq!(zero.header, ());
143 assert_eq!(&zero.tail, &[(), ()]);
144 }
145
146 #[test]
147 fn non_copy_non_slice_types() {
148 let tail = Rc::new(42) as Rc<dyn Display>;
149 let tail_weak = Rc::downgrade(&tail);
150 let mixed = DynStruct::new(41, dyn_arg!(tail));
151 assert_eq!(mixed.header, 41);
152 assert_eq!(format!("{}", mixed.tail), "42");
153
154 assert!(tail_weak.upgrade().is_some());
156
157 drop(mixed); assert!(tail_weak.upgrade().is_none());
162 }
163
164 #[repr(C)]
165 struct SomeStruct {
166 foo: bool,
167 bar: usize,
168 baz: [u32]
169 }
170
171 #[test]
172 fn transmute() {
173 let tail = [1u32, 2, 3, 4];
174 let mixed = DynStruct::new((false, 50usize), dyn_arg!(tail) as DynArg<[u32]>);
175 assert_eq!(mixed.header, (false, 50usize));
176 assert_eq!(&mixed.tail, &[1u32, 2, 3, 4]);
177
178 let mixed = unsafe { mixed.transmute::<SomeStruct>() };
179 assert_eq!(mixed.foo, false);
180 assert_eq!(mixed.bar, 50);
181 assert_eq!(&mixed.baz, &[1u32, 2, 3, 4]);
182
183 }
188}
189