volatile/
volatile_ref.rs

1use crate::{
2    access::{Access, Copyable, ReadOnly, ReadWrite, RestrictAccess, WriteOnly},
3    volatile_ptr::VolatilePtr,
4};
5use core::{cmp::Ordering, fmt, hash, marker::PhantomData, ptr::NonNull};
6
7/// Volatile pointer type that respects Rust's aliasing rules.
8///
9/// This pointer type behaves similar to Rust's reference types:
10///
11/// - it requires exclusive `&mut self` access for mutability
12/// - only read-only types implement [`Clone`] and [`Copy`]
13/// - [`Send`] and [`Sync`] are implemented if `T: Sync`
14///
15/// However, trait implementations like [`fmt::Debug`] and [`Eq`] behave like they do on pointer
16/// types and don't access the referenced value.
17///
18/// To perform volatile operations on `VolatileRef` types, use the [`as_ptr`][Self::as_ptr]
19/// or [`as_mut_ptr`](Self::as_mut_ptr) methods to create a temporary
20/// [`VolatilePtr`][crate::VolatilePtr] instance.
21///
22/// Since not all volatile resources (e.g. memory mapped device registers) are both readable
23/// and writable, this type supports limiting the allowed access types through an optional second
24/// generic parameter `A` that can be one of `ReadWrite`, `ReadOnly`, or `WriteOnly`. It defaults
25/// to `ReadWrite`, which allows all operations.
26///
27/// The size of this struct is the same as the size of the contained reference.
28#[must_use]
29#[repr(transparent)]
30pub struct VolatileRef<'a, T, A = ReadWrite>
31where
32    T: ?Sized,
33{
34    pointer: NonNull<T>,
35    reference: PhantomData<&'a T>,
36    access: PhantomData<A>,
37}
38
39/// Constructor functions.
40///
41/// These functions construct new `VolatileRef` values. While the `new`
42/// function creates a `VolatileRef` instance with unrestricted access, there
43/// are also functions for creating read-only or write-only instances.
44impl<'a, T> VolatileRef<'a, T>
45where
46    T: ?Sized,
47{
48    /// Turns the given pointer into a `VolatileRef`.
49    ///
50    /// ## Safety
51    ///
52    /// - The pointer must be properly aligned.
53    /// - It must be “dereferenceable” in the sense defined in the [`core::ptr`] documentation.
54    /// - The pointer must point to an initialized instance of T.
55    /// - You must enforce Rust’s aliasing rules, since the returned lifetime 'a is arbitrarily
56    ///   chosen and does not necessarily reflect the actual lifetime of the data. In particular,
57    ///   while this `VolatileRef` exists, the memory the pointer points to must not get accessed
58    ///   (_read or written_) through any other pointer.
59    pub unsafe fn new(pointer: NonNull<T>) -> Self {
60        unsafe { VolatileRef::new_restricted(ReadWrite, pointer) }
61    }
62
63    /// Turns the given pointer into a read-only `VolatileRef`.
64    ///
65    /// ## Safety
66    ///
67    /// - The pointer must be properly aligned.
68    /// - It must be “dereferenceable” in the sense defined in the [`core::ptr`] documentation.
69    /// - The pointer must point to an initialized instance of T.
70    /// - You must enforce Rust’s aliasing rules, since the returned lifetime 'a is arbitrarily
71    ///   chosen and does not necessarily reflect the actual lifetime of the data. In particular,
72    ///   while this `VolatileRef` exists, the memory the pointer points to _must not get mutated_.
73    pub const unsafe fn new_read_only(pointer: NonNull<T>) -> VolatileRef<'a, T, ReadOnly> {
74        unsafe { Self::new_restricted(ReadOnly, pointer) }
75    }
76
77    /// Turns the given pointer into a `VolatileRef` instance with the given access.
78    ///
79    /// ## Safety
80    ///
81    /// - The pointer must be properly aligned.
82    /// - It must be “dereferenceable” in the sense defined in the [`core::ptr`] documentation.
83    /// - The pointer must point to an initialized instance of T.
84    /// - You must enforce Rust’s aliasing rules, since the returned lifetime 'a is arbitrarily
85    ///   chosen and does not necessarily reflect the actual lifetime of the data. In particular,
86    ///   while this `VolatileRef` exists, the memory the pointer points to _must not get mutated_.
87    ///   If the given `access` parameter allows write access, the pointer _must not get read
88    ///   either_ while this `VolatileRef` exists.
89    pub const unsafe fn new_restricted<A>(access: A, pointer: NonNull<T>) -> VolatileRef<'a, T, A>
90    where
91        A: Access,
92    {
93        let _ = access;
94        unsafe { Self::new_generic(pointer) }
95    }
96
97    /// Creates a `VolatileRef` from the given shared reference.
98    ///
99    /// **Note:** This function is only intended for testing, not for accessing real volatile
100    /// data. The reason is that the `&mut T` argument is considered _dereferenceable_ by Rust,
101    /// so the compiler is allowed to insert non-volatile reads. This might lead to undesired
102    /// (or even undefined?) behavior when accessing volatile data. So to be safe, only create
103    /// raw pointers to volatile data and use the [`Self::new`] constructor instead.
104    pub fn from_ref(reference: &'a T) -> VolatileRef<'a, T, ReadOnly>
105    where
106        T: 'a,
107    {
108        unsafe { VolatileRef::new_restricted(ReadOnly, reference.into()) }
109    }
110
111    /// Creates a `VolatileRef` from the given mutable reference.
112    ///
113    /// **Note:** This function is only intended for testing, not for accessing real volatile
114    /// data. The reason is that the `&mut T` argument is considered _dereferenceable_ by Rust,
115    /// so the compiler is allowed to insert non-volatile reads. This might lead to undesired
116    /// (or even undefined?) behavior when accessing volatile data. So to be safe, only create
117    /// raw pointers to volatile data and use the [`Self::new`] constructor instead.
118    pub fn from_mut_ref(reference: &'a mut T) -> Self
119    where
120        T: 'a,
121    {
122        unsafe { VolatileRef::new(reference.into()) }
123    }
124
125    const unsafe fn new_generic<A>(pointer: NonNull<T>) -> VolatileRef<'a, T, A> {
126        VolatileRef {
127            pointer,
128            reference: PhantomData,
129            access: PhantomData,
130        }
131    }
132}
133
134impl<'a, T, A> VolatileRef<'a, T, A>
135where
136    T: ?Sized,
137{
138    /// Immutably borrows from this `VolatileRef`.
139    ///
140    /// This method creates a `VolatileRef` tied to the lifetime of the `&VolatileRef` it is created from.
141    /// This is useful for providing a volatile reference without moving the original `VolatileRef`.
142    /// In comparison with creating a `&VolatileRef<'a, T>`, this avoids the additional indirection and lifetime.
143    pub fn borrow(&self) -> VolatileRef<'_, T, A::Restricted>
144    where
145        A: RestrictAccess<ReadOnly>,
146    {
147        unsafe { VolatileRef::new_restricted(Default::default(), self.pointer) }
148    }
149
150    /// Mutably borrows from this `VolatileRef`.
151    ///
152    /// This method creates a `VolatileRef` tied to the lifetime of the `&mut VolatileRef` it is created from.
153    /// This is useful for providing a volatile reference without moving the original `VolatileRef`.
154    /// In comparison with creating a `&mut VolatileRef<'a, T>`, this avoids the additional indirection and lifetime.
155    pub fn borrow_mut(&mut self) -> VolatileRef<'_, T, A>
156    where
157        A: Access,
158    {
159        unsafe { VolatileRef::new_restricted(Default::default(), self.pointer) }
160    }
161
162    /// Borrows this `VolatileRef` as a read-only [`VolatilePtr`].
163    ///
164    /// Use this method to do (partial) volatile reads of the referenced data.
165    pub fn as_ptr(&self) -> VolatilePtr<'_, T, A::Restricted>
166    where
167        A: RestrictAccess<ReadOnly>,
168    {
169        unsafe { VolatilePtr::new_restricted(Default::default(), self.pointer) }
170    }
171
172    /// Borrows this `VolatileRef` as a mutable [`VolatilePtr`].
173    ///
174    /// Use this method to do (partial) volatile reads or writes of the referenced data.
175    pub fn as_mut_ptr(&mut self) -> VolatilePtr<'_, T, A>
176    where
177        A: Access,
178    {
179        unsafe { VolatilePtr::new_restricted(Default::default(), self.pointer) }
180    }
181
182    /// Converts this `VolatileRef` into a [`VolatilePtr`] with full access without shortening
183    /// the lifetime.
184    ///
185    /// Use this method when you need a [`VolatilePtr`] instance that lives for the full
186    /// lifetime `'a`.
187    ///
188    /// This method consumes the `VolatileRef`.
189    pub fn into_ptr(self) -> VolatilePtr<'a, T, A>
190    where
191        A: Access,
192    {
193        unsafe { VolatilePtr::new_restricted(Default::default(), self.pointer) }
194    }
195}
196
197/// Methods for restricting access.
198impl<'a, T, A> VolatileRef<'a, T, A>
199where
200    T: ?Sized,
201{
202    /// Restricts access permissions to `A`.
203    ///
204    /// ## Example
205    ///
206    /// ```
207    /// use volatile::access::{ReadOnly, WriteOnly};
208    /// use volatile::VolatileRef;
209    ///
210    /// let mut value: i16 = -4;
211    /// let volatile = VolatileRef::from_mut_ref(&mut value);
212    ///
213    /// let read_only = volatile.restrict::<ReadOnly>();
214    /// assert_eq!(read_only.as_ptr().read(), -4);
215    /// // read_only.as_ptr().write(10); // compile-time error
216    ///
217    /// let no_access = read_only.restrict::<WriteOnly>();
218    /// // no_access.read(); // compile-time error
219    /// // no_access.write(10); // compile-time error
220    /// ```
221    pub fn restrict<To>(self) -> VolatileRef<'a, T, A::Restricted>
222    where
223        A: RestrictAccess<To>,
224    {
225        unsafe { VolatileRef::new_restricted(Default::default(), self.pointer) }
226    }
227}
228
229/// Methods for restricting access.
230impl<'a, T> VolatileRef<'a, T, ReadWrite>
231where
232    T: ?Sized,
233{
234    /// Restricts access permissions to read-only.
235    ///
236    /// ## Example
237    ///
238    /// ```
239    /// use volatile::VolatileRef;
240    ///
241    /// let mut value: i16 = -4;
242    /// let volatile = VolatileRef::from_mut_ref(&mut value);
243    ///
244    /// let read_only = volatile.read_only();
245    /// assert_eq!(read_only.as_ptr().read(), -4);
246    /// // read_only.as_ptr().write(10); // compile-time error
247    /// ```
248    pub fn read_only(self) -> VolatileRef<'a, T, ReadOnly> {
249        self.restrict()
250    }
251
252    /// Restricts access permissions to write-only.
253    ///
254    /// ## Example
255    ///
256    /// Creating a write-only reference to a struct field:
257    ///
258    /// ```
259    /// use volatile::{VolatileRef};
260    ///
261    /// #[derive(Clone, Copy)]
262    /// struct Example { field_1: u32, field_2: u8, }
263    /// let mut value = Example { field_1: 15, field_2: 255 };
264    /// let volatile = VolatileRef::from_mut_ref(&mut value);
265    ///
266    /// let write_only = volatile.write_only();
267    /// // write_only.as_ptr().read(); // compile-time error
268    /// ```
269    pub fn write_only(self) -> VolatileRef<'a, T, WriteOnly> {
270        self.restrict()
271    }
272}
273
274impl<'a, T, A> Clone for VolatileRef<'a, T, A>
275where
276    T: ?Sized,
277    A: Access + Copyable,
278{
279    fn clone(&self) -> Self {
280        *self
281    }
282}
283
284impl<'a, T, A> Copy for VolatileRef<'a, T, A>
285where
286    T: ?Sized,
287    A: Access + Copyable,
288{
289}
290
291unsafe impl<T, A> Send for VolatileRef<'_, T, A> where T: Sync + ?Sized {}
292unsafe impl<T, A> Sync for VolatileRef<'_, T, A> where T: Sync + ?Sized {}
293
294impl<T, A> fmt::Debug for VolatileRef<'_, T, A>
295where
296    T: ?Sized,
297{
298    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
299        fmt::Pointer::fmt(&self.pointer.as_ptr(), f)
300    }
301}
302
303impl<T, A> fmt::Pointer for VolatileRef<'_, T, A>
304where
305    T: ?Sized,
306{
307    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
308        fmt::Pointer::fmt(&self.pointer.as_ptr(), f)
309    }
310}
311
312impl<T, A> PartialEq for VolatileRef<'_, T, A>
313where
314    T: ?Sized,
315{
316    fn eq(&self, other: &Self) -> bool {
317        core::ptr::eq(self.pointer.as_ptr(), other.pointer.as_ptr())
318    }
319}
320
321impl<T, A> Eq for VolatileRef<'_, T, A> where T: ?Sized {}
322
323impl<T, A> PartialOrd for VolatileRef<'_, T, A>
324where
325    T: ?Sized,
326{
327    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
328        Some(self.cmp(other))
329    }
330}
331
332impl<T, A> Ord for VolatileRef<'_, T, A>
333where
334    T: ?Sized,
335{
336    fn cmp(&self, other: &Self) -> Ordering {
337        #[allow(ambiguous_wide_pointer_comparisons)]
338        Ord::cmp(&self.pointer.as_ptr(), &other.pointer.as_ptr())
339    }
340}
341
342impl<T, A> hash::Hash for VolatileRef<'_, T, A>
343where
344    T: ?Sized,
345{
346    fn hash<H: hash::Hasher>(&self, state: &mut H) {
347        self.pointer.as_ptr().hash(state);
348    }
349}