packed_ptr/
reference.rs

1use core::{
2    fmt::{Debug, Formatter, Pointer},
3    marker::PhantomData,
4    ops::Deref,
5};
6
7use crate::{config::PtrCfg, error::PackedPtrError, Packable, TypedPackedPtr};
8
9/// A type safe reference to a packed pointer.
10///
11/// Equivalent to `&T` where `T` is the type of the pointer.
12#[repr(transparent)]
13pub struct PackedRef<'a, T, C: PtrCfg, D: Packable>(TypedPackedPtr<T, C, D>, PhantomData<&'a T>);
14
15impl<'a, T, C: PtrCfg, D: Packable> PackedRef<'a, T, C, D> {
16    /// Creates a new [`PackedRef`] from a reference and some data.
17    ///
18    /// # Errors
19    ///
20    /// * [`PackedPtrError::DataOverflow`] if the data is too large to fit in the pointer.
21    /// * [`PackedPtrError::UnsafeConfig`] if the pointer is not compatible with the configuration.
22    pub fn new(ptr: &'a T, data: D, cfg: C) -> Result<Self, PackedPtrError> {
23        Ok(Self(TypedPackedPtr::new(ptr, data, cfg)?, PhantomData))
24    }
25
26    /// Creates a new [`PackedRef`] from a reference and some data without performing any safety
27    /// checks.
28    ///
29    /// # Safety
30    ///
31    /// This function is unsafe because the caller assumes the responsibility of ensuring that the
32    /// provided `ptr` and `data` are valid and that they are compatible with the configuration.
33    ///
34    /// References are always aligned, therefore `ptr` should always be valid, but if the `ptr` of
35    /// the system is incompatible with the configuration, then the reference will be corrupted,
36    /// resulting in UB.
37    ///
38    /// Also, if the `data` is too big to fit in the pointer, undefined behavior may occur.
39    ///
40    /// # Arguments
41    ///
42    /// - `ptr`: A reference to a `T`
43    /// - `data`: The data to be packed into the pointer.
44    ///
45    /// # Returns
46    ///
47    /// A new [`PackedRef`] with the given `ptr` and `data`.
48    pub unsafe fn new_unchecked(ptr: &'a T, data: D) -> Self {
49        Self(TypedPackedPtr::new_unchecked(ptr, data), PhantomData)
50    }
51
52    /// Returns the reference to the data.
53    fn r#ref(self) -> &'a T {
54        // SAFETY: the pointer is always valid per type invariant.
55        unsafe { &*self.0.ptr() }
56    }
57
58    /// Returns the packed data value of the Packed Pointer.
59    #[must_use]
60    pub fn data(self) -> D {
61        self.0.data()
62    }
63
64    /// Returns a tuple containing the reference and the packed data value.
65    #[must_use]
66    pub fn get(self) -> (&'a T, D) {
67        (self.r#ref(), self.0.data())
68    }
69
70    /// Sets the packed data value of the pointer.
71    ///
72    /// # Arguments
73    ///
74    /// * `data`: The data to pack into the pointer.
75    pub fn set_data(&mut self, data: D) {
76        self.0.set_data(data);
77    }
78}
79
80impl<T, C: PtrCfg, D: Packable> Deref for PackedRef<'_, T, C, D> {
81    type Target = T;
82
83    fn deref(&self) -> &Self::Target {
84        self.r#ref()
85    }
86}
87
88impl<T, C: PtrCfg, D: Packable> Clone for PackedRef<'_, T, C, D> {
89    fn clone(&self) -> Self {
90        *self
91    }
92}
93
94impl<T, C: PtrCfg, D: Packable> Copy for PackedRef<'_, T, C, D> {}
95
96impl<T: Debug, C: PtrCfg, D: Packable + Debug> Debug for PackedRef<'_, T, C, D> {
97    fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
98        f.debug_tuple("PackedRef")
99            .field(&self.r#ref())
100            .field(&self.data())
101            .finish()
102    }
103}
104
105impl<T, C: PtrCfg, D: Packable> Pointer for PackedRef<'_, T, C, D> {
106    fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
107        Pointer::fmt(&self.r#ref(), f)
108    }
109}
110
111#[cfg(test)]
112mod tests {
113    use super::*;
114    use crate::config::AlignOnly;
115
116    #[test]
117    fn new() {
118        let data = 0xdead_beef_u32;
119        let packed = (true, false);
120
121        let ok = PackedRef::new(&data, packed, AlignOnly).unwrap();
122
123        assert_eq!(*ok, data);
124        assert_eq!(ok.data(), packed);
125        assert_eq!(ok.get(), (&data, packed));
126
127        let packed = 255u32;
128        let overflow = PackedRef::new(&data, packed, AlignOnly);
129
130        assert!(overflow.is_err());
131        assert!(matches!(
132            overflow.unwrap_err(),
133            PackedPtrError::DataOverflow
134        ));
135    }
136
137    #[test]
138    fn set_data() {
139        let data = 0xdead_beef_u32;
140        let packed = (true, false);
141
142        let mut ref1 = PackedRef::new(&data, packed, AlignOnly).unwrap();
143
144        assert_eq!(*ref1, data);
145        assert_eq!(ref1.data(), packed);
146        assert_eq!(ref1.get(), (&data, packed));
147
148        let new_packed = (false, true);
149        ref1.set_data(new_packed);
150
151        assert_eq!(*ref1, data);
152        assert_eq!(ref1.data(), new_packed);
153        assert_eq!(ref1.get(), (&data, new_packed));
154    }
155}