1#[cfg(any(feature = "alloc", doc))]
4use alloc::boxed::Box as StdBox;
5use core::{
6 alloc::Layout,
7 hint::unreachable_unchecked,
8 marker::{PhantomData, PhantomPinned},
9 mem::MaybeUninit,
10 pin::Pin,
11 ptr::NonNull,
12};
13
14pub use elain::{Align, Alignment};
15
16pub type DefaultStorage = RawOrBox<{ 128 * size_of::<usize>() }>;
18
19pub unsafe trait Storage: Sized {
26 fn new<T>(data: T) -> Self;
28 fn ptr(&self) -> NonNull<()>;
30 fn ptr_mut(&mut self) -> NonNull<()>;
32 unsafe fn as_ref<T>(&self) -> &T {
38 unsafe { self.ptr().cast().as_ref() }
40 }
41 unsafe fn as_mut<T>(&mut self) -> &mut T {
47 unsafe { self.ptr_mut().cast().as_mut() }
49 }
50 unsafe fn as_pinned_mut<T>(self: Pin<&mut Self>) -> Pin<&mut T> {
56 unsafe { self.map_unchecked_mut(|this| this.as_mut()) }
58 }
59 unsafe fn drop_in_place(&mut self, layout: Layout);
68}
69
70#[cfg(feature = "alloc")]
72pub trait FromBox: Storage {
73 fn from_box<T>(boxed: StdBox<T>) -> Self;
77}
78
79#[derive(Debug)]
85#[repr(C)]
86pub struct Raw<const SIZE: usize, const ALIGN: usize = { align_of::<usize>() }>
87where
88 Align<ALIGN>: Alignment,
89{
90 data: MaybeUninit<[u8; SIZE]>,
91 _align: Align<ALIGN>,
92 _not_send_sync: PhantomData<*mut ()>,
93 _pinned: PhantomPinned,
94}
95
96impl<const SIZE: usize, const ALIGN: usize> Raw<SIZE, ALIGN>
97where
98 Align<ALIGN>: Alignment,
99{
100 pub const fn can_store<T>() -> bool {
102 size_of::<T>() <= SIZE && align_of::<T>() <= ALIGN
103 }
104
105 pub const fn new<T>(data: T) -> Self {
107 const { assert!(Self::can_store::<T>()) };
108 unsafe { Self::new_unchecked::<T>(data) }
110 }
111
112 const unsafe fn new_unchecked<T>(data: T) -> Self {
116 let mut raw = Self {
117 data: MaybeUninit::uninit(),
118 _align: Align::NEW,
119 _not_send_sync: PhantomData,
120 _pinned: PhantomPinned,
121 };
122 unsafe { raw.data.as_mut_ptr().cast::<T>().write(data) };
125 raw
126 }
127}
128
129unsafe impl<const SIZE: usize, const ALIGN: usize> Storage for Raw<SIZE, ALIGN>
131where
132 Align<ALIGN>: Alignment,
133{
134 fn new<T>(data: T) -> Self {
135 Self::new(data)
136 }
137 fn ptr(&self) -> NonNull<()> {
138 NonNull::from(&self.data).cast()
139 }
140 fn ptr_mut(&mut self) -> NonNull<()> {
141 NonNull::from(&mut self.data).cast()
142 }
143 unsafe fn drop_in_place(&mut self, _layout: Layout) {}
144}
145
146#[cfg(any(feature = "alloc", doc))]
148#[derive(Debug)]
149pub struct Box(NonNull<()>);
150
151#[cfg(feature = "alloc")]
152impl FromBox for Box {
153 fn from_box<T>(data: StdBox<T>) -> Self {
154 Self(NonNull::new(StdBox::into_raw(data).cast()).unwrap())
155 }
156}
157
158#[cfg(feature = "alloc")]
160unsafe impl Storage for Box {
161 fn new<T>(data: T) -> Self {
162 Self::from_box(StdBox::new(data))
163 }
164 fn ptr(&self) -> NonNull<()> {
165 self.0
166 }
167 fn ptr_mut(&mut self) -> NonNull<()> {
168 self.0
169 }
170 unsafe fn drop_in_place(&mut self, layout: Layout) {
171 if layout.size() != 0 {
172 unsafe { alloc::alloc::dealloc(self.0.as_ptr().cast(), layout) };
175 }
176 }
177}
178
179#[derive(Debug)]
180enum RawOrBoxInner<const SIZE: usize, const ALIGN: usize = { align_of::<usize>() }>
181where
182 Align<ALIGN>: Alignment,
183{
184 Raw(Raw<SIZE, ALIGN>),
185 #[cfg(feature = "alloc")]
186 Box(Box),
187}
188
189#[derive(Debug)]
193pub struct RawOrBox<const SIZE: usize, const ALIGN: usize = { align_of::<usize>() }>(
194 RawOrBoxInner<SIZE, ALIGN>,
195)
196where
197 Align<ALIGN>: Alignment;
198
199#[cfg_attr(coverage_nightly, coverage(off))]
200impl<const SIZE: usize, const ALIGN: usize> RawOrBox<SIZE, ALIGN>
201where
202 Align<ALIGN>: Alignment,
203{
204 pub const fn new_raw<T>(data: T) -> Self {
206 Self(RawOrBoxInner::Raw(Raw::new(data)))
207 }
208}
209
210#[cfg_attr(coverage_nightly, coverage(off))]
211#[cfg(feature = "alloc")]
212impl<const SIZE: usize, const ALIGN: usize> FromBox for RawOrBox<SIZE, ALIGN>
213where
214 Align<ALIGN>: Alignment,
215{
216 fn from_box<T>(data: StdBox<T>) -> Self {
217 if Raw::<SIZE, ALIGN>::can_store::<T>() {
218 Self::new(*data)
219 } else {
220 Self(RawOrBoxInner::Box(Box::from_box(data)))
221 }
222 }
223}
224
225#[cfg_attr(coverage_nightly, coverage(off))]
230unsafe impl<const SIZE: usize, const ALIGN: usize> Storage for RawOrBox<SIZE, ALIGN>
231where
232 Align<ALIGN>: Alignment,
233{
234 fn new<T>(data: T) -> Self {
235 #[cfg(feature = "alloc")]
236 if Raw::<SIZE, ALIGN>::can_store::<T>() {
237 Self(RawOrBoxInner::Raw(unsafe { Raw::new_unchecked(data) }))
239 } else {
240 Self(RawOrBoxInner::Box(Box::new(data)))
241 }
242 #[cfg(not(feature = "alloc"))]
243 {
244 Self(RawOrBoxInner::Raw(Raw::new(data)))
245 }
246 }
247 fn ptr(&self) -> NonNull<()> {
248 match &self.0 {
249 RawOrBoxInner::Raw(s) => s.ptr(),
250 #[cfg(feature = "alloc")]
251 RawOrBoxInner::Box(s) => s.ptr(),
252 }
253 }
254 fn ptr_mut(&mut self) -> NonNull<()> {
255 match &mut self.0 {
256 RawOrBoxInner::Raw(s) => s.ptr_mut(),
257 #[cfg(feature = "alloc")]
258 RawOrBoxInner::Box(s) => s.ptr_mut(),
259 }
260 }
261 unsafe fn as_ref<T>(&self) -> &T {
262 match &self.0 {
263 RawOrBoxInner::Raw(s) if Raw::<SIZE, ALIGN>::can_store::<T>() => unsafe { s.as_ref() },
265 #[cfg(feature = "alloc")]
266 RawOrBoxInner::Box(s) if !Raw::<SIZE, ALIGN>::can_store::<T>() => unsafe { s.as_ref() },
268 _ => unsafe { unreachable_unchecked() },
270 }
271 }
272 unsafe fn as_mut<T>(&mut self) -> &mut T {
273 match &mut self.0 {
274 RawOrBoxInner::Raw(s) if Raw::<SIZE, ALIGN>::can_store::<T>() => unsafe { s.as_mut() },
276 #[cfg(feature = "alloc")]
277 RawOrBoxInner::Box(s) if !Raw::<SIZE, ALIGN>::can_store::<T>() => unsafe { s.as_mut() },
279 _ => unsafe { unreachable_unchecked() },
281 }
282 }
283 unsafe fn drop_in_place(&mut self, layout: Layout) {
284 match &mut self.0 {
285 RawOrBoxInner::Raw(s) => unsafe { s.drop_in_place(layout) },
287 #[cfg(feature = "alloc")]
288 RawOrBoxInner::Box(s) => unsafe { s.drop_in_place(layout) },
290 }
291 }
292}
293
294#[cfg_attr(coverage_nightly, coverage(off))]
295#[cfg(test)]
296#[allow(clippy::undocumented_unsafe_blocks)]
297mod tests {
298 use core::mem;
299
300 use elain::{Align, Alignment};
301
302 use crate::{DynObject, storage::Storage};
303
304 trait Test {}
305 const _: () = {
306 #[derive(Debug)]
307 pub struct __Vtable {
308 __drop_in_place: Option<unsafe fn(::core::ptr::NonNull<()>)>,
309 __layout: ::core::alloc::Layout,
310 }
311 impl<'__lt> crate::object::DynTrait for dyn Test + '__lt {
312 type Vtable = __Vtable;
313 fn drop_in_place_fn(
314 vtable: &Self::Vtable,
315 ) -> Option<unsafe fn(core::ptr::NonNull<()>)> {
316 vtable.__drop_in_place
317 }
318 fn layout(vtable: &Self::Vtable) -> core::alloc::Layout {
319 vtable.__layout
320 }
321 }
322 unsafe impl<'__lt, __Dyn: Test + '__lt> crate::object::Vtable<__Dyn> for dyn Test + '__lt {
323 fn vtable<__Storage: crate::storage::Storage>() -> &'static Self::Vtable {
324 &const {
325 __Vtable {
326 __drop_in_place: <Self as crate::object::Vtable<__Dyn>>::DROP_IN_PLACE_FN,
327 __layout: core::alloc::Layout::new::<__Dyn>(),
328 }
329 }
330 }
331 }
332 };
333 impl Test for () {}
334 impl<const N: usize> Test for [u8; N] {}
335 impl Test for u64 {}
336 type TestObject<'__dyn, S> = DynObject<dyn Test + '__dyn, S>;
337
338 #[test]
339 fn raw_alignment() {
340 fn check_alignment<const ALIGN: usize>()
341 where
342 Align<ALIGN>: Alignment,
343 {
344 let storages = [(); 2].map(TestObject::<super::Raw<0, ALIGN>>::new);
345 for s in &storages {
346 assert!(s.storage().ptr().cast::<Align<ALIGN>>().is_aligned());
347 }
348 const { assert!(ALIGN < 2048) };
349 assert!(
350 storages
351 .iter()
352 .any(|s| !s.storage().ptr().cast::<Align<2048>>().is_aligned())
353 );
354 }
355 check_alignment::<1>();
356 check_alignment::<8>();
357 check_alignment::<64>();
358 check_alignment::<1024>();
359 }
360
361 #[cfg(feature = "alloc")]
362 #[test]
363 fn raw_or_box() {
364 fn check_variant<const N: usize>(variant: impl Fn(&super::RawOrBox<8>) -> bool) {
365 let array = core::array::from_fn::<u8, N, _>(|i| i as u8);
366 let storage = TestObject::<super::RawOrBox<8>>::new(array);
367 assert!(variant(storage.storage()));
368 assert_eq!(
369 unsafe { storage.storage().ptr().cast::<[u8; N]>().read() },
370 array
371 );
372 }
373 check_variant::<4>(|s| matches!(s.0, super::RawOrBoxInner::Raw(_)));
374 check_variant::<64>(|s| matches!(s.0, super::RawOrBoxInner::Box(_)));
375
376 let storage = TestObject::<super::RawOrBox<8, 1>>::new(0u64);
377 assert!(matches!(storage.storage().0, super::RawOrBoxInner::Box(_)));
378 }
379
380 struct SetDropped<'a>(&'a mut bool);
381 impl Test for SetDropped<'_> {}
382 impl Drop for SetDropped<'_> {
383 fn drop(&mut self) {
384 assert!(!mem::replace(self.0, true));
385 }
386 }
387
388 #[test]
389 fn storage_drop() {
390 fn check_drop<S: Storage>() {
391 let mut dropped = false;
392 let storage = TestObject::<S>::new(SetDropped(&mut dropped));
393 assert!(!*unsafe { storage.storage().ptr().cast::<SetDropped>().as_ref() }.0);
394 drop(storage);
395 assert!(dropped);
396 }
397 check_drop::<super::Raw<{ size_of::<SetDropped>() }, { align_of::<SetDropped>() }>>();
398 #[cfg(feature = "alloc")]
399 check_drop::<super::Box>();
400 #[cfg(feature = "alloc")]
401 check_drop::<super::RawOrBox<{ size_of::<SetDropped>() }>>();
402 #[cfg(feature = "alloc")]
403 check_drop::<super::RawOrBox<0>>();
404 }
405
406 #[test]
407 fn storage_dst() {
408 fn check_dst<S: Storage>() {
409 drop(TestObject::<S>::new(()));
410 }
411 check_dst::<super::Raw<{ size_of::<SetDropped>() }, { align_of::<SetDropped>() }>>();
412 #[cfg(feature = "alloc")]
413 check_dst::<super::Box>();
414 #[cfg(feature = "alloc")]
415 check_dst::<super::RawOrBox<{ size_of::<SetDropped>() }>>();
416 #[cfg(feature = "alloc")]
417 check_dst::<super::RawOrBox<0>>();
418 }
419}