1#[cfg(feature = "full")]
20mod user_ptr;
21
22use core::fmt;
23use core::fmt::Formatter;
24use core::num::NonZero;
25use core::ptr::NonNull;
26#[cfg(feature = "full")]
27pub use user_ptr::*;
28
29#[cfg(feature = "full")]
30mod user_local;
31#[cfg(feature = "full")]
32pub use user_local::*;
33
34#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
35#[path = "x86_64.rs"]
36#[cfg(feature = "full")]
37mod impls;
38
39#[derive(Debug)]
41#[non_exhaustive]
42pub struct PointerError {}
43
44#[derive(Copy, Clone, Debug, Ord, PartialOrd, Eq, PartialEq, Hash)]
49#[repr(transparent)]
50pub struct TaggedNonNull<T: ?Sized>(NonNull<T>);
51
52impl<T> TaggedNonNull<T> {
53 pub const TAG_HIGH_BITS: u32 = cfg_select! {
54 target_arch = "x86_64" => 16,
56 };
57 pub const TAG_LOW_BITS: u32 = align_of::<T>().trailing_zeros();
58
59 const MAX_BOUND_VALUE: usize = {
60 assert!(
61 Self::TAG_HIGH_BITS + Self::TAG_LOW_BITS < usize::BITS,
62 "cannot have pointer which is only tag",
63 );
64
65 1 << (Self::TAG_HIGH_BITS + Self::TAG_LOW_BITS)
66 };
67
68 pub const MAXIMUM_TAG: usize = Self::MAX_BOUND_VALUE - 1;
70
71 const TAG_HIGH_MASK: usize = ((1 << Self::TAG_HIGH_BITS) - 1) << (usize::BITS - Self::TAG_HIGH_BITS);
72 const TAG_LOW_MASK: usize = (1 << Self::TAG_LOW_BITS) - 1;
73
74 pub fn new(ptr: NonNull<T>, tag: usize) -> Self {
76 assert!(tag <= Self::MAX_BOUND_VALUE, "requested tag cannot fit in pointer");
77 assert!(ptr.is_aligned(), "cannot tag unaligned pointer");
78 let tag_low = tag & Self::TAG_LOW_MASK;
79 let tag_high = tag >> Self::TAG_LOW_BITS;
80 let ptr = ptr.map_addr(|mut addr| {
81 addr = unsafe { NonZero::new_unchecked(addr.get() & !(Self::TAG_HIGH_MASK | Self::TAG_LOW_MASK)) };
83 addr |= tag_low;
84 addr |= tag_high << (usize::BITS - Self::TAG_HIGH_BITS);
85 addr
86 });
87 Self(ptr)
88 }
89
90 pub fn as_ptr(&self) -> NonNull<T> {
92 let ptr = self.0.as_ptr();
93 let ptr = ptr.map_addr(|addr| {
94 let masked = addr & !(Self::TAG_HIGH_MASK | Self::TAG_LOW_MASK);
95 let canonical = (masked << Self::TAG_HIGH_BITS).cast_signed() >> Self::TAG_HIGH_BITS;
96 canonical.cast_unsigned()
97 });
98 unsafe { NonNull::new_unchecked(ptr) }
100 }
101
102 pub fn tag(&self) -> usize {
104 let addr = self.0.addr().get();
105 let low = addr & Self::TAG_LOW_MASK;
106 let high = addr & Self::TAG_HIGH_MASK;
107 low | ((high >> (usize::BITS - Self::TAG_HIGH_BITS)) << Self::TAG_LOW_BITS)
108 }
109
110 pub fn addr_eq(&self, other: &Self) -> bool {
113 core::ptr::addr_eq(self.as_ptr().as_ptr(), other.as_ptr().as_ptr())
114 }
115
116 pub fn as_tagged_ptr(&self) -> NonNull<T> {
117 self.0
118 }
119
120 pub fn from_tagged_ptr(ptr: NonNull<T>) -> Self {
121 Self(ptr)
122 }
123}
124
125impl<T> fmt::Pointer for TaggedNonNull<T> {
126 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
127 let addr = self.as_ptr().addr().get();
128 let meta = core::ptr::metadata(self.as_ptr().as_ptr());
129 let tag = self.tag();
130
131 let mut ptr_options = f.options();
133 if f.options().get_alternate() {
134 ptr_options.sign_aware_zero_pad(true);
135
136 if f.options().get_width().is_none() {
137 ptr_options.width(Some((usize::BITS / 4) as u16 + 2));
138 }
139 }
140
141 f.debug_struct("TaggedNonNull")
142 .field_with("addr", move |f| fmt::LowerHex::fmt(&addr, &mut f.with_options(ptr_options)))
143 .field("metadata", &meta)
144 .field("tag", &tag)
145 .finish()
146 }
147}
148
149#[cfg(test)]
150mod tests {
151 use super::*;
152
153 #[test]
154 fn tagged_nonnull_round_trip() {
155 let x = &5;
156 let tag = 0xCAFEusize;
157 let ptr = TaggedNonNull::new(
158 NonNull::from_ref(x),
159 tag,
160 );
161 assert_eq!(ptr.tag(), tag);
162 assert_eq!(ptr.as_ptr(), NonNull::from_ref(x));
163 }
164}