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}