dst_container/
smart_ptr.rs

1use crate::*;
2use std::{
3    alloc::{handle_alloc_error, AllocError, Allocator, Global, Layout},
4    ptr::NonNull,
5    rc::Rc,
6    sync::Arc,
7};
8
9/// An abstract of smart pointers.
10pub trait SmartPtr: Sized {
11    /// The inner type of the pointer.
12    type Content: ?Sized;
13
14    /// Rebind the smart pointer to another content type.
15    type Rebind<U: ?Sized>: SmartPtr<Content = U>;
16
17    /// Create the smart pointer from pointer allocated from [`Global`].
18    /// # Safety
19    /// See `Box::from_raw`.
20    unsafe fn from_alloc(p: *mut Self::Content) -> Self;
21
22    /// Convert the smart pointer to another content type one.
23    /// # Safety
24    /// See [`std::mem::transmute`].
25    unsafe fn rebind<U>(self) -> Self::Rebind<U>
26    where
27        U: Pointee<Metadata = <Self::Content as Pointee>::Metadata> + ?Sized;
28}
29
30impl<T: ?Sized> SmartPtr for Box<T> {
31    type Content = T;
32
33    type Rebind<U: ?Sized> = Box<U>;
34
35    unsafe fn from_alloc(p: *mut Self::Content) -> Self {
36        Self::from_raw(p)
37    }
38
39    unsafe fn rebind<U>(self) -> Self::Rebind<U>
40    where
41        U: Pointee<Metadata = <Self::Content as Pointee>::Metadata> + ?Sized,
42    {
43        let (ptr, metadata) = Box::into_raw(self).to_raw_parts();
44        Box::from_raw(std::ptr::from_raw_parts_mut(ptr, metadata))
45    }
46}
47
48impl<T: ?Sized> SmartPtr for Rc<T> {
49    type Content = T;
50
51    type Rebind<U: ?Sized> = Rc<U>;
52
53    unsafe fn from_alloc(p: *mut Self::Content) -> Self {
54        Box::from_alloc(p).into()
55    }
56
57    unsafe fn rebind<U>(self) -> Self::Rebind<U>
58    where
59        U: Pointee<Metadata = <Self::Content as Pointee>::Metadata> + ?Sized,
60    {
61        let (ptr, metadata) = Rc::into_raw(self).to_raw_parts();
62        Rc::from_raw(std::ptr::from_raw_parts(ptr, metadata))
63    }
64}
65
66impl<T: ?Sized> SmartPtr for Arc<T> {
67    type Content = T;
68
69    type Rebind<U: ?Sized> = Arc<U>;
70
71    unsafe fn from_alloc(p: *mut Self::Content) -> Self {
72        Box::from_alloc(p).into()
73    }
74
75    unsafe fn rebind<U>(self) -> Self::Rebind<U>
76    where
77        U: Pointee<Metadata = <Self::Content as Pointee>::Metadata> + ?Sized,
78    {
79        let (ptr, metadata) = Arc::into_raw(self).to_raw_parts();
80        Arc::from_raw(std::ptr::from_raw_parts(ptr, metadata))
81    }
82}
83
84type RebindPtr<P, U> = <P as SmartPtr>::Rebind<U>;
85
86mod sealed {
87    pub trait Sealed {}
88}
89
90use sealed::Sealed;
91impl<P: SmartPtr> Sealed for P {}
92
93/// Provide functions for smart pointers to create DST instances on heap.
94pub trait NewUninit: Sealed + SmartPtr
95where
96    Self::Content: MaybeUninitProject,
97{
98    /// Create maybe-uninit DST.
99    fn new_uninit_unsized(
100        metadata: <Self::Content as Pointee>::Metadata,
101    ) -> RebindPtr<Self, <Self::Content as MaybeUninitProject>::Target> {
102        unsafe {
103            RebindPtr::<Self, <Self::Content as MaybeUninitProject>::Target>::from_alloc(
104                alloc_with_metadata::<Self::Content>(metadata),
105            )
106        }
107    }
108
109    /// Create maybe-uninit zero-initialized DST.
110    fn new_zeroed_unsized(
111        metadata: <Self::Content as Pointee>::Metadata,
112    ) -> RebindPtr<Self, <Self::Content as MaybeUninitProject>::Target> {
113        unsafe {
114            RebindPtr::<Self, <Self::Content as MaybeUninitProject>::Target>::from_alloc(
115                zeroed_with_metadata::<Self::Content>(metadata),
116            )
117        }
118    }
119
120    /// Create DST and initialize the instance with user-provided function.
121    /// # Safety
122    /// The caller should ensure all fields are properly initialized.
123    unsafe fn new_unsized_with(
124        metadata: <Self::Content as Pointee>::Metadata,
125        f: impl FnOnce(&mut <Self::Content as MaybeUninitProject>::Target),
126    ) -> Self
127    // To make compiler happy.
128    where
129        Self::Rebind<<Self::Content as MaybeUninitProject>::Target>:
130            SmartPtr<Rebind<Self::Content> = Self>,
131    {
132        let ptr = alloc_with_metadata::<Self::Content>(metadata);
133        f(&mut *ptr);
134        RebindPtr::<Self, <Self::Content as MaybeUninitProject>::Target>::from_alloc(ptr)
135            .rebind::<Self::Content>()
136    }
137}
138
139impl<P: SmartPtr> NewUninit for P where P::Content: MaybeUninitProject {}
140
141unsafe fn alloc_with_metadata_impl<T: ?Sized + MaybeUninitProject>(
142    metadata: <T as Pointee>::Metadata,
143    alloc: impl FnOnce(Layout) -> Result<NonNull<[u8]>, AllocError>,
144) -> *mut T::Target {
145    let null_ptr: *mut T = std::ptr::from_raw_parts_mut(std::ptr::null_mut::<()>(), metadata);
146    let layout = Layout::for_value_raw(null_ptr);
147    if let Ok(ptr) = alloc(layout) {
148        std::ptr::from_raw_parts_mut(ptr.as_mut_ptr() as *mut (), metadata)
149    } else {
150        handle_alloc_error(layout)
151    }
152}
153
154unsafe fn alloc_with_metadata<T: ?Sized + MaybeUninitProject>(
155    metadata: <T as Pointee>::Metadata,
156) -> *mut T::Target {
157    alloc_with_metadata_impl::<T>(metadata, |layout| Global.allocate(layout))
158}
159
160unsafe fn zeroed_with_metadata<T: ?Sized + MaybeUninitProject>(
161    metadata: <T as Pointee>::Metadata,
162) -> *mut T::Target {
163    alloc_with_metadata_impl::<T>(metadata, |layout| Global.allocate_zeroed(layout))
164}
165
166/// Provide `assume_init` for smart pointers of maybe-uninit project types.
167pub trait AssumeInit<T: ?Sized + MaybeUninitProject>:
168    Sealed + SmartPtr<Content = T::Target>
169{
170    /// Converts to initialized smart pointer.
171    /// # Safety
172    /// See `MaybeUninit::assume_init`.
173    unsafe fn assume_init(self) -> RebindPtr<Self, T> {
174        self.rebind()
175    }
176}
177
178impl<T: ?Sized + MaybeUninitProject, P: SmartPtr<Content = T::Target>> AssumeInit<T> for P {}
179
180#[cfg(test)]
181mod test {
182    use crate::*;
183    use std::{rc::Rc, sync::Arc};
184
185    #[test]
186    fn sized() {
187        let s: Box<u32> = unsafe { Box::<u32>::new_zeroed_unsized(()).assume_init() };
188        assert_eq!(s.as_ref(), &0);
189    }
190
191    #[derive(MaybeUninitProject)]
192    #[repr(transparent)]
193    struct SliceWrapper([u8]);
194
195    #[test]
196    fn slice_wrapper() {
197        let s: Box<SliceWrapper> =
198            unsafe { Box::<SliceWrapper>::new_zeroed_unsized(64).assume_init() };
199        assert_eq!(&s.0, &[0u8; 64]);
200
201        let s: Rc<SliceWrapper> =
202            unsafe { Rc::<SliceWrapper>::new_zeroed_unsized(64).assume_init() };
203        assert_eq!(&s.0, &[0u8; 64]);
204
205        let s: Arc<SliceWrapper> =
206            unsafe { Arc::<SliceWrapper>::new_zeroed_unsized(64).assume_init() };
207        assert_eq!(&s.0, &[0u8; 64]);
208    }
209}
210
211#[cfg(test)]
212mod bench {
213    use crate::*;
214    use test::{black_box, Bencher};
215
216    const SLICE_LEN: usize = 10000;
217
218    #[bench]
219    fn dst_uninit(b: &mut Bencher) {
220        b.iter(|| unsafe {
221            let b: Box<UnsizedSlice<(), u32>> =
222                Box::<UnsizedSlice<(), u32>>::new_uninit_unsized(SLICE_LEN).assume_init();
223            black_box(b)
224        })
225    }
226
227    #[bench]
228    fn dst_uninit_write(b: &mut Bencher) {
229        b.iter(|| unsafe {
230            let b = Box::<UnsizedSlice<(), u32>>::new_unsized_with(SLICE_LEN, |slice| {
231                slice.slice.write_copy_of_slice(&[0; SLICE_LEN]);
232            });
233            black_box(b)
234        })
235    }
236
237    #[bench]
238    fn dst_zeroed(b: &mut Bencher) {
239        b.iter(|| unsafe {
240            let b: Box<UnsizedSlice<(), u32>> =
241                Box::<UnsizedSlice<(), u32>>::new_zeroed_unsized(SLICE_LEN).assume_init();
242            black_box(b)
243        })
244    }
245
246    #[bench]
247    fn box_from(b: &mut Bencher) {
248        b.iter(|| unsafe {
249            let b: Box<[u32]> = Box::new_zeroed_slice(SLICE_LEN).assume_init();
250            black_box(b)
251        })
252    }
253
254    #[bench]
255    fn vec_into(b: &mut Bencher) {
256        b.iter(|| {
257            let b = vec![0u32; SLICE_LEN].into_boxed_slice();
258            black_box(b)
259        })
260    }
261}