]>
Commit | Line | Data |
---|---|---|
f20569fa XL |
1 | use super::{Pointer, Tag}; |
2 | use crate::stable_hasher::{HashStable, StableHasher}; | |
3 | use std::fmt; | |
4 | use std::marker::PhantomData; | |
5 | use std::num::NonZeroUsize; | |
6 | ||
7 | /// A `Copy` TaggedPtr. | |
8 | /// | |
9 | /// You should use this instead of the `TaggedPtr` type in all cases where | |
10 | /// `P: Copy`. | |
11 | /// | |
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> | |
16 | where | |
17 | P: Pointer, | |
18 | T: Tag, | |
19 | { | |
20 | packed: NonZeroUsize, | |
21 | data: PhantomData<(P, T)>, | |
22 | } | |
23 | ||
24 | impl<P, T, const COMPARE_PACKED: bool> Copy for CopyTaggedPtr<P, T, COMPARE_PACKED> | |
25 | where | |
26 | P: Pointer, | |
27 | T: Tag, | |
28 | P: Copy, | |
29 | { | |
30 | } | |
31 | ||
32 | impl<P, T, const COMPARE_PACKED: bool> Clone for CopyTaggedPtr<P, T, COMPARE_PACKED> | |
33 | where | |
34 | P: Pointer, | |
35 | T: Tag, | |
36 | P: Copy, | |
37 | { | |
38 | fn clone(&self) -> Self { | |
39 | *self | |
40 | } | |
41 | } | |
42 | ||
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> | |
47 | where | |
48 | P: Pointer, | |
49 | T: Tag, | |
50 | { | |
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>()); | |
56 | }; | |
57 | ||
58 | pub fn new(pointer: P, tag: T) -> Self { | |
59 | // Trigger assert! | |
60 | let () = Self::ASSERTION; | |
61 | let packed_tag = tag.into_usize() << Self::TAG_BIT_SHIFT; | |
62 | ||
63 | Self { | |
64 | // SAFETY: We know that the pointer is non-null, as it must be | |
65 | // dereferenceable per `Pointer` safety contract. | |
66 | packed: unsafe { | |
67 | NonZeroUsize::new_unchecked((P::into_usize(pointer) >> T::BITS) | packed_tag) | |
68 | }, | |
69 | data: PhantomData, | |
70 | } | |
71 | } | |
72 | ||
73 | pub(super) fn pointer_raw(&self) -> usize { | |
74 | self.packed.get() << T::BITS | |
75 | } | |
76 | pub fn pointer(self) -> P | |
77 | where | |
78 | P: Copy, | |
79 | { | |
80 | // SAFETY: pointer_raw returns the original pointer | |
81 | // | |
82 | // Note that this isn't going to double-drop or anything because we have | |
83 | // P: Copy | |
84 | unsafe { P::from_usize(self.pointer_raw()) } | |
85 | } | |
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()) } | |
89 | } | |
90 | pub fn pointer_mut(&mut self) -> &mut P::Target | |
91 | where | |
92 | P: std::ops::DerefMut, | |
93 | { | |
94 | // SAFETY: pointer_raw returns the original pointer | |
95 | unsafe { std::mem::transmute_copy(&self.pointer_raw()) } | |
96 | } | |
97 | pub fn tag(&self) -> T { | |
98 | unsafe { T::from_usize(self.packed.get() >> Self::TAG_BIT_SHIFT) } | |
99 | } | |
100 | pub fn set_tag(&mut self, tag: T) { | |
101 | let mut packed = self.packed.get(); | |
102 | let new_tag = T::into_usize(tag) << Self::TAG_BIT_SHIFT; | |
103 | let tag_mask = (1 << T::BITS) - 1; | |
104 | packed &= !(tag_mask << Self::TAG_BIT_SHIFT); | |
105 | packed |= new_tag; | |
106 | self.packed = unsafe { NonZeroUsize::new_unchecked(packed) }; | |
107 | } | |
108 | } | |
109 | ||
110 | impl<P, T, const COMPARE_PACKED: bool> std::ops::Deref for CopyTaggedPtr<P, T, COMPARE_PACKED> | |
111 | where | |
112 | P: Pointer, | |
113 | T: Tag, | |
114 | { | |
115 | type Target = P::Target; | |
116 | fn deref(&self) -> &Self::Target { | |
117 | self.pointer_ref() | |
118 | } | |
119 | } | |
120 | ||
121 | impl<P, T, const COMPARE_PACKED: bool> std::ops::DerefMut for CopyTaggedPtr<P, T, COMPARE_PACKED> | |
122 | where | |
123 | P: Pointer + std::ops::DerefMut, | |
124 | T: Tag, | |
125 | { | |
126 | fn deref_mut(&mut self) -> &mut Self::Target { | |
127 | self.pointer_mut() | |
128 | } | |
129 | } | |
130 | ||
131 | impl<P, T, const COMPARE_PACKED: bool> fmt::Debug for CopyTaggedPtr<P, T, COMPARE_PACKED> | |
132 | where | |
133 | P: Pointer, | |
134 | P::Target: fmt::Debug, | |
135 | T: Tag + fmt::Debug, | |
136 | { | |
137 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | |
138 | f.debug_struct("CopyTaggedPtr") | |
139 | .field("pointer", &self.pointer_ref()) | |
140 | .field("tag", &self.tag()) | |
141 | .finish() | |
142 | } | |
143 | } | |
144 | ||
145 | impl<P, T> PartialEq for CopyTaggedPtr<P, T, true> | |
146 | where | |
147 | P: Pointer, | |
148 | T: Tag, | |
149 | { | |
150 | fn eq(&self, other: &Self) -> bool { | |
151 | self.packed == other.packed | |
152 | } | |
153 | } | |
154 | ||
155 | impl<P, T> Eq for CopyTaggedPtr<P, T, true> | |
156 | where | |
157 | P: Pointer, | |
158 | T: Tag, | |
159 | { | |
160 | } | |
161 | ||
162 | impl<P, T> std::hash::Hash for CopyTaggedPtr<P, T, true> | |
163 | where | |
164 | P: Pointer, | |
165 | T: Tag, | |
166 | { | |
167 | fn hash<H: std::hash::Hasher>(&self, state: &mut H) { | |
168 | self.packed.hash(state); | |
169 | } | |
170 | } | |
171 | ||
172 | impl<P, T, HCX, const COMPARE_PACKED: bool> HashStable<HCX> for CopyTaggedPtr<P, T, COMPARE_PACKED> | |
173 | where | |
174 | P: Pointer + HashStable<HCX>, | |
175 | T: Tag + HashStable<HCX>, | |
176 | { | |
177 | fn hash_stable(&self, hcx: &mut HCX, hasher: &mut StableHasher) { | |
178 | unsafe { | |
179 | Pointer::with_ref(self.pointer_raw(), |p: &P| p.hash_stable(hcx, hasher)); | |
180 | } | |
181 | self.tag().hash_stable(hcx, hasher); | |
182 | } | |
183 | } |