tagptr/imp/
non_null.rs

1use core::{
2    cmp,
3    convert::TryFrom,
4    fmt,
5    hash::{Hash, Hasher},
6    marker::PhantomData,
7    mem,
8    ptr::NonNull,
9};
10
11use crate::{Null, TagNonNull, TagPtr};
12
13/********** impl Clone ****************************************************************************/
14
15impl<T, const N: usize> Clone for TagNonNull<T, N> {
16    impl_clone!();
17}
18
19/********** impl Copy *****************************************************************************/
20
21impl<T, const N: usize> Copy for TagNonNull<T, N> {}
22
23/********** impl inherent *************************************************************************/
24
25impl<T, const N: usize> TagNonNull<T, N> {
26    doc_comment! {
27        doc_tag_bits!(),
28        pub const TAG_BITS: usize = N;
29    }
30
31    doc_comment! {
32        doc_tag_mask!(),
33        pub const TAG_MASK: usize = crate::mark_mask(Self::TAG_BITS);
34    }
35
36    doc_comment! {
37        doc_ptr_mask!(),
38        pub const POINTER_MASK: usize = !Self::TAG_MASK;
39    }
40
41    const COMPOSE_ERR_MSG: &'static str =
42        "argument `ptr` is mis-aligned for `N` tag bits and could be parsed as marked `null` \
43        pointer.";
44
45    /// Creates a new marked non-null pointer from `marked_ptr` without
46    /// checking if it is `null`.
47    ///
48    /// # Safety
49    ///
50    /// The caller has to ensure that `marked_ptr` is not `null`.
51    /// This includes `null` pointers with non-zero tag values.
52    #[inline]
53    pub const unsafe fn new_unchecked(marked_ptr: TagPtr<T, N>) -> Self {
54        Self { inner: NonNull::new_unchecked(marked_ptr.inner), _marker: PhantomData }
55    }
56
57    doc_comment! {
58        doc_from_usize!(),
59        #[inline]
60        pub const unsafe fn from_usize(val: usize) -> Self {
61            Self { inner: NonNull::new_unchecked(val as *mut _), _marker: PhantomData }
62        }
63    }
64
65    doc_comment! {
66        doc_into_raw!(),
67        #[inline]
68        pub const fn into_raw(self) -> NonNull<T> {
69            self.inner
70        }
71    }
72
73    doc_comment! {
74        doc_cast!(),
75        pub const fn cast<U>(self) -> TagNonNull<U, N> {
76            TagNonNull { inner: self.inner.cast(), _marker: PhantomData }
77        }
78    }
79
80    doc_comment! {
81        doc_into_usize!(),
82        #[inline]
83        pub fn into_usize(self) -> usize {
84            self.inner.as_ptr() as _
85        }
86    }
87
88    /// Converts `self` into a (nullable) marked pointer.
89    #[inline]
90    pub const fn into_marked_ptr(self) -> TagPtr<T, N> {
91        TagPtr::new(self.inner.as_ptr())
92    }
93
94    /// Creates a new non-null pointer from `marked_ptr`.
95    ///
96    /// # Errors
97    ///
98    /// Fails if `marked_ptr` is `null`, in which case a [`Null`] instance is
99    /// returned containing argument pointer's tag value.
100    #[inline]
101    pub fn new(marked_ptr: TagPtr<T, N>) -> Result<Self, Null> {
102        Self::try_from(marked_ptr)
103    }
104
105    /// Creates a new pointer that is dangling but well aligned.
106    #[inline]
107    pub const fn dangling() -> Self {
108        let alignment = mem::align_of::<T>();
109        let val = if alignment >= Self::TAG_MASK + 1 { alignment } else { Self::TAG_MASK + 1 };
110        // SAFETY: a type's alignment is never 0, so val is always non-zero
111        unsafe { Self::from_usize(val) }
112    }
113
114    doc_comment! {
115        doc_compose!(),
116        /// # Panics
117        ///
118        /// Panics if `ptr` is mis-aligned for `N` tag bits and contains only
119        /// zero bits in the upper bits, i.e., it would be parsed as a marked
120        /// `null` pointer.
121        #[inline]
122        pub fn compose(ptr: NonNull<T>, tag: usize) -> Self {
123            Self::try_compose(ptr, tag).expect(Self::COMPOSE_ERR_MSG)
124        }
125    }
126
127    /// Attempts to compose a new marked pointer from a raw (non-null) `ptr` and
128    /// a `tag` value.
129    ///
130    /// # Errors
131    ///
132    /// Fails if `ptr` is mis-aligned for `N` tag bits and contains only
133    /// zero bits in the upper bits, i.e., it would be parsed as a marked
134    /// `null` pointer.
135    /// In this case a [`Null`] instance is returned containing the argument
136    /// pointer's tag value.
137    #[inline]
138    pub fn try_compose(ptr: NonNull<T>, tag: usize) -> Result<Self, Null> {
139        Self::try_compose_inner(ptr.as_ptr(), tag)
140    }
141
142    /// Composes a new marked pointer from a raw (non-null) `ptr` and a `tag`
143    /// value without checking if `ptr` is valid.
144    ///
145    /// # Safety
146    ///
147    /// The caller has to ensure that `ptr` is non-null even after considering
148    /// its `N` lower bits as tag bits.
149    #[inline]
150    pub unsafe fn compose_unchecked(ptr: NonNull<T>, tag: usize) -> Self {
151        Self::new_unchecked(TagPtr::compose(ptr.as_ptr(), tag))
152    }
153
154    doc_comment! {
155        doc_clear_tag!(),
156        #[inline]
157        pub fn clear_tag(self) -> Self {
158            Self { inner: self.decompose_non_null(), _marker: PhantomData }
159        }
160    }
161
162    doc_comment! {
163        doc_split_tag!(),
164        #[inline]
165        pub fn split_tag(self) -> (Self, usize) {
166            let (inner, tag) = self.decompose();
167            (Self { inner, _marker: PhantomData }, tag)
168        }
169    }
170
171    doc_comment! {
172        doc_set_tag!(),
173        #[inline]
174        pub fn set_tag(self, tag: usize) -> Self {
175            let ptr = self.decompose_non_null();
176            // SAFETY: ptr was decomposed from a valid marked non-nullable pointer
177            unsafe { Self::compose_unchecked(ptr, tag) }
178        }
179    }
180
181    doc_comment! {
182        doc_update_tag!(),
183        #[inline]
184        pub fn update_tag(self, func: impl FnOnce(usize) -> usize) -> Self {
185            let (ptr, tag) = self.decompose();
186            // SAFETY: ptr was decomposed from a valid marked non-nullable pointer
187            unsafe { Self::compose_unchecked(ptr, func(tag)) }
188        }
189    }
190
191    doc_comment! {
192        doc_add_tag!(),
193        /// # Safety
194        ///
195        /// The caller has to ensure that the resulting pointer is not
196        /// `null` (neither marked nor unmarked).
197        #[inline]
198        pub unsafe fn add_tag(self, value: usize) -> Self {
199            Self::from_usize(self.into_usize().wrapping_add(value))
200        }
201    }
202
203    doc_comment! {
204        doc_sub_tag!(),
205        /// # Safety
206        ///
207        /// The caller has to ensure that the resulting pointer is not
208        /// `null` (neither marked nor unmarked).
209        #[inline]
210        pub unsafe fn sub_tag(self, value: usize) -> Self {
211            Self::from_usize(self.into_usize().wrapping_sub(value))
212        }
213    }
214
215    doc_comment! {
216        doc_decompose!(),
217        #[inline]
218        pub fn decompose(self) -> (NonNull<T>, usize) {
219            (self.decompose_non_null(), self.decompose_tag())
220        }
221    }
222
223    doc_comment! {
224        doc_decompose_ptr!(),
225        #[inline]
226        pub fn decompose_ptr(self) -> *mut T {
227            crate::decompose_ptr(self.inner.as_ptr() as usize, Self::TAG_BITS)
228        }
229    }
230
231    doc_comment! {
232        doc_decompose_non_null!(),
233        #[inline]
234        pub fn decompose_non_null(self) -> NonNull<T> {
235            // SAFETY: every valid TagNonNull is also a valid NonNull
236            unsafe { NonNull::new_unchecked(self.decompose_ptr()) }
237        }
238    }
239
240    doc_comment! {
241        doc_decompose_tag!(),
242        #[inline]
243        pub fn decompose_tag(self) -> usize {
244            crate::decompose_tag::<T>(self.inner.as_ptr() as usize, Self::TAG_BITS)
245        }
246    }
247
248    doc_comment! {
249        doc_as_ref!("non-nullable"),
250        #[inline]
251        pub unsafe fn as_ref(&self) -> &T {
252            &*self.decompose_non_null().as_ptr()
253        }
254    }
255
256    doc_comment! {
257        doc_as_mut!("non-nullable", TagNonNull),
258        #[inline]
259        pub unsafe fn as_mut(&mut self) -> &mut T {
260            &mut *self.decompose_non_null().as_ptr()
261        }
262    }
263
264    /// Decomposes the marked pointer, returning a reference and the separated
265    /// tag.
266    ///
267    /// # Safety
268    ///
269    /// The same safety caveats as with [`as_ref`][TagNonNull::as_ref] apply.
270    #[inline]
271    pub unsafe fn decompose_ref(&self) -> (&T, usize) {
272        let (ptr, tag) = self.decompose();
273        (&*ptr.as_ptr(), tag)
274    }
275
276    /// Decomposes the marked pointer, returning a *mutable* reference and the
277    /// separated tag.
278    ///
279    /// # Safety
280    ///
281    /// The same safety caveats as with [`as_mut`][TagNonNull::as_mut] apply.
282    #[inline]
283    pub unsafe fn decompose_mut(&mut self) -> (&mut T, usize) {
284        let (ptr, tag) = self.decompose();
285        (&mut *ptr.as_ptr(), tag)
286    }
287
288    #[inline]
289    fn try_compose_inner(ptr: *mut T, tag: usize) -> Result<Self, Null> {
290        match ptr as usize & Self::POINTER_MASK {
291            0 => Err(Null(ptr as usize)),
292            // SAFETY: the pointer's upper bits are non-zero,
293            _ => Ok(unsafe { Self::new_unchecked(TagPtr::compose(ptr, tag)) }),
294        }
295    }
296}
297
298/********** impl Debug ****************************************************************************/
299
300impl<T, const N: usize> fmt::Debug for TagNonNull<T, N> {
301    impl_debug!("TagNonNull");
302}
303
304/********** impl Pointer **************************************************************************/
305
306impl<T, const N: usize> fmt::Pointer for TagNonNull<T, N> {
307    impl_pointer!();
308}
309
310/********** impl From (&T) ************************************************************************/
311
312impl<T, const N: usize> From<&T> for TagNonNull<T, N> {
313    #[inline]
314    fn from(reference: &T) -> Self {
315        Self { inner: NonNull::from(reference), _marker: PhantomData }
316    }
317}
318
319/********** impl From (&mut T) ********************************************************************/
320
321impl<T, const N: usize> From<&mut T> for TagNonNull<T, N> {
322    #[inline]
323    fn from(reference: &mut T) -> Self {
324        Self { inner: NonNull::from(reference), _marker: PhantomData }
325    }
326}
327
328/********** impl PartialEq ************************************************************************/
329
330impl<T, const N: usize> PartialEq for TagNonNull<T, N> {
331    impl_partial_eq!();
332}
333
334/********** impl PartialOrd ***********************************************************************/
335
336impl<T, const N: usize> PartialOrd for TagNonNull<T, N> {
337    impl_partial_ord!();
338}
339
340/********** impl Eq *******************************************************************************/
341
342impl<T, const N: usize> Eq for TagNonNull<T, N> {}
343
344/********** impl Ord ******************************************************************************/
345
346impl<T, const N: usize> Ord for TagNonNull<T, N> {
347    impl_ord!();
348}
349
350/********** impl Hash *****************************************************************************/
351
352impl<T, const N: usize> Hash for TagNonNull<T, N> {
353    impl_hash!();
354}
355
356/********** impl TryFrom (*mut T) *****************************************************************/
357
358impl<T, const N: usize> TryFrom<*mut T> for TagNonNull<T, N> {
359    type Error = Null;
360
361    #[inline]
362    fn try_from(ptr: *mut T) -> Result<Self, Self::Error> {
363        Self::try_compose_inner(ptr, 0)
364    }
365}
366
367/********** impl TryFrom (*const T) ***************************************************************/
368
369impl<T, const N: usize> TryFrom<*const T> for TagNonNull<T, N> {
370    type Error = Null;
371
372    #[inline]
373    fn try_from(ptr: *const T) -> Result<Self, Self::Error> {
374        Self::try_from(ptr as *mut _)
375    }
376}
377
378/********** impl TryFrom (TagPtr) **************************************************************/
379
380impl<T, const N: usize> TryFrom<TagPtr<T, N>> for TagNonNull<T, N> {
381    type Error = Null;
382
383    #[inline]
384    fn try_from(ptr: TagPtr<T, N>) -> Result<Self, Self::Error> {
385        Self::try_from(ptr.into_raw())
386    }
387}
388
389/********** impl TryFrom (NonNull) ****************************************************************/
390
391impl<T, const N: usize> TryFrom<NonNull<T>> for TagNonNull<T, N> {
392    type Error = Null;
393
394    #[inline]
395    fn try_from(ptr: NonNull<T>) -> Result<Self, Self::Error> {
396        Self::try_from(ptr.as_ptr())
397    }
398}
399
400#[cfg(test)]
401mod tests {
402    use core::ptr::NonNull;
403
404    use crate::Null;
405
406    type TagNonNull = crate::TagNonNull<i32, 2>;
407
408    #[test]
409    fn test_dangling() {
410        assert_eq!(TagNonNull::dangling().into_raw(), NonNull::dangling());
411
412        #[repr(align(64))]
413        struct Alignment64;
414        assert_eq!(crate::TagNonNull::<Alignment64, 0>::dangling().into_usize(), 64);
415    }
416
417    #[test]
418    fn test_try_compose() {
419        let reference = &1;
420        let ptr = NonNull::from(reference);
421        let res = TagNonNull::try_compose(ptr, 0b11).map(|ptr| ptr.decompose());
422        assert_eq!(res, Ok((ptr, 0b11)));
423
424        let dangling = NonNull::dangling();
425        let res = TagNonNull::try_compose(dangling, 0).map(|ptr| ptr.decompose());
426        assert_eq!(res, Ok((dangling, 0)));
427
428        let ptr = NonNull::new(0b11 as *mut i32).unwrap();
429        let res = TagNonNull::try_compose(ptr, 0b11);
430        assert_eq!(res, Err(Null(0b11)));
431    }
432}