ghost_gc/unique_gc.rs
1use core::{
2 alloc::Layout,
3 mem::MaybeUninit,
4 ops::{Deref, DerefMut},
5 ptr::Pointee,
6};
7use std::fmt::Debug;
8
9use crate::{context::Mutation, gc::Gc, gc_box::GcBox, Collect, Invariant};
10
11/// A thin, garbage collected pointer type, which is guaranteed to be unique.
12pub struct UniqueGc<'b, T: ?Sized>(GcBox<T>, Invariant<'b>);
13
14impl<'b, T> UniqueGc<'b, T> {
15 /// Allocates garbage collected memory on the heap and then places `val`
16 /// into it.
17 ///
18 /// This allocates regardless of if `T` is zero-sized.
19 ///
20 /// # Examples
21 /// ```
22 /// # use ghost_gc::{once_arena, UniqueGc};
23 /// # once_arena(|mt| {
24 /// let five = UniqueGc::new(5, mt);
25 /// # });
26 /// ```
27 pub fn new(val: T, mt: &Mutation<'b>) -> UniqueGc<'b, T>
28 where
29 T: Collect,
30 {
31 let inner = mt.allocate::<T>((), Layout::new::<T>());
32 // .context()
33 // .allocate::<T>((), Layout::new::<T>(), mt.alloc());
34 // Safety: No references exist, as the pointer was just created.
35 unsafe { inner.data_ptr().write(val) };
36 // Safety: The value was just written.
37 unsafe { inner.set_init() };
38
39 UniqueGc(inner, Invariant)
40 }
41
42 /// Constructs a new garbage collected pointer with uninitialized contents.
43 ///
44 /// # Examples
45 /// ```
46 /// # use ghost_gc::{once_arena, UniqueGc};
47 /// # once_arena(|mt| {
48 /// let mut five = UniqueGc::<u32>::new_uninit(mt);
49 ///
50 /// let five = unsafe {
51 /// five.as_mut_ptr().write(5);
52 ///
53 /// five.assume_init()
54 /// };
55 ///
56 /// assert_eq!(*five, 5);
57 /// # });
58 /// ```
59 pub fn new_uninit(mt: &Mutation<'b>) -> UniqueGc<'b, MaybeUninit<T>> {
60 UniqueGc::new(MaybeUninit::uninit(), mt)
61 }
62
63 /// Constructs a new garbage collected pointer with uninitialized contents,
64 /// with the memory being filled with `0` bytes.
65 ///
66 /// See [`MaybeUninit::zeroed`] for examples of correct and incorrect usage
67 /// of this method.
68 ///
69 /// # Examples
70 /// ```
71 /// # use ghost_gc::{once_arena, UniqueGc};
72 /// # once_arena(|mt| {
73 /// let zero = UniqueGc::<u32>::new_zeroed(mt);
74 /// let zero = unsafe { zero.assume_init() };
75 ///
76 /// assert_eq!(*zero, 0);
77 /// # });
78 /// ```
79 pub fn new_zeroed(mt: &Mutation<'b>) -> UniqueGc<'b, MaybeUninit<T>> {
80 UniqueGc::new(MaybeUninit::zeroed(), mt)
81 }
82}
83
84impl<'b, T> UniqueGc<'b, [T]> {
85 /// Constructs a new garbage collected slice with uninitialized contents.
86 ///
87 /// # Examples
88 /// ```
89 /// # use ghost_gc::{once_arena, UniqueGc};
90 /// # once_arena(|mt| {
91 /// let mut values = UniqueGc::<[u32]>::new_uninit_slice(3, mt);
92 ///
93 /// let values = unsafe {
94 /// values[0].as_mut_ptr().write(1);
95 /// values[1].as_mut_ptr().write(2);
96 /// values[2].as_mut_ptr().write(3);
97 ///
98 /// values.assume_init()
99 /// };
100 ///
101 /// assert_eq!(*values, [1, 2, 3]);
102 /// # });
103 /// ```
104 pub fn new_uninit_slice(len: usize, mt: &Mutation<'b>) -> UniqueGc<'b, [MaybeUninit<T>]> {
105 let inner = mt
106 .context()
107 .allocate::<[MaybeUninit<T>]>(len, Layout::array::<T>(len).unwrap());
108
109 unsafe { inner.set_init() };
110
111 UniqueGc(inner, Invariant)
112 }
113
114 /// Constructs a new garbage collected slice with uninitialized contents, with the memory being
115 /// filled with `0` bytes.
116 ///
117 /// See [`MaybeUninit::zeroed`] for examples of correct and incorrect usage of this method.
118 ///
119 /// # Examples
120 /// ```
121 /// # use ghost_gc::{once_arena, UniqueGc};
122 /// # once_arena(|mt| {
123 /// let values = UniqueGc::<[u32]>::new_zeroed_slice(3, mt);
124 /// let values = unsafe { values.assume_init() };
125 ///
126 /// assert_eq!(*values, [0, 0, 0]);
127 /// # });
128 /// ```
129 pub fn new_zeroed_slice(len: usize, mt: &Mutation<'b>) -> UniqueGc<'b, [MaybeUninit<T>]> {
130 let inner = mt
131 .context()
132 .allocate::<[MaybeUninit<T>]>(len, Layout::array::<T>(len).unwrap());
133
134 unsafe { core::ptr::write_bytes(inner.data_ptr().cast::<T>(), 0, len) };
135
136 unsafe { inner.set_init() };
137
138 UniqueGc(inner, Invariant)
139 }
140}
141
142impl<'b> UniqueGc<'b, str> {
143 /// Constructs a new garbage collected string, copied from the passed value.
144 ///
145 /// ```
146 /// # use ghost_gc::{once_arena, UniqueGc};
147 /// # once_arena(|mt| {
148 /// let s = UniqueGc::from_str("Hello, World!", mt);
149 /// assert_eq!(&*s, "Hello, World!");
150 /// # });
151 /// ```
152 pub fn from_str(s: &str, mt: &Mutation<'b>) -> UniqueGc<'b, str> {
153 let mut gc = UniqueGc::<[u8]>::new_uninit_slice(s.len(), mt);
154
155 unsafe { std::ptr::copy_nonoverlapping(s.as_ptr(), gc.as_mut_ptr().cast(), s.len()) };
156
157 unsafe { gc.transmute() }
158 }
159}
160
161impl<'b, T: Collect> UniqueGc<'b, MaybeUninit<T>> {
162 /// Converts to `UniqueGc<'b, T>`.
163 ///
164 /// # Safety
165 /// As with [`MaybeUninit::assume_init`], it is up to the caller to
166 /// guarantee that the value really is in an initialized state. Calling
167 /// this when the content is not yet fully initialized causes immediate
168 /// undefined behaviour.
169 ///
170 /// # Examples
171 /// ```
172 /// # use ghost_gc::{once_arena, UniqueGc};
173 /// # once_arena(|mt| {
174 /// let mut five = UniqueGc::<u32>::new_uninit(mt);
175 ///
176 /// let five = unsafe {
177 /// five.as_mut_ptr().write(5);
178 ///
179 /// five.assume_init()
180 /// };
181 ///
182 /// assert_eq!(*five, 5);
183 /// # });
184 /// ```
185 pub unsafe fn assume_init(self) -> UniqueGc<'b, T> {
186 unsafe { self.transmute() }
187 }
188
189 /// Writes the value and converts to `UniqueGc<'b, T>`.
190 ///
191 /// This method converts the pointer similarly to [`UniqueGc::assume_init`],
192 /// but writes `value` into it before the conversion, thus guaranteeing safety.
193 /// In some scenarios use of this method may improve performance because the
194 /// compiler may be able to optimize copying from stack.
195 pub fn write(mut self, value: T) -> UniqueGc<'b, T> {
196 (*self).write(value);
197 unsafe { self.assume_init() }
198 }
199}
200
201impl<'b, T: Collect> UniqueGc<'b, [MaybeUninit<T>]> {
202 /// Converts to `Gc<'b, [T]>`.
203 ///
204 /// # Safety
205 /// As with [`MaybeUninit::assume_init`], it is up to the caller to guarantee that the values
206 /// really are in an initialized state. Calling this when the content is not yet fully
207 /// initialized causes immediate undefined behaviour.
208 ///
209 /// # Examples
210 /// ```
211 /// # use ghost_gc::{once_arena, UniqueGc};
212 /// # once_arena(|mt| {
213 /// let mut values = UniqueGc::<[u32]>::new_uninit_slice(3, mt);
214 ///
215 /// let values = unsafe {
216 /// values[0].as_mut_ptr().write(1);
217 /// values[1].as_mut_ptr().write(2);
218 /// values[2].as_mut_ptr().write(3);
219 ///
220 /// values.assume_init()
221 /// };
222 ///
223 /// assert_eq!(*values, [1, 2, 3]);
224 /// # });
225 /// ```
226 pub unsafe fn assume_init(self) -> UniqueGc<'b, [T]> {
227 unsafe { self.transmute::<[T]>() }
228 }
229}
230
231impl<'b, T: ?Sized> UniqueGc<'b, T> {
232 /// Converts the `UniqueGc` into a regular [`Gc`].
233 ///
234 /// This consumes the `UniqueGc` and returns a regular [`Gc`] which points to the same data
235 /// as the `UniqueGc`.
236 ///
237 /// # Examples
238 /// ```
239 /// # use ghost_gc::{once_arena, UniqueGc, Gc};
240 /// # once_arena(|mt| {
241 /// let gc: Gc<u32> = UniqueGc::into_gc(UniqueGc::new(5, mt));
242 /// # });
243 /// ```
244 pub fn into_gc(this: Self) -> Gc<'b, T> {
245 unsafe { Gc::from_box(this.0) }
246 }
247
248 pub fn as_ptr(&self) -> *const T {
249 self.0.data_ptr()
250 }
251
252 pub fn as_ptr_mut(&mut self) -> *mut T {
253 self.0.data_ptr()
254 }
255
256 // /// Consumes the `UniqueGc`, returning a raw pointer.
257 // ///
258 // /// The resultant pointer is only guaranteed to point to an allocation for the remainder of
259 // /// [`Arena::view`] closure.
260 // pub(crate) fn into_box(self) -> GcBox<T> {
261 // self.0
262 // }
263
264 // /// Constructs a `UniqueGc` from a raw pointer.
265 // ///
266 // /// # Safety
267 // /// The pointer must have come from a previous call to [`UniqueGc::into_raw`].
268 // pub(crate) unsafe fn from_box(ptr: GcBox<T>) -> UniqueGc<'b, T> {
269 // UniqueGc(ptr, PhantomData)
270 // }
271
272 /// # Safety
273 /// Layouts have to match, pointed to data has to match.
274 pub(crate) unsafe fn transmute<U: ?Sized + Collect>(self) -> UniqueGc<'b, U> {
275 debug_assert_eq!(
276 Layout::new::<<T as Pointee>::Metadata>(),
277 Layout::new::<<U as Pointee>::Metadata>(),
278 );
279
280 UniqueGc::<'b, U>(unsafe { self.0.transmute::<U>() }, Invariant)
281 }
282}
283
284impl<T: ?Sized> Deref for UniqueGc<'_, T> {
285 type Target = T;
286
287 fn deref(&self) -> &Self::Target {
288 unsafe { self.0.data() }
289 }
290}
291
292impl<T: ?Sized> DerefMut for UniqueGc<'_, T> {
293 fn deref_mut(&mut self) -> &mut Self::Target {
294 unsafe { self.0.data_mut() }
295 }
296}
297
298unsafe impl<T: ?Sized> Collect for UniqueGc<'_, T> {
299 const NEEDS_TRACE: bool = true;
300
301 fn trace(&self, c: &crate::Collector) {
302 use crate::gc_box::Colour;
303
304 match self.0.colour() {
305 Colour::Gray | Colour::White | Colour::Weak => {
306 unsafe { self.0.set_colour(Colour::Gray) };
307
308 c.context().push_box(self.0.erase());
309 }
310 Colour::Black => {}
311 }
312 }
313}
314
315impl<T: ?Sized + Debug> Debug for UniqueGc<'_, T> {
316 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
317 (**self).fmt(f)
318 }
319}
320
321impl<T: ?Sized + PartialEq> PartialEq for UniqueGc<'_, T> {
322 fn eq(&self, other: &Self) -> bool {
323 (**self) == (**other)
324 }
325}
326
327impl<T: ?Sized + Eq> Eq for UniqueGc<'_, T> {}
328
329impl<T: ?Sized + PartialOrd> PartialOrd for UniqueGc<'_, T> {
330 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
331 (**self).partial_cmp(other)
332 }
333}
334
335impl<T: ?Sized + Ord> Ord for UniqueGc<'_, T> {
336 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
337 (**self).cmp(other)
338 }
339}
340
341impl<T: ?Sized + std::hash::Hash> std::hash::Hash for UniqueGc<'_, T> {
342 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
343 (**self).hash(state);
344 }
345}