sdd/ptr.rs
1use std::marker::PhantomData;
2use std::panic::UnwindSafe;
3use std::sync::atomic::Ordering::Relaxed;
4use std::{ptr, ptr::NonNull};
5
6use super::ref_counted::RefCounted;
7use super::{Shared, Tag};
8
9/// [`Ptr`] is a pointer that allows to safely access the pointed-to instance.
10#[derive(Debug)]
11pub struct Ptr<'g, T> {
12 ptr: *const RefCounted<T>,
13 _phantom: PhantomData<&'g T>,
14}
15
16impl<'g, T> Ptr<'g, T> {
17 /// Creates a null [`Ptr`].
18 ///
19 /// # Examples
20 ///
21 /// ```
22 /// use sdd::Ptr;
23 ///
24 /// let ptr: Ptr<usize> = Ptr::null();
25 /// ```
26 #[inline]
27 #[must_use]
28 pub const fn null() -> Self {
29 Self {
30 ptr: ptr::null(),
31 _phantom: PhantomData,
32 }
33 }
34
35 /// Returns `true` if the [`Ptr`] is null.
36 ///
37 /// # Examples
38 ///
39 /// ```
40 /// use sdd::Ptr;
41 ///
42 /// let ptr: Ptr<usize> = Ptr::null();
43 /// assert!(ptr.is_null());
44 /// ```
45 #[inline]
46 #[must_use]
47 pub fn is_null(&self) -> bool {
48 Tag::unset_tag(self.ptr).is_null()
49 }
50
51 /// Returns its [`Tag`].
52 ///
53 /// # Examples
54 ///
55 /// ```
56 /// use sdd::{Ptr, Tag};
57 ///
58 /// let ptr: Ptr<usize> = Ptr::null();
59 /// assert_eq!(ptr.tag(), Tag::None);
60 /// ```
61 #[inline]
62 #[must_use]
63 pub fn tag(&self) -> Tag {
64 Tag::into_tag(self.ptr)
65 }
66
67 /// Sets a [`Tag`], overwriting its existing [`Tag`].
68 ///
69 /// Returns the previous tag value.
70 ///
71 /// # Examples
72 ///
73 /// ```
74 /// use sdd::{Ptr, Tag};
75 ///
76 /// let mut ptr: Ptr<usize> = Ptr::null();
77 /// assert_eq!(ptr.set_tag(Tag::Both), Tag::None);
78 /// assert_eq!(ptr.tag(), Tag::Both);
79 /// ```
80 #[inline]
81 pub fn set_tag(&mut self, tag: Tag) -> Tag {
82 let old_tag = Tag::into_tag(self.ptr);
83 self.ptr = Tag::update_tag(self.ptr, tag);
84 old_tag
85 }
86
87 /// Clears its [`Tag`].
88 ///
89 /// Returns the previous tag value.
90 ///
91 /// # Examples
92 ///
93 /// ```
94 /// use sdd::{Ptr, Tag};
95 ///
96 /// let mut ptr: Ptr<usize> = Ptr::null().with_tag(Tag::Both);
97 /// assert_eq!(ptr.unset_tag(), Tag::Both);
98 /// ```
99 #[inline]
100 pub fn unset_tag(&mut self) -> Tag {
101 let old_tag = Tag::into_tag(self.ptr);
102 self.ptr = Tag::unset_tag(self.ptr);
103 old_tag
104 }
105
106 /// Returns a copy of `self` with a [`Tag`] set.
107 ///
108 /// # Examples
109 ///
110 /// ```
111 /// use sdd::{Ptr, Tag};
112 ///
113 /// let mut ptr: Ptr<usize> = Ptr::null();
114 /// assert_eq!(ptr.tag(), Tag::None);
115 ///
116 /// let ptr_with_tag = ptr.with_tag(Tag::First);
117 /// assert_eq!(ptr_with_tag.tag(), Tag::First);
118 /// ```
119 #[inline]
120 #[must_use]
121 pub fn with_tag(self, tag: Tag) -> Self {
122 Self::from(Tag::update_tag(self.ptr, tag))
123 }
124
125 /// Returns a copy of `self` with its [`Tag`] erased.
126 ///
127 /// # Examples
128 ///
129 /// ```
130 /// use sdd::{Ptr, Tag};
131 ///
132 /// let mut ptr: Ptr<usize> = Ptr::null();
133 /// ptr.set_tag(Tag::Second);
134 /// assert_eq!(ptr.tag(), Tag::Second);
135 ///
136 /// let ptr_without_tag = ptr.without_tag();
137 /// assert_eq!(ptr_without_tag.tag(), Tag::None);
138 /// ```
139 #[inline]
140 #[must_use]
141 pub fn without_tag(self) -> Self {
142 Self::from(Tag::unset_tag(self.ptr))
143 }
144
145 /// Tries to create a reference to the underlying instance.
146 ///
147 /// # Examples
148 ///
149 /// ```
150 /// use sdd::{AtomicShared, Guard};
151 /// use std::sync::atomic::Ordering::Relaxed;
152 ///
153 /// let atomic_shared: AtomicShared<usize> = AtomicShared::new(21);
154 /// let guard = Guard::new();
155 /// let ptr = atomic_shared.load(Relaxed, &guard);
156 /// assert_eq!(*ptr.as_ref().unwrap(), 21);
157 /// ```
158 #[inline]
159 #[must_use]
160 pub fn as_ref(&self) -> Option<&'g T> {
161 let ptr = Tag::unset_tag(self.ptr);
162 if ptr.is_null() {
163 return None;
164 }
165 unsafe { Some(&*ptr) }
166 }
167
168 /// Tries to create a reference to the underlying instance without checking tag bits.
169 ///
170 /// # Safety
171 ///
172 /// This [`Ptr`] must not have any tag bits set, otherwise dereferencing the pointer may lead to
173 /// undefined behavior.
174 ///
175 /// # Examples
176 ///
177 /// ```
178 /// use sdd::{AtomicShared, Guard};
179 /// use std::sync::atomic::Ordering::Relaxed;
180 ///
181 /// let atomic_shared: AtomicShared<usize> = AtomicShared::new(21);
182 /// let guard = Guard::new();
183 /// let ptr = atomic_shared.load(Relaxed, &guard);
184 /// assert_eq!(unsafe { *ptr.as_ref_unchecked().unwrap() }, 21);
185 /// ```
186 #[inline]
187 #[must_use]
188 pub const unsafe fn as_ref_unchecked(&self) -> Option<&'g T> {
189 unsafe { RefCounted::inst_ptr(self.ptr).as_ref() }
190 }
191
192 /// Provides a raw pointer to the instance.
193 ///
194 /// # Examples
195 ///
196 /// ```
197 /// use sdd::{Guard, Shared};
198 /// use std::sync::atomic::Ordering::Relaxed;
199 ///
200 /// let shared: Shared<usize> = Shared::new(29);
201 /// let guard = Guard::new();
202 /// let ptr = shared.get_guarded_ptr(&guard);
203 /// drop(shared);
204 ///
205 /// assert_eq!(unsafe { *ptr.as_ptr() }, 29);
206 /// ```
207 #[inline]
208 #[must_use]
209 pub fn as_ptr(&self) -> *const T {
210 RefCounted::inst_ptr(Tag::unset_tag(self.ptr))
211 }
212
213 /// Tries to create a pointer to the underlying instance without checking tag bits.
214 ///
215 /// # Safety
216 ///
217 /// This [`Ptr`] must not have any tag bits set, otherwise dereferencing the pointer may lead to
218 /// undefined behavior.
219 ///
220 /// # Examples
221 ///
222 /// ```
223 /// use sdd::{AtomicShared, Guard};
224 /// use std::sync::atomic::Ordering::Relaxed;
225 ///
226 /// let atomic_shared: AtomicShared<usize> = AtomicShared::new(21);
227 /// let guard = Guard::new();
228 /// let ptr = atomic_shared.load(Relaxed, &guard);
229 /// assert_eq!(unsafe { *ptr.as_ptr_unchecked() }, 21);
230 /// ```
231 #[inline]
232 #[must_use]
233 pub const unsafe fn as_ptr_unchecked(&self) -> *const T {
234 RefCounted::inst_ptr(self.ptr)
235 }
236
237 /// Tries to convert itself into a [`Shared`].
238 ///
239 /// # Examples
240 ///
241 /// ```
242 /// use sdd::{Guard, Shared};
243 ///
244 /// let shared: Shared<usize> = Shared::new(83);
245 /// let guard = Guard::new();
246 /// let ptr = shared.get_guarded_ptr(&guard);
247 /// let shared_restored = ptr.get_shared().unwrap();
248 /// assert_eq!(*shared_restored, 83);
249 ///
250 /// drop(shared);
251 /// drop(shared_restored);
252 ///
253 /// assert!(ptr.get_shared().is_none());
254 /// ```
255 #[inline]
256 #[must_use]
257 pub fn get_shared(self) -> Option<Shared<T>> {
258 unsafe {
259 if let Some(ptr) = NonNull::new(Tag::unset_tag(self.ptr).cast_mut()) {
260 if (*ptr.as_ptr()).try_add_ref(Relaxed) {
261 return Some(Shared::from(ptr));
262 }
263 }
264 }
265 None
266 }
267
268 /// Creates a new [`Ptr`] from a [`RefCounted`] pointer.
269 #[inline]
270 pub(super) const fn from(ptr: *const RefCounted<T>) -> Self {
271 Self {
272 ptr,
273 _phantom: PhantomData,
274 }
275 }
276
277 /// Returns a pointer to the [`RefCounted`], including tag bits.
278 #[inline]
279 pub(super) const fn underlying_ptr(self) -> *const RefCounted<T> {
280 self.ptr
281 }
282}
283
284impl<T> Clone for Ptr<'_, T> {
285 #[inline]
286 fn clone(&self) -> Self {
287 *self
288 }
289}
290
291impl<T> Copy for Ptr<'_, T> {}
292
293impl<T> Default for Ptr<'_, T> {
294 #[inline]
295 fn default() -> Self {
296 Self::null()
297 }
298}
299
300impl<T> Eq for Ptr<'_, T> {}
301
302impl<T> PartialEq for Ptr<'_, T> {
303 #[inline]
304 fn eq(&self, other: &Self) -> bool {
305 self.ptr == other.ptr
306 }
307}
308
309impl<T: UnwindSafe> UnwindSafe for Ptr<'_, T> {}