1#[repr(transparent)]
22pub struct PackedPointer<T, const DATA_BITS: usize, const CHECK_ALIGNMENT: bool = true> {
23 ptr: *mut T,
24}
25
26impl<T, const DATA_BITS: usize, const CHECK_ALIGNMENT: bool> Clone
28 for PackedPointer<T, DATA_BITS, CHECK_ALIGNMENT>
29{
30 fn clone(&self) -> Self {
31 *self
32 }
33}
34
35impl<T, const DATA_BITS: usize, const CHECK_ALIGNMENT: bool> Copy
36 for PackedPointer<T, DATA_BITS, CHECK_ALIGNMENT>
37{
38}
39
40impl<T, const DATA_BITS: usize, const CHECK_ALIGNMENT: bool> PartialEq
42 for PackedPointer<T, DATA_BITS, CHECK_ALIGNMENT>
43{
44 fn eq(&self, other: &Self) -> bool {
45 self.ptr == other.ptr
46 }
47}
48
49impl<T, const DATA_BITS: usize, const CHECK_ALIGNMENT: bool> Eq
50 for PackedPointer<T, DATA_BITS, CHECK_ALIGNMENT>
51{
52}
53
54impl<T, const DATA_BITS: usize, const CHECK_ALIGNMENT: bool> PartialEq<*mut T>
56 for PackedPointer<T, DATA_BITS, CHECK_ALIGNMENT>
57{
58 fn eq(&self, other: &*mut T) -> bool {
59 self.ptr() == *other
60 }
61}
62
63impl<T, const DATA_BITS: usize, const CHECK_ALIGNMENT: bool> PartialEq<*const T>
64 for PackedPointer<T, DATA_BITS, CHECK_ALIGNMENT>
65{
66 fn eq(&self, other: &*const T) -> bool {
67 self.ptr() as *const T == *other
68 }
69}
70
71impl<T, const DATA_BITS: usize, const CHECK_ALIGNMENT: bool>
72 PackedPointer<T, DATA_BITS, CHECK_ALIGNMENT>
73{
74 const DATA_MASK: usize = (1 << DATA_BITS) - 1;
75 const PTR_MASK: usize = !Self::DATA_MASK;
76
77 const _ASSERT: () = {
78 assert!(DATA_BITS > 0, "PackedPointer requires at least one data bit.");
79 assert!(DATA_BITS < usize::BITS as usize, "Too many data bits requested.");
80 if CHECK_ALIGNMENT {
81 assert!(
82 core::mem::align_of::<T>() >= (1 << DATA_BITS),
83 "T has insufficient alignment for the requested number of data bits."
84 );
85 }
86 };
87
88 pub fn new(ptr: *mut T, data: usize) -> Self {
95 let _ = Self::_ASSERT;
96 debug_assert!(
97 ptr.addr() & Self::DATA_MASK == 0,
98 "Pointer {:?} is not aligned to at least {} bytes",
99 ptr,
100 1 << DATA_BITS
101 );
102 debug_assert!(data & Self::PTR_MASK == 0, "Data {} exceeds {} bits", data, DATA_BITS);
103
104 let packed_addr = (ptr.addr() & Self::PTR_MASK) | (data & Self::DATA_MASK);
105 Self { ptr: ptr.with_addr(packed_addr) }
106 }
107
108 pub const fn null() -> Self {
110 let _ = Self::_ASSERT;
111 Self { ptr: core::ptr::null_mut() }
112 }
113
114 pub fn from_ptr(ptr: *mut T) -> Self {
116 Self::new(ptr, 0)
117 }
118
119 pub const fn from_data(data: usize) -> Self {
125 let _ = Self::_ASSERT;
126 assert!(data & Self::PTR_MASK == 0, "Data exceeds allowed bits");
127
128 Self { ptr: (data & Self::DATA_MASK) as *mut T }
129 }
130
131 pub fn ptr(&self) -> *mut T {
133 self.ptr.map_addr(|addr| addr & Self::PTR_MASK)
134 }
135
136 pub fn data(&self) -> usize {
138 self.ptr.addr() & Self::DATA_MASK
139 }
140
141 pub fn set_ptr(&mut self, ptr: *mut T) {
143 debug_assert!(
144 ptr.addr() & Self::DATA_MASK == 0,
145 "Pointer {:?} is not aligned to at least {} bytes",
146 ptr,
147 1 << DATA_BITS
148 );
149 let data = self.data();
150 let packed_addr = (ptr.addr() & Self::PTR_MASK) | data;
151 self.ptr = ptr.with_addr(packed_addr);
152 }
153
154 pub fn set_data(&mut self, data: usize) {
156 debug_assert!(data & Self::PTR_MASK == 0, "Data {} exceeds {} bits", data, DATA_BITS);
157 let packed_addr = (self.ptr.addr() & Self::PTR_MASK) | (data & Self::DATA_MASK);
158 self.ptr = self.ptr.with_addr(packed_addr);
159 }
160
161 pub fn reset(&mut self) {
163 self.ptr = core::ptr::null_mut();
164 }
165
166 pub fn is_null(&self) -> bool {
168 self.ptr().is_null()
169 }
170}
171
172impl<T, const DATA_BITS: usize, const CHECK_ALIGNMENT: bool> Default
173 for PackedPointer<T, DATA_BITS, CHECK_ALIGNMENT>
174{
175 fn default() -> Self {
176 Self::null()
177 }
178}
179
180impl<T, const DATA_BITS: usize, const CHECK_ALIGNMENT: bool> From<*mut T>
181 for PackedPointer<T, DATA_BITS, CHECK_ALIGNMENT>
182{
183 fn from(ptr: *mut T) -> Self {
184 Self::from_ptr(ptr)
185 }
186}
187
188impl<T, const DATA_BITS: usize, const CHECK_ALIGNMENT: bool> core::fmt::Debug
189 for PackedPointer<T, DATA_BITS, CHECK_ALIGNMENT>
190{
191 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
192 f.debug_struct("PackedPointer")
193 .field("ptr", &self.ptr())
194 .field("data", &self.data())
195 .finish()
196 }
197}
198
199#[cfg(test)]
200mod tests {
201 use super::*;
202
203 #[derive(Debug)]
204 #[repr(align(8))]
205 struct Align8(#[allow(dead_code)] u64);
206
207 #[test]
208 fn test_basic_pack_unpack() {
209 let mut val = Align8(42);
210 let raw_ptr = &mut val as *mut Align8;
211
212 let packed = PackedPointer::<Align8, 3>::new(raw_ptr, 5);
213 assert_eq!(packed.ptr(), raw_ptr);
214 assert_eq!(packed.data(), 5);
215 assert!(!packed.is_null());
216
217 unsafe {
218 assert_eq!((*packed.ptr()).0, 42);
219 }
220 }
221
222 #[test]
223 fn test_setters() {
224 let mut val1 = Align8(10);
225 let mut val2 = Align8(20);
226 let raw_ptr1 = &mut val1 as *mut Align8;
227 let raw_ptr2 = &mut val2 as *mut Align8;
228
229 let mut packed = PackedPointer::<Align8, 3>::from_ptr(raw_ptr1);
230 assert_eq!(packed.ptr(), raw_ptr1);
231 assert_eq!(packed.data(), 0);
232
233 packed.set_data(7);
234 assert_eq!(packed.ptr(), raw_ptr1);
235 assert_eq!(packed.data(), 7);
236
237 packed.set_ptr(raw_ptr2);
238 assert_eq!(packed.ptr(), raw_ptr2);
239 assert_eq!(packed.data(), 7);
240
241 packed.reset();
242 assert!(packed.is_null());
243 assert_eq!(packed.data(), 0);
244 }
245
246 #[test]
247 fn test_default() {
248 let packed = PackedPointer::<Align8, 3>::default();
249 assert!(packed.is_null());
250 assert_eq!(packed.data(), 0);
251 }
252
253 #[test]
254 fn test_const_constructors() {
255 const MY_NULL_PTR: PackedPointer<Align8, 3> = PackedPointer::null();
256 const MY_DATA_PTR: PackedPointer<Align8, 3> = PackedPointer::from_data(5);
257
258 assert!(MY_NULL_PTR.is_null());
259 assert_eq!(MY_NULL_PTR.data(), 0);
260
261 assert!(MY_DATA_PTR.is_null());
262 assert_eq!(MY_DATA_PTR.data(), 5);
263 }
264
265 #[test]
266 fn test_pointer_deref() {
267 let mut val = Align8(42);
268 let packed = PackedPointer::<Align8, 3>::from_ptr(&mut val);
269 unsafe {
270 assert_eq!((*packed.ptr()).0, 42);
271 (*packed.ptr()).0 = 100;
272 }
273 assert_eq!(val.0, 100);
274 }
275
276 #[test]
277 fn test_comparisons() {
278 let mut val1 = Align8(10);
279 let mut val2 = Align8(20);
280 let raw_ptr1 = &mut val1 as *mut Align8;
281 let raw_ptr2 = &mut val2 as *mut Align8;
282
283 let ptr1 = PackedPointer::<Align8, 3>::new(raw_ptr1, 1);
284 let ptr1_again = PackedPointer::<Align8, 3>::new(raw_ptr1, 1);
285 let ptr1_diff_data = PackedPointer::<Align8, 3>::new(raw_ptr1, 2);
286 let ptr2 = PackedPointer::<Align8, 3>::new(raw_ptr2, 1);
287
288 assert_eq!(ptr1, ptr1_again);
289 assert_ne!(ptr1, ptr1_diff_data);
290 assert_ne!(ptr1, ptr2);
291
292 let null_ptr = PackedPointer::<Align8, 3>::default();
293 assert_eq!(null_ptr, core::ptr::null_mut());
294 assert_ne!(ptr1, core::ptr::null_mut());
295 }
296
297 #[test]
298 fn test_disabled_alignment_check() {
299 #[derive(Debug)]
300 #[repr(align(4))]
301 struct Align4(#[allow(dead_code)] u32);
302
303 #[repr(align(8))]
310 struct Align8Buffer(#[allow(dead_code)] [u8; 8]);
311 let mut buffer = Align8Buffer([0; 8]);
312 let raw_ptr = &mut buffer as *mut Align8Buffer as *mut Align4;
313
314 let packed = PackedPointer::<Align4, 3, false>::new(raw_ptr, 5);
315 assert_eq!(packed.ptr(), raw_ptr);
316 assert_eq!(packed.data(), 5);
317 }
318
319 #[test]
320 fn test_packed_pointer_clone() {
321 let mut val = Align8(42);
322 let raw_ptr = &mut val as *mut Align8;
323 let packed = PackedPointer::<Align8, 3>::new(raw_ptr, 5);
324 let cloned = packed.clone();
325 assert_eq!(cloned, packed);
326 }
327
328 #[test]
329 fn test_packed_pointer_partial_eq_const_ptr() {
330 let mut val = Align8(42);
331 let raw_ptr = &mut val as *mut Align8;
332 let packed = PackedPointer::<Align8, 3>::new(raw_ptr, 5);
333 let const_ptr: *const Align8 = raw_ptr as *const Align8;
334 assert!(packed == const_ptr);
335 }
336
337 #[test]
338 fn test_packed_pointer_from_mut_ptr() {
339 let mut val = Align8(42);
340 let raw_ptr = &mut val as *mut Align8;
341 let from_ptr = PackedPointer::<Align8, 3>::from(raw_ptr);
342 assert_eq!(from_ptr.ptr(), raw_ptr);
343 assert_eq!(from_ptr.data(), 0);
344 }
345
346 #[test]
347 fn test_packed_pointer_debug_fmt() {
348 extern crate alloc;
349 let mut val = Align8(42);
350 let raw_ptr = &mut val as *mut Align8;
351 let packed = PackedPointer::<Align8, 3>::new(raw_ptr, 5);
352 let debug_str = alloc::format!("{:?}", packed);
353 assert!(debug_str.contains("PackedPointer"));
354 assert!(debug_str.contains("ptr"));
355 assert!(debug_str.contains("data"));
356 }
357
358 #[test]
359 fn test_packed_pointer_from_data() {
360 let from_d = PackedPointer::<Align8, 3>::from_data(6);
361 assert!(from_d.is_null());
362 assert_eq!(from_d.data(), 6);
363 }
364
365 #[test]
366 #[cfg(debug_assertions)]
367 #[should_panic(expected = "is not aligned")]
368 fn test_unaligned_pointer_panics() {
369 let mut val = Align8(42);
370 let raw_ptr = &mut val as *mut Align8;
371 let unaligned_ptr = raw_ptr.with_addr(raw_ptr.addr() | 1);
372 let _ = PackedPointer::<Align8, 3>::new(unaligned_ptr, 0);
373 }
374
375 #[test]
376 #[cfg(debug_assertions)]
377 #[should_panic(expected = "is not aligned")]
378 fn test_unaligned_set_ptr_panics() {
379 let mut val = Align8(42);
380 let mut packed = PackedPointer::<Align8, 3>::from_ptr(&mut val);
381 let raw_ptr = &mut val as *mut Align8;
382 let unaligned_ptr = raw_ptr.with_addr(raw_ptr.addr() | 1);
383 packed.set_ptr(unaligned_ptr);
384 }
385}