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}