tagged_box/
tagged_pointer.rs1use crate::discriminant::{
2 Discriminant, DISCRIMINANT_MASK, MAX_DISCRIMINANT, MAX_POINTER_VALUE, POINTER_WIDTH,
3};
4use core::fmt;
5
6#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
11#[repr(transparent)]
12pub struct TaggedPointer {
13 tagged_ptr: usize,
17}
18
19impl TaggedPointer {
20 #[inline]
30 #[allow(clippy::absurd_extreme_comparisons)]
31 pub fn new(ptr: usize, discriminant: Discriminant) -> Self {
32 assert!(
33 discriminant <= MAX_DISCRIMINANT,
34 "Attempted to store a discriminant of {} while the max value is {}",
35 discriminant,
36 MAX_DISCRIMINANT,
37 );
38 assert!(
39 ptr <= MAX_POINTER_VALUE,
40 "If you are receiving this error, then your hardware uses more than {} bits of a pointer to store addresses. \
41 It is recommended that you use a different feature for the `tagged-box` crate using `features = [\"{}bits\"]` or above.
42 ",
43 POINTER_WIDTH,
44 POINTER_WIDTH + 1,
45 );
46
47 let tagged_ptr = unsafe { Self::store_discriminant_unchecked(ptr, discriminant) };
49
50 Self { tagged_ptr }
51 }
52
53 #[inline]
64 pub unsafe fn new_unchecked(ptr: usize, discriminant: Discriminant) -> Self {
65 let tagged_ptr = Self::store_discriminant_unchecked(ptr, discriminant);
66
67 Self { tagged_ptr }
68 }
69
70 #[inline]
72 pub const fn discriminant(self) -> Discriminant {
73 Self::fetch_discriminant(self.tagged_ptr)
74 }
75
76 #[inline]
84 #[allow(clippy::should_implement_trait)]
85 pub unsafe fn as_ref<T>(&self) -> &T {
86 &*(Self::strip_discriminant(self.tagged_ptr) as *const T)
87 }
88
89 #[inline]
97 pub unsafe fn as_mut_ref<T>(&mut self) -> &mut T {
98 &mut *(Self::strip_discriminant(self.tagged_ptr) as *mut T)
99 }
100
101 #[inline]
103 pub const fn as_usize(self) -> usize {
104 Self::strip_discriminant(self.tagged_ptr)
105 }
106
107 #[inline]
114 pub const fn as_raw_usize(self) -> usize {
115 self.tagged_ptr
116 }
117
118 #[inline]
120 pub const fn as_ptr<T>(self) -> *const T {
121 Self::strip_discriminant(self.tagged_ptr) as *const T
122 }
123
124 #[inline]
126 pub fn as_mut_ptr<T>(self) -> *mut T {
127 Self::strip_discriminant(self.tagged_ptr) as *mut T
128 }
129
130 #[inline]
141 #[allow(clippy::absurd_extreme_comparisons)]
142 pub fn store_discriminant(pointer: usize, discriminant: Discriminant) -> usize {
143 assert!(
144 discriminant <= MAX_DISCRIMINANT,
145 "Attempted to store a discriminant of {} while the max value is {}",
146 discriminant,
147 MAX_DISCRIMINANT,
148 );
149 assert!(
150 pointer <= MAX_POINTER_VALUE,
151 "If you are receiving this error, then your hardware uses more than {} bits of a pointer to store addresses. \
152 It is recommended that you use a different feature for the `tagged-box` crate using `features = [\"{}bits\"]` or above.
153 ",
154 POINTER_WIDTH,
155 POINTER_WIDTH + 1,
156 );
157
158 pointer | ((discriminant as usize) << POINTER_WIDTH)
159 }
160
161 #[inline]
172 pub unsafe fn store_discriminant_unchecked(
173 pointer: usize,
174 discriminant: Discriminant,
175 ) -> usize {
176 pointer | ((discriminant as usize) << POINTER_WIDTH)
177 }
178
179 #[inline]
183 #[allow(clippy::cast_possible_truncation)]
184 pub const fn fetch_discriminant(pointer: usize) -> Discriminant {
185 (pointer >> POINTER_WIDTH) as Discriminant
186 }
187
188 #[inline]
192 pub const fn strip_discriminant(pointer: usize) -> usize {
193 pointer & DISCRIMINANT_MASK
194 }
195}
196
197impl fmt::Debug for TaggedPointer {
198 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
199 f.debug_struct("TaggedPointer")
200 .field("raw", &(self.as_raw_usize() as *const ()))
201 .field("ptr", &self.as_ptr::<()>())
202 .field("discriminant", &self.discriminant())
203 .finish()
204 }
205}
206
207impl fmt::Pointer for TaggedPointer {
208 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
209 fmt::Pointer::fmt(&self.as_ptr::<()>(), f)
210 }
211}
212
213impl_fmt!(TaggedPointer => LowerHex, UpperHex, Binary, Octal);
214
215#[cfg(test)]
216mod tests {
217 use super::*;
218 use crate::discriminant;
219 use alloc::string::String;
220 use core::slice;
221
222 #[test]
223 fn utility_functions() {
224 let ptr = 0xF00D_BEEF;
225 let discrim = discriminant::MAX_DISCRIMINANT / 2;
226 let stored = TaggedPointer::new(ptr, discrim).as_raw_usize();
227
228 assert_eq!(TaggedPointer::strip_discriminant(stored), ptr);
229 assert_eq!(TaggedPointer::fetch_discriminant(stored), discrim);
230 assert_eq!(TaggedPointer::store_discriminant(ptr, discrim), stored);
231 }
232
233 #[test]
234 fn tagged_pointer() {
235 let integer = 100i32;
236 let int_ptr = &integer as *const _ as usize;
237 let discriminant = 10;
238
239 let ptr = TaggedPointer::new(int_ptr, discriminant);
240
241 assert_eq!(ptr.discriminant(), discriminant);
242 assert_eq!(ptr.as_usize(), int_ptr);
243
244 unsafe {
245 assert_eq!(ptr.as_ref::<i32>(), &integer);
246 }
247 }
248
249 #[test]
250 fn max_pointer() {
251 let ptr = discriminant::MAX_POINTER_VALUE;
252 let discriminant = discriminant::MAX_DISCRIMINANT;
253
254 let tagged = TaggedPointer::new(ptr, discriminant);
255
256 assert_eq!(tagged.discriminant(), discriminant);
257 assert_eq!(tagged.as_usize(), ptr);
258 }
259
260 #[test]
261 fn min_pointer() {
262 let ptr: usize = 0;
263 let discriminant = discriminant::MAX_DISCRIMINANT;
264
265 let tagged = TaggedPointer::new(ptr, discriminant);
266
267 assert_eq!(tagged.discriminant(), discriminant);
268 assert_eq!(tagged.as_usize(), ptr);
269 }
270
271 #[test]
272 fn max_discriminant() {
273 let integer = 100usize;
274 let int_ptr = &integer as *const _ as usize;
275 let discriminant = discriminant::MAX_DISCRIMINANT;
276
277 let ptr = TaggedPointer::new(int_ptr, discriminant);
278
279 assert_eq!(ptr.discriminant(), discriminant);
280 assert_eq!(ptr.as_usize(), int_ptr);
281
282 unsafe {
283 assert_eq!(ptr.as_ref::<usize>(), &integer);
284 }
285 }
286
287 #[test]
288 fn min_discriminant() {
289 let integer = 100usize;
290 let int_ptr = &integer as *const _ as usize;
291 let discriminant = 0;
292
293 let ptr = TaggedPointer::new(int_ptr, discriminant);
294
295 assert_eq!(ptr.discriminant(), discriminant);
296 assert_eq!(ptr.as_usize(), int_ptr);
297
298 unsafe {
299 assert_eq!(ptr.as_ref::<usize>(), &integer);
300 }
301 }
302
303 #[test]
304 fn string_pointer() {
305 let string = String::from("Hello world!");
306 let str_ptr = string.as_ptr() as usize;
307 let discriminant = discriminant::MAX_DISCRIMINANT;
308
309 let ptr = TaggedPointer::new(str_ptr, discriminant);
310
311 assert_eq!(ptr.discriminant(), discriminant);
312 assert_eq!(ptr.as_usize(), str_ptr);
313
314 unsafe {
315 let temp_str = slice::from_raw_parts(ptr.as_ptr::<u8>(), string.len());
316 assert_eq!(core::str::from_utf8(temp_str).unwrap(), &string);
317 }
318 }
319
320 #[test]
321 #[should_panic]
322 #[cfg_attr(miri, ignore)]
323 fn oversized_discriminant() {
324 let pointer = 0xF00D_BEEF;
325 let discriminant = if let Some(discrim) = discriminant::MAX_DISCRIMINANT.checked_add(1) {
326 discrim
327 } else {
328 panic!("Adding one to discriminant would overflow type, aborting test");
329 };
330
331 TaggedPointer::new(pointer, discriminant);
332 }
333}