cell_gc/ptr.rs
1//! Non-pinning pointers into the GC heap.
2
3use pages::{PAGE_ALIGN, TypedPage};
4use std::fmt;
5use std::marker::PhantomData;
6use std::mem;
7
8/// A pointer to some `T` in the GC heap.
9///
10/// # Safety
11///
12/// A `Pointer<T>` is a small step up in safety from a raw pointer. A valid
13/// `Pointer<T>` instance is a guarantee that:
14///
15/// * The pointer points into the GC heap, within a GC-allocated page, and not
16/// on top of the `PageHeader`.
17///
18/// * The pointer has the alignment required by the type and GC.
19///
20/// Note that `Pointer<T>` does **not** pin its referent like `GcRef<T>`
21/// does. Therefore, a `Pointer<T>` can still easily be made to dangle or point
22/// at uninitialized memory! The `Pointer<T>` type is not for general use (use
23/// `GcRef<T>` for that instead), only for GC internals. We have to make it
24/// `pub` so that `#[derive(IntoHeap)]` can generate code that uses it, but no
25/// one else should!
26#[derive(Hash, PartialOrd, Ord)]
27pub struct Pointer<T> {
28 ptr: UntypedPointer,
29 phantom: PhantomData<*const T>,
30}
31
32impl<T> Pointer<T> {
33 /// Construct a new `Pointer<T>` from a raw `*const T`.
34 ///
35 /// # Safety
36 ///
37 /// It is the responsibility of callers to ensure that the guarantees
38 /// mentioned above hold true.
39 ///
40 /// # Panics
41 ///
42 /// Panics if the pointer is not aligned properly, or if it would clobber
43 /// the GC's internal `PageHeader`.
44 #[inline]
45 pub unsafe fn new(ptr: *const T) -> Pointer<T> {
46 assert_eq!(
47 ptr as usize & (mem::align_of::<T>() - 1),
48 0,
49 "heap pointers respect T's alignment"
50 );
51 assert!(
52 {
53 let ptr = ptr as usize;
54 let page = ptr & !(PAGE_ALIGN - 1);
55 let allocs = page + TypedPage::<T>::first_allocation_offset();
56 ptr >= allocs
57 },
58 "heap pointers shouldn't clobber the PageHeader"
59 );
60 Pointer {
61 ptr: UntypedPointer::new(ptr as *const ()),
62 phantom: PhantomData,
63 }
64 }
65
66 /// Is this pointer null?
67 #[inline]
68 pub fn is_null(&self) -> bool {
69 self.ptr.is_null()
70 }
71
72 /// Get a reference to the pointed-to `T` instance.
73 ///
74 /// # Safety
75 ///
76 /// If a GC happens and reclaims the referent while the returned reference
77 /// is in use, it will result in use-after-free.
78 ///
79 /// If this pointer doesn't point at a valid `T` instance, then all hell
80 /// will break loose.
81 ///
82 /// # Panics
83 ///
84 /// Panics if the pointer is null.
85 #[inline]
86 pub unsafe fn as_ref(&self) -> &T {
87 assert!(!self.is_null());
88 &*self.as_raw()
89 }
90
91 /// Get the underlying raw pointer.
92 #[inline]
93 pub fn as_raw(&self) -> *const T {
94 self.ptr.as_void() as *const T
95 }
96
97 /// Get the underlying raw pointer as a `*const ()`.
98 #[inline]
99 pub fn as_void(&self) -> *const () {
100 self.ptr.as_void()
101 }
102
103 /// Get the underlying raw pointer as a `usize`.
104 #[inline]
105 pub fn as_usize(&self) -> usize {
106 self.ptr.as_usize()
107 }
108}
109
110impl<T> fmt::Debug for Pointer<T> {
111 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
112 write!(f, "Pointer {{ {:p} }}", self.ptr.as_void())
113 }
114}
115
116impl<T> Clone for Pointer<T> {
117 fn clone(&self) -> Pointer<T> {
118 *self
119 }
120}
121
122impl<T> Copy for Pointer<T> {}
123
124impl<T> PartialEq for Pointer<T> {
125 fn eq(&self, other: &Pointer<T>) -> bool {
126 self.ptr == other.ptr
127 }
128}
129
130impl<T> Eq for Pointer<T> {}
131
132impl<T> From<Pointer<T>> for UntypedPointer {
133 fn from(p: Pointer<T>) -> UntypedPointer {
134 p.ptr
135 }
136}
137
138impl<T> From<Pointer<T>> for usize {
139 fn from(p: Pointer<T>) -> usize {
140 p.ptr.as_void() as usize
141 }
142}
143
144/// Similar to `Pointer<T>` but provides no guarantees other than that it points
145/// to some allocation inside the GC heap, and has the minimal GC-required
146/// alignment.
147///
148/// # Safety
149///
150/// See `Pointer<T>`.
151///
152// TODO: The pointer should probably be wrapped in `Option<Shared<...>>` once
153// `Shared` and `NonZero` are stabilized.
154#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, PartialOrd, Ord)]
155pub struct UntypedPointer(*const ());
156
157impl UntypedPointer {
158 #[inline]
159 unsafe fn new(ptr: *const ()) -> UntypedPointer {
160 assert_eq!(
161 ptr as usize & (mem::size_of::<usize>() - 1),
162 0,
163 "All GC heap pointers are at least word-aligned"
164 );
165 assert!(
166 {
167 let ptr = ptr as usize;
168 let page = ptr & !(PAGE_ALIGN - 1);
169 let allocs = page + TypedPage::<usize>::first_allocation_offset();
170 ptr >= allocs
171 },
172 "heap pointers shouldn't clobber the PageHeader"
173 );
174 UntypedPointer(ptr)
175 }
176
177 /// Convert this `UntypedPointer` into a `Pointer<T>`.
178 ///
179 /// # Safety
180 ///
181 /// You had better be damn sure that this pointer points at a `T`
182 /// instance. See also the `Pointer<T>` safety rules and its `new` method's
183 /// safety rules.
184 #[inline]
185 pub unsafe fn as_typed_ptr<T>(&self) -> Pointer<T> {
186 Pointer::new(self.0 as *const T)
187 }
188
189 /// Is this pointer null?
190 #[inline]
191 pub fn is_null(&self) -> bool {
192 self.0.is_null()
193 }
194
195 /// Get the underlying raw pointer.
196 #[inline]
197 pub fn as_void(&self) -> *const () {
198 self.0
199 }
200
201 /// Get the underlying raw pointer as a `usize`.
202 #[inline]
203 pub fn as_usize(&self) -> usize {
204 self.0 as usize
205 }
206}