varlen/owned.rs
1//! A pointer to `T` that calls its destructor but not its deallocator when dropped.
2//!
3//! # Examples
4//!
5//! ```
6//! # #[cfg(feature = "bumpalo")] {
7//! use varlen::prelude::*;
8//! type TypeWithDrop = Tup2<FixedLen<Box<u32>>, Str>;
9//! let arena = bumpalo::Bump::new();
10//! let owned: Owned<TypeWithDrop> = Owned::new_in(
11//! tup2::Init(
12//! FixedLen(Box::new(42)),
13//! Str::copy("hello")
14//! ),
15//! &arena);
16//! assert_eq!(42, *owned.refs().0.0);
17//! drop(owned); // Calls drop() on the Box<u32>
18//! drop(arena); // Deallocates the arena storage.
19//! # }
20//! ```
21use crate::Initializer;
22
23use super::{Layout, VarLen};
24
25use core::marker::PhantomData;
26use core::pin::Pin;
27use core::ptr::NonNull;
28
29/// A pointer to `T` that calls its destructor but not its deallocator when dropped.
30///
31/// # Examples
32///
33/// ```
34/// #[cfg(feature = "bumpalo")] {
35/// use varlen::prelude::*;
36/// type TypeWithDrop = Tup2<FixedLen<Box<u32>>, Str>;
37/// let arena = bumpalo::Bump::new();
38/// let owned: Owned<TypeWithDrop> = Owned::new_in(
39/// tup2::Init(
40/// FixedLen(Box::new(42)),
41/// Str::copy("hello")
42/// ),
43/// &arena);
44/// assert_eq!(42, *owned.refs().0.0);
45/// drop(owned); // Calls drop() on the Box<u32>
46/// drop(arena); // Deallocates the arena storage.
47/// }
48/// ```
49///
50/// # Comparison to `T` on fixed length types
51///
52/// The type [`Owned<T>`] fills a role for variable-length types similar to what `T` fills
53/// for fixed-length types, typically in "into" or "take" APIs. For example, whereas
54/// `Vec<T>::into_iter` iterates over `T` items (taking them and potentially dropping them
55/// one-by-one), [`crate::seq::Seq<T>::take_elems`] iterates over `Owned<T>` items, taking them and
56/// potentially dropping them one-by-bone:
57///
58/// ```
59/// use varlen::prelude::*;
60/// fn consume_seq<T: VarLen>(mut seq: Seq<T>) {
61/// for t in seq.take_elems() {
62/// let t: Owned<T> = t;
63/// // T's destructor runs here, freeing any memory it points to.
64/// }
65/// assert_eq!(0, seq.len());
66/// // Seq<T>'s destructor runs here, freeing its underlying storage.
67/// }
68/// ```
69pub struct Owned<'storage, T: VarLen>(NonNull<T>, PhantomData<&'storage [u8]>);
70
71#[allow(rustdoc::missing_doc_code_examples)]
72impl<'storage, T: VarLen> Owned<'storage, T> {
73 /// Constructs an `Owned<T>` pointer from a `NonNull<T>` pointer.
74 ///
75 /// # Safety
76 ///
77 /// The layout of `T`'s _tail_, which is the variable-sized part not included in
78 /// `std::mem::size_of::<T>()`, must be consistent with the layout specified by
79 /// `T`'s header. For example, this can have been produced by a
80 /// [`crate::Initializer<T>`] call or similar, on a buffer sufficiently sized for
81 /// the initializer's layout.
82 ///
83 /// # Example
84 ///
85 /// Safe roundtripping through a raw pointer:
86 ///
87 /// ```
88 /// use varlen::prelude::*;
89 /// fn roundtrip<T: VarLen>(x: Owned<T>) -> Owned<T> {
90 /// unsafe {
91 /// let p = x.into_raw();
92 /// Owned::from_raw(p)
93 /// }
94 /// }
95 /// ```
96 pub unsafe fn from_raw(raw: NonNull<T>) -> Self {
97 Owned(raw, PhantomData)
98 }
99
100 /// Converts this to a raw pointer representation.
101 ///
102 /// # Safety
103 ///
104 /// Because `T` is a variable-length type, there are additional safety obligations
105 /// above and beyond the usual treatment of `NonNull<T>`. In particular, the caller
106 /// responsible for ensuring that whenever a `&T` is produced, the header-specified
107 /// layout matches the layout of the tail. This prohibits code patterns such as
108 /// overwriting the header in a way that changes the layout.
109 ///
110 /// # Example
111 ///
112 /// Safe roundtripping through a raw pointer:
113 ///
114 /// ```
115 /// use varlen::prelude::*;
116 /// fn roundtrip<T: VarLen>(x: Owned<T>) -> Owned<T> {
117 /// unsafe {
118 /// let p = x.into_raw();
119 /// Owned::from_raw(p)
120 /// }
121 /// }
122 /// ```
123 pub unsafe fn into_raw(self) -> NonNull<T> {
124 let result = self.0;
125 core::mem::forget(self);
126 result
127 }
128
129 /// Gets a (pinned) mutable reference.
130 ///
131 /// # Example
132 ///
133 /// ```
134 /// # #[cfg(feature = "bumpalo")] {
135 /// use varlen::prelude::*;
136 /// use bumpalo::Bump;
137 ///
138 /// let arena = Bump::new();
139 /// let mut s = Owned::new_in(Str::copy("Hello"), &arena);
140 /// s.as_mut().mut_slice().make_ascii_uppercase();
141 /// assert_eq!("HELLO", &s[..]);
142 /// s.as_mut().mut_slice().make_ascii_lowercase();
143 /// assert_eq!("hello", &s[..]);
144 /// # }
145 /// ```
146 pub fn as_mut(&mut self) -> Pin<&mut T> {
147 unsafe { Pin::new_unchecked(self.0.as_mut()) }
148 }
149
150 /// Forgets the obligation to drop this.
151 ///
152 /// # Example
153 ///
154 /// ```
155 /// # #[cfg(feature = "bumpalo")] {
156 /// use bumpalo::Bump;
157 /// use varlen::prelude::*;
158 /// let arena = Bump::new();
159 /// let s = Owned::new_in(Str::copy("hello"), &arena);
160 /// let s = s.leak();
161 /// assert_eq!("hello", &s[..]);
162 /// # }
163 /// ```
164 pub fn leak(self) -> Pin<&'storage mut T> {
165 let Owned(mut ptr, _) = self;
166 unsafe { Pin::new_unchecked(ptr.as_mut()) }
167 }
168
169 /// Allocates a `T` on the arena.
170 ///
171 /// # Example
172 ///
173 /// ```
174 /// # #[cfg(feature = "bumpalo")] {
175 /// use bumpalo::Bump;
176 /// use varlen::prelude::*;
177 /// let arena = Bump::new();
178 /// let s = Owned::new_in(Str::copy("hello"), &arena);
179 /// assert_eq!("hello", &s[..]);
180 /// # }
181 /// ```
182 #[cfg(feature = "bumpalo")]
183 #[inline]
184 pub fn new_in(init: impl Initializer<T>, bump: &'storage bumpalo::Bump) -> Self {
185 use core::alloc;
186 let layout = init.calculate_layout_cautious().unwrap();
187 let ptr = bump
188 .alloc_layout(alloc::Layout::from_size_align(layout.size(), T::ALIGN).unwrap())
189 .cast::<T>();
190 unsafe {
191 init.initialize(ptr, layout);
192 }
193 Owned(ptr, PhantomData)
194 }
195}
196
197impl<T: VarLen> Drop for Owned<'_, T> {
198 fn drop(&mut self) {
199 unsafe {
200 // Safety: vdrop is called only once, because Self::drop() is called only once.
201 let layout = T::calculate_layout(&*self);
202 T::vdrop(self.as_mut(), layout);
203 }
204 }
205}
206
207impl<T: VarLen> core::ops::Deref for Owned<'_, T> {
208 type Target = T;
209 fn deref(&self) -> &T {
210 unsafe { self.0.as_ref() }
211 }
212}
213
214unsafe impl<T: VarLen> Initializer<T> for Owned<'_, T> {
215 unsafe fn initialize(self, dst: NonNull<T>, layout: T::Layout) {
216 // Safety:
217 // * Owned has unique access to its pointer
218 // * dst is unique
219 // * dst size is guaranteed by the SizedInitializer call
220 core::ptr::copy_nonoverlapping(self.0.as_ptr(), dst.as_ptr(), layout.size());
221 core::mem::forget(self);
222 }
223
224 #[inline]
225 fn calculate_layout_cautious(&self) -> Option<T::Layout> {
226 Some(T::calculate_layout(&*self))
227 }
228}