tagptr/imp/
ptr.rs

1use core::{
2    cmp, fmt,
3    hash::{Hash, Hasher},
4    marker::PhantomData,
5    ptr::{self, NonNull},
6};
7
8use crate::{TagNonNull, TagPtr};
9
10/********** impl Clone ****************************************************************************/
11
12impl<T, const N: usize> Clone for TagPtr<T, N> {
13    impl_clone!();
14}
15
16/********** impl Copy *****************************************************************************/
17
18impl<T, const N: usize> Copy for TagPtr<T, N> {}
19
20/********** impl inherent *************************************************************************/
21
22impl<T, const N: usize> TagPtr<T, N> {
23    doc_comment! {
24        doc_tag_bits!(),
25        pub const TAG_BITS: usize = N;
26    }
27
28    doc_comment! {
29        doc_tag_mask!(),
30        pub const TAG_MASK: usize = crate::mark_mask(Self::TAG_BITS);
31    }
32
33    doc_comment! {
34        doc_ptr_mask!(),
35        pub const POINTER_MASK: usize = !Self::TAG_MASK;
36    }
37
38    doc_comment! {
39        doc_null!(),
40        ///
41        /// # Examples
42        ///
43        /// ```
44        /// use core::ptr;
45        ///
46        /// type TagPtr = tagptr::TagPtr<i32, 2>;
47        ///
48        /// let ptr = TagPtr::null();
49        /// assert_eq!(ptr.decompose(), (ptr::null_mut(), 0));
50        /// ```
51        #[inline]
52        pub const fn null() -> Self {
53            Self::new(ptr::null_mut())
54        }
55    }
56
57    doc_comment! {
58        doc_new!(),
59        ///
60        /// # Examples
61        ///
62        /// ```
63        /// type TagPtr = tagptr::TagPtr<i32, 2>;
64        ///
65        /// let reference = &mut 1;
66        /// let ptr = TagPtr::new(reference);
67        /// assert_eq!(ptr.decompose(), (reference as *mut _, 0));
68        /// ```
69        #[inline]
70        pub const fn new(ptr: *mut T) -> Self {
71            Self { inner: ptr, _marker: PhantomData }
72        }
73    }
74
75    doc_comment! {
76        doc_from_usize!(),
77        ///
78        /// # Examples
79        ///
80        /// ```
81        /// use core::ptr;
82        ///
83        /// type TagPtr = tagptr::TagPtr<i32, 2>;
84        ///
85        /// let ptr = TagPtr::from_usize(0b11);
86        /// assert_eq!(ptr.decompose(), (ptr::null_mut(), 0b11));
87        /// ```
88        #[inline]
89        pub const fn from_usize(val: usize) -> Self {
90            Self::new(val as _)
91        }
92    }
93
94    doc_comment! {
95        doc_into_raw!(),
96        ///
97        /// # Examples
98        ///
99        /// ```
100        /// type TagPtr = tagptr::TagPtr<i32, 2>;
101        ///
102        /// let ptr = TagPtr::from_usize(0b11);
103        /// assert_eq!(ptr.into_raw(), 0b11 as *mut _);
104        /// ```
105        #[inline]
106        pub const fn into_raw(self) -> *mut T {
107            self.inner
108        }
109    }
110
111    doc_comment! {
112        doc_cast!(),
113        pub const fn cast<U>(self) -> TagPtr<U, N> {
114            TagPtr { inner: self.inner.cast(), _marker: PhantomData }
115        }
116    }
117
118    doc_comment! {
119        doc_into_usize!(),
120        ///
121        /// # Examples
122        ///
123        /// ```
124        /// type TagPtr = tagptr::TagPtr<i32, 2>;
125        ///
126        /// let ptr = TagPtr::from_usize(0b11);
127        /// assert_eq!(ptr.into_usize(), 0b11);
128        /// ```
129        #[inline]
130        pub fn into_usize(self) -> usize {
131            self.inner as usize
132        }
133    }
134
135    doc_comment! {
136        doc_compose!(),
137        ///
138        /// # Examples
139        ///
140        /// ```
141        /// type TagPtr = tagptr::TagPtr<i32, 2>;
142        ///
143        /// let raw = &1 as *const i32 as *mut i32;
144        /// let ptr = TagPtr::compose(raw, 0b11);
145        /// assert_eq!(ptr.decompose(), (raw, 0b11));
146        /// // excess bits are silently truncated
147        /// let ptr = TagPtr::compose(raw, 0b101);
148        /// assert_eq!(ptr.decompose(), (raw, 0b01));
149        /// ```
150        #[inline]
151        pub fn compose(ptr: *mut T, tag: usize) -> Self {
152            Self::new(crate::compose::<T, N>(ptr, tag))
153        }
154    }
155
156    /// Returns `true` if the marked pointer is `null`.
157    ///
158    /// # Examples
159    ///
160    /// ```
161    /// use core::ptr;
162    ///
163    /// type TagPtr = tagptr::TagPtr<i32, 2>;
164    ///
165    /// let ptr = TagPtr::compose(ptr::null_mut(), 0b11);
166    /// assert!(ptr.is_null());
167    /// ```
168    #[inline]
169    pub fn is_null(self) -> bool {
170        self.decompose_ptr().is_null()
171    }
172
173    doc_comment! {
174        doc_clear_tag!(),
175        ///
176        /// # Examples
177        ///
178        /// ```
179        /// type TagPtr = tagptr::TagPtr<i32, 2>;
180        ///
181        /// let reference = &mut 1;
182        /// let ptr = TagPtr::compose(reference, 0b11);
183        ///
184        /// assert_eq!(ptr.clear_tag().decompose(), (reference as *mut _, 0));
185        /// ```
186        #[inline]
187        pub fn clear_tag(self) -> Self {
188            Self::new(self.decompose_ptr())
189        }
190    }
191
192    doc_comment! {
193        doc_split_tag!(),
194        ///
195        /// # Examples
196        ///
197        /// ```
198        /// type TagPtr = tagptr::TagPtr<i32, 2>;
199        ///
200        /// let reference = &mut 1;
201        /// let ptr = TagPtr::compose(reference, 0b11);
202        ///
203        /// assert_eq!(ptr.split_tag(), (TagPtr::new(reference), 0b11));
204        /// ```
205        #[inline]
206        pub fn split_tag(self) -> (Self, usize) {
207            let (ptr, tag) = self.decompose();
208            (Self::new(ptr), tag)
209        }
210    }
211
212    doc_comment! {
213        doc_set_tag!(),
214        ///
215        /// # Examples
216        ///
217        /// ```
218        /// type TagPtr = tagptr::TagPtr<i32, 2>;
219        ///
220        /// let reference = &mut 1;
221        /// let ptr = TagPtr::compose(reference, 0b11);
222        ///
223        /// assert_eq!(ptr.set_tag(0b01).decompose(), (reference as *mut _, 0b01));
224        /// ```
225        #[inline]
226        pub fn set_tag(self, tag: usize) -> Self {
227            let ptr = self.decompose_ptr();
228            Self::compose(ptr, tag)
229        }
230    }
231
232    doc_comment! {
233        doc_update_tag!(),
234        ///
235        /// # Examples
236        ///
237        /// ```
238        /// type TagPtr = tagptr::TagPtr<i32, 2>;
239        ///
240        /// let reference = &mut 1;
241        /// let ptr = TagPtr::compose(reference, 0b11);
242        ///
243        /// assert_eq!(ptr.update_tag(|tag| tag - 1).decompose(), (reference as *mut _, 0b10));
244        /// ```
245        #[inline]
246        pub fn update_tag(self, func: impl FnOnce(usize) -> usize) -> Self {
247            let (ptr, tag) = self.decompose();
248            Self::compose(ptr, func(tag))
249        }
250    }
251
252    doc_comment! {
253        doc_add_tag!(),
254        ///
255        /// # Examples
256        ///
257        /// ```
258        /// type TagPtr = tagptr::TagPtr<i32, 2>;
259        ///
260        /// let reference = &mut 1;
261        /// let ptr = TagPtr::compose(reference, 0b10);
262        ///
263        /// assert_eq!(ptr.add_tag(1).decompose(), (reference as *mut _, 0b11));
264        /// ```
265        #[inline]
266        pub fn add_tag(self, value: usize) -> Self {
267            Self::from_usize(self.into_usize().wrapping_add(value))
268        }
269    }
270
271    doc_comment! {
272        doc_sub_tag!(),
273        ///
274        /// # Examples
275        ///
276        /// ```
277        /// type TagPtr = tagptr::TagPtr<i32, 2>;
278        ///
279        /// let reference = &mut 1;
280        /// let ptr = TagPtr::compose(reference, 0b10);
281        ///
282        /// assert_eq!(ptr.sub_tag(1).decompose(), (reference as *mut _, 0b01));
283        /// ```
284        #[inline]
285        pub fn sub_tag(self, value: usize) -> Self {
286            Self::from_usize(self.into_usize().wrapping_sub(value))
287        }
288    }
289
290    doc_comment! {
291        doc_decompose!(),
292        #[inline]
293        pub fn decompose(self) -> (*mut T, usize) {
294            (self.decompose_ptr(), self.decompose_tag())
295        }
296    }
297
298    doc_comment! {
299        doc_decompose_ptr!(),
300        #[inline]
301        pub fn decompose_ptr(self) -> *mut T {
302            crate::decompose_ptr::<T>(self.inner as usize, Self::TAG_BITS)
303        }
304    }
305
306    doc_comment! {
307        doc_decompose_tag!(),
308        #[inline]
309        pub fn decompose_tag(self) -> usize {
310            crate::decompose_tag::<T>(self.inner as usize, Self::TAG_BITS)
311        }
312    }
313
314    doc_comment! {
315        doc_as_ref!("nullable"),
316        ///
317        /// # Examples
318        ///
319        /// ```
320        /// type TagPtr = tagptr::TagPtr<i32, 2>;
321        ///
322        /// let reference = &1;
323        /// let ptr = TagPtr::compose(reference as *const _ as *mut _, 0b11);
324        ///
325        /// unsafe {
326        ///     assert_eq!(ptr.as_ref(), Some(&1));
327        /// }
328        /// ```
329        #[inline]
330        pub unsafe fn as_ref<'a>(self) -> Option<&'a T> {
331            self.decompose_ptr().as_ref()
332        }
333    }
334
335    doc_comment! {
336        doc_as_mut!("nullable", TagPtr),
337        ///
338        /// # Examples
339        ///
340        /// ```
341        /// type TagPtr = tagptr::TagPtr<i32, 2>;
342        ///
343        /// let mut val = 1;
344        /// let ptr = TagPtr::compose(&mut val, 0b11);
345        ///
346        /// unsafe {
347        ///     assert_eq!(ptr.as_mut(), Some(&mut 1));
348        /// }
349        /// ```
350        #[inline]
351        pub unsafe fn as_mut<'a>(self) -> Option<&'a mut T> {
352            self.decompose_ptr().as_mut()
353        }
354    }
355
356    /// Decomposes the marked pointer, returning an optional reference and the
357    /// separated tag.
358    ///
359    /// # Safety
360    ///
361    /// The same safety caveats as with [`as_ref`][TagPtr::as_ref] apply.
362    ///
363    /// # Examples
364    ///
365    /// ```
366    /// type TagPtr = tagptr::TagPtr<i32, 2>;
367    ///
368    /// let reference = &1;
369    /// let ptr = TagPtr::compose(reference as *const _ as *mut _, 0b11);
370    ///
371    /// unsafe {
372    ///     assert_eq!(ptr.decompose_ref(), (Some(&1), 0b11));
373    /// }
374    /// ```
375    #[inline]
376    pub unsafe fn decompose_ref<'a>(self) -> (Option<&'a T>, usize) {
377        (self.as_ref(), self.decompose_tag())
378    }
379
380    /// Decomposes the marked pointer, returning an optional *mutable* reference
381    /// and the separated tag.
382    ///
383    /// # Safety
384    ///
385    /// The same safety caveats as with [`as_mut`][TagPtr::as_mut] apply.
386    ///
387    /// # Examples
388    ///
389    /// ```
390    /// type TagPtr = tagptr::TagPtr<i32, 2>;
391    ///
392    /// let mut val = 1;
393    /// let ptr = TagPtr::compose(&mut val, 0b11);
394    ///
395    /// unsafe {
396    ///     assert_eq!(ptr.decompose_mut(), (Some(&mut 1), 0b11));
397    /// }
398    /// ```
399    #[inline]
400    pub unsafe fn decompose_mut<'a>(self) -> (Option<&'a mut T>, usize) {
401        (self.as_mut(), self.decompose_tag())
402    }
403}
404
405/********** impl Debug ****************************************************************************/
406
407impl<T, const N: usize> fmt::Debug for TagPtr<T, N> {
408    impl_debug!("TagPtr");
409}
410
411/********** impl Default **************************************************************************/
412
413impl<T, const N: usize> Default for TagPtr<T, N> {
414    impl_default!();
415}
416
417/********** impl From (*mut T) ********************************************************************/
418
419impl<T, const N: usize> From<*mut T> for TagPtr<T, N> {
420    #[inline]
421    fn from(ptr: *mut T) -> Self {
422        Self::new(ptr)
423    }
424}
425
426/********** impl From (*const T) ******************************************************************/
427
428impl<T, const N: usize> From<*const T> for TagPtr<T, N> {
429    #[inline]
430    fn from(ptr: *const T) -> Self {
431        Self::new(ptr as _)
432    }
433}
434
435/********** impl From (&T) ************************************************************************/
436
437impl<T, const N: usize> From<&T> for TagPtr<T, N> {
438    #[inline]
439    fn from(reference: &T) -> Self {
440        Self::from(reference as *const _)
441    }
442}
443
444/********** impl From ((&T, usize)) ***************************************************************/
445
446impl<T, const N: usize> From<(&T, usize)> for TagPtr<T, N> {
447    #[inline]
448    fn from((reference, tag): (&T, usize)) -> Self {
449        Self::compose(reference as *const T as *mut T, tag)
450    }
451}
452
453/********** impl From (&mut T) ********************************************************************/
454
455impl<T, const N: usize> From<&mut T> for TagPtr<T, N> {
456    #[inline]
457    fn from(reference: &mut T) -> Self {
458        Self::from(reference as *const _)
459    }
460}
461
462/********** impl From ((&mut T, usize)) ***********************************************************/
463
464impl<T, const N: usize> From<(&mut T, usize)> for TagPtr<T, N> {
465    #[inline]
466    fn from((reference, tag): (&mut T, usize)) -> Self {
467        Self::compose(reference, tag)
468    }
469}
470
471/********** impl From (NonNull) *******************************************************************/
472
473impl<T, const N: usize> From<NonNull<T>> for TagPtr<T, N> {
474    #[inline]
475    fn from(ptr: NonNull<T>) -> Self {
476        Self::new(ptr.as_ptr())
477    }
478}
479
480/********** impl From (TagNonNull) *************************************************************/
481
482impl<T, const N: usize> From<TagNonNull<T, N>> for TagPtr<T, N> {
483    #[inline]
484    fn from(ptr: TagNonNull<T, N>) -> Self {
485        ptr.into_marked_ptr()
486    }
487}
488
489/********** impl PartialEq ************************************************************************/
490
491impl<T, const N: usize> PartialEq for TagPtr<T, N> {
492    impl_partial_eq!();
493}
494
495/********** impl PartialOrd ***********************************************************************/
496
497impl<T, const N: usize> PartialOrd for TagPtr<T, N> {
498    impl_partial_ord!();
499}
500
501/********** impl Pointer **************************************************************************/
502
503impl<T, const N: usize> fmt::Pointer for TagPtr<T, N> {
504    impl_pointer!();
505}
506
507/********** impl Eq *******************************************************************************/
508
509impl<T, const N: usize> Eq for TagPtr<T, N> {}
510
511/********** impl Ord ******************************************************************************/
512
513impl<T, const N: usize> Ord for TagPtr<T, N> {
514    impl_ord!();
515}
516
517/********** impl Hash *****************************************************************************/
518
519impl<T, const N: usize> Hash for TagPtr<T, N> {
520    impl_hash!();
521}
522
523#[cfg(test)]
524mod tests {
525    type TagPtr = crate::TagPtr<i32, 2>;
526
527    #[test]
528    fn test_debug() {
529        let reference = &mut 1;
530        let ptr = TagPtr::compose(reference, 0b11);
531        assert_eq!(
532            std::format!("{:?}", ptr),
533            std::format!("TagPtr {{ ptr: {:0p}, tag: {} }}", reference as *mut _, 0b11)
534        );
535    }
536
537    #[test]
538    fn test_cast() {
539        type ErasedPtr = crate::TagPtr<(), 2>;
540
541        let reference = &mut 1;
542        let ptr = TagPtr::compose(reference, 0b11);
543        let cast: ErasedPtr = ptr.cast().set_tag(0b10);
544
545        assert_eq!(cast.into_usize(), reference as *mut _ as usize | 0b10);
546        assert_eq!(cast.cast(), TagPtr::compose(reference, 0b10));
547    }
548
549    #[test]
550    fn test_from_usize() {
551        let reference = &1;
552        let ptr = TagPtr::from_usize(reference as *const i32 as usize | 0b1);
553        assert_eq!(ptr.decompose(), (reference as *const _ as *mut _, 0b1));
554    }
555
556    #[test]
557    fn test_compose() {
558        let reference = &mut 1;
559        let ptr1 = TagPtr::compose(reference, 0b11);
560        let ptr2 = TagPtr::compose(reference, 0b111);
561        // compose silently truncates excess bits, so ptr1 and ptr2 are identical
562        assert_eq!(ptr1, ptr2);
563        assert_eq!(ptr2.decompose(), (reference as *mut _, 0b11));
564    }
565
566    #[test]
567    fn test_set_tag() {
568        let reference = &mut 1;
569        let ptr = TagPtr::compose(reference, 0b11);
570        // set_tag must silently truncate excess tag bits
571        assert_eq!(ptr, ptr.set_tag(0b111));
572    }
573
574    #[test]
575    fn test_overflow_tag() {
576        let reference = &mut 1;
577        let ptr = TagPtr::compose(reference, 0b11);
578
579        // add must cause overflow (corrupt the pointer)
580        assert_eq!(ptr.add_tag(1).into_usize(), reference as *mut _ as usize + 0b11 + 1);
581        // update must only overflow the tag bits
582        assert_eq!(ptr.update_tag(|tag| tag + 1).decompose(), (reference as *mut _, 0));
583    }
584
585    #[test]
586    fn test_underflow_tag() {
587        let reference = &mut 1;
588        let ptr = TagPtr::new(reference);
589
590        // sub_tag must underflow the entire pointer
591        assert_eq!(ptr.sub_tag(1).into_usize(), reference as *mut _ as usize - 1);
592        // update_tag must only underflow the tag value
593        assert_eq!(
594            ptr.update_tag(|tag| tag.wrapping_sub(1)).decompose(),
595            (reference as *mut _, 0b11)
596        );
597    }
598
599    #[test]
600    fn test_erase() {
601        #[repr(align(64))]
602        struct Aligned64(i32);
603
604        let reference = &Aligned64(1);
605        let ptr = crate::TagPtr::<Aligned64, 6>::from((reference, 55));
606        let mut erased: crate::TagPtr<(), 6> = ptr.cast();
607        erased = erased.update_tag(|tag| tag + 3);
608        let ptr: crate::TagPtr<Aligned64, 6> = erased.cast();
609
610        assert_eq!(ptr.decompose(), (reference as *const _ as *mut _, 58));
611    }
612}