|
1 | 1 | use core::cell::RefCell;
|
| 2 | +use core::num::NonZeroUsize; |
2 | 3 | use core::ptr;
|
3 | 4 | use core::ptr::*;
|
4 | 5 | use std::fmt::{Debug, Display};
|
@@ -691,3 +692,80 @@ fn thin_box() {
|
691 | 692 | }
|
692 | 693 | }
|
693 | 694 | }
|
| 695 | + |
| 696 | +#[test] |
| 697 | +fn nonnull_tagged_pointer_with_provenance() { |
| 698 | + let raw_pointer = Box::into_raw(Box::new(10)); |
| 699 | + |
| 700 | + let mut p = TaggedPointer::new(raw_pointer).unwrap(); |
| 701 | + assert_eq!(p.tag(), 0); |
| 702 | + |
| 703 | + p.set_tag(1); |
| 704 | + assert_eq!(p.tag(), 1); |
| 705 | + assert_eq!(unsafe { *p.pointer().as_ptr() }, 10); |
| 706 | + |
| 707 | + p.set_tag(3); |
| 708 | + assert_eq!(p.tag(), 3); |
| 709 | + assert_eq!(unsafe { *p.pointer().as_ptr() }, 10); |
| 710 | + |
| 711 | + unsafe { Box::from_raw(p.pointer().as_ptr()) }; |
| 712 | + |
| 713 | + /// A non-null pointer type which carries several bits of metadata and maintains provenance. |
| 714 | + #[repr(transparent)] |
| 715 | + pub struct TaggedPointer<T>(NonNull<T>); |
| 716 | + |
| 717 | + impl<T> Clone for TaggedPointer<T> { |
| 718 | + fn clone(&self) -> Self { |
| 719 | + Self(self.0) |
| 720 | + } |
| 721 | + } |
| 722 | + |
| 723 | + impl<T> Copy for TaggedPointer<T> {} |
| 724 | + |
| 725 | + impl<T> TaggedPointer<T> { |
| 726 | + /// The ABI-required minimum alignment of the `P` type. |
| 727 | + pub const ALIGNMENT: usize = core::mem::align_of::<T>(); |
| 728 | + /// A mask for data-carrying bits of the address. |
| 729 | + pub const DATA_MASK: usize = !Self::ADDRESS_MASK; |
| 730 | + /// Number of available bits of storage in the address. |
| 731 | + pub const NUM_BITS: u32 = Self::ALIGNMENT.trailing_zeros(); |
| 732 | + /// A mask for the non-data-carrying bits of the address. |
| 733 | + pub const ADDRESS_MASK: usize = usize::MAX << Self::NUM_BITS; |
| 734 | + |
| 735 | + /// Create a new tagged pointer from a possibly null pointer. |
| 736 | + pub fn new(pointer: *mut T) -> Option<TaggedPointer<T>> { |
| 737 | + Some(TaggedPointer(NonNull::new(pointer)?)) |
| 738 | + } |
| 739 | + |
| 740 | + /// Consume this tagged pointer and produce a raw mutable pointer to the |
| 741 | + /// memory location. |
| 742 | + pub fn pointer(self) -> NonNull<T> { |
| 743 | + // SAFETY: The `addr` guaranteed to have bits set in the Self::ADDRESS_MASK, so the result will be non-null. |
| 744 | + self.0.map_addr(|addr| unsafe { |
| 745 | + NonZeroUsize::new_unchecked(addr.get() & Self::ADDRESS_MASK) |
| 746 | + }) |
| 747 | + } |
| 748 | + |
| 749 | + /// Consume this tagged pointer and produce the data it carries. |
| 750 | + pub fn tag(&self) -> usize { |
| 751 | + self.0.addr().get() & Self::DATA_MASK |
| 752 | + } |
| 753 | + |
| 754 | + /// Update the data this tagged pointer carries to a new value. |
| 755 | + pub fn set_tag(&mut self, data: usize) { |
| 756 | + assert_eq!( |
| 757 | + data & Self::ADDRESS_MASK, |
| 758 | + 0, |
| 759 | + "cannot set more data beyond the lowest NUM_BITS" |
| 760 | + ); |
| 761 | + let data = data & Self::DATA_MASK; |
| 762 | + |
| 763 | + // SAFETY: This value will always be non-zero because the upper bits (from |
| 764 | + // ADDRESS_MASK) will always be non-zero. This a property of the type and its |
| 765 | + // construction. |
| 766 | + self.0 = self.0.map_addr(|addr| unsafe { |
| 767 | + NonZeroUsize::new_unchecked((addr.get() & Self::ADDRESS_MASK) | data) |
| 768 | + }) |
| 769 | + } |
| 770 | + } |
| 771 | +} |
0 commit comments