1 use super::{Pointer, Tag};
2 use crate::stable_hasher::{HashStable, StableHasher};
4 use std::marker::PhantomData;
5 use std::num::NonZeroUsize;
7 /// A `Copy` TaggedPtr.
9 /// You should use this instead of the `TaggedPtr` type in all cases where
12 /// If `COMPARE_PACKED` is true, then the pointers will be compared and hashed without
13 /// unpacking. Otherwise we don't implement PartialEq/Eq/Hash; if you want that,
14 /// wrap the TaggedPtr.
15 pub struct CopyTaggedPtr<P, T, const COMPARE_PACKED: bool>
21 data: PhantomData<(P, T)>,
24 impl<P, T, const COMPARE_PACKED: bool> Copy for CopyTaggedPtr<P, T, COMPARE_PACKED>
32 impl<P, T, const COMPARE_PACKED: bool> Clone for CopyTaggedPtr<P, T, COMPARE_PACKED>
38 fn clone(&self) -> Self {
43 // We pack the tag into the *upper* bits of the pointer to ease retrieval of the
44 // value; a left shift is a multiplication and those are embeddable in
45 // instruction encoding.
46 impl<P, T, const COMPARE_PACKED: bool> CopyTaggedPtr<P, T, COMPARE_PACKED>
51 const TAG_BIT_SHIFT: usize = usize::BITS as usize - T::BITS;
52 const ASSERTION: () = {
53 assert!(T::BITS <= P::BITS);
54 // Used for the transmute_copy's below
55 assert!(std::mem::size_of::<&P::Target>() == std::mem::size_of::<usize>());
58 pub fn new(pointer: P, tag: T) -> Self {
60 let () = Self::ASSERTION;
61 let packed_tag = tag.into_usize() << Self::TAG_BIT_SHIFT;
64 // SAFETY: We know that the pointer is non-null, as it must be
65 // dereferenceable per `Pointer` safety contract.
67 NonZeroUsize::new_unchecked((P::into_usize(pointer) >> T::BITS) | packed_tag)
73 pub(super) fn pointer_raw(&self) -> usize {
74 self.packed.get() << T::BITS
76 pub fn pointer(self) -> P
80 // SAFETY: pointer_raw returns the original pointer
82 // Note that this isn't going to double-drop or anything because we have
84 unsafe { P::from_usize(self.pointer_raw()) }
86 pub fn pointer_ref(&self) -> &P::Target {
87 // SAFETY: pointer_raw returns the original pointer
88 unsafe { std::mem::transmute_copy(&self.pointer_raw()) }
90 pub fn pointer_mut(&mut self) -> &mut P::Target
92 P: std::ops::DerefMut,
94 // SAFETY: pointer_raw returns the original pointer
95 unsafe { std::mem::transmute_copy(&self.pointer_raw()) }
98 pub fn tag(&self) -> T {
99 unsafe { T::from_usize(self.packed.get() >> Self::TAG_BIT_SHIFT) }
102 pub fn set_tag(&mut self, tag: T) {
103 let mut packed = self.packed.get();
104 let new_tag = T::into_usize(tag) << Self::TAG_BIT_SHIFT;
105 let tag_mask = (1 << T::BITS) - 1;
106 packed &= !(tag_mask << Self::TAG_BIT_SHIFT);
108 self.packed = unsafe { NonZeroUsize::new_unchecked(packed) };
112 impl<P, T, const COMPARE_PACKED: bool> std::ops::Deref for CopyTaggedPtr<P, T, COMPARE_PACKED>
117 type Target = P::Target;
118 fn deref(&self) -> &Self::Target {
123 impl<P, T, const COMPARE_PACKED: bool> std::ops::DerefMut for CopyTaggedPtr<P, T, COMPARE_PACKED>
125 P: Pointer + std::ops::DerefMut,
128 fn deref_mut(&mut self) -> &mut Self::Target {
133 impl<P, T, const COMPARE_PACKED: bool> fmt::Debug for CopyTaggedPtr<P, T, COMPARE_PACKED>
136 P::Target: fmt::Debug,
139 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
140 f.debug_struct("CopyTaggedPtr")
141 .field("pointer", &self.pointer_ref())
142 .field("tag", &self.tag())
147 impl<P, T> PartialEq for CopyTaggedPtr<P, T, true>
152 fn eq(&self, other: &Self) -> bool {
153 self.packed == other.packed
157 impl<P, T> Eq for CopyTaggedPtr<P, T, true>
164 impl<P, T> std::hash::Hash for CopyTaggedPtr<P, T, true>
169 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
170 self.packed.hash(state);
174 impl<P, T, HCX, const COMPARE_PACKED: bool> HashStable<HCX> for CopyTaggedPtr<P, T, COMPARE_PACKED>
176 P: Pointer + HashStable<HCX>,
177 T: Tag + HashStable<HCX>,
179 fn hash_stable(&self, hcx: &mut HCX, hasher: &mut StableHasher) {
181 Pointer::with_ref(self.pointer_raw(), |p: &P| p.hash_stable(hcx, hasher));
183 self.tag().hash_stable(hcx, hasher);