From a7ae5797c6d9ffd5bd93c9d0c6599c5d16385612 Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Tue, 18 May 2021 14:18:21 +0200 Subject: [PATCH 01/16] New design based on `UnsafeCell` --- src/access.rs | 7 + src/lib.rs | 567 +++++++++++++++++++++++++------------------------- 2 files changed, 290 insertions(+), 284 deletions(-) diff --git a/src/access.rs b/src/access.rs index 438d233..fff67e7 100644 --- a/src/access.rs +++ b/src/access.rs @@ -1,3 +1,6 @@ + +pub trait Access {} + /// Helper trait that is implemented by [`ReadWrite`] and [`ReadOnly`]. pub trait Readable {} @@ -7,6 +10,7 @@ pub trait Writable {} /// Zero-sized marker type for allowing both read and write access. #[derive(Debug, Copy, Clone)] pub struct ReadWrite; +impl Access for ReadWrite {} impl Readable for ReadWrite {} impl Writable for ReadWrite {} @@ -14,9 +18,12 @@ impl Writable for ReadWrite {} #[derive(Debug, Copy, Clone)] pub struct ReadOnly; +impl Access for ReadOnly {} impl Readable for ReadOnly {} /// Zero-sized marker type for allowing only write access. #[derive(Debug, Copy, Clone)] pub struct WriteOnly; + +impl Access for WriteOnly {} impl Writable for WriteOnly {} diff --git a/src/lib.rs b/src/lib.rs index b5b99ce..11d22f1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -9,24 +9,20 @@ #![cfg_attr(feature = "unstable", feature(core_intrinsics))] #![cfg_attr(feature = "unstable", feature(const_generics))] #![cfg_attr(feature = "unstable", feature(slice_range))] +#![cfg_attr(feature = "unstable", feature(slice_ptr_get))] +#![cfg_attr(feature = "unstable", feature(slice_ptr_len))] #![cfg_attr(feature = "unstable", allow(incomplete_features))] #![cfg_attr(all(feature = "unstable", test), feature(slice_as_chunks))] #![warn(missing_docs)] +#![deny(unsafe_op_in_unsafe_fn)] use access::{ReadOnly, ReadWrite, Readable, Writable, WriteOnly}; -use core::{ - fmt, - marker::PhantomData, - ops::Deref, - ops::{DerefMut, Index, IndexMut}, - ptr, - slice::SliceIndex, -}; +use core::{cell::UnsafeCell, fmt, marker::PhantomData, ptr}; #[cfg(feature = "unstable")] use core::{ intrinsics, ops::{Range, RangeBounds}, - slice::range, + slice::{range, SliceIndex}, }; /// Allows creating read-only and write-only `Volatile` values. @@ -46,8 +42,11 @@ pub mod access; /// The size of this struct is the same as the size of the contained reference. #[derive(Clone)] #[repr(transparent)] -pub struct Volatile { - reference: R, +pub struct Volatile<'a, T, A = ReadWrite> +where + T: ?Sized, +{ + reference: &'a UnsafeCell, access: PhantomData, } @@ -56,7 +55,10 @@ pub struct Volatile { /// These functions allow to construct a new `Volatile` instance from a reference type. While /// the `new` function creates a `Volatile` instance with unrestricted access, there are also /// functions for creating read-only or write-only instances. -impl Volatile { +impl Volatile<'_, T> +where + T: ?Sized, +{ /// Constructs a new volatile instance wrapping the given reference. /// /// While it is possible to construct `Volatile` instances from arbitrary values (including @@ -78,95 +80,38 @@ impl Volatile { /// volatile.write(1); /// assert_eq!(volatile.read(), 1); /// ``` - pub const fn new(reference: R) -> Volatile { + pub unsafe fn new(reference: &UnsafeCell, access: A) -> Volatile<'_, T, A> { Volatile { reference, access: PhantomData, } } - /// Constructs a new read-only volatile instance wrapping the given reference. - /// - /// This is equivalent to the `new` function with the difference that the returned - /// `Volatile` instance does not permit write operations. This is for example useful - /// with memory-mapped hardware registers that are defined as read-only by the hardware. - /// - /// ## Example - /// - /// Reading is allowed: - /// - /// ```rust - /// use volatile::Volatile; - /// - /// let value = 0u32; - /// - /// let volatile = Volatile::new_read_only(&value); - /// assert_eq!(volatile.read(), 0); - /// ``` - /// - /// But writing is not: - /// - /// ```compile_fail - /// use volatile::Volatile; - /// - /// let mut value = 0u32; - /// - /// let mut volatile = Volatile::new_read_only(&mut value); - /// volatile.write(1); - /// //ERROR: ^^^^^ the trait `volatile::access::Writable` is not implemented - /// // for `volatile::access::ReadOnly` - /// ``` - pub const fn new_read_only(reference: R) -> Volatile { - Volatile { - reference, - access: PhantomData, - } + pub fn from_ref(reference: &T) -> Volatile<'_, T, ReadOnly> { + let raw = reference as *const T as *const UnsafeCell; + unsafe { Volatile::new(&*raw, ReadOnly) } } - /// Constructs a new write-only volatile instance wrapping the given reference. - /// - /// This is equivalent to the `new` function with the difference that the returned - /// `Volatile` instance does not permit read operations. This is for example useful - /// with memory-mapped hardware registers that are defined as write-only by the hardware. - /// - /// ## Example - /// - /// Writing is allowed: - /// - /// ```rust - /// use volatile::Volatile; - /// - /// let mut value = 0u32; - /// - /// let mut volatile = Volatile::new_write_only(&mut value); - /// volatile.write(1); - /// ``` - /// - /// But reading is not: - /// - /// ```compile_fail - /// use volatile::Volatile; - /// - /// let value = 0u32; - /// - /// let volatile = Volatile::new_write_only(&value); - /// volatile.read(); - /// //ERROR: ^^^^ the trait `volatile::access::Readable` is not implemented - /// // for `volatile::access::WriteOnly` - /// ``` - pub const fn new_write_only(reference: R) -> Volatile { - Volatile { - reference, - access: PhantomData, - } + pub fn from_mut_ref(reference: &mut T) -> Volatile<'_, T> { + let raw = reference as *mut T as *mut UnsafeCell; + unsafe { Volatile::new(&*raw, ReadWrite) } + } + + pub unsafe fn from_ptr<'a>(reference: *const T) -> Volatile<'a, T, ReadOnly> { + let raw = reference as *const UnsafeCell; + unsafe { Volatile::new(&*raw, ReadOnly) } + } + + pub unsafe fn from_mut_ptr<'a>(reference: *mut T) -> Volatile<'a, T> { + let raw = reference as *mut UnsafeCell; + unsafe { Volatile::new(&*raw, ReadWrite) } } } /// Methods for references to `Copy` types -impl Volatile +impl<'a, T, A> Volatile<'a, T, A> where - R: Deref, - T: Copy, + T: Copy + ?Sized, { /// Performs a volatile read of the contained value. /// @@ -192,8 +137,8 @@ where where A: Readable, { - // UNSAFE: Safe, as we know that our internal value exists. - unsafe { ptr::read_volatile(&*self.reference) } + // UNSAFE: Safe, as ... TODO + unsafe { ptr::read_volatile(self.reference.get()) } } /// Performs a volatile write, setting the contained value to the given `value`. @@ -216,10 +161,9 @@ where pub fn write(&mut self, value: T) where A: Writable, - R: DerefMut, { - // UNSAFE: Safe, as we know that our internal value exists. - unsafe { ptr::write_volatile(&mut *self.reference, value) }; + // UNSAFE: Safe, as ... TODO + unsafe { ptr::write_volatile(self.reference.get(), value) }; } /// Updates the contained value using the given closure and volatile instructions. @@ -240,7 +184,6 @@ where pub fn update(&mut self, f: F) where A: Readable + Writable, - R: DerefMut, F: FnOnce(&mut T), { let mut value = self.read(); @@ -250,7 +193,10 @@ where } /// Method for extracting the wrapped value. -impl Volatile { +impl<'a, T, A> Volatile<'a, T, A> +where + T: ?Sized, +{ /// Extracts the inner value stored in the wrapper type. /// /// This method gives direct access to the wrapped reference and thus allows @@ -275,15 +221,14 @@ impl Volatile { /// /// assert_eq!(*unwrapped, 50); // non volatile access, be careful! /// ``` - pub fn extract_inner(self) -> R { + pub fn extract_inner(self) -> &'a UnsafeCell { self.reference } } /// Transformation methods for accessing struct fields -impl Volatile +impl Volatile<'_, T, A> where - R: Deref, T: ?Sized, { /// Constructs a new `Volatile` reference by mapping the wrapped value. @@ -325,76 +270,32 @@ where /// value /// }); /// ``` - pub fn map<'a, F, U>(&'a self, f: F) -> Volatile<&'a U, A> + pub fn map(&self, f: F) -> Volatile where - F: FnOnce(&'a T) -> &'a U, + F: FnOnce(&UnsafeCell) -> &UnsafeCell, U: ?Sized, - T: 'a, { Volatile { - reference: f(self.reference.deref()), - access: self.access, + reference: f(&self.reference), + access: PhantomData, } } - /// Constructs a new mutable `Volatile` reference by mapping the wrapped value. - /// - /// This method is useful for accessing individual fields of volatile structs. - /// - /// Note that this method gives temporary access to the wrapped reference, which allows - /// accessing the value in a non-volatile way. This is normally not what you want, so - /// **this method should only be used for reference-to-reference transformations**. - /// - /// ## Examples - /// - /// Accessing a struct field: - /// - /// ``` - /// use volatile::Volatile; - /// - /// struct Example { field_1: u32, field_2: u8, } - /// let mut value = Example { field_1: 15, field_2: 255 }; - /// let mut volatile = Volatile::new(&mut value); - /// - /// // construct a volatile reference to a field - /// let mut field_2 = volatile.map_mut(|example| &mut example.field_2); - /// field_2.write(128); - /// assert_eq!(field_2.read(), 128); - /// ``` - /// - /// Don't misuse this method to do a non-volatile read or write of the referenced value: - /// - /// ``` - /// use volatile::Volatile; - /// - /// let mut value = 5; - /// let mut volatile = Volatile::new(&mut value); - /// - /// // DON'T DO THIS: - /// volatile.map_mut(|value| { - /// *value = 10; // non-volatile write, might lead to bugs - /// value - /// }); - /// ``` - pub fn map_mut<'a, F, U>(&'a mut self, f: F) -> Volatile<&'a mut U, A> + pub fn map_mut(&mut self, f: F) -> Volatile where - F: FnOnce(&mut T) -> &mut U, - R: DerefMut, + F: FnOnce(&UnsafeCell) -> &UnsafeCell, U: ?Sized, - T: 'a, { Volatile { - reference: f(&mut self.reference), + reference: f(&self.reference), access: self.access, } } } /// Methods for volatile slices -impl Volatile -where - R: Deref, -{ +#[cfg(feature = "unstable")] +impl<'a, T, A> Volatile<'a, [T], A> { /// Applies the index operation on the wrapped slice. /// /// Returns a shared `Volatile` reference to the resulting subslice. @@ -427,55 +328,24 @@ where /// let subslice = volatile.index(1..); /// assert_eq!(subslice.index(0).read(), 2); /// ``` - pub fn index<'a, I>(&'a self, index: I) -> Volatile<&'a I::Output, A> + pub fn index(&self, index: I) -> Volatile where I: SliceIndex<[T]>, - T: 'a, { - self.map(|slice| slice.index(index)) + self.map(|slice| unsafe { + let element: *mut I::Output = slice.get().get_unchecked_mut(index); + &*(element as *mut UnsafeCell) + }) } - /// Applies the mutable index operation on the wrapped slice. - /// - /// Returns a mutable `Volatile` reference to the resulting subslice. - /// - /// This is a convenience method for the `map_mut(|slice| slice.index_mut(index))` - /// operation, so it has the same behavior as the indexing operation on slice - /// (e.g. panic if index is out-of-bounds). - /// - /// ## Examples - /// - /// Accessing a single slice element: - /// - /// ``` - /// use volatile::Volatile; - /// - /// let mut array = [1, 2, 3]; - /// let slice = &mut array[..]; - /// let mut volatile = Volatile::new(slice); - /// volatile.index_mut(1).write(6); - /// assert_eq!(volatile.index(1).read(), 6); - /// ``` - /// - /// Accessing a subslice: - /// - /// ``` - /// use volatile::Volatile; - /// - /// let mut array = [1, 2, 3]; - /// let slice = &mut array[..]; - /// let mut volatile = Volatile::new(slice); - /// let mut subslice = volatile.index_mut(1..); - /// subslice.index_mut(0).write(6); - /// assert_eq!(subslice.index(0).read(), 6); - /// ``` - pub fn index_mut<'a, I>(&'a mut self, index: I) -> Volatile<&mut I::Output, A> + pub fn index_mut(&mut self, index: I) -> Volatile where I: SliceIndex<[T]>, - R: DerefMut, - T: 'a, { - self.map_mut(|slice| slice.index_mut(index)) + self.map_mut(|slice| unsafe { + let element: *mut I::Output = slice.get().get_unchecked_mut(index); + &*(element as *mut UnsafeCell) + }) } /// Copies all elements from `self` into `dst`, using a volatile memcpy. @@ -510,21 +380,21 @@ where /// assert_eq!(src, [1, 2]); /// assert_eq!(dst, [5, 1, 2]); /// ``` - #[cfg(feature = "unstable")] pub fn copy_into_slice(&self, dst: &mut [T]) where T: Copy, { + let len = self.reference.get().len(); assert_eq!( - self.reference.len(), + len, dst.len(), "destination and source slices have different lengths" ); unsafe { intrinsics::volatile_copy_nonoverlapping_memory( dst.as_mut_ptr(), - self.reference.as_ptr(), - self.reference.len(), + self.reference.get().as_mut_ptr(), + len, ); } } @@ -564,22 +434,21 @@ where /// assert_eq!(src, [1, 2, 3, 4]); /// assert_eq!(dst, [3, 4]); /// ``` - #[cfg(feature = "unstable")] pub fn copy_from_slice(&mut self, src: &[T]) where T: Copy, - R: DerefMut, { + let len = self.reference.get().len(); assert_eq!( - self.reference.len(), + len, src.len(), "destination and source slices have different lengths" ); unsafe { intrinsics::volatile_copy_nonoverlapping_memory( - self.reference.as_mut_ptr(), + self.reference.get().as_mut_ptr(), src.as_ptr(), - self.reference.len(), + len, ); } } @@ -611,44 +480,189 @@ where /// /// let mut byte_array = *b"Hello, World!"; /// let mut slice: &mut [u8] = &mut byte_array[..]; - /// let mut volatile = Volatile::new(slice); + /// let mut volatile = Volatile::from_mut_ref(slice); /// /// volatile.copy_within(1..5, 8); /// /// assert_eq!(&byte_array, b"Hello, Wello!"); - #[cfg(feature = "unstable")] pub fn copy_within(&mut self, src: impl RangeBounds, dest: usize) where T: Copy, - R: DerefMut, { + let len = self.reference.get().len(); // implementation taken from https://github.com/rust-lang/rust/blob/683d1bcd405727fcc9209f64845bd3b9104878b8/library/core/src/slice/mod.rs#L2726-L2738 let Range { start: src_start, end: src_end, - } = range(src, ..self.reference.len()); + } = range(src, ..len); let count = src_end - src_start; - assert!( - dest <= self.reference.len() - count, - "dest is out of bounds" - ); + assert!(dest <= len - count, "dest is out of bounds"); // SAFETY: the conditions for `volatile_copy_memory` have all been checked above, // as have those for `ptr::add`. unsafe { intrinsics::volatile_copy_memory( - self.reference.as_mut_ptr().add(dest), - self.reference.as_ptr().add(src_start), + self.reference.get().as_mut_ptr().add(dest), + self.reference.get().as_mut_ptr().add(src_start), count, ); } } + + pub fn split_at(&self, mid: usize) -> (Volatile<[T], ReadOnly>, Volatile<[T], ReadOnly>) { + assert!(mid <= self.reference.get().len()); + // SAFETY: `[ptr; mid]` and `[mid; len]` are inside `self`, which + // fulfills the requirements of `from_raw_parts_mut`. + unsafe { self.split_at_unchecked(mid) } + } + + pub fn split_at_mut(&mut self, mid: usize) -> (Volatile<[T], A>, Volatile<[T], A>) { + assert!(mid <= self.reference.get().len()); + // SAFETY: `[ptr; mid]` and `[mid; len]` are inside `self`, which + // fulfills the requirements of `from_raw_parts_mut`. + unsafe { self.split_at_mut_unchecked(mid) } + } + + unsafe fn split_at_unchecked( + &self, + mid: usize, + ) -> (Volatile<[T], ReadOnly>, Volatile<[T], ReadOnly>) { + // SAFETY: Caller has to check that `0 <= mid <= self.len()` + unsafe { + ( + Volatile { + reference: { + let raw: *const [T] = + (self.reference.get() as *const [T]).get_unchecked(..mid); + &*(raw as *const UnsafeCell<[T]>) + }, + access: PhantomData, + }, + Volatile { + reference: { + let raw: *const [T] = + (self.reference.get() as *const [T]).get_unchecked(mid..); + &*(raw as *const UnsafeCell<[T]>) + }, + access: PhantomData, + }, + ) + } + } + + unsafe fn split_at_mut_unchecked( + &mut self, + mid: usize, + ) -> (Volatile<[T], A>, Volatile<[T], A>) { + let len = self.reference.get().len(); + let ptr = self.reference.get().as_mut_ptr(); + + // SAFETY: Caller has to check that `0 <= mid <= self.len()`. + // + // `[ptr; mid]` and `[mid; len]` are not overlapping, so returning a mutable reference + // is fine. + unsafe { + ( + Volatile { + reference: { + let raw: *mut [T] = ptr::slice_from_raw_parts_mut(ptr, mid); + &*(raw as *mut UnsafeCell<[T]>) + }, + access: self.access, + }, + Volatile { + reference: { + let raw: *mut [T] = ptr::slice_from_raw_parts_mut(ptr.add(mid), len - mid); + &*(raw as *mut UnsafeCell<[T]>) + }, + access: self.access, + }, + ) + } + } + + pub fn as_chunks( + &self, + ) -> (Volatile<[[T; N]], ReadOnly>, Volatile<[T], ReadOnly>) { + assert_ne!(N, 0); + let len = self.reference.get().len() / N; + let (multiple_of_n, remainder) = self.split_at(len * N); + // SAFETY: We already panicked for zero, and ensured by construction + // that the length of the subslice is a multiple of N. + let array_slice = unsafe { multiple_of_n.as_chunks_unchecked_by_val() }; + (array_slice, remainder) + } + + pub unsafe fn as_chunks_unchecked(&self) -> Volatile<[[T; N]], ReadOnly> { + debug_assert_ne!(N, 0); + debug_assert_eq!(self.reference.get().len() % N, 0); + let new_len = + // SAFETY: Our precondition is exactly what's needed to call this + unsafe { crate::intrinsics::exact_div(self.reference.get().len(), N) }; + // SAFETY: We cast a slice of `new_len * N` elements into + // a slice of `new_len` many `N` elements chunks. + let reference = unsafe { + let raw: *const [[T; N]] = + ptr::slice_from_raw_parts(self.reference.get().as_mut_ptr().cast(), new_len); + &*(raw as *const UnsafeCell<[[T; N]]>) + }; + Volatile { + reference, + access: PhantomData, + } + } + + pub fn as_chunks_mut(&mut self) -> (Volatile<[[T; N]], A>, Volatile<[T], A>) { + assert_ne!(N, 0); + let len = self.reference.get().len() / N; + let (multiple_of_n, remainder) = self.split_at_mut(len * N); + // SAFETY: We already panicked for zero, and ensured by construction + // that the length of the subslice is a multiple of N. + let array_slice = unsafe { multiple_of_n.as_chunks_unchecked_by_val() }; + (array_slice, remainder) + } + + pub unsafe fn as_chunks_unchecked_mut(&mut self) -> Volatile<[[T; N]], A> { + debug_assert_ne!(N, 0); + debug_assert_eq!(self.reference.get().len() % N, 0); + let new_len = + // SAFETY: Our precondition is exactly what's needed to call this + unsafe { crate::intrinsics::exact_div(self.reference.get().len(), N) }; + // SAFETY: We cast a slice of `new_len * N` elements into + // a slice of `new_len` many `N` elements chunks. + let reference = unsafe { + let raw: *mut [[T; N]] = + ptr::slice_from_raw_parts_mut(self.reference.get().as_mut_ptr().cast(), new_len); + &*(raw as *mut UnsafeCell<[[T; N]]>) + }; + Volatile { + reference, + access: self.access, + } + } + + pub unsafe fn as_chunks_unchecked_by_val(self) -> Volatile<'a, [[T; N]], A> { + debug_assert_ne!(N, 0); + debug_assert_eq!(self.reference.get().len() % N, 0); + let new_len = + // SAFETY: Our precondition is exactly what's needed to call this + unsafe { crate::intrinsics::exact_div(self.reference.get().len(), N) }; + // SAFETY: We cast a slice of `new_len * N` elements into + // a slice of `new_len` many `N` elements chunks. + let reference = unsafe { + let raw: *mut [[T; N]] = + ptr::slice_from_raw_parts_mut(self.reference.get().as_mut_ptr().cast(), new_len); + &*(raw as *mut UnsafeCell<[[T; N]]>) + }; + Volatile { + reference, + access: self.access, + } + } } /// Methods for volatile byte slices -impl Volatile -where - R: Deref, -{ +#[cfg(feature = "unstable")] +impl Volatile<'_, [u8], A> { /// Sets all elements of the byte slice to the given `value` using a volatile `memset`. /// /// This method is similar to the `slice::fill` method of the standard library, with the @@ -668,16 +682,12 @@ where /// buf.fill(1); /// assert_eq!(buf.extract_inner(), vec![1; 10]); /// ``` - #[cfg(feature = "unstable")] - pub fn fill(&mut self, value: u8) - where - R: DerefMut, - { + pub fn fill(&mut self, value: u8) { unsafe { intrinsics::volatile_set_memory( - self.reference.as_mut_ptr(), + self.reference.get().as_mut_ptr(), value, - self.reference.len(), + self.reference.get().len(), ); } } @@ -688,10 +698,7 @@ where /// These methods are only available with the `unstable` feature enabled (requires a nightly /// Rust compiler). #[cfg(feature = "unstable")] -impl Volatile -where - R: Deref, -{ +impl Volatile<'_, [T; N], A> { /// Converts an array reference to a shared slice. /// /// This makes it possible to use the methods defined on slices. @@ -714,42 +721,19 @@ where /// /// assert_eq!(dst, [1, 2]); /// ``` - pub fn as_slice(&self) -> Volatile<&[T], A> { - self.map(|array| &array[..]) - } - - /// Converts a mutable array reference to a mutable slice. - /// - /// This makes it possible to use the methods defined on slices. - /// - /// ## Example - /// - /// Copying two elements from a slice into a mutable array reference: - /// - /// ``` - /// use volatile::Volatile; - /// - /// let src = [1, 2, 3, 4]; - /// let mut dst = [0, 0]; - /// let mut volatile = Volatile::new(&mut dst); - /// - /// // convert the `Volatile<&mut [i32; 2]>` array reference to a `Volatile<&mut [i32]>` slice - /// let mut volatile_slice = volatile.as_mut_slice(); - /// // we can now use the slice methods - /// volatile_slice.copy_from_slice(&src[2..]); - /// - /// assert_eq!(dst, [3, 4]); - /// ``` - pub fn as_mut_slice(&mut self) -> Volatile<&mut [T], A> - where - R: DerefMut, - { - self.map_mut(|array| &mut array[..]) + pub fn as_slice(&self) -> Volatile<[T], ReadOnly> { + self.map(|array| { + let slice: *mut [T] = ptr::slice_from_raw_parts_mut(array.get() as *mut T, N); + unsafe { &*(slice as *const UnsafeCell<[T]>) } + }) } } /// Methods for restricting access. -impl Volatile { +impl<'a, T> Volatile<'a, T> +where + T: ?Sized, +{ /// Restricts access permissions to read-only. /// /// ## Example @@ -764,7 +748,7 @@ impl Volatile { /// assert_eq!(read_only.read(), -4); /// // read_only.write(10); // compile-time error /// ``` - pub fn read_only(self) -> Volatile { + pub fn read_only(self) -> Volatile<'a, T, ReadOnly> { Volatile { reference: self.reference, access: PhantomData, @@ -789,7 +773,7 @@ impl Volatile { /// field_2.write(14); /// // field_2.read(); // compile-time error /// ``` - pub fn write_only(self) -> Volatile { + pub fn write_only(self) -> Volatile<'a, T, WriteOnly> { Volatile { reference: self.reference, access: PhantomData, @@ -797,10 +781,9 @@ impl Volatile { } } -impl fmt::Debug for Volatile +impl fmt::Debug for Volatile<'_, T, A> where - R: Deref, - T: Copy + fmt::Debug, + T: Copy + fmt::Debug + ?Sized, A: Readable, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { @@ -808,9 +791,9 @@ where } } -impl fmt::Debug for Volatile +impl fmt::Debug for Volatile<'_, T, WriteOnly> where - R: Deref, + T: ?Sized, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_tuple("Volatile").field(&"[write-only]").finish() @@ -820,17 +803,18 @@ where #[cfg(test)] mod tests { use super::Volatile; + use core::cell::UnsafeCell; #[test] fn test_read() { let val = 42; - assert_eq!(Volatile::new(&val).read(), 42); + assert_eq!(Volatile::from_ref(&val).read(), 42); } #[test] fn test_write() { let mut val = 50; - let mut volatile = Volatile::new(&mut val); + let mut volatile = Volatile::from_mut_ref(&mut val); volatile.write(50); assert_eq!(val, 50); } @@ -838,21 +822,14 @@ mod tests { #[test] fn test_update() { let mut val = 42; - let mut volatile = Volatile::new(&mut val); + let mut volatile = Volatile::from_mut_ref(&mut val); volatile.update(|v| *v += 1); assert_eq!(val, 43); } - #[test] - fn test_slice() { - let mut val = [1, 2, 3]; - let mut volatile = Volatile::new(&mut val[..]); - volatile.index_mut(0).update(|v| *v += 1); - assert_eq!(val, [2, 2, 3]); - } - #[test] fn test_struct() { + #[derive(Debug, PartialEq)] struct S { field_1: u32, field_2: bool, @@ -862,21 +839,43 @@ mod tests { field_1: 60, field_2: true, }; - let mut volatile = Volatile::new(&mut val); - volatile.map_mut(|s| &mut s.field_1).update(|v| *v += 1); - let mut field_2 = volatile.map_mut(|s| &mut s.field_2); + let mut volatile = Volatile::from_mut_ref(&mut val); + volatile + .map_mut(|s| unsafe { + let ptr = core::ptr::addr_of_mut!((*s.get()).field_1); + &*(ptr as *mut UnsafeCell) + }) + .update(|v| *v += 1); + let mut field_2 = volatile.map_mut(|s| unsafe { + let ptr = core::ptr::addr_of_mut!((*s.get()).field_2); + &*(ptr as *mut UnsafeCell) + }); assert!(field_2.read()); field_2.write(false); - assert_eq!(volatile.map(|s| &s.field_1).read(), 61); - assert_eq!(volatile.map(|s| &s.field_2).read(), false); + assert_eq!( + val, + S { + field_1: 61, + field_2: false + } + ); + } + + #[cfg(feature = "unstable")] + #[test] + fn test_slice() { + let mut val: &mut [u32] = &mut [1, 2, 3]; + let mut volatile = Volatile::from_mut_ref(val); + volatile.index_mut(0).update(|v| *v += 1); + assert_eq!(val, [2, 2, 3]); } #[cfg(feature = "unstable")] #[test] fn test_chunks() { - let mut val = [1, 2, 3, 4, 5, 6]; - let mut volatile = Volatile::new(&mut val[..]); - let mut chunks = volatile.map_mut(|s| s.as_chunks_mut().0); + let mut val: &mut [u32] = &mut [1, 2, 3, 4, 5, 6]; + let mut volatile = Volatile::from_mut_ref(val); + let mut chunks = volatile.as_chunks_mut().0; chunks.index_mut(1).write([10, 11, 12]); assert_eq!(chunks.index(0).read(), [1, 2, 3]); assert_eq!(chunks.index(1).read(), [10, 11, 12]); From 946b37bef1052ee8eb78cc292904a25353436523 Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Fri, 21 May 2021 15:56:47 +0200 Subject: [PATCH 02/16] Use `*mut T` instead of `&UnsafeCell` since the latter is dereferencable --- src/access.rs | 1 - src/lib.rs | 254 +++++++++++++++++++++++--------------------------- 2 files changed, 118 insertions(+), 137 deletions(-) diff --git a/src/access.rs b/src/access.rs index fff67e7..88b3df6 100644 --- a/src/access.rs +++ b/src/access.rs @@ -1,4 +1,3 @@ - pub trait Access {} /// Helper trait that is implemented by [`ReadWrite`] and [`ReadOnly`]. diff --git a/src/lib.rs b/src/lib.rs index 11d22f1..8a492a9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -17,7 +17,7 @@ #![deny(unsafe_op_in_unsafe_fn)] use access::{ReadOnly, ReadWrite, Readable, Writable, WriteOnly}; -use core::{cell::UnsafeCell, fmt, marker::PhantomData, ptr}; +use core::{fmt, marker::PhantomData, ptr}; #[cfg(feature = "unstable")] use core::{ intrinsics, @@ -40,13 +40,12 @@ pub mod access; /// to `ReadWrite`, which allows all operations. /// /// The size of this struct is the same as the size of the contained reference. -#[derive(Clone)] #[repr(transparent)] -pub struct Volatile<'a, T, A = ReadWrite> +pub struct Volatile where T: ?Sized, { - reference: &'a UnsafeCell, + pointer: *mut T, access: PhantomData, } @@ -55,7 +54,7 @@ where /// These functions allow to construct a new `Volatile` instance from a reference type. While /// the `new` function creates a `Volatile` instance with unrestricted access, there are also /// functions for creating read-only or write-only instances. -impl Volatile<'_, T> +impl Volatile where T: ?Sized, { @@ -80,36 +79,25 @@ where /// volatile.write(1); /// assert_eq!(volatile.read(), 1); /// ``` - pub unsafe fn new(reference: &UnsafeCell, access: A) -> Volatile<'_, T, A> { + pub unsafe fn new(pointer: *mut T, access: A) -> Volatile { + let _: A = access; Volatile { - reference, + pointer, access: PhantomData, } } - pub fn from_ref(reference: &T) -> Volatile<'_, T, ReadOnly> { - let raw = reference as *const T as *const UnsafeCell; - unsafe { Volatile::new(&*raw, ReadOnly) } + pub unsafe fn from_ptr(pointer: *const T) -> Volatile { + unsafe { Volatile::new(pointer as *mut _, ReadOnly) } } - pub fn from_mut_ref(reference: &mut T) -> Volatile<'_, T> { - let raw = reference as *mut T as *mut UnsafeCell; - unsafe { Volatile::new(&*raw, ReadWrite) } - } - - pub unsafe fn from_ptr<'a>(reference: *const T) -> Volatile<'a, T, ReadOnly> { - let raw = reference as *const UnsafeCell; - unsafe { Volatile::new(&*raw, ReadOnly) } - } - - pub unsafe fn from_mut_ptr<'a>(reference: *mut T) -> Volatile<'a, T> { - let raw = reference as *mut UnsafeCell; - unsafe { Volatile::new(&*raw, ReadWrite) } + pub unsafe fn from_mut_ptr(pointer: *mut T) -> Volatile { + unsafe { Volatile::new(pointer, ReadWrite) } } } /// Methods for references to `Copy` types -impl<'a, T, A> Volatile<'a, T, A> +impl<'a, T, A> Volatile where T: Copy + ?Sized, { @@ -138,7 +126,7 @@ where A: Readable, { // UNSAFE: Safe, as ... TODO - unsafe { ptr::read_volatile(self.reference.get()) } + unsafe { ptr::read_volatile(self.pointer) } } /// Performs a volatile write, setting the contained value to the given `value`. @@ -163,7 +151,7 @@ where A: Writable, { // UNSAFE: Safe, as ... TODO - unsafe { ptr::write_volatile(self.reference.get(), value) }; + unsafe { ptr::write_volatile(self.pointer, value) }; } /// Updates the contained value using the given closure and volatile instructions. @@ -193,7 +181,7 @@ where } /// Method for extracting the wrapped value. -impl<'a, T, A> Volatile<'a, T, A> +impl<'a, T, A> Volatile where T: ?Sized, { @@ -221,13 +209,13 @@ where /// /// assert_eq!(*unwrapped, 50); // non volatile access, be careful! /// ``` - pub fn extract_inner(self) -> &'a UnsafeCell { - self.reference + pub fn as_ptr(&self) -> *mut T { + self.pointer } } /// Transformation methods for accessing struct fields -impl Volatile<'_, T, A> +impl Volatile where T: ?Sized, { @@ -270,24 +258,24 @@ where /// value /// }); /// ``` - pub fn map(&self, f: F) -> Volatile + pub unsafe fn map(&self, f: F) -> Volatile where - F: FnOnce(&UnsafeCell) -> &UnsafeCell, + F: FnOnce(*mut T) -> *mut U, U: ?Sized, { Volatile { - reference: f(&self.reference), + pointer: f(self.pointer), access: PhantomData, } } - pub fn map_mut(&mut self, f: F) -> Volatile + pub unsafe fn map_mut(&mut self, f: F) -> Volatile where - F: FnOnce(&UnsafeCell) -> &UnsafeCell, + F: FnOnce(*mut T) -> *mut U, U: ?Sized, { Volatile { - reference: f(&self.reference), + pointer: f(self.pointer), access: self.access, } } @@ -295,7 +283,7 @@ where /// Methods for volatile slices #[cfg(feature = "unstable")] -impl<'a, T, A> Volatile<'a, [T], A> { +impl<'a, T, A> Volatile<[T], A> { /// Applies the index operation on the wrapped slice. /// /// Returns a shared `Volatile` reference to the resulting subslice. @@ -332,20 +320,14 @@ impl<'a, T, A> Volatile<'a, [T], A> { where I: SliceIndex<[T]>, { - self.map(|slice| unsafe { - let element: *mut I::Output = slice.get().get_unchecked_mut(index); - &*(element as *mut UnsafeCell) - }) + unsafe { self.map(|slice| slice.get_unchecked_mut(index)) } } pub fn index_mut(&mut self, index: I) -> Volatile where I: SliceIndex<[T]>, { - self.map_mut(|slice| unsafe { - let element: *mut I::Output = slice.get().get_unchecked_mut(index); - &*(element as *mut UnsafeCell) - }) + unsafe { self.map_mut(|slice| slice.get_unchecked_mut(index)) } } /// Copies all elements from `self` into `dst`, using a volatile memcpy. @@ -384,7 +366,7 @@ impl<'a, T, A> Volatile<'a, [T], A> { where T: Copy, { - let len = self.reference.get().len(); + let len = self.pointer.len(); assert_eq!( len, dst.len(), @@ -393,7 +375,7 @@ impl<'a, T, A> Volatile<'a, [T], A> { unsafe { intrinsics::volatile_copy_nonoverlapping_memory( dst.as_mut_ptr(), - self.reference.get().as_mut_ptr(), + self.pointer.as_mut_ptr(), len, ); } @@ -438,7 +420,7 @@ impl<'a, T, A> Volatile<'a, [T], A> { where T: Copy, { - let len = self.reference.get().len(); + let len = self.pointer.len(); assert_eq!( len, src.len(), @@ -446,7 +428,7 @@ impl<'a, T, A> Volatile<'a, [T], A> { ); unsafe { intrinsics::volatile_copy_nonoverlapping_memory( - self.reference.get().as_mut_ptr(), + self.pointer.as_mut_ptr(), src.as_ptr(), len, ); @@ -480,7 +462,7 @@ impl<'a, T, A> Volatile<'a, [T], A> { /// /// let mut byte_array = *b"Hello, World!"; /// let mut slice: &mut [u8] = &mut byte_array[..]; - /// let mut volatile = Volatile::from_mut_ref(slice); + /// let mut volatile = Volatile::from_mut_ptr(slice); /// /// volatile.copy_within(1..5, 8); /// @@ -489,7 +471,7 @@ impl<'a, T, A> Volatile<'a, [T], A> { where T: Copy, { - let len = self.reference.get().len(); + let len = self.pointer.len(); // implementation taken from https://github.com/rust-lang/rust/blob/683d1bcd405727fcc9209f64845bd3b9104878b8/library/core/src/slice/mod.rs#L2726-L2738 let Range { start: src_start, @@ -501,22 +483,22 @@ impl<'a, T, A> Volatile<'a, [T], A> { // as have those for `ptr::add`. unsafe { intrinsics::volatile_copy_memory( - self.reference.get().as_mut_ptr().add(dest), - self.reference.get().as_mut_ptr().add(src_start), + self.pointer.as_mut_ptr().add(dest), + self.pointer.as_mut_ptr().add(src_start), count, ); } } pub fn split_at(&self, mid: usize) -> (Volatile<[T], ReadOnly>, Volatile<[T], ReadOnly>) { - assert!(mid <= self.reference.get().len()); + assert!(mid <= self.pointer.len()); // SAFETY: `[ptr; mid]` and `[mid; len]` are inside `self`, which // fulfills the requirements of `from_raw_parts_mut`. unsafe { self.split_at_unchecked(mid) } } pub fn split_at_mut(&mut self, mid: usize) -> (Volatile<[T], A>, Volatile<[T], A>) { - assert!(mid <= self.reference.get().len()); + assert!(mid <= self.pointer.len()); // SAFETY: `[ptr; mid]` and `[mid; len]` are inside `self`, which // fulfills the requirements of `from_raw_parts_mut`. unsafe { self.split_at_mut_unchecked(mid) } @@ -530,19 +512,11 @@ impl<'a, T, A> Volatile<'a, [T], A> { unsafe { ( Volatile { - reference: { - let raw: *const [T] = - (self.reference.get() as *const [T]).get_unchecked(..mid); - &*(raw as *const UnsafeCell<[T]>) - }, + pointer: { (self.pointer).get_unchecked_mut(..mid) }, access: PhantomData, }, Volatile { - reference: { - let raw: *const [T] = - (self.reference.get() as *const [T]).get_unchecked(mid..); - &*(raw as *const UnsafeCell<[T]>) - }, + pointer: { (self.pointer).get_unchecked_mut(mid..) }, access: PhantomData, }, ) @@ -553,8 +527,8 @@ impl<'a, T, A> Volatile<'a, [T], A> { &mut self, mid: usize, ) -> (Volatile<[T], A>, Volatile<[T], A>) { - let len = self.reference.get().len(); - let ptr = self.reference.get().as_mut_ptr(); + let len = self.pointer.len(); + let ptr = self.pointer.as_mut_ptr(); // SAFETY: Caller has to check that `0 <= mid <= self.len()`. // @@ -563,17 +537,11 @@ impl<'a, T, A> Volatile<'a, [T], A> { unsafe { ( Volatile { - reference: { - let raw: *mut [T] = ptr::slice_from_raw_parts_mut(ptr, mid); - &*(raw as *mut UnsafeCell<[T]>) - }, + pointer: { ptr::slice_from_raw_parts_mut(ptr, mid) }, access: self.access, }, Volatile { - reference: { - let raw: *mut [T] = ptr::slice_from_raw_parts_mut(ptr.add(mid), len - mid); - &*(raw as *mut UnsafeCell<[T]>) - }, + pointer: { ptr::slice_from_raw_parts_mut(ptr.add(mid), len - mid) }, access: self.access, }, ) @@ -584,7 +552,7 @@ impl<'a, T, A> Volatile<'a, [T], A> { &self, ) -> (Volatile<[[T; N]], ReadOnly>, Volatile<[T], ReadOnly>) { assert_ne!(N, 0); - let len = self.reference.get().len() / N; + let len = self.pointer.len() / N; let (multiple_of_n, remainder) = self.split_at(len * N); // SAFETY: We already panicked for zero, and ensured by construction // that the length of the subslice is a multiple of N. @@ -594,26 +562,22 @@ impl<'a, T, A> Volatile<'a, [T], A> { pub unsafe fn as_chunks_unchecked(&self) -> Volatile<[[T; N]], ReadOnly> { debug_assert_ne!(N, 0); - debug_assert_eq!(self.reference.get().len() % N, 0); + debug_assert_eq!(self.pointer.len() % N, 0); let new_len = // SAFETY: Our precondition is exactly what's needed to call this - unsafe { crate::intrinsics::exact_div(self.reference.get().len(), N) }; + unsafe { crate::intrinsics::exact_div(self.pointer.len(), N) }; // SAFETY: We cast a slice of `new_len * N` elements into // a slice of `new_len` many `N` elements chunks. - let reference = unsafe { - let raw: *const [[T; N]] = - ptr::slice_from_raw_parts(self.reference.get().as_mut_ptr().cast(), new_len); - &*(raw as *const UnsafeCell<[[T; N]]>) - }; + let pointer = ptr::slice_from_raw_parts_mut(self.pointer.as_mut_ptr().cast(), new_len); Volatile { - reference, + pointer: pointer, access: PhantomData, } } pub fn as_chunks_mut(&mut self) -> (Volatile<[[T; N]], A>, Volatile<[T], A>) { assert_ne!(N, 0); - let len = self.reference.get().len() / N; + let len = self.pointer.len() / N; let (multiple_of_n, remainder) = self.split_at_mut(len * N); // SAFETY: We already panicked for zero, and ensured by construction // that the length of the subslice is a multiple of N. @@ -623,38 +587,30 @@ impl<'a, T, A> Volatile<'a, [T], A> { pub unsafe fn as_chunks_unchecked_mut(&mut self) -> Volatile<[[T; N]], A> { debug_assert_ne!(N, 0); - debug_assert_eq!(self.reference.get().len() % N, 0); + debug_assert_eq!(self.pointer.len() % N, 0); let new_len = // SAFETY: Our precondition is exactly what's needed to call this - unsafe { crate::intrinsics::exact_div(self.reference.get().len(), N) }; + unsafe { crate::intrinsics::exact_div(self.pointer.len(), N) }; // SAFETY: We cast a slice of `new_len * N` elements into // a slice of `new_len` many `N` elements chunks. - let reference = unsafe { - let raw: *mut [[T; N]] = - ptr::slice_from_raw_parts_mut(self.reference.get().as_mut_ptr().cast(), new_len); - &*(raw as *mut UnsafeCell<[[T; N]]>) - }; + let pointer = ptr::slice_from_raw_parts_mut(self.pointer.as_mut_ptr().cast(), new_len); Volatile { - reference, + pointer, access: self.access, } } - pub unsafe fn as_chunks_unchecked_by_val(self) -> Volatile<'a, [[T; N]], A> { + pub unsafe fn as_chunks_unchecked_by_val(self) -> Volatile<[[T; N]], A> { debug_assert_ne!(N, 0); - debug_assert_eq!(self.reference.get().len() % N, 0); + debug_assert_eq!(self.pointer.len() % N, 0); let new_len = // SAFETY: Our precondition is exactly what's needed to call this - unsafe { crate::intrinsics::exact_div(self.reference.get().len(), N) }; + unsafe { crate::intrinsics::exact_div(self.pointer.len(), N) }; // SAFETY: We cast a slice of `new_len * N` elements into // a slice of `new_len` many `N` elements chunks. - let reference = unsafe { - let raw: *mut [[T; N]] = - ptr::slice_from_raw_parts_mut(self.reference.get().as_mut_ptr().cast(), new_len); - &*(raw as *mut UnsafeCell<[[T; N]]>) - }; + let pointer = ptr::slice_from_raw_parts_mut(self.pointer.as_mut_ptr().cast(), new_len); Volatile { - reference, + pointer, access: self.access, } } @@ -662,7 +618,7 @@ impl<'a, T, A> Volatile<'a, [T], A> { /// Methods for volatile byte slices #[cfg(feature = "unstable")] -impl Volatile<'_, [u8], A> { +impl Volatile<[u8], A> { /// Sets all elements of the byte slice to the given `value` using a volatile `memset`. /// /// This method is similar to the `slice::fill` method of the standard library, with the @@ -684,11 +640,7 @@ impl Volatile<'_, [u8], A> { /// ``` pub fn fill(&mut self, value: u8) { unsafe { - intrinsics::volatile_set_memory( - self.reference.get().as_mut_ptr(), - value, - self.reference.get().len(), - ); + intrinsics::volatile_set_memory(self.pointer.as_mut_ptr(), value, self.pointer.len()); } } } @@ -698,7 +650,7 @@ impl Volatile<'_, [u8], A> { /// These methods are only available with the `unstable` feature enabled (requires a nightly /// Rust compiler). #[cfg(feature = "unstable")] -impl Volatile<'_, [T; N], A> { +impl Volatile<[T; N], A> { /// Converts an array reference to a shared slice. /// /// This makes it possible to use the methods defined on slices. @@ -722,15 +674,12 @@ impl Volatile<'_, [T; N], A> { /// assert_eq!(dst, [1, 2]); /// ``` pub fn as_slice(&self) -> Volatile<[T], ReadOnly> { - self.map(|array| { - let slice: *mut [T] = ptr::slice_from_raw_parts_mut(array.get() as *mut T, N); - unsafe { &*(slice as *const UnsafeCell<[T]>) } - }) + unsafe { self.map(|array| ptr::slice_from_raw_parts_mut(array as *mut T, N)) } } } /// Methods for restricting access. -impl<'a, T> Volatile<'a, T> +impl<'a, T> Volatile where T: ?Sized, { @@ -748,9 +697,9 @@ where /// assert_eq!(read_only.read(), -4); /// // read_only.write(10); // compile-time error /// ``` - pub fn read_only(self) -> Volatile<'a, T, ReadOnly> { + pub fn read_only(self) -> Volatile { Volatile { - reference: self.reference, + pointer: self.pointer, access: PhantomData, } } @@ -773,15 +722,15 @@ where /// field_2.write(14); /// // field_2.read(); // compile-time error /// ``` - pub fn write_only(self) -> Volatile<'a, T, WriteOnly> { + pub fn write_only(self) -> Volatile { Volatile { - reference: self.reference, + pointer: self.pointer, access: PhantomData, } } } -impl fmt::Debug for Volatile<'_, T, A> +impl fmt::Debug for Volatile where T: Copy + fmt::Debug + ?Sized, A: Readable, @@ -791,7 +740,7 @@ where } } -impl fmt::Debug for Volatile<'_, T, WriteOnly> +impl fmt::Debug for Volatile where T: ?Sized, { @@ -800,6 +749,20 @@ where } } +#[macro_export] +macro_rules! map_field { + ($volatile:ident.$place:ident) => { + unsafe { $volatile.map(|ptr| core::ptr::addr_of_mut!((*ptr).$place)) } + }; +} + +#[macro_export] +macro_rules! map_field_mut { + ($volatile:ident.$place:ident) => { + unsafe { $volatile.map_mut(|ptr| core::ptr::addr_of_mut!((*ptr).$place)) } + }; +} + #[cfg(test)] mod tests { use super::Volatile; @@ -808,13 +771,13 @@ mod tests { #[test] fn test_read() { let val = 42; - assert_eq!(Volatile::from_ref(&val).read(), 42); + assert_eq!(unsafe { Volatile::from_ptr(&val) }.read(), 42); } #[test] fn test_write() { let mut val = 50; - let mut volatile = Volatile::from_mut_ref(&mut val); + let mut volatile = unsafe { Volatile::from_mut_ptr(&mut val) }; volatile.write(50); assert_eq!(val, 50); } @@ -822,7 +785,7 @@ mod tests { #[test] fn test_update() { let mut val = 42; - let mut volatile = Volatile::from_mut_ref(&mut val); + let mut volatile = unsafe { Volatile::from_mut_ptr(&mut val) }; volatile.update(|v| *v += 1); assert_eq!(val, 43); } @@ -839,17 +802,36 @@ mod tests { field_1: 60, field_2: true, }; - let mut volatile = Volatile::from_mut_ref(&mut val); - volatile - .map_mut(|s| unsafe { - let ptr = core::ptr::addr_of_mut!((*s.get()).field_1); - &*(ptr as *mut UnsafeCell) - }) - .update(|v| *v += 1); - let mut field_2 = volatile.map_mut(|s| unsafe { - let ptr = core::ptr::addr_of_mut!((*s.get()).field_2); - &*(ptr as *mut UnsafeCell) - }); + let mut volatile = unsafe { Volatile::from_mut_ptr(&mut val) }; + unsafe { volatile.map_mut(|s| core::ptr::addr_of_mut!((*s).field_1)) }.update(|v| *v += 1); + let mut field_2 = unsafe { volatile.map_mut(|s| core::ptr::addr_of_mut!((*s).field_2)) }; + assert!(field_2.read()); + field_2.write(false); + assert_eq!( + val, + S { + field_1: 61, + field_2: false + } + ); + } + + #[test] + fn test_struct_macro() { + #[derive(Debug, PartialEq)] + struct S { + field_1: u32, + field_2: bool, + } + + let mut val = S { + field_1: 60, + field_2: true, + }; + let mut volatile = unsafe { Volatile::from_mut_ptr(&mut val) }; + let mut field_1 = map_field_mut!(volatile.field_1); + field_1.update(|v| *v += 1); + let mut field_2 = map_field_mut!(volatile.field_2); assert!(field_2.read()); field_2.write(false); assert_eq!( @@ -865,7 +847,7 @@ mod tests { #[test] fn test_slice() { let mut val: &mut [u32] = &mut [1, 2, 3]; - let mut volatile = Volatile::from_mut_ref(val); + let mut volatile = Volatile::from_mut_ptr(val); volatile.index_mut(0).update(|v| *v += 1); assert_eq!(val, [2, 2, 3]); } @@ -874,7 +856,7 @@ mod tests { #[test] fn test_chunks() { let mut val: &mut [u32] = &mut [1, 2, 3, 4, 5, 6]; - let mut volatile = Volatile::from_mut_ref(val); + let mut volatile = Volatile::from_mut_ptr(val); let mut chunks = volatile.as_chunks_mut().0; chunks.index_mut(1).write([10, 11, 12]); assert_eq!(chunks.index(0).read(), [1, 2, 3]); From 682dd7092e8fc0cc7226db5cfaf4208d09530321 Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Fri, 21 May 2021 18:36:01 +0200 Subject: [PATCH 03/16] Add access types for allowing only unsafe reads and/or writes --- Cargo.toml | 3 ++ src/access.rs | 32 +++++++++++++- src/lib.rs | 116 +++++++++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 147 insertions(+), 4 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index d353c41..63a2565 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,6 +14,9 @@ repository = "https://github.com/rust-osdev/volatile" # Enable unstable features; requires Rust nightly; might break on compiler updates unstable = [] +[dev-dependencies] +rand = "0.8.3" + [package.metadata.release] no-dev-version = true pre-release-replacements = [ diff --git a/src/access.rs b/src/access.rs index 88b3df6..d1f098e 100644 --- a/src/access.rs +++ b/src/access.rs @@ -1,17 +1,23 @@ pub trait Access {} /// Helper trait that is implemented by [`ReadWrite`] and [`ReadOnly`]. -pub trait Readable {} +pub trait Readable: UnsafelyReadable {} /// Helper trait that is implemented by [`ReadWrite`] and [`WriteOnly`]. -pub trait Writable {} +pub trait Writable: UnsafelyWritable {} + +pub trait UnsafelyReadable {} + +pub trait UnsafelyWritable {} /// Zero-sized marker type for allowing both read and write access. #[derive(Debug, Copy, Clone)] pub struct ReadWrite; impl Access for ReadWrite {} impl Readable for ReadWrite {} +impl UnsafelyReadable for ReadWrite {} impl Writable for ReadWrite {} +impl UnsafelyWritable for ReadWrite {} /// Zero-sized marker type for allowing only read access. #[derive(Debug, Copy, Clone)] @@ -19,6 +25,7 @@ pub struct ReadOnly; impl Access for ReadOnly {} impl Readable for ReadOnly {} +impl UnsafelyReadable for ReadOnly {} /// Zero-sized marker type for allowing only write access. #[derive(Debug, Copy, Clone)] @@ -26,3 +33,24 @@ pub struct WriteOnly; impl Access for WriteOnly {} impl Writable for WriteOnly {} +impl UnsafelyWritable for WriteOnly {} + +#[derive(Clone, Copy, PartialEq, Eq)] +pub struct Custom { + pub read: R, + pub write: W, +} + +#[derive(Clone, Copy, PartialEq, Eq)] +pub struct NoAccess; +#[derive(Clone, Copy, PartialEq, Eq)] +pub struct SafeAccess; +#[derive(Clone, Copy, PartialEq, Eq)] +pub struct UnsafeAccess; + +impl Readable for Custom {} +impl UnsafelyReadable for Custom {} +impl UnsafelyReadable for Custom {} +impl Writable for Custom {} +impl UnsafelyWritable for Custom {} +impl UnsafelyWritable for Custom {} diff --git a/src/lib.rs b/src/lib.rs index 8a492a9..745366e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -16,7 +16,9 @@ #![warn(missing_docs)] #![deny(unsafe_op_in_unsafe_fn)] -use access::{ReadOnly, ReadWrite, Readable, Writable, WriteOnly}; +use access::{ + ReadOnly, ReadWrite, Readable, UnsafelyReadable, UnsafelyWritable, Writable, WriteOnly, +}; use core::{fmt, marker::PhantomData, ptr}; #[cfg(feature = "unstable")] use core::{ @@ -178,6 +180,30 @@ where f(&mut value); self.write(value); } + + pub unsafe fn read_unsafe(&self) -> T + where + A: UnsafelyReadable, + { + unsafe { ptr::read_volatile(self.pointer) } + } + + pub unsafe fn write_unsafe(&mut self, value: T) + where + A: UnsafelyWritable, + { + unsafe { ptr::write_volatile(self.pointer, value) }; + } + + pub unsafe fn update_unsafe(&mut self, f: F) + where + A: UnsafelyReadable + UnsafelyWritable, + F: FnOnce(&mut T), + { + let mut value = unsafe { self.read_unsafe() }; + f(&mut value); + unsafe { self.write_unsafe(value) }; + } } /// Method for extracting the wrapped value. @@ -765,7 +791,7 @@ macro_rules! map_field_mut { #[cfg(test)] mod tests { - use super::Volatile; + use super::{access::*, Volatile}; use core::cell::UnsafeCell; #[test] @@ -790,6 +816,92 @@ mod tests { assert_eq!(val, 43); } + #[test] + fn test_access() { + let mut val: i64 = 42; + + // ReadWrite + assert_eq!(unsafe { Volatile::new(&mut val, ReadWrite) }.read(), 42); + unsafe { Volatile::new(&mut val, ReadWrite) }.write(50); + assert_eq!(val, 50); + unsafe { Volatile::new(&mut val, ReadWrite) }.update(|i| *i += 1); + assert_eq!(val, 51); + + // ReadOnly and WriteOnly + assert_eq!(unsafe { Volatile::new(&mut val, ReadOnly) }.read(), 51); + unsafe { Volatile::new(&mut val, WriteOnly) }.write(12); + assert_eq!(val, 12); + + // Custom: safe read + safe write + { + let access = Custom { + read: SafeAccess, + write: SafeAccess, + }; + let mut volatile = unsafe { Volatile::new(&mut val, access) }; + let random: i32 = rand::random(); + volatile.write(i64::from(random)); + assert_eq!(volatile.read(), i64::from(random)); + let random2: i32 = rand::random(); + volatile.update(|i| *i += i64::from(random2)); + assert_eq!(volatile.read(), i64::from(random) + i64::from(random2)); + } + + // Custom: safe read + unsafe write + { + let access = Custom { + read: SafeAccess, + write: UnsafeAccess, + }; + let mut volatile = unsafe { Volatile::new(&mut val, access) }; + let random: i32 = rand::random(); + unsafe { volatile.write_unsafe(i64::from(random)) }; + assert_eq!(volatile.read(), i64::from(random)); + let random2: i32 = rand::random(); + unsafe { volatile.update_unsafe(|i| *i += i64::from(random2)) }; + assert_eq!(volatile.read(), i64::from(random) + i64::from(random2)); + } + + // Custom: safe read + no write + { + let access = Custom { + read: SafeAccess, + write: NoAccess, + }; + let random = rand::random(); + val = random; + let mut volatile = unsafe { Volatile::new(&mut val, access) }; + assert_eq!(volatile.read(), i64::from(random)); + } + + // Custom: unsafe read + safe write + { + let access = Custom { + read: UnsafeAccess, + write: SafeAccess, + }; + let mut volatile = unsafe { Volatile::new(&mut val, access) }; + let random: i32 = rand::random(); + volatile.write(i64::from(random)); + assert_eq!(unsafe { volatile.read_unsafe() }, i64::from(random)); + let random2: i32 = rand::random(); + volatile.update_unsafe(|i| *i += i64::from(random2)); + assert_eq!( + volatile.read_unsafe(), + i64::from(random) + i64::from(random2) + ); + } + + // Todo: Custom: unsafe read + unsafe write + // Todo: Custom: unsafe read + no write + // Todo: Custom: no read + safe write + // Todo: Custom: no read + unsafe write + // Todo: Custom: no read + no write + + // Todo: is there a way to check that a compile error occurs when trying to use + // unavailable methods (e.g. `write` when write permission is `UnsafeAccess`)? + } + #[test] fn test_struct() { #[derive(Debug, PartialEq)] From 0699e4e2279437e232d45a80443e36bd33260be7 Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Sun, 13 Jun 2021 13:20:16 +0200 Subject: [PATCH 04/16] Remove stabilized const_generics feature --- src/lib.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index 745366e..7c7d779 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -7,7 +7,6 @@ #![no_std] #![cfg_attr(feature = "unstable", feature(core_intrinsics))] -#![cfg_attr(feature = "unstable", feature(const_generics))] #![cfg_attr(feature = "unstable", feature(slice_range))] #![cfg_attr(feature = "unstable", feature(slice_ptr_get))] #![cfg_attr(feature = "unstable", feature(slice_ptr_len))] From 2ece021ac1e3cbcf0ddcc73f45a9f5cd19ec5e99 Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Sun, 13 Jun 2021 15:05:43 +0200 Subject: [PATCH 05/16] Redesign access types and adjust (doc)tests --- src/access.rs | 84 +++++---- src/lib.rs | 458 ++++++++++++++++++-------------------------------- src/tests.rs | 188 +++++++++++++++++++++ 3 files changed, 391 insertions(+), 339 deletions(-) create mode 100644 src/tests.rs diff --git a/src/access.rs b/src/access.rs index d1f098e..a9ab53d 100644 --- a/src/access.rs +++ b/src/access.rs @@ -1,56 +1,48 @@ -pub trait Access {} - -/// Helper trait that is implemented by [`ReadWrite`] and [`ReadOnly`]. -pub trait Readable: UnsafelyReadable {} - -/// Helper trait that is implemented by [`ReadWrite`] and [`WriteOnly`]. -pub trait Writable: UnsafelyWritable {} - -pub trait UnsafelyReadable {} - -pub trait UnsafelyWritable {} - -/// Zero-sized marker type for allowing both read and write access. -#[derive(Debug, Copy, Clone)] -pub struct ReadWrite; -impl Access for ReadWrite {} -impl Readable for ReadWrite {} -impl UnsafelyReadable for ReadWrite {} -impl Writable for ReadWrite {} -impl UnsafelyWritable for ReadWrite {} +#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)] +pub struct NoAccess; -/// Zero-sized marker type for allowing only read access. -#[derive(Debug, Copy, Clone)] -pub struct ReadOnly; +#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)] +pub struct UnsafeAccess; -impl Access for ReadOnly {} -impl Readable for ReadOnly {} -impl UnsafelyReadable for ReadOnly {} +#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)] +pub struct SafeAccess; -/// Zero-sized marker type for allowing only write access. -#[derive(Debug, Copy, Clone)] -pub struct WriteOnly; +pub trait Unsafe {} +pub trait Safe: Unsafe {} -impl Access for WriteOnly {} -impl Writable for WriteOnly {} -impl UnsafelyWritable for WriteOnly {} +impl Unsafe for UnsafeAccess {} +impl Unsafe for SafeAccess {} +impl Safe for SafeAccess {} -#[derive(Clone, Copy, PartialEq, Eq)] -pub struct Custom { +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct Access { pub read: R, pub write: W, } -#[derive(Clone, Copy, PartialEq, Eq)] -pub struct NoAccess; -#[derive(Clone, Copy, PartialEq, Eq)] -pub struct SafeAccess; -#[derive(Clone, Copy, PartialEq, Eq)] -pub struct UnsafeAccess; +impl Access { + pub const fn read_only() -> ReadOnly { + Access { + read: SafeAccess, + write: NoAccess, + } + } + + pub fn write_only() -> WriteOnly { + Access { + read: NoAccess, + write: SafeAccess, + } + } + + pub fn read_write() -> ReadWrite { + Access { + read: SafeAccess, + write: SafeAccess, + } + } +} -impl Readable for Custom {} -impl UnsafelyReadable for Custom {} -impl UnsafelyReadable for Custom {} -impl Writable for Custom {} -impl UnsafelyWritable for Custom {} -impl UnsafelyWritable for Custom {} +pub type ReadOnly = Access; +pub type WriteOnly = Access; +pub type ReadWrite = Access; diff --git a/src/lib.rs b/src/lib.rs index 7c7d779..a62e649 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -15,10 +15,8 @@ #![warn(missing_docs)] #![deny(unsafe_op_in_unsafe_fn)] -use access::{ - ReadOnly, ReadWrite, Readable, UnsafelyReadable, UnsafelyWritable, Writable, WriteOnly, -}; -use core::{fmt, marker::PhantomData, ptr}; +use access::{Access, ReadOnly, SafeAccess}; +use core::{fmt, marker::PhantomData, mem, ptr}; #[cfg(feature = "unstable")] use core::{ intrinsics, @@ -29,6 +27,41 @@ use core::{ /// Allows creating read-only and write-only `Volatile` values. pub mod access; +/// TODO +/// +/// ## Examples +/// +/// Accessing a struct field: +/// +/// ``` +/// use volatile::{Volatile, map_field}; +/// +/// struct Example { field_1: u32, field_2: u8, } +/// let mut value = Example { field_1: 15, field_2: 255 }; +/// let mut volatile = unsafe { Volatile::from_mut_ptr(&mut value) }; +/// +/// // construct a volatile reference to a field +/// let field_2 = map_field!(volatile.field_2); +/// assert_eq!(field_2.read(), 255); +/// ``` +#[macro_export] +macro_rules! map_field { + ($volatile:ident.$place:ident) => { + unsafe { $volatile.map(|ptr| core::ptr::addr_of_mut!((*ptr).$place)) } + }; +} + +#[macro_export] +macro_rules! map_field_mut { + ($volatile:ident.$place:ident) => { + unsafe { $volatile.map_mut(|ptr| core::ptr::addr_of_mut!((*ptr).$place)) } + }; +} + +// this must be defined after the `map_field` macros +#[cfg(test)] +mod tests; + /// Wraps a reference to make accesses to the referenced value volatile. /// /// Allows volatile reads and writes on the referenced value. The referenced value needs to @@ -42,7 +75,7 @@ pub mod access; /// /// The size of this struct is the same as the size of the contained reference. #[repr(transparent)] -pub struct Volatile +pub struct Volatile> where T: ?Sized, { @@ -76,29 +109,29 @@ where /// /// let mut value = 0u32; /// - /// let mut volatile = Volatile::new(&mut value); + /// let mut volatile = unsafe { Volatile::from_mut_ptr(&mut value) }; /// volatile.write(1); /// assert_eq!(volatile.read(), 1); /// ``` - pub unsafe fn new(pointer: *mut T, access: A) -> Volatile { - let _: A = access; + pub const unsafe fn new(pointer: *mut T, access: A) -> Volatile { + mem::forget(access); // needed because we cannot require `A: Copy` on stable Rust yet Volatile { pointer, access: PhantomData, } } - pub unsafe fn from_ptr(pointer: *const T) -> Volatile { - unsafe { Volatile::new(pointer as *mut _, ReadOnly) } + pub const unsafe fn from_ptr(pointer: *const T) -> Volatile { + unsafe { Volatile::new(pointer as *mut _, Access::read_only()) } } pub unsafe fn from_mut_ptr(pointer: *mut T) -> Volatile { - unsafe { Volatile::new(pointer, ReadWrite) } + unsafe { Volatile::new(pointer, Access::read_write()) } } } /// Methods for references to `Copy` types -impl<'a, T, A> Volatile +impl<'a, T, R, W> Volatile> where T: Copy + ?Sized, { @@ -115,19 +148,19 @@ where /// use volatile::Volatile; /// /// let value = 42; - /// let shared_reference = Volatile::new(&value); + /// let shared_reference = unsafe { Volatile::from_ptr(&value) }; /// assert_eq!(shared_reference.read(), 42); /// /// let mut value = 50; - /// let mut_reference = Volatile::new(&mut value); + /// let mut_reference = unsafe { Volatile::from_mut_ptr(&mut value) }; /// assert_eq!(mut_reference.read(), 50); /// ``` pub fn read(&self) -> T where - A: Readable, + R: access::Safe, { // UNSAFE: Safe, as ... TODO - unsafe { ptr::read_volatile(self.pointer) } + unsafe { self.read_unsafe() } } /// Performs a volatile write, setting the contained value to the given `value`. @@ -142,17 +175,17 @@ where /// use volatile::Volatile; /// /// let mut value = 42; - /// let mut volatile = Volatile::new(&mut value); + /// let mut volatile = unsafe { Volatile::from_mut_ptr(&mut value) }; /// volatile.write(50); /// /// assert_eq!(volatile.read(), 50); /// ``` pub fn write(&mut self, value: T) where - A: Writable, + W: access::Safe, { // UNSAFE: Safe, as ... TODO - unsafe { ptr::write_volatile(self.pointer, value) }; + unsafe { self.write_unsafe(value) }; } /// Updates the contained value using the given closure and volatile instructions. @@ -165,43 +198,18 @@ where /// use volatile::Volatile; /// /// let mut value = 42; - /// let mut volatile = Volatile::new(&mut value); + /// let mut volatile = unsafe { Volatile::from_mut_ptr(&mut value) }; /// volatile.update(|val| *val += 1); /// /// assert_eq!(volatile.read(), 43); /// ``` pub fn update(&mut self, f: F) where - A: Readable + Writable, + R: access::Safe, + W: access::Safe, F: FnOnce(&mut T), { - let mut value = self.read(); - f(&mut value); - self.write(value); - } - - pub unsafe fn read_unsafe(&self) -> T - where - A: UnsafelyReadable, - { - unsafe { ptr::read_volatile(self.pointer) } - } - - pub unsafe fn write_unsafe(&mut self, value: T) - where - A: UnsafelyWritable, - { - unsafe { ptr::write_volatile(self.pointer, value) }; - } - - pub unsafe fn update_unsafe(&mut self, f: F) - where - A: UnsafelyReadable + UnsafelyWritable, - F: FnOnce(&mut T), - { - let mut value = unsafe { self.read_unsafe() }; - f(&mut value); - unsafe { self.write_unsafe(value) }; + unsafe { self.update_unsafe(f) } } } @@ -228,11 +236,11 @@ where /// use volatile::Volatile; /// /// let mut value = 42; - /// let mut volatile = Volatile::new(&mut value); + /// let mut volatile = unsafe { Volatile::from_mut_ptr(&mut value) }; /// volatile.write(50); - /// let unwrapped: &mut i32 = volatile.extract_inner(); + /// let unwrapped: *mut i32 = volatile.as_ptr(); /// - /// assert_eq!(*unwrapped, 50); // non volatile access, be careful! + /// assert_eq!(unsafe { *unwrapped }, 50); // non volatile access, be careful! /// ``` pub fn as_ptr(&self) -> *mut T { self.pointer @@ -240,17 +248,15 @@ where } /// Transformation methods for accessing struct fields -impl Volatile +impl Volatile> where T: ?Sized, { - /// Constructs a new `Volatile` reference by mapping the wrapped value. + /// Constructs a new `Volatile` reference by mapping the wrapped pointer. /// - /// This method is useful for accessing individual fields of volatile structs. - /// - /// Note that this method gives temporary access to the wrapped reference, which allows - /// accessing the value in a non-volatile way. This is normally not what you want, so - /// **this method should only be used for reference-to-reference transformations**. + /// This method is useful for accessing only a part of a volatile value, e.g. a subslice or + /// a struct field. For struct field access, there is also the safe [`map_field`] macro that + /// wraps this function. /// /// ## Examples /// @@ -261,10 +267,10 @@ where /// /// struct Example { field_1: u32, field_2: u8, } /// let mut value = Example { field_1: 15, field_2: 255 }; - /// let mut volatile = Volatile::new(&mut value); + /// let mut volatile = unsafe { Volatile::from_mut_ptr(&mut value) }; /// /// // construct a volatile reference to a field - /// let field_2 = volatile.map(|example| &example.field_2); + /// let field_2 = unsafe { volatile.map(|ptr| core::ptr::addr_of_mut!((*ptr).field_2)) }; /// assert_eq!(field_2.read(), 255); /// ``` /// @@ -274,16 +280,16 @@ where /// use volatile::Volatile; /// /// let mut value = 5; - /// let mut volatile = Volatile::new(&mut value); + /// let mut volatile = unsafe { Volatile::from_mut_ptr(&mut value) }; /// /// // DON'T DO THIS: /// let mut readout = 0; - /// volatile.map(|value| { + /// unsafe { volatile.map(|value| { /// readout = *value; // non-volatile read, might lead to bugs /// value - /// }); + /// })}; /// ``` - pub unsafe fn map(&self, f: F) -> Volatile + pub unsafe fn map(&self, f: F) -> Volatile> where F: FnOnce(*mut T) -> *mut U, U: ?Sized, @@ -294,7 +300,7 @@ where } } - pub unsafe fn map_mut(&mut self, f: F) -> Volatile + pub unsafe fn map_mut(&mut self, f: F) -> Volatile> where F: FnOnce(*mut T) -> *mut U, U: ?Sized, @@ -308,7 +314,7 @@ where /// Methods for volatile slices #[cfg(feature = "unstable")] -impl<'a, T, A> Volatile<[T], A> { +impl<'a, T, R, W> Volatile<[T], Access> { /// Applies the index operation on the wrapped slice. /// /// Returns a shared `Volatile` reference to the resulting subslice. @@ -341,14 +347,14 @@ impl<'a, T, A> Volatile<[T], A> { /// let subslice = volatile.index(1..); /// assert_eq!(subslice.index(0).read(), 2); /// ``` - pub fn index(&self, index: I) -> Volatile + pub fn index(&self, index: I) -> Volatile> where I: SliceIndex<[T]>, { unsafe { self.map(|slice| slice.get_unchecked_mut(index)) } } - pub fn index_mut(&mut self, index: I) -> Volatile + pub fn index_mut(&mut self, index: I) -> Volatile> where I: SliceIndex<[T]>, { @@ -515,14 +521,23 @@ impl<'a, T, A> Volatile<[T], A> { } } - pub fn split_at(&self, mid: usize) -> (Volatile<[T], ReadOnly>, Volatile<[T], ReadOnly>) { + pub fn split_at( + &self, + mid: usize, + ) -> ( + Volatile<[T], Access>, + Volatile<[T], Access>, + ) { assert!(mid <= self.pointer.len()); // SAFETY: `[ptr; mid]` and `[mid; len]` are inside `self`, which // fulfills the requirements of `from_raw_parts_mut`. unsafe { self.split_at_unchecked(mid) } } - pub fn split_at_mut(&mut self, mid: usize) -> (Volatile<[T], A>, Volatile<[T], A>) { + pub fn split_at_mut( + &mut self, + mid: usize, + ) -> (Volatile<[T], Access>, Volatile<[T], Access>) { assert!(mid <= self.pointer.len()); // SAFETY: `[ptr; mid]` and `[mid; len]` are inside `self`, which // fulfills the requirements of `from_raw_parts_mut`. @@ -532,7 +547,10 @@ impl<'a, T, A> Volatile<[T], A> { unsafe fn split_at_unchecked( &self, mid: usize, - ) -> (Volatile<[T], ReadOnly>, Volatile<[T], ReadOnly>) { + ) -> ( + Volatile<[T], Access>, + Volatile<[T], Access>, + ) { // SAFETY: Caller has to check that `0 <= mid <= self.len()` unsafe { ( @@ -551,7 +569,7 @@ impl<'a, T, A> Volatile<[T], A> { unsafe fn split_at_mut_unchecked( &mut self, mid: usize, - ) -> (Volatile<[T], A>, Volatile<[T], A>) { + ) -> (Volatile<[T], Access>, Volatile<[T], Access>) { let len = self.pointer.len(); let ptr = self.pointer.as_mut_ptr(); @@ -575,7 +593,10 @@ impl<'a, T, A> Volatile<[T], A> { pub fn as_chunks( &self, - ) -> (Volatile<[[T; N]], ReadOnly>, Volatile<[T], ReadOnly>) { + ) -> ( + Volatile<[[T; N]], Access>, + Volatile<[T], Access>, + ) { assert_ne!(N, 0); let len = self.pointer.len() / N; let (multiple_of_n, remainder) = self.split_at(len * N); @@ -585,22 +606,29 @@ impl<'a, T, A> Volatile<[T], A> { (array_slice, remainder) } - pub unsafe fn as_chunks_unchecked(&self) -> Volatile<[[T; N]], ReadOnly> { + pub unsafe fn as_chunks_unchecked( + &self, + ) -> Volatile<[[T; N]], Access> { debug_assert_ne!(N, 0); debug_assert_eq!(self.pointer.len() % N, 0); let new_len = // SAFETY: Our precondition is exactly what's needed to call this - unsafe { crate::intrinsics::exact_div(self.pointer.len(), N) }; + unsafe { core::intrinsics::exact_div(self.pointer.len(), N) }; // SAFETY: We cast a slice of `new_len * N` elements into // a slice of `new_len` many `N` elements chunks. let pointer = ptr::slice_from_raw_parts_mut(self.pointer.as_mut_ptr().cast(), new_len); Volatile { - pointer: pointer, + pointer, access: PhantomData, } } - pub fn as_chunks_mut(&mut self) -> (Volatile<[[T; N]], A>, Volatile<[T], A>) { + pub fn as_chunks_mut( + &mut self, + ) -> ( + Volatile<[[T; N]], Access>, + Volatile<[T], Access>, + ) { assert_ne!(N, 0); let len = self.pointer.len() / N; let (multiple_of_n, remainder) = self.split_at_mut(len * N); @@ -610,12 +638,14 @@ impl<'a, T, A> Volatile<[T], A> { (array_slice, remainder) } - pub unsafe fn as_chunks_unchecked_mut(&mut self) -> Volatile<[[T; N]], A> { + pub unsafe fn as_chunks_unchecked_mut( + &mut self, + ) -> Volatile<[[T; N]], Access> { debug_assert_ne!(N, 0); debug_assert_eq!(self.pointer.len() % N, 0); let new_len = // SAFETY: Our precondition is exactly what's needed to call this - unsafe { crate::intrinsics::exact_div(self.pointer.len(), N) }; + unsafe { core::intrinsics::exact_div(self.pointer.len(), N) }; // SAFETY: We cast a slice of `new_len * N` elements into // a slice of `new_len` many `N` elements chunks. let pointer = ptr::slice_from_raw_parts_mut(self.pointer.as_mut_ptr().cast(), new_len); @@ -625,12 +655,14 @@ impl<'a, T, A> Volatile<[T], A> { } } - pub unsafe fn as_chunks_unchecked_by_val(self) -> Volatile<[[T; N]], A> { + pub unsafe fn as_chunks_unchecked_by_val( + self, + ) -> Volatile<[[T; N]], Access> { debug_assert_ne!(N, 0); debug_assert_eq!(self.pointer.len() % N, 0); let new_len = // SAFETY: Our precondition is exactly what's needed to call this - unsafe { crate::intrinsics::exact_div(self.pointer.len(), N) }; + unsafe { core::intrinsics::exact_div(self.pointer.len(), N) }; // SAFETY: We cast a slice of `new_len * N` elements into // a slice of `new_len` many `N` elements chunks. let pointer = ptr::slice_from_raw_parts_mut(self.pointer.as_mut_ptr().cast(), new_len); @@ -661,7 +693,7 @@ impl Volatile<[u8], A> { /// /// let mut buf = Volatile::new(vec![0; 10]); /// buf.fill(1); - /// assert_eq!(buf.extract_inner(), vec![1; 10]); + /// assert_eq!(buf.as_ptr(), vec![1; 10]); /// ``` pub fn fill(&mut self, value: u8) { unsafe { @@ -675,7 +707,7 @@ impl Volatile<[u8], A> { /// These methods are only available with the `unstable` feature enabled (requires a nightly /// Rust compiler). #[cfg(feature = "unstable")] -impl Volatile<[T; N], A> { +impl Volatile<[T; N], Access> { /// Converts an array reference to a shared slice. /// /// This makes it possible to use the methods defined on slices. @@ -698,13 +730,13 @@ impl Volatile<[T; N], A> { /// /// assert_eq!(dst, [1, 2]); /// ``` - pub fn as_slice(&self) -> Volatile<[T], ReadOnly> { + pub fn as_slice(&self) -> Volatile<[T], Access> { unsafe { self.map(|array| ptr::slice_from_raw_parts_mut(array as *mut T, N)) } } } /// Methods for restricting access. -impl<'a, T> Volatile +impl<'a, T, R, W> Volatile> where T: ?Sized, { @@ -716,13 +748,13 @@ where /// use volatile::Volatile; /// /// let mut value: i16 = -4; - /// let mut volatile = Volatile::new(&mut value); + /// let mut volatile = unsafe { Volatile::from_mut_ptr(&mut value) }; /// /// let read_only = volatile.read_only(); /// assert_eq!(read_only.read(), -4); /// // read_only.write(10); // compile-time error /// ``` - pub fn read_only(self) -> Volatile { + pub fn read_only(self) -> Volatile> { Volatile { pointer: self.pointer, access: PhantomData, @@ -736,18 +768,18 @@ where /// Creating a write-only reference to a struct field: /// /// ``` - /// use volatile::Volatile; + /// use volatile::{Volatile, map_field_mut}; /// /// struct Example { field_1: u32, field_2: u8, } /// let mut value = Example { field_1: 15, field_2: 255 }; - /// let mut volatile = Volatile::new(&mut value); + /// let mut volatile = unsafe { Volatile::from_mut_ptr(&mut value) }; /// /// // construct a volatile write-only reference to `field_2` - /// let mut field_2 = volatile.map_mut(|example| &mut example.field_2).write_only(); + /// let mut field_2 = map_field_mut!(volatile.field_2).write_only(); /// field_2.write(14); /// // field_2.read(); // compile-time error /// ``` - pub fn write_only(self) -> Volatile { + pub fn write_only(self) -> Volatile> { Volatile { pointer: self.pointer, access: PhantomData, @@ -755,222 +787,62 @@ where } } -impl fmt::Debug for Volatile +/// Unsafe access methods for references to `Copy` types +impl<'a, T, R, W> Volatile> +where + T: Copy + ?Sized, +{ + pub unsafe fn read_unsafe(&self) -> T + where + R: access::Unsafe, + { + unsafe { ptr::read_volatile(self.pointer) } + } + + pub unsafe fn write_unsafe(&mut self, value: T) + where + W: access::Unsafe, + { + unsafe { ptr::write_volatile(self.pointer, value) }; + } + + pub unsafe fn update_unsafe(&mut self, f: F) + where + R: access::Unsafe, + W: access::Unsafe, + F: FnOnce(&mut T), + { + let mut value = unsafe { self.read_unsafe() }; + f(&mut value); + unsafe { self.write_unsafe(value) }; + } +} + +impl fmt::Debug for Volatile> where T: Copy + fmt::Debug + ?Sized, - A: Readable, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_tuple("Volatile").field(&self.read()).finish() } } -impl fmt::Debug for Volatile +impl fmt::Debug for Volatile> where T: ?Sized, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_tuple("Volatile").field(&"[write-only]").finish() + f.debug_tuple("Volatile").field(&"[unsafe read]").finish() } } -#[macro_export] -macro_rules! map_field { - ($volatile:ident.$place:ident) => { - unsafe { $volatile.map(|ptr| core::ptr::addr_of_mut!((*ptr).$place)) } - }; -} - -#[macro_export] -macro_rules! map_field_mut { - ($volatile:ident.$place:ident) => { - unsafe { $volatile.map_mut(|ptr| core::ptr::addr_of_mut!((*ptr).$place)) } - }; -} - -#[cfg(test)] -mod tests { - use super::{access::*, Volatile}; - use core::cell::UnsafeCell; - - #[test] - fn test_read() { - let val = 42; - assert_eq!(unsafe { Volatile::from_ptr(&val) }.read(), 42); - } - - #[test] - fn test_write() { - let mut val = 50; - let mut volatile = unsafe { Volatile::from_mut_ptr(&mut val) }; - volatile.write(50); - assert_eq!(val, 50); - } - - #[test] - fn test_update() { - let mut val = 42; - let mut volatile = unsafe { Volatile::from_mut_ptr(&mut val) }; - volatile.update(|v| *v += 1); - assert_eq!(val, 43); - } - - #[test] - fn test_access() { - let mut val: i64 = 42; - - // ReadWrite - assert_eq!(unsafe { Volatile::new(&mut val, ReadWrite) }.read(), 42); - unsafe { Volatile::new(&mut val, ReadWrite) }.write(50); - assert_eq!(val, 50); - unsafe { Volatile::new(&mut val, ReadWrite) }.update(|i| *i += 1); - assert_eq!(val, 51); - - // ReadOnly and WriteOnly - assert_eq!(unsafe { Volatile::new(&mut val, ReadOnly) }.read(), 51); - unsafe { Volatile::new(&mut val, WriteOnly) }.write(12); - assert_eq!(val, 12); - - // Custom: safe read + safe write - { - let access = Custom { - read: SafeAccess, - write: SafeAccess, - }; - let mut volatile = unsafe { Volatile::new(&mut val, access) }; - let random: i32 = rand::random(); - volatile.write(i64::from(random)); - assert_eq!(volatile.read(), i64::from(random)); - let random2: i32 = rand::random(); - volatile.update(|i| *i += i64::from(random2)); - assert_eq!(volatile.read(), i64::from(random) + i64::from(random2)); - } - - // Custom: safe read + unsafe write - { - let access = Custom { - read: SafeAccess, - write: UnsafeAccess, - }; - let mut volatile = unsafe { Volatile::new(&mut val, access) }; - let random: i32 = rand::random(); - unsafe { volatile.write_unsafe(i64::from(random)) }; - assert_eq!(volatile.read(), i64::from(random)); - let random2: i32 = rand::random(); - unsafe { volatile.update_unsafe(|i| *i += i64::from(random2)) }; - assert_eq!(volatile.read(), i64::from(random) + i64::from(random2)); - } - - // Custom: safe read + no write - { - let access = Custom { - read: SafeAccess, - write: NoAccess, - }; - let random = rand::random(); - val = random; - let mut volatile = unsafe { Volatile::new(&mut val, access) }; - assert_eq!(volatile.read(), i64::from(random)); - } - - // Custom: unsafe read + safe write - { - let access = Custom { - read: UnsafeAccess, - write: SafeAccess, - }; - let mut volatile = unsafe { Volatile::new(&mut val, access) }; - let random: i32 = rand::random(); - volatile.write(i64::from(random)); - assert_eq!(unsafe { volatile.read_unsafe() }, i64::from(random)); - let random2: i32 = rand::random(); - volatile.update_unsafe(|i| *i += i64::from(random2)); - assert_eq!( - volatile.read_unsafe(), - i64::from(random) + i64::from(random2) - ); - } - - // Todo: Custom: unsafe read + unsafe write - // Todo: Custom: unsafe read + no write - // Todo: Custom: no read + safe write - // Todo: Custom: no read + unsafe write - // Todo: Custom: no read + no write - - // Todo: is there a way to check that a compile error occurs when trying to use - // unavailable methods (e.g. `write` when write permission is `UnsafeAccess`)? - } - - #[test] - fn test_struct() { - #[derive(Debug, PartialEq)] - struct S { - field_1: u32, - field_2: bool, - } - - let mut val = S { - field_1: 60, - field_2: true, - }; - let mut volatile = unsafe { Volatile::from_mut_ptr(&mut val) }; - unsafe { volatile.map_mut(|s| core::ptr::addr_of_mut!((*s).field_1)) }.update(|v| *v += 1); - let mut field_2 = unsafe { volatile.map_mut(|s| core::ptr::addr_of_mut!((*s).field_2)) }; - assert!(field_2.read()); - field_2.write(false); - assert_eq!( - val, - S { - field_1: 61, - field_2: false - } - ); - } - - #[test] - fn test_struct_macro() { - #[derive(Debug, PartialEq)] - struct S { - field_1: u32, - field_2: bool, - } - - let mut val = S { - field_1: 60, - field_2: true, - }; - let mut volatile = unsafe { Volatile::from_mut_ptr(&mut val) }; - let mut field_1 = map_field_mut!(volatile.field_1); - field_1.update(|v| *v += 1); - let mut field_2 = map_field_mut!(volatile.field_2); - assert!(field_2.read()); - field_2.write(false); - assert_eq!( - val, - S { - field_1: 61, - field_2: false - } - ); - } - - #[cfg(feature = "unstable")] - #[test] - fn test_slice() { - let mut val: &mut [u32] = &mut [1, 2, 3]; - let mut volatile = Volatile::from_mut_ptr(val); - volatile.index_mut(0).update(|v| *v += 1); - assert_eq!(val, [2, 2, 3]); - } - - #[cfg(feature = "unstable")] - #[test] - fn test_chunks() { - let mut val: &mut [u32] = &mut [1, 2, 3, 4, 5, 6]; - let mut volatile = Volatile::from_mut_ptr(val); - let mut chunks = volatile.as_chunks_mut().0; - chunks.index_mut(1).write([10, 11, 12]); - assert_eq!(chunks.index(0).read(), [1, 2, 3]); - assert_eq!(chunks.index(1).read(), [10, 11, 12]); +impl fmt::Debug for Volatile> +where + T: ?Sized, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_tuple("Volatile") + .field(&"[no read access]") + .finish() } } diff --git a/src/tests.rs b/src/tests.rs new file mode 100644 index 0000000..5dcf9a9 --- /dev/null +++ b/src/tests.rs @@ -0,0 +1,188 @@ +use super::{access::*, Volatile}; + +#[test] +fn test_read() { + let val = 42; + assert_eq!(unsafe { Volatile::from_ptr(&val) }.read(), 42); +} + +#[test] +fn test_write() { + let mut val = 50; + let mut volatile = unsafe { Volatile::from_mut_ptr(&mut val) }; + volatile.write(50); + assert_eq!(val, 50); +} + +#[test] +fn test_update() { + let mut val = 42; + let mut volatile = unsafe { Volatile::from_mut_ptr(&mut val) }; + volatile.update(|v| *v += 1); + assert_eq!(val, 43); +} + +#[test] +fn test_access() { + let mut val: i64 = 42; + + // ReadWrite + assert_eq!( + unsafe { Volatile::new(&mut val, Access::read_write()) }.read(), + 42 + ); + unsafe { Volatile::new(&mut val, Access::read_write()) }.write(50); + assert_eq!(val, 50); + unsafe { Volatile::new(&mut val, Access::read_write()) }.update(|i| *i += 1); + assert_eq!(val, 51); + + // ReadOnly and WriteOnly + assert_eq!( + unsafe { Volatile::new(&mut val, Access::read_only()) }.read(), + 51 + ); + unsafe { Volatile::new(&mut val, Access::write_only()) }.write(12); + assert_eq!(val, 12); + + // Custom: safe read + safe write + { + let access = Access { + read: SafeAccess, + write: SafeAccess, + }; + let mut volatile = unsafe { Volatile::new(&mut val, access) }; + let random: i32 = rand::random(); + volatile.write(i64::from(random)); + assert_eq!(volatile.read(), i64::from(random)); + let random2: i32 = rand::random(); + volatile.update(|i| *i += i64::from(random2)); + assert_eq!(volatile.read(), i64::from(random) + i64::from(random2)); + } + + // Custom: safe read + unsafe write + { + let access = Access { + read: SafeAccess, + write: UnsafeAccess, + }; + let mut volatile = unsafe { Volatile::new(&mut val, access) }; + let random: i32 = rand::random(); + unsafe { volatile.write_unsafe(i64::from(random)) }; + assert_eq!(volatile.read(), i64::from(random)); + let random2: i32 = rand::random(); + unsafe { volatile.update_unsafe(|i| *i += i64::from(random2)) }; + assert_eq!(volatile.read(), i64::from(random) + i64::from(random2)); + } + + // Custom: safe read + no write + { + let access = Access { + read: SafeAccess, + write: NoAccess, + }; + let random = rand::random(); + val = random; + let mut volatile = unsafe { Volatile::new(&mut val, access) }; + assert_eq!(volatile.read(), i64::from(random)); + } + + // Custom: unsafe read + safe write + { + let access = Access { + read: UnsafeAccess, + write: SafeAccess, + }; + let mut volatile = unsafe { Volatile::new(&mut val, access) }; + let random: i32 = rand::random(); + volatile.write(i64::from(random)); + assert_eq!(unsafe { volatile.read_unsafe() }, i64::from(random)); + let random2: i32 = rand::random(); + unsafe { volatile.update_unsafe(|i| *i += i64::from(random2)) }; + assert_eq!( + unsafe { volatile.read_unsafe() }, + i64::from(random) + i64::from(random2) + ); + } + + // Todo: Custom: unsafe read + unsafe write + // Todo: Custom: unsafe read + no write + // Todo: Custom: no read + safe write + // Todo: Custom: no read + unsafe write + // Todo: Custom: no read + no write + + // Todo: is there a way to check that a compile error occurs when trying to use + // unavailable methods (e.g. `write` when write permission is `UnsafeAccess`)? +} + +#[test] +fn test_struct() { + #[derive(Debug, PartialEq)] + struct S { + field_1: u32, + field_2: bool, + } + + let mut val = S { + field_1: 60, + field_2: true, + }; + let mut volatile = unsafe { Volatile::from_mut_ptr(&mut val) }; + unsafe { volatile.map_mut(|s| core::ptr::addr_of_mut!((*s).field_1)) }.update(|v| *v += 1); + let mut field_2 = unsafe { volatile.map_mut(|s| core::ptr::addr_of_mut!((*s).field_2)) }; + assert!(field_2.read()); + field_2.write(false); + assert_eq!( + val, + S { + field_1: 61, + field_2: false + } + ); +} + +#[test] +fn test_struct_macro() { + #[derive(Debug, PartialEq)] + struct S { + field_1: u32, + field_2: bool, + } + + let mut val = S { + field_1: 60, + field_2: true, + }; + let mut volatile = unsafe { Volatile::from_mut_ptr(&mut val) }; + let mut field_1 = map_field_mut!(volatile.field_1); + field_1.update(|v| *v += 1); + let mut field_2 = map_field_mut!(volatile.field_2); + assert!(field_2.read()); + field_2.write(false); + assert_eq!( + val, + S { + field_1: 61, + field_2: false + } + ); +} + +#[cfg(feature = "unstable")] +#[test] +fn test_slice() { + let mut val: &mut [u32] = &mut [1, 2, 3]; + let mut volatile = unsafe { Volatile::from_mut_ptr(val) }; + volatile.index_mut(0).update(|v| *v += 1); + assert_eq!(val, [2, 2, 3]); +} + +#[cfg(feature = "unstable")] +#[test] +fn test_chunks() { + let mut val: &mut [u32] = &mut [1, 2, 3, 4, 5, 6]; + let mut volatile = unsafe { Volatile::from_mut_ptr(val) }; + let mut chunks = volatile.as_chunks_mut().0; + chunks.index_mut(1).write([10, 11, 12]); + assert_eq!(chunks.index(0).read(), [1, 2, 3]); + assert_eq!(chunks.index(1).read(), [10, 11, 12]); +} From 534ec6e172fa2d6ab94ed23262335a51897b3aeb Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Sun, 13 Jun 2021 15:57:55 +0200 Subject: [PATCH 06/16] Use `NonNull` instead of `*mut T` --- src/access.rs | 4 +- src/lib.rs | 166 +++++++++++++++++++++++++++++++++++--------------- src/tests.rs | 54 ++++++++++------ 3 files changed, 154 insertions(+), 70 deletions(-) diff --git a/src/access.rs b/src/access.rs index a9ab53d..be88c83 100644 --- a/src/access.rs +++ b/src/access.rs @@ -28,14 +28,14 @@ impl Access { } } - pub fn write_only() -> WriteOnly { + pub const fn write_only() -> WriteOnly { Access { read: NoAccess, write: SafeAccess, } } - pub fn read_write() -> ReadWrite { + pub const fn read_write() -> ReadWrite { Access { read: SafeAccess, write: SafeAccess, diff --git a/src/lib.rs b/src/lib.rs index a62e649..4044cec 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -15,8 +15,13 @@ #![warn(missing_docs)] #![deny(unsafe_op_in_unsafe_fn)] -use access::{Access, ReadOnly, SafeAccess}; -use core::{fmt, marker::PhantomData, mem, ptr}; +use access::{Access, ReadOnly, ReadWrite, SafeAccess, WriteOnly}; +use core::{ + fmt, + marker::PhantomData, + mem, + ptr::{self, NonNull}, +}; #[cfg(feature = "unstable")] use core::{ intrinsics, @@ -34,11 +39,13 @@ pub mod access; /// Accessing a struct field: /// /// ``` +/// # extern crate core; /// use volatile::{Volatile, map_field}; +/// use core::ptr::NonNull; /// /// struct Example { field_1: u32, field_2: u8, } /// let mut value = Example { field_1: 15, field_2: 255 }; -/// let mut volatile = unsafe { Volatile::from_mut_ptr(&mut value) }; +/// let mut volatile = unsafe { Volatile::new_read_write(NonNull::from(&mut value)) }; /// /// // construct a volatile reference to a field /// let field_2 = map_field!(volatile.field_2); @@ -47,14 +54,22 @@ pub mod access; #[macro_export] macro_rules! map_field { ($volatile:ident.$place:ident) => { - unsafe { $volatile.map(|ptr| core::ptr::addr_of_mut!((*ptr).$place)) } + unsafe { + $volatile.map(|ptr| { + core::ptr::NonNull::new(core::ptr::addr_of_mut!((*ptr.as_ptr()).$place)).unwrap() + }) + } }; } #[macro_export] macro_rules! map_field_mut { ($volatile:ident.$place:ident) => { - unsafe { $volatile.map_mut(|ptr| core::ptr::addr_of_mut!((*ptr).$place)) } + unsafe { + $volatile.map_mut(|ptr| { + core::ptr::NonNull::new(core::ptr::addr_of_mut!((*ptr.as_ptr()).$place)).unwrap() + }) + } }; } @@ -75,11 +90,11 @@ mod tests; /// /// The size of this struct is the same as the size of the contained reference. #[repr(transparent)] -pub struct Volatile> +pub struct Volatile where T: ?Sized, { - pointer: *mut T, + pointer: NonNull, access: PhantomData, } @@ -92,6 +107,18 @@ impl Volatile where T: ?Sized, { + pub unsafe fn new_read_write(pointer: NonNull) -> Volatile { + unsafe { Volatile::new_with_access(pointer, Access::read_write()) } + } + + pub const unsafe fn new_read_only(pointer: NonNull) -> Volatile { + unsafe { Volatile::new_with_access(pointer, Access::read_only()) } + } + + pub const unsafe fn new_write_only(pointer: NonNull) -> Volatile { + unsafe { Volatile::new_with_access(pointer, Access::write_only()) } + } + /// Constructs a new volatile instance wrapping the given reference. /// /// While it is possible to construct `Volatile` instances from arbitrary values (including @@ -105,29 +132,23 @@ where /// ## Example /// /// ```rust + /// # extern crate core; /// use volatile::Volatile; + /// use core::ptr::NonNull; /// /// let mut value = 0u32; /// - /// let mut volatile = unsafe { Volatile::from_mut_ptr(&mut value) }; + /// let mut volatile = unsafe { Volatile::new_read_write(NonNull::from(&mut value)) }; /// volatile.write(1); /// assert_eq!(volatile.read(), 1); /// ``` - pub const unsafe fn new(pointer: *mut T, access: A) -> Volatile { + pub const unsafe fn new_with_access(pointer: NonNull, access: A) -> Volatile { mem::forget(access); // needed because we cannot require `A: Copy` on stable Rust yet Volatile { pointer, access: PhantomData, } } - - pub const unsafe fn from_ptr(pointer: *const T) -> Volatile { - unsafe { Volatile::new(pointer as *mut _, Access::read_only()) } - } - - pub unsafe fn from_mut_ptr(pointer: *mut T) -> Volatile { - unsafe { Volatile::new(pointer, Access::read_write()) } - } } /// Methods for references to `Copy` types @@ -145,14 +166,16 @@ where /// ## Examples /// /// ```rust + /// # extern crate core; /// use volatile::Volatile; + /// use core::ptr::NonNull; /// /// let value = 42; - /// let shared_reference = unsafe { Volatile::from_ptr(&value) }; + /// let shared_reference = unsafe { Volatile::new_read_only(NonNull::from(&value)) }; /// assert_eq!(shared_reference.read(), 42); /// /// let mut value = 50; - /// let mut_reference = unsafe { Volatile::from_mut_ptr(&mut value) }; + /// let mut_reference = unsafe { Volatile::new_read_write(NonNull::from(&mut value)) }; /// assert_eq!(mut_reference.read(), 50); /// ``` pub fn read(&self) -> T @@ -172,10 +195,12 @@ where /// ## Example /// /// ```rust + /// # extern crate core; /// use volatile::Volatile; + /// use core::ptr::NonNull; /// /// let mut value = 42; - /// let mut volatile = unsafe { Volatile::from_mut_ptr(&mut value) }; + /// let mut volatile = unsafe { Volatile::new_read_write(NonNull::from(&mut value)) }; /// volatile.write(50); /// /// assert_eq!(volatile.read(), 50); @@ -195,10 +220,12 @@ where /// the contained value. /// /// ```rust + /// # extern crate core; /// use volatile::Volatile; + /// use core::ptr::NonNull; /// /// let mut value = 42; - /// let mut volatile = unsafe { Volatile::from_mut_ptr(&mut value) }; + /// let mut volatile = unsafe { Volatile::new_read_write(NonNull::from(&mut value)) }; /// volatile.update(|val| *val += 1); /// /// assert_eq!(volatile.read(), 43); @@ -233,16 +260,18 @@ where /// ## Example /// /// ``` + /// # extern crate core; /// use volatile::Volatile; + /// use core::ptr::NonNull; /// /// let mut value = 42; - /// let mut volatile = unsafe { Volatile::from_mut_ptr(&mut value) }; + /// let mut volatile = unsafe { Volatile::new_read_write(NonNull::from(&mut value)) }; /// volatile.write(50); - /// let unwrapped: *mut i32 = volatile.as_ptr(); + /// let unwrapped: *mut i32 = volatile.as_ptr().as_ptr(); /// /// assert_eq!(unsafe { *unwrapped }, 50); // non volatile access, be careful! /// ``` - pub fn as_ptr(&self) -> *mut T { + pub fn as_ptr(&self) -> NonNull { self.pointer } } @@ -263,35 +292,39 @@ where /// Accessing a struct field: /// /// ``` + /// # extern crate core; /// use volatile::Volatile; + /// use core::ptr::NonNull; /// /// struct Example { field_1: u32, field_2: u8, } /// let mut value = Example { field_1: 15, field_2: 255 }; - /// let mut volatile = unsafe { Volatile::from_mut_ptr(&mut value) }; + /// let mut volatile = unsafe { Volatile::new_read_write(NonNull::from(&mut value)) }; /// /// // construct a volatile reference to a field - /// let field_2 = unsafe { volatile.map(|ptr| core::ptr::addr_of_mut!((*ptr).field_2)) }; + /// let field_2 = unsafe { volatile.map(|ptr| NonNull::new(core::ptr::addr_of_mut!((*ptr.as_ptr()).field_2)).unwrap()) }; /// assert_eq!(field_2.read(), 255); /// ``` /// /// Don't misuse this method to do a non-volatile read of the referenced value: /// /// ``` + /// # extern crate core; /// use volatile::Volatile; + /// use core::ptr::NonNull; /// /// let mut value = 5; - /// let mut volatile = unsafe { Volatile::from_mut_ptr(&mut value) }; + /// let mut volatile = unsafe { Volatile::new_read_write(NonNull::from(&mut value)) }; /// /// // DON'T DO THIS: /// let mut readout = 0; /// unsafe { volatile.map(|value| { - /// readout = *value; // non-volatile read, might lead to bugs + /// readout = *value.as_ptr(); // non-volatile read, might lead to bugs /// value /// })}; /// ``` pub unsafe fn map(&self, f: F) -> Volatile> where - F: FnOnce(*mut T) -> *mut U, + F: FnOnce(NonNull) -> NonNull, U: ?Sized, { Volatile { @@ -302,7 +335,7 @@ where pub unsafe fn map_mut(&mut self, f: F) -> Volatile> where - F: FnOnce(*mut T) -> *mut U, + F: FnOnce(NonNull) -> NonNull, U: ?Sized, { Volatile { @@ -328,22 +361,26 @@ impl<'a, T, R, W> Volatile<[T], Access> { /// Accessing a single slice element: /// /// ``` + /// # extern crate core; /// use volatile::Volatile; + /// use core::ptr::NonNull; /// /// let array = [1, 2, 3]; /// let slice = &array[..]; - /// let volatile = Volatile::new(slice); + /// let volatile = unsafe { Volatile::new_read_only(NonNull::from(slice)) }; /// assert_eq!(volatile.index(1).read(), 2); /// ``` /// /// Accessing a subslice: /// /// ``` + /// # extern crate core; /// use volatile::Volatile; + /// use core::ptr::NonNull; /// /// let array = [1, 2, 3]; /// let slice = &array[..]; - /// let volatile = Volatile::new(slice); + /// let volatile = unsafe { Volatile::new_read_only(NonNull::from(slice)) }; /// let subslice = volatile.index(1..); /// assert_eq!(subslice.index(0).read(), 2); /// ``` @@ -377,12 +414,14 @@ impl<'a, T, R, W> Volatile<[T], Access> { /// Copying two elements from a volatile slice: /// /// ``` + /// # extern crate core; /// use volatile::Volatile; + /// use core::ptr::NonNull; /// /// let src = [1, 2]; /// // the `Volatile` type does not work with arrays, so convert `src` to a slice /// let slice = &src[..]; - /// let volatile = Volatile::new(slice); + /// let volatile = unsafe { Volatile::new_read_only(NonNull::from(slice)) }; /// let mut dst = [5, 0, 0]; /// /// // Because the slices have to be the same length, @@ -431,13 +470,15 @@ impl<'a, T, R, W> Volatile<[T], Access> { /// Copying two elements from a slice into a volatile slice: /// /// ``` + /// # extern crate core; /// use volatile::Volatile; + /// use core::ptr::NonNull; /// /// let src = [1, 2, 3, 4]; /// let mut dst = [0, 0]; /// // the `Volatile` type does not work with arrays, so convert `dst` to a slice /// let slice = &mut dst[..]; - /// let mut volatile = Volatile::new(slice); + /// let mut volatile = unsafe { Volatile::new_read_write(NonNull::from(slice))}; /// /// // Because the slices have to be the same length, /// // we slice the source slice from four elements @@ -489,11 +530,13 @@ impl<'a, T, R, W> Volatile<[T], Access> { /// Copying four bytes within a slice: /// /// ``` + /// extern crate core; /// use volatile::Volatile; + /// use core::ptr::NonNull; /// /// let mut byte_array = *b"Hello, World!"; /// let mut slice: &mut [u8] = &mut byte_array[..]; - /// let mut volatile = Volatile::from_mut_ptr(slice); + /// let mut volatile = unsafe { Volatile::new_read_write(NonNull::from(slice)) }; /// /// volatile.copy_within(1..5, 8); /// @@ -580,11 +623,12 @@ impl<'a, T, R, W> Volatile<[T], Access> { unsafe { ( Volatile { - pointer: { ptr::slice_from_raw_parts_mut(ptr, mid) }, + pointer: NonNull::new(ptr::slice_from_raw_parts_mut(ptr, mid)).unwrap(), access: self.access, }, Volatile { - pointer: { ptr::slice_from_raw_parts_mut(ptr.add(mid), len - mid) }, + pointer: NonNull::new(ptr::slice_from_raw_parts_mut(ptr.add(mid), len - mid)) + .unwrap(), access: self.access, }, ) @@ -616,7 +660,13 @@ impl<'a, T, R, W> Volatile<[T], Access> { unsafe { core::intrinsics::exact_div(self.pointer.len(), N) }; // SAFETY: We cast a slice of `new_len * N` elements into // a slice of `new_len` many `N` elements chunks. - let pointer = ptr::slice_from_raw_parts_mut(self.pointer.as_mut_ptr().cast(), new_len); + let pointer = unsafe { + NonNull::new(ptr::slice_from_raw_parts_mut( + self.pointer.as_mut_ptr().cast(), + new_len, + )) + .unwrap() + }; Volatile { pointer, access: PhantomData, @@ -648,7 +698,11 @@ impl<'a, T, R, W> Volatile<[T], Access> { unsafe { core::intrinsics::exact_div(self.pointer.len(), N) }; // SAFETY: We cast a slice of `new_len * N` elements into // a slice of `new_len` many `N` elements chunks. - let pointer = ptr::slice_from_raw_parts_mut(self.pointer.as_mut_ptr().cast(), new_len); + let pointer = NonNull::new(ptr::slice_from_raw_parts_mut( + self.pointer.as_mut_ptr().cast(), + new_len, + )) + .unwrap(); Volatile { pointer, access: self.access, @@ -665,7 +719,11 @@ impl<'a, T, R, W> Volatile<[T], Access> { unsafe { core::intrinsics::exact_div(self.pointer.len(), N) }; // SAFETY: We cast a slice of `new_len * N` elements into // a slice of `new_len` many `N` elements chunks. - let pointer = ptr::slice_from_raw_parts_mut(self.pointer.as_mut_ptr().cast(), new_len); + let pointer = NonNull::new(ptr::slice_from_raw_parts_mut( + self.pointer.as_mut_ptr().cast(), + new_len, + )) + .unwrap(); Volatile { pointer, access: self.access, @@ -689,11 +747,13 @@ impl Volatile<[u8], A> { /// ## Example /// /// ```rust + /// # extern crate core; /// use volatile::Volatile; + /// use core::ptr::NonNull; /// - /// let mut buf = Volatile::new(vec![0; 10]); + /// let mut buf = unsafe { Volatile::new_read_write(NonNull::from(vec![0; 10].as_mut_slice())) }; /// buf.fill(1); - /// assert_eq!(buf.as_ptr(), vec![1; 10]); + /// assert_eq!(unsafe { buf.as_ptr().as_mut() }, &mut vec![1; 10]); /// ``` pub fn fill(&mut self, value: u8) { unsafe { @@ -717,10 +777,12 @@ impl Volatile<[T; N], Access> { /// Copying two elements from a volatile array reference using `copy_into_slice`: /// /// ``` + /// # extern crate core; /// use volatile::Volatile; + /// use core::ptr::NonNull; /// /// let src = [1, 2]; - /// let volatile = Volatile::new(&src); + /// let volatile = unsafe { Volatile::new_read_only(NonNull::from(&src)) }; /// let mut dst = [0, 0]; /// /// // convert the `Volatile<&[i32; 2]>` array reference to a `Volatile<&[i32]>` slice @@ -731,7 +793,11 @@ impl Volatile<[T; N], Access> { /// assert_eq!(dst, [1, 2]); /// ``` pub fn as_slice(&self) -> Volatile<[T], Access> { - unsafe { self.map(|array| ptr::slice_from_raw_parts_mut(array as *mut T, N)) } + unsafe { + self.map(|array| { + NonNull::new(ptr::slice_from_raw_parts_mut(array.as_ptr() as *mut T, N)).unwrap() + }) + } } } @@ -745,10 +811,12 @@ where /// ## Example /// /// ``` + /// # extern crate core; /// use volatile::Volatile; + /// use core::ptr::NonNull; /// /// let mut value: i16 = -4; - /// let mut volatile = unsafe { Volatile::from_mut_ptr(&mut value) }; + /// let mut volatile = unsafe { Volatile::new_read_write(NonNull::from(&mut value)) }; /// /// let read_only = volatile.read_only(); /// assert_eq!(read_only.read(), -4); @@ -768,11 +836,13 @@ where /// Creating a write-only reference to a struct field: /// /// ``` + /// # extern crate core; /// use volatile::{Volatile, map_field_mut}; + /// use core::ptr::NonNull; /// /// struct Example { field_1: u32, field_2: u8, } /// let mut value = Example { field_1: 15, field_2: 255 }; - /// let mut volatile = unsafe { Volatile::from_mut_ptr(&mut value) }; + /// let mut volatile = unsafe { Volatile::new_read_write(NonNull::from(&mut value)) }; /// /// // construct a volatile write-only reference to `field_2` /// let mut field_2 = map_field_mut!(volatile.field_2).write_only(); @@ -796,14 +866,14 @@ where where R: access::Unsafe, { - unsafe { ptr::read_volatile(self.pointer) } + unsafe { ptr::read_volatile(self.pointer.as_ptr()) } } pub unsafe fn write_unsafe(&mut self, value: T) where W: access::Unsafe, { - unsafe { ptr::write_volatile(self.pointer, value) }; + unsafe { ptr::write_volatile(self.pointer.as_ptr(), value) }; } pub unsafe fn update_unsafe(&mut self, f: F) diff --git a/src/tests.rs b/src/tests.rs index 5dcf9a9..0d0f0e2 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -1,15 +1,20 @@ +use core::ptr::NonNull; + use super::{access::*, Volatile}; #[test] fn test_read() { let val = 42; - assert_eq!(unsafe { Volatile::from_ptr(&val) }.read(), 42); + assert_eq!( + unsafe { Volatile::new_read_only(NonNull::from(&val)) }.read(), + 42 + ); } #[test] fn test_write() { let mut val = 50; - let mut volatile = unsafe { Volatile::from_mut_ptr(&mut val) }; + let mut volatile = unsafe { Volatile::new_read_write(NonNull::from(&mut val)) }; volatile.write(50); assert_eq!(val, 50); } @@ -17,7 +22,7 @@ fn test_write() { #[test] fn test_update() { let mut val = 42; - let mut volatile = unsafe { Volatile::from_mut_ptr(&mut val) }; + let mut volatile = unsafe { Volatile::new_read_write(NonNull::from(&mut val)) }; volatile.update(|v| *v += 1); assert_eq!(val, 43); } @@ -28,20 +33,21 @@ fn test_access() { // ReadWrite assert_eq!( - unsafe { Volatile::new(&mut val, Access::read_write()) }.read(), + unsafe { Volatile::new_with_access(NonNull::from(&mut val), Access::read_write()) }.read(), 42 ); - unsafe { Volatile::new(&mut val, Access::read_write()) }.write(50); + unsafe { Volatile::new_with_access(NonNull::from(&mut val), Access::read_write()) }.write(50); assert_eq!(val, 50); - unsafe { Volatile::new(&mut val, Access::read_write()) }.update(|i| *i += 1); + unsafe { Volatile::new_with_access(NonNull::from(&mut val), Access::read_write()) } + .update(|i| *i += 1); assert_eq!(val, 51); // ReadOnly and WriteOnly assert_eq!( - unsafe { Volatile::new(&mut val, Access::read_only()) }.read(), + unsafe { Volatile::new_with_access(NonNull::from(&mut val), Access::read_only()) }.read(), 51 ); - unsafe { Volatile::new(&mut val, Access::write_only()) }.write(12); + unsafe { Volatile::new_with_access(NonNull::from(&mut val), Access::write_only()) }.write(12); assert_eq!(val, 12); // Custom: safe read + safe write @@ -50,7 +56,7 @@ fn test_access() { read: SafeAccess, write: SafeAccess, }; - let mut volatile = unsafe { Volatile::new(&mut val, access) }; + let mut volatile = unsafe { Volatile::new_with_access(NonNull::from(&mut val), access) }; let random: i32 = rand::random(); volatile.write(i64::from(random)); assert_eq!(volatile.read(), i64::from(random)); @@ -65,7 +71,7 @@ fn test_access() { read: SafeAccess, write: UnsafeAccess, }; - let mut volatile = unsafe { Volatile::new(&mut val, access) }; + let mut volatile = unsafe { Volatile::new_with_access(NonNull::from(&mut val), access) }; let random: i32 = rand::random(); unsafe { volatile.write_unsafe(i64::from(random)) }; assert_eq!(volatile.read(), i64::from(random)); @@ -82,7 +88,7 @@ fn test_access() { }; let random = rand::random(); val = random; - let mut volatile = unsafe { Volatile::new(&mut val, access) }; + let mut volatile = unsafe { Volatile::new_with_access(NonNull::from(&mut val), access) }; assert_eq!(volatile.read(), i64::from(random)); } @@ -92,7 +98,7 @@ fn test_access() { read: UnsafeAccess, write: SafeAccess, }; - let mut volatile = unsafe { Volatile::new(&mut val, access) }; + let mut volatile = unsafe { Volatile::new_with_access(NonNull::from(&mut val), access) }; let random: i32 = rand::random(); volatile.write(i64::from(random)); assert_eq!(unsafe { volatile.read_unsafe() }, i64::from(random)); @@ -126,9 +132,14 @@ fn test_struct() { field_1: 60, field_2: true, }; - let mut volatile = unsafe { Volatile::from_mut_ptr(&mut val) }; - unsafe { volatile.map_mut(|s| core::ptr::addr_of_mut!((*s).field_1)) }.update(|v| *v += 1); - let mut field_2 = unsafe { volatile.map_mut(|s| core::ptr::addr_of_mut!((*s).field_2)) }; + let mut volatile = unsafe { Volatile::new_read_write(NonNull::from(&mut val)) }; + unsafe { + volatile.map_mut(|s| NonNull::new(core::ptr::addr_of_mut!((*s.as_ptr()).field_1)).unwrap()) + } + .update(|v| *v += 1); + let mut field_2 = unsafe { + volatile.map_mut(|s| NonNull::new(core::ptr::addr_of_mut!((*s.as_ptr()).field_2)).unwrap()) + }; assert!(field_2.read()); field_2.write(false); assert_eq!( @@ -152,7 +163,7 @@ fn test_struct_macro() { field_1: 60, field_2: true, }; - let mut volatile = unsafe { Volatile::from_mut_ptr(&mut val) }; + let mut volatile = unsafe { Volatile::new_read_write(NonNull::from(&mut val)) }; let mut field_1 = map_field_mut!(volatile.field_1); field_1.update(|v| *v += 1); let mut field_2 = map_field_mut!(volatile.field_2); @@ -170,17 +181,20 @@ fn test_struct_macro() { #[cfg(feature = "unstable")] #[test] fn test_slice() { - let mut val: &mut [u32] = &mut [1, 2, 3]; - let mut volatile = unsafe { Volatile::from_mut_ptr(val) }; + let val: &mut [u32] = &mut [1, 2, 3]; + let mut volatile = unsafe { Volatile::new_read_write(NonNull::from(val)) }; volatile.index_mut(0).update(|v| *v += 1); - assert_eq!(val, [2, 2, 3]); + + let mut dst = [0; 3]; + volatile.copy_into_slice(&mut dst); + assert_eq!(dst, [2, 2, 3]); } #[cfg(feature = "unstable")] #[test] fn test_chunks() { let mut val: &mut [u32] = &mut [1, 2, 3, 4, 5, 6]; - let mut volatile = unsafe { Volatile::from_mut_ptr(val) }; + let mut volatile = unsafe { Volatile::new_read_write(NonNull::from(val)) }; let mut chunks = volatile.as_chunks_mut().0; chunks.index_mut(1).write([10, 11, 12]); assert_eq!(chunks.index(0).read(), [1, 2, 3]); From c3d6b9a8a1692d71a90e2947b11c4c0d24a729b0 Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Mon, 14 Jun 2021 10:19:15 +0200 Subject: [PATCH 07/16] Add lifetime parameter and rename to `VolatilePtr` The lifetime parameter is required to make `map_mut` sound (otherwise we might get mutable aliasing). Also: - add new `from_ref` and `from_mut_ref` contructor methods again. - add a `new_generic` constructor method to construct a `VolatilePtr` with arbitrary lifetime and access parameters. --- src/lib.rs | 255 +++++++++++++++++++++++++-------------------------- src/tests.rs | 48 ++++++---- 2 files changed, 156 insertions(+), 147 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 4044cec..6f142f9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -15,7 +15,7 @@ #![warn(missing_docs)] #![deny(unsafe_op_in_unsafe_fn)] -use access::{Access, ReadOnly, ReadWrite, SafeAccess, WriteOnly}; +use access::{Access, ReadOnly, ReadWrite, WriteOnly}; use core::{ fmt, marker::PhantomData, @@ -40,12 +40,12 @@ pub mod access; /// /// ``` /// # extern crate core; -/// use volatile::{Volatile, map_field}; +/// use volatile::{VolatilePtr, map_field}; /// use core::ptr::NonNull; /// /// struct Example { field_1: u32, field_2: u8, } /// let mut value = Example { field_1: 15, field_2: 255 }; -/// let mut volatile = unsafe { Volatile::new_read_write(NonNull::from(&mut value)) }; +/// let mut volatile = unsafe { VolatilePtr::new_read_write(NonNull::from(&mut value)) }; /// /// // construct a volatile reference to a field /// let field_2 = map_field!(volatile.field_2); @@ -90,11 +90,12 @@ mod tests; /// /// The size of this struct is the same as the size of the contained reference. #[repr(transparent)] -pub struct Volatile +pub struct VolatilePtr<'a, T, A = ReadWrite> where T: ?Sized, { pointer: NonNull, + reference: PhantomData<&'a T>, access: PhantomData, } @@ -103,20 +104,20 @@ where /// These functions allow to construct a new `Volatile` instance from a reference type. While /// the `new` function creates a `Volatile` instance with unrestricted access, there are also /// functions for creating read-only or write-only instances. -impl Volatile +impl VolatilePtr<'_, T> where T: ?Sized, { - pub unsafe fn new_read_write(pointer: NonNull) -> Volatile { - unsafe { Volatile::new_with_access(pointer, Access::read_write()) } + pub unsafe fn new_read_write(pointer: NonNull) -> VolatilePtr<'static, T> { + unsafe { VolatilePtr::new_with_access(pointer, Access::read_write()) } } - pub const unsafe fn new_read_only(pointer: NonNull) -> Volatile { - unsafe { Volatile::new_with_access(pointer, Access::read_only()) } + pub const unsafe fn new_read_only(pointer: NonNull) -> VolatilePtr<'static, T, ReadOnly> { + unsafe { VolatilePtr::new_with_access(pointer, Access::read_only()) } } - pub const unsafe fn new_write_only(pointer: NonNull) -> Volatile { - unsafe { Volatile::new_with_access(pointer, Access::write_only()) } + pub const unsafe fn new_write_only(pointer: NonNull) -> VolatilePtr<'static, T, WriteOnly> { + unsafe { VolatilePtr::new_with_access(pointer, Access::write_only()) } } /// Constructs a new volatile instance wrapping the given reference. @@ -133,26 +134,48 @@ where /// /// ```rust /// # extern crate core; - /// use volatile::Volatile; + /// use volatile::VolatilePtr; /// use core::ptr::NonNull; /// /// let mut value = 0u32; /// - /// let mut volatile = unsafe { Volatile::new_read_write(NonNull::from(&mut value)) }; + /// let mut volatile = unsafe { VolatilePtr::new_read_write(NonNull::from(&mut value)) }; /// volatile.write(1); /// assert_eq!(volatile.read(), 1); /// ``` - pub const unsafe fn new_with_access(pointer: NonNull, access: A) -> Volatile { + pub const unsafe fn new_with_access( + pointer: NonNull, + access: A, + ) -> VolatilePtr<'static, T, A> { mem::forget(access); // needed because we cannot require `A: Copy` on stable Rust yet - Volatile { + unsafe { Self::new_generic(pointer) } + } + + pub const unsafe fn new_generic<'a, A>(pointer: NonNull) -> VolatilePtr<'a, T, A> { + VolatilePtr { pointer, + reference: PhantomData, access: PhantomData, } } + + pub fn from_ref<'a>(reference: &'a T) -> VolatilePtr<'a, T, ReadOnly> + where + T: 'a, + { + unsafe { VolatilePtr::new_generic(reference.into()) } + } + + pub fn from_mut_ref<'a>(reference: &'a mut T) -> VolatilePtr<'a, T> + where + T: 'a, + { + unsafe { VolatilePtr::new_generic(reference.into()) } + } } /// Methods for references to `Copy` types -impl<'a, T, R, W> Volatile> +impl VolatilePtr<'_, T, Access> where T: Copy + ?Sized, { @@ -167,15 +190,15 @@ where /// /// ```rust /// # extern crate core; - /// use volatile::Volatile; + /// use volatile::VolatilePtr; /// use core::ptr::NonNull; /// /// let value = 42; - /// let shared_reference = unsafe { Volatile::new_read_only(NonNull::from(&value)) }; + /// let shared_reference = unsafe { VolatilePtr::new_read_only(NonNull::from(&value)) }; /// assert_eq!(shared_reference.read(), 42); /// /// let mut value = 50; - /// let mut_reference = unsafe { Volatile::new_read_write(NonNull::from(&mut value)) }; + /// let mut_reference = unsafe { VolatilePtr::new_read_write(NonNull::from(&mut value)) }; /// assert_eq!(mut_reference.read(), 50); /// ``` pub fn read(&self) -> T @@ -196,11 +219,11 @@ where /// /// ```rust /// # extern crate core; - /// use volatile::Volatile; + /// use volatile::VolatilePtr; /// use core::ptr::NonNull; /// /// let mut value = 42; - /// let mut volatile = unsafe { Volatile::new_read_write(NonNull::from(&mut value)) }; + /// let mut volatile = unsafe { VolatilePtr::new_read_write(NonNull::from(&mut value)) }; /// volatile.write(50); /// /// assert_eq!(volatile.read(), 50); @@ -221,11 +244,11 @@ where /// /// ```rust /// # extern crate core; - /// use volatile::Volatile; + /// use volatile::VolatilePtr; /// use core::ptr::NonNull; /// /// let mut value = 42; - /// let mut volatile = unsafe { Volatile::new_read_write(NonNull::from(&mut value)) }; + /// let mut volatile = unsafe { VolatilePtr::new_read_write(NonNull::from(&mut value)) }; /// volatile.update(|val| *val += 1); /// /// assert_eq!(volatile.read(), 43); @@ -241,7 +264,7 @@ where } /// Method for extracting the wrapped value. -impl<'a, T, A> Volatile +impl VolatilePtr<'_, T, A> where T: ?Sized, { @@ -261,11 +284,11 @@ where /// /// ``` /// # extern crate core; - /// use volatile::Volatile; + /// use volatile::VolatilePtr; /// use core::ptr::NonNull; /// /// let mut value = 42; - /// let mut volatile = unsafe { Volatile::new_read_write(NonNull::from(&mut value)) }; + /// let mut volatile = unsafe { VolatilePtr::new_read_write(NonNull::from(&mut value)) }; /// volatile.write(50); /// let unwrapped: *mut i32 = volatile.as_ptr().as_ptr(); /// @@ -277,7 +300,7 @@ where } /// Transformation methods for accessing struct fields -impl Volatile> +impl VolatilePtr<'_, T, Access> where T: ?Sized, { @@ -293,12 +316,12 @@ where /// /// ``` /// # extern crate core; - /// use volatile::Volatile; + /// use volatile::VolatilePtr; /// use core::ptr::NonNull; /// /// struct Example { field_1: u32, field_2: u8, } /// let mut value = Example { field_1: 15, field_2: 255 }; - /// let mut volatile = unsafe { Volatile::new_read_write(NonNull::from(&mut value)) }; + /// let mut volatile = unsafe { VolatilePtr::new_read_write(NonNull::from(&mut value)) }; /// /// // construct a volatile reference to a field /// let field_2 = unsafe { volatile.map(|ptr| NonNull::new(core::ptr::addr_of_mut!((*ptr.as_ptr()).field_2)).unwrap()) }; @@ -309,11 +332,11 @@ where /// /// ``` /// # extern crate core; - /// use volatile::Volatile; + /// use volatile::VolatilePtr; /// use core::ptr::NonNull; /// /// let mut value = 5; - /// let mut volatile = unsafe { Volatile::new_read_write(NonNull::from(&mut value)) }; + /// let mut volatile = unsafe { VolatilePtr::new_read_write(NonNull::from(&mut value)) }; /// /// // DON'T DO THIS: /// let mut readout = 0; @@ -322,32 +345,26 @@ where /// value /// })}; /// ``` - pub unsafe fn map(&self, f: F) -> Volatile> + pub unsafe fn map<'a, F, U>(&'a self, f: F) -> VolatilePtr<'a, U, Access> where F: FnOnce(NonNull) -> NonNull, U: ?Sized, { - Volatile { - pointer: f(self.pointer), - access: PhantomData, - } + unsafe { VolatilePtr::new_generic(f(self.pointer)) } } - pub unsafe fn map_mut(&mut self, f: F) -> Volatile> + pub unsafe fn map_mut(&mut self, f: F) -> VolatilePtr> where F: FnOnce(NonNull) -> NonNull, U: ?Sized, { - Volatile { - pointer: f(self.pointer), - access: self.access, - } + unsafe { VolatilePtr::new_generic(f(self.pointer)) } } } /// Methods for volatile slices #[cfg(feature = "unstable")] -impl<'a, T, R, W> Volatile<[T], Access> { +impl<'a, T, R, W> VolatilePtr<'a, [T], Access> { /// Applies the index operation on the wrapped slice. /// /// Returns a shared `Volatile` reference to the resulting subslice. @@ -362,12 +379,12 @@ impl<'a, T, R, W> Volatile<[T], Access> { /// /// ``` /// # extern crate core; - /// use volatile::Volatile; + /// use volatile::VolatilePtr; /// use core::ptr::NonNull; /// /// let array = [1, 2, 3]; /// let slice = &array[..]; - /// let volatile = unsafe { Volatile::new_read_only(NonNull::from(slice)) }; + /// let volatile = unsafe { VolatilePtr::new_read_only(NonNull::from(slice)) }; /// assert_eq!(volatile.index(1).read(), 2); /// ``` /// @@ -375,23 +392,23 @@ impl<'a, T, R, W> Volatile<[T], Access> { /// /// ``` /// # extern crate core; - /// use volatile::Volatile; + /// use volatile::VolatilePtr; /// use core::ptr::NonNull; /// /// let array = [1, 2, 3]; /// let slice = &array[..]; - /// let volatile = unsafe { Volatile::new_read_only(NonNull::from(slice)) }; + /// let volatile = unsafe { VolatilePtr::new_read_only(NonNull::from(slice)) }; /// let subslice = volatile.index(1..); /// assert_eq!(subslice.index(0).read(), 2); /// ``` - pub fn index(&self, index: I) -> Volatile> + pub fn index(&self, index: I) -> VolatilePtr> where I: SliceIndex<[T]>, { unsafe { self.map(|slice| slice.get_unchecked_mut(index)) } } - pub fn index_mut(&mut self, index: I) -> Volatile> + pub fn index_mut(&mut self, index: I) -> VolatilePtr> where I: SliceIndex<[T]>, { @@ -415,13 +432,13 @@ impl<'a, T, R, W> Volatile<[T], Access> { /// /// ``` /// # extern crate core; - /// use volatile::Volatile; + /// use volatile::VolatilePtr; /// use core::ptr::NonNull; /// /// let src = [1, 2]; /// // the `Volatile` type does not work with arrays, so convert `src` to a slice /// let slice = &src[..]; - /// let volatile = unsafe { Volatile::new_read_only(NonNull::from(slice)) }; + /// let volatile = unsafe { VolatilePtr::new_read_only(NonNull::from(slice)) }; /// let mut dst = [5, 0, 0]; /// /// // Because the slices have to be the same length, @@ -471,14 +488,14 @@ impl<'a, T, R, W> Volatile<[T], Access> { /// /// ``` /// # extern crate core; - /// use volatile::Volatile; + /// use volatile::VolatilePtr; /// use core::ptr::NonNull; /// /// let src = [1, 2, 3, 4]; /// let mut dst = [0, 0]; /// // the `Volatile` type does not work with arrays, so convert `dst` to a slice /// let slice = &mut dst[..]; - /// let mut volatile = unsafe { Volatile::new_read_write(NonNull::from(slice))}; + /// let mut volatile = unsafe { VolatilePtr::new_read_write(NonNull::from(slice))}; /// /// // Because the slices have to be the same length, /// // we slice the source slice from four elements @@ -531,12 +548,12 @@ impl<'a, T, R, W> Volatile<[T], Access> { /// /// ``` /// extern crate core; - /// use volatile::Volatile; + /// use volatile::VolatilePtr; /// use core::ptr::NonNull; /// /// let mut byte_array = *b"Hello, World!"; /// let mut slice: &mut [u8] = &mut byte_array[..]; - /// let mut volatile = unsafe { Volatile::new_read_write(NonNull::from(slice)) }; + /// let mut volatile = unsafe { VolatilePtr::new_read_write(NonNull::from(slice)) }; /// /// volatile.copy_within(1..5, 8); /// @@ -568,8 +585,8 @@ impl<'a, T, R, W> Volatile<[T], Access> { &self, mid: usize, ) -> ( - Volatile<[T], Access>, - Volatile<[T], Access>, + VolatilePtr<[T], Access>, + VolatilePtr<[T], Access>, ) { assert!(mid <= self.pointer.len()); // SAFETY: `[ptr; mid]` and `[mid; len]` are inside `self`, which @@ -580,7 +597,10 @@ impl<'a, T, R, W> Volatile<[T], Access> { pub fn split_at_mut( &mut self, mid: usize, - ) -> (Volatile<[T], Access>, Volatile<[T], Access>) { + ) -> ( + VolatilePtr<[T], Access>, + VolatilePtr<[T], Access>, + ) { assert!(mid <= self.pointer.len()); // SAFETY: `[ptr; mid]` and `[mid; len]` are inside `self`, which // fulfills the requirements of `from_raw_parts_mut`. @@ -591,20 +611,14 @@ impl<'a, T, R, W> Volatile<[T], Access> { &self, mid: usize, ) -> ( - Volatile<[T], Access>, - Volatile<[T], Access>, + VolatilePtr<[T], Access>, + VolatilePtr<[T], Access>, ) { // SAFETY: Caller has to check that `0 <= mid <= self.len()` unsafe { ( - Volatile { - pointer: { (self.pointer).get_unchecked_mut(..mid) }, - access: PhantomData, - }, - Volatile { - pointer: { (self.pointer).get_unchecked_mut(mid..) }, - access: PhantomData, - }, + VolatilePtr::new_generic((self.pointer).get_unchecked_mut(..mid)), + VolatilePtr::new_generic((self.pointer).get_unchecked_mut(mid..)), ) } } @@ -612,7 +626,10 @@ impl<'a, T, R, W> Volatile<[T], Access> { unsafe fn split_at_mut_unchecked( &mut self, mid: usize, - ) -> (Volatile<[T], Access>, Volatile<[T], Access>) { + ) -> ( + VolatilePtr<[T], Access>, + VolatilePtr<[T], Access>, + ) { let len = self.pointer.len(); let ptr = self.pointer.as_mut_ptr(); @@ -622,15 +639,12 @@ impl<'a, T, R, W> Volatile<[T], Access> { // is fine. unsafe { ( - Volatile { - pointer: NonNull::new(ptr::slice_from_raw_parts_mut(ptr, mid)).unwrap(), - access: self.access, - }, - Volatile { - pointer: NonNull::new(ptr::slice_from_raw_parts_mut(ptr.add(mid), len - mid)) - .unwrap(), - access: self.access, - }, + VolatilePtr::new_generic( + NonNull::new(ptr::slice_from_raw_parts_mut(ptr, mid)).unwrap(), + ), + VolatilePtr::new_generic( + NonNull::new(ptr::slice_from_raw_parts_mut(ptr.add(mid), len - mid)).unwrap(), + ), ) } } @@ -638,8 +652,8 @@ impl<'a, T, R, W> Volatile<[T], Access> { pub fn as_chunks( &self, ) -> ( - Volatile<[[T; N]], Access>, - Volatile<[T], Access>, + VolatilePtr<[[T; N]], Access>, + VolatilePtr<[T], Access>, ) { assert_ne!(N, 0); let len = self.pointer.len() / N; @@ -652,7 +666,7 @@ impl<'a, T, R, W> Volatile<[T], Access> { pub unsafe fn as_chunks_unchecked( &self, - ) -> Volatile<[[T; N]], Access> { + ) -> VolatilePtr<[[T; N]], Access> { debug_assert_ne!(N, 0); debug_assert_eq!(self.pointer.len() % N, 0); let new_len = @@ -660,24 +674,19 @@ impl<'a, T, R, W> Volatile<[T], Access> { unsafe { core::intrinsics::exact_div(self.pointer.len(), N) }; // SAFETY: We cast a slice of `new_len * N` elements into // a slice of `new_len` many `N` elements chunks. - let pointer = unsafe { - NonNull::new(ptr::slice_from_raw_parts_mut( - self.pointer.as_mut_ptr().cast(), - new_len, - )) - .unwrap() - }; - Volatile { - pointer, - access: PhantomData, - } + let pointer = NonNull::new(ptr::slice_from_raw_parts_mut( + self.pointer.as_mut_ptr().cast(), + new_len, + )) + .unwrap(); + unsafe { VolatilePtr::new_generic(pointer) } } pub fn as_chunks_mut( &mut self, ) -> ( - Volatile<[[T; N]], Access>, - Volatile<[T], Access>, + VolatilePtr<[[T; N]], Access>, + VolatilePtr<[T], Access>, ) { assert_ne!(N, 0); let len = self.pointer.len() / N; @@ -690,7 +699,7 @@ impl<'a, T, R, W> Volatile<[T], Access> { pub unsafe fn as_chunks_unchecked_mut( &mut self, - ) -> Volatile<[[T; N]], Access> { + ) -> VolatilePtr<[[T; N]], Access> { debug_assert_ne!(N, 0); debug_assert_eq!(self.pointer.len() % N, 0); let new_len = @@ -703,15 +712,12 @@ impl<'a, T, R, W> Volatile<[T], Access> { new_len, )) .unwrap(); - Volatile { - pointer, - access: self.access, - } + unsafe { VolatilePtr::new_generic(pointer) } } pub unsafe fn as_chunks_unchecked_by_val( self, - ) -> Volatile<[[T; N]], Access> { + ) -> VolatilePtr<'a, [[T; N]], Access> { debug_assert_ne!(N, 0); debug_assert_eq!(self.pointer.len() % N, 0); let new_len = @@ -724,16 +730,13 @@ impl<'a, T, R, W> Volatile<[T], Access> { new_len, )) .unwrap(); - Volatile { - pointer, - access: self.access, - } + unsafe { VolatilePtr::new_generic(pointer) } } } /// Methods for volatile byte slices #[cfg(feature = "unstable")] -impl Volatile<[u8], A> { +impl VolatilePtr<'_, [u8], A> { /// Sets all elements of the byte slice to the given `value` using a volatile `memset`. /// /// This method is similar to the `slice::fill` method of the standard library, with the @@ -748,10 +751,10 @@ impl Volatile<[u8], A> { /// /// ```rust /// # extern crate core; - /// use volatile::Volatile; + /// use volatile::VolatilePtr; /// use core::ptr::NonNull; /// - /// let mut buf = unsafe { Volatile::new_read_write(NonNull::from(vec![0; 10].as_mut_slice())) }; + /// let mut buf = unsafe { VolatilePtr::new_read_write(NonNull::from(vec![0; 10].as_mut_slice())) }; /// buf.fill(1); /// assert_eq!(unsafe { buf.as_ptr().as_mut() }, &mut vec![1; 10]); /// ``` @@ -767,7 +770,7 @@ impl Volatile<[u8], A> { /// These methods are only available with the `unstable` feature enabled (requires a nightly /// Rust compiler). #[cfg(feature = "unstable")] -impl Volatile<[T; N], Access> { +impl VolatilePtr<'_, [T; N], Access> { /// Converts an array reference to a shared slice. /// /// This makes it possible to use the methods defined on slices. @@ -778,11 +781,11 @@ impl Volatile<[T; N], Access> { /// /// ``` /// # extern crate core; - /// use volatile::Volatile; + /// use volatile::VolatilePtr; /// use core::ptr::NonNull; /// /// let src = [1, 2]; - /// let volatile = unsafe { Volatile::new_read_only(NonNull::from(&src)) }; + /// let volatile = unsafe { VolatilePtr::new_read_only(NonNull::from(&src)) }; /// let mut dst = [0, 0]; /// /// // convert the `Volatile<&[i32; 2]>` array reference to a `Volatile<&[i32]>` slice @@ -792,7 +795,7 @@ impl Volatile<[T; N], Access> { /// /// assert_eq!(dst, [1, 2]); /// ``` - pub fn as_slice(&self) -> Volatile<[T], Access> { + pub fn as_slice(&self) -> VolatilePtr<[T], Access> { unsafe { self.map(|array| { NonNull::new(ptr::slice_from_raw_parts_mut(array.as_ptr() as *mut T, N)).unwrap() @@ -802,7 +805,7 @@ impl Volatile<[T; N], Access> { } /// Methods for restricting access. -impl<'a, T, R, W> Volatile> +impl<'a, T, R, W> VolatilePtr<'a, T, Access> where T: ?Sized, { @@ -812,21 +815,18 @@ where /// /// ``` /// # extern crate core; - /// use volatile::Volatile; + /// use volatile::VolatilePtr; /// use core::ptr::NonNull; /// /// let mut value: i16 = -4; - /// let mut volatile = unsafe { Volatile::new_read_write(NonNull::from(&mut value)) }; + /// let mut volatile = unsafe { VolatilePtr::new_read_write(NonNull::from(&mut value)) }; /// /// let read_only = volatile.read_only(); /// assert_eq!(read_only.read(), -4); /// // read_only.write(10); // compile-time error /// ``` - pub fn read_only(self) -> Volatile> { - Volatile { - pointer: self.pointer, - access: PhantomData, - } + pub fn read_only(self) -> VolatilePtr<'a, T, Access> { + unsafe { VolatilePtr::new_generic(self.pointer) } } /// Restricts access permissions to write-only. @@ -837,28 +837,25 @@ where /// /// ``` /// # extern crate core; - /// use volatile::{Volatile, map_field_mut}; + /// use volatile::{VolatilePtr, map_field_mut}; /// use core::ptr::NonNull; /// /// struct Example { field_1: u32, field_2: u8, } /// let mut value = Example { field_1: 15, field_2: 255 }; - /// let mut volatile = unsafe { Volatile::new_read_write(NonNull::from(&mut value)) }; + /// let mut volatile = unsafe { VolatilePtr::new_read_write(NonNull::from(&mut value)) }; /// /// // construct a volatile write-only reference to `field_2` /// let mut field_2 = map_field_mut!(volatile.field_2).write_only(); /// field_2.write(14); /// // field_2.read(); // compile-time error /// ``` - pub fn write_only(self) -> Volatile> { - Volatile { - pointer: self.pointer, - access: PhantomData, - } + pub fn write_only(self) -> VolatilePtr<'a, T, Access> { + unsafe { VolatilePtr::new_generic(self.pointer) } } } /// Unsafe access methods for references to `Copy` types -impl<'a, T, R, W> Volatile> +impl VolatilePtr<'_, T, Access> where T: Copy + ?Sized, { @@ -888,7 +885,7 @@ where } } -impl fmt::Debug for Volatile> +impl fmt::Debug for VolatilePtr<'_, T, Access> where T: Copy + fmt::Debug + ?Sized, { @@ -897,7 +894,7 @@ where } } -impl fmt::Debug for Volatile> +impl fmt::Debug for VolatilePtr<'_, T, Access> where T: ?Sized, { @@ -906,7 +903,7 @@ where } } -impl fmt::Debug for Volatile> +impl fmt::Debug for VolatilePtr<'_, T, Access> where T: ?Sized, { diff --git a/src/tests.rs b/src/tests.rs index 0d0f0e2..da809b3 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -1,12 +1,12 @@ use core::ptr::NonNull; -use super::{access::*, Volatile}; +use super::{access::*, VolatilePtr}; #[test] fn test_read() { let val = 42; assert_eq!( - unsafe { Volatile::new_read_only(NonNull::from(&val)) }.read(), + unsafe { VolatilePtr::new_read_only(NonNull::from(&val)) }.read(), 42 ); } @@ -14,7 +14,7 @@ fn test_read() { #[test] fn test_write() { let mut val = 50; - let mut volatile = unsafe { Volatile::new_read_write(NonNull::from(&mut val)) }; + let mut volatile = unsafe { VolatilePtr::new_read_write(NonNull::from(&mut val)) }; volatile.write(50); assert_eq!(val, 50); } @@ -22,7 +22,7 @@ fn test_write() { #[test] fn test_update() { let mut val = 42; - let mut volatile = unsafe { Volatile::new_read_write(NonNull::from(&mut val)) }; + let mut volatile = unsafe { VolatilePtr::new_read_write(NonNull::from(&mut val)) }; volatile.update(|v| *v += 1); assert_eq!(val, 43); } @@ -33,21 +33,25 @@ fn test_access() { // ReadWrite assert_eq!( - unsafe { Volatile::new_with_access(NonNull::from(&mut val), Access::read_write()) }.read(), + unsafe { VolatilePtr::new_with_access(NonNull::from(&mut val), Access::read_write()) } + .read(), 42 ); - unsafe { Volatile::new_with_access(NonNull::from(&mut val), Access::read_write()) }.write(50); + unsafe { VolatilePtr::new_with_access(NonNull::from(&mut val), Access::read_write()) } + .write(50); assert_eq!(val, 50); - unsafe { Volatile::new_with_access(NonNull::from(&mut val), Access::read_write()) } + unsafe { VolatilePtr::new_with_access(NonNull::from(&mut val), Access::read_write()) } .update(|i| *i += 1); assert_eq!(val, 51); // ReadOnly and WriteOnly assert_eq!( - unsafe { Volatile::new_with_access(NonNull::from(&mut val), Access::read_only()) }.read(), + unsafe { VolatilePtr::new_with_access(NonNull::from(&mut val), Access::read_only()) } + .read(), 51 ); - unsafe { Volatile::new_with_access(NonNull::from(&mut val), Access::write_only()) }.write(12); + unsafe { VolatilePtr::new_with_access(NonNull::from(&mut val), Access::write_only()) } + .write(12); assert_eq!(val, 12); // Custom: safe read + safe write @@ -56,7 +60,7 @@ fn test_access() { read: SafeAccess, write: SafeAccess, }; - let mut volatile = unsafe { Volatile::new_with_access(NonNull::from(&mut val), access) }; + let mut volatile = unsafe { VolatilePtr::new_with_access(NonNull::from(&mut val), access) }; let random: i32 = rand::random(); volatile.write(i64::from(random)); assert_eq!(volatile.read(), i64::from(random)); @@ -71,7 +75,7 @@ fn test_access() { read: SafeAccess, write: UnsafeAccess, }; - let mut volatile = unsafe { Volatile::new_with_access(NonNull::from(&mut val), access) }; + let mut volatile = unsafe { VolatilePtr::new_with_access(NonNull::from(&mut val), access) }; let random: i32 = rand::random(); unsafe { volatile.write_unsafe(i64::from(random)) }; assert_eq!(volatile.read(), i64::from(random)); @@ -88,7 +92,7 @@ fn test_access() { }; let random = rand::random(); val = random; - let mut volatile = unsafe { Volatile::new_with_access(NonNull::from(&mut val), access) }; + let volatile = unsafe { VolatilePtr::new_with_access(NonNull::from(&mut val), access) }; assert_eq!(volatile.read(), i64::from(random)); } @@ -98,7 +102,7 @@ fn test_access() { read: UnsafeAccess, write: SafeAccess, }; - let mut volatile = unsafe { Volatile::new_with_access(NonNull::from(&mut val), access) }; + let mut volatile = unsafe { VolatilePtr::new_with_access(NonNull::from(&mut val), access) }; let random: i32 = rand::random(); volatile.write(i64::from(random)); assert_eq!(unsafe { volatile.read_unsafe() }, i64::from(random)); @@ -132,7 +136,7 @@ fn test_struct() { field_1: 60, field_2: true, }; - let mut volatile = unsafe { Volatile::new_read_write(NonNull::from(&mut val)) }; + let mut volatile = unsafe { VolatilePtr::new_read_write(NonNull::from(&mut val)) }; unsafe { volatile.map_mut(|s| NonNull::new(core::ptr::addr_of_mut!((*s.as_ptr()).field_1)).unwrap()) } @@ -163,7 +167,7 @@ fn test_struct_macro() { field_1: 60, field_2: true, }; - let mut volatile = unsafe { Volatile::new_read_write(NonNull::from(&mut val)) }; + let mut volatile = unsafe { VolatilePtr::new_read_write(NonNull::from(&mut val)) }; let mut field_1 = map_field_mut!(volatile.field_1); field_1.update(|v| *v += 1); let mut field_2 = map_field_mut!(volatile.field_2); @@ -182,7 +186,7 @@ fn test_struct_macro() { #[test] fn test_slice() { let val: &mut [u32] = &mut [1, 2, 3]; - let mut volatile = unsafe { Volatile::new_read_write(NonNull::from(val)) }; + let mut volatile = unsafe { VolatilePtr::new_read_write(NonNull::from(val)) }; volatile.index_mut(0).update(|v| *v += 1); let mut dst = [0; 3]; @@ -193,10 +197,18 @@ fn test_slice() { #[cfg(feature = "unstable")] #[test] fn test_chunks() { - let mut val: &mut [u32] = &mut [1, 2, 3, 4, 5, 6]; - let mut volatile = unsafe { Volatile::new_read_write(NonNull::from(val)) }; + let val: &mut [u32] = &mut [1, 2, 3, 4, 5, 6]; + let mut volatile = unsafe { VolatilePtr::new_read_write(NonNull::from(val)) }; let mut chunks = volatile.as_chunks_mut().0; chunks.index_mut(1).write([10, 11, 12]); assert_eq!(chunks.index(0).read(), [1, 2, 3]); assert_eq!(chunks.index(1).read(), [10, 11, 12]); } + +#[test] +fn test_lifetime() { + let mut val = 50; + let mut volatile = VolatilePtr::from_mut_ref(&mut val); + volatile.write(50); + assert_eq!(val, 50); +} From 9f7b015a7dd976ec89214ecca0c44d2ee970e54a Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Mon, 14 Jun 2021 12:12:41 +0200 Subject: [PATCH 08/16] Fix: do bounds checking in `index` and `index_mut` --- src/lib.rs | 30 ++++++++++++++++++++++++++---- src/tests.rs | 44 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 70 insertions(+), 4 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 6f142f9..2e7c5cf 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -365,6 +365,10 @@ where /// Methods for volatile slices #[cfg(feature = "unstable")] impl<'a, T, R, W> VolatilePtr<'a, [T], Access> { + pub fn len(&self) -> usize { + self.pointer.len() + } + /// Applies the index operation on the wrapped slice. /// /// Returns a shared `Volatile` reference to the resulting subslice. @@ -401,17 +405,27 @@ impl<'a, T, R, W> VolatilePtr<'a, [T], Access> { /// let subslice = volatile.index(1..); /// assert_eq!(subslice.index(0).read(), 2); /// ``` - pub fn index(&self, index: I) -> VolatilePtr> + pub fn index( + &self, + index: I, + ) -> VolatilePtr<>::Output, Access> where - I: SliceIndex<[T]>, + I: SliceIndex<[T]> + SliceIndex<[()]> + Clone, { + bounds_check(self.pointer.len(), index.clone()); + unsafe { self.map(|slice| slice.get_unchecked_mut(index)) } } - pub fn index_mut(&mut self, index: I) -> VolatilePtr> + pub fn index_mut( + &mut self, + index: I, + ) -> VolatilePtr<>::Output, Access> where - I: SliceIndex<[T]>, + I: SliceIndex<[T]> + SliceIndex<[()]> + Clone, { + bounds_check(self.pointer.len(), index.clone()); + unsafe { self.map_mut(|slice| slice.get_unchecked_mut(index)) } } @@ -913,3 +927,11 @@ where .finish() } } + +#[cfg(feature = "unstable")] +fn bounds_check(len: usize, index: impl SliceIndex<[()]>) { + const MAX_ARRAY: [(); usize::MAX] = [(); usize::MAX]; + + let bound_check_slice = &MAX_ARRAY[..len]; + &bound_check_slice[index]; +} diff --git a/src/tests.rs b/src/tests.rs index da809b3..840c7ff 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -194,6 +194,50 @@ fn test_slice() { assert_eq!(dst, [2, 2, 3]); } +#[cfg(feature = "unstable")] +#[test] +#[should_panic] +fn test_bounds_check_1() { + let val: &mut [u32] = &mut [1, 2, 3]; + let mut volatile = unsafe { VolatilePtr::new_read_write(NonNull::from(val)) }; + volatile.index_mut(3); +} + +#[cfg(feature = "unstable")] +#[test] +#[should_panic] +fn test_bounds_check_2() { + let val: &mut [u32] = &mut [1, 2, 3]; + let mut volatile = unsafe { VolatilePtr::new_read_write(NonNull::from(val)) }; + volatile.index_mut(2..1); +} + +#[cfg(feature = "unstable")] +#[test] +#[should_panic] +fn test_bounds_check_3() { + let val: &mut [u32] = &mut [1, 2, 3]; + let mut volatile = unsafe { VolatilePtr::new_read_write(NonNull::from(val)) }; + volatile.index_mut(4..); // `3..` is is still ok (see next test) +} + +#[cfg(feature = "unstable")] +#[test] +fn test_bounds_check_4() { + let val: &mut [u32] = &mut [1, 2, 3]; + let mut volatile = unsafe { VolatilePtr::new_read_write(NonNull::from(val)) }; + assert_eq!(volatile.index_mut(3..).len(), 0); +} + +#[cfg(feature = "unstable")] +#[test] +#[should_panic] +fn test_bounds_check_5() { + let val: &mut [u32] = &mut [1, 2, 3]; + let mut volatile = unsafe { VolatilePtr::new_read_write(NonNull::from(val)) }; + volatile.index_mut(..4); +} + #[cfg(feature = "unstable")] #[test] fn test_chunks() { From b525f383af7db00f392bd78e5b05390c252acce9 Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Mon, 14 Jun 2021 12:18:06 +0200 Subject: [PATCH 09/16] Add `const` index functions under a new `very_unstable` feature --- Cargo.toml | 4 +++- src/lib.rs | 49 ++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 51 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 63a2565..55940af 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,6 +13,8 @@ repository = "https://github.com/rust-osdev/volatile" [features] # Enable unstable features; requires Rust nightly; might break on compiler updates unstable = [] +# Enable unstable and experimental features; requires Rust nightly; might break on compiler updates +very_unstable = ["unstable"] [dev-dependencies] rand = "0.8.3" @@ -20,7 +22,7 @@ rand = "0.8.3" [package.metadata.release] no-dev-version = true pre-release-replacements = [ - { file="Changelog.md", search="# Unreleased", replace="# Unreleased\n\n# {{version}} – {{date}}", exactly=1 }, + { file = "Changelog.md", search = "# Unreleased", replace = "# Unreleased\n\n# {{version}} – {{date}}", exactly = 1 }, ] pre-release-commit-message = "Release version {{version}}" diff --git a/src/lib.rs b/src/lib.rs index 2e7c5cf..43afb11 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -10,7 +10,13 @@ #![cfg_attr(feature = "unstable", feature(slice_range))] #![cfg_attr(feature = "unstable", feature(slice_ptr_get))] #![cfg_attr(feature = "unstable", feature(slice_ptr_len))] -#![cfg_attr(feature = "unstable", allow(incomplete_features))] +#![cfg_attr(feature = "very_unstable", feature(const_slice_ptr_len))] +#![cfg_attr(feature = "very_unstable", feature(const_panic))] +#![cfg_attr(feature = "very_unstable", feature(const_fn_trait_bound))] +#![cfg_attr(feature = "very_unstable", feature(const_fn_fn_ptr_basics))] +#![cfg_attr(feature = "very_unstable", feature(const_trait_impl))] +#![cfg_attr(feature = "very_unstable", feature(const_mut_refs))] +#![cfg_attr(feature = "very_unstable", allow(incomplete_features))] #![cfg_attr(all(feature = "unstable", test), feature(slice_as_chunks))] #![warn(missing_docs)] #![deny(unsafe_op_in_unsafe_fn)] @@ -353,6 +359,18 @@ where unsafe { VolatilePtr::new_generic(f(self.pointer)) } } + #[cfg(feature = "very_unstable")] + pub const unsafe fn map_const<'a, F, U>( + &'a self, + f: F, + ) -> VolatilePtr<'a, U, Access> + where + F: FnOnce(NonNull) -> NonNull, + U: ?Sized, + { + unsafe { VolatilePtr::new_generic(f(self.pointer)) } + } + pub unsafe fn map_mut(&mut self, f: F) -> VolatilePtr> where F: FnOnce(NonNull) -> NonNull, @@ -360,6 +378,15 @@ where { unsafe { VolatilePtr::new_generic(f(self.pointer)) } } + + #[cfg(feature = "very_unstable")] + pub const unsafe fn map_mut_const(&mut self, f: F) -> VolatilePtr> + where + F: FnOnce(NonNull) -> NonNull, + U: ?Sized, + { + unsafe { VolatilePtr::new_generic(f(self.pointer)) } + } } /// Methods for volatile slices @@ -417,6 +444,16 @@ impl<'a, T, R, W> VolatilePtr<'a, [T], Access> { unsafe { self.map(|slice| slice.get_unchecked_mut(index)) } } + #[cfg(feature = "very_unstable")] + pub const fn index_const(&self, index: usize) -> VolatilePtr> { + assert!(index < self.pointer.len(), "index out of bounds"); + unsafe { + self.map_const(|slice| { + NonNull::new_unchecked(slice.as_non_null_ptr().as_ptr().add(index)) + }) + } + } + pub fn index_mut( &mut self, index: I, @@ -429,6 +466,16 @@ impl<'a, T, R, W> VolatilePtr<'a, [T], Access> { unsafe { self.map_mut(|slice| slice.get_unchecked_mut(index)) } } + #[cfg(feature = "very_unstable")] + pub const fn index_mut_const(&mut self, index: usize) -> VolatilePtr> { + assert!(index < self.pointer.len(), "index out of bounds"); + unsafe { + self.map_mut_const(|slice| { + NonNull::new_unchecked(slice.as_non_null_ptr().as_ptr().add(index)) + }) + } + } + /// Copies all elements from `self` into `dst`, using a volatile memcpy. /// /// The length of `dst` must be the same as `self`. From 4accd5192b6903b5fa968904fbd7c154c20d17db Mon Sep 17 00:00:00 2001 From: Tom Dohrmann Date: Sat, 4 Jun 2022 17:06:32 +0200 Subject: [PATCH 10/16] add `VolatilePtr::as_slice_mut` --- src/lib.rs | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index 43afb11..053f15c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -863,6 +863,38 @@ impl VolatilePtr<'_, [T; N], Access> { }) } } + + /// Converts an array reference to a shared slice. + /// + /// This makes it possible to use the methods defined on slices. + /// + /// ## Example + /// + /// Copying two elements into a volatile array reference using `copy_from_slice`: + /// + /// ``` + /// # extern crate core; + /// use volatile::VolatilePtr; + /// use core::ptr::NonNull; + /// + /// let src = [1, 2]; + /// let mut dst = [0, 0]; + /// let mut volatile = unsafe { VolatilePtr::new_write_only(NonNull::from(&dst)) }; + /// + /// // convert the `Volatile<[i32; 2]>` array reference to a `Volatile<[i32]>` slice + /// let mut volatile_slice = volatile.as_slice_mut(); + /// // we can now use the slice methods + /// volatile_slice.copy_from_slice(&src); + /// + /// assert_eq!(dst, [1, 2]); + /// ``` + pub fn as_slice_mut<'a>(&'a mut self) -> VolatilePtr<'a, [T], Access> { + unsafe { + self.map_mut(|array| { + NonNull::new(ptr::slice_from_raw_parts_mut(array.as_ptr() as *mut T, N)).unwrap() + }) + } + } } /// Methods for restricting access. From a9e8509b18ea19c4a41d9edd4e3ba800d5e53ed8 Mon Sep 17 00:00:00 2001 From: Tom Dohrmann Date: Sat, 4 Jun 2022 17:06:55 +0200 Subject: [PATCH 11/16] add `VolatilePtr::iter` & `VolatilePtr::iter_mut` --- src/lib.rs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index 053f15c..ec92e8d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -476,6 +476,24 @@ impl<'a, T, R, W> VolatilePtr<'a, [T], Access> { } } + /// Returns an iterator over the slice. + pub fn iter<'b>( + &'b self, + ) -> impl Iterator>> + 'b { + let len = self.len(); + (0..len).map(move |i| self.index(i)) + } + + /// Returns an iterator that allows modifying each value. + pub fn iter_mut<'b>( + &'b mut self, + ) -> impl Iterator>> + 'b { + let ptr = self.as_ptr().as_ptr() as *mut T; + let len = self.len(); + (0..len) + .map(move |i| unsafe { VolatilePtr::new_generic(NonNull::new_unchecked(ptr.add(i))) }) + } + /// Copies all elements from `self` into `dst`, using a volatile memcpy. /// /// The length of `dst` must be the same as `self`. From 167adec176cecc731049b10e1b552d38c9204242 Mon Sep 17 00:00:00 2001 From: Tom Dohrmann Date: Sat, 4 Jun 2022 17:08:49 +0200 Subject: [PATCH 12/16] add `VolatilePtr::is_empty` --- src/lib.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index ec92e8d..fb8ad0b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -396,6 +396,10 @@ impl<'a, T, R, W> VolatilePtr<'a, [T], Access> { self.pointer.len() } + pub fn is_empty(&self) -> bool { + self.pointer.len() == 0 + } + /// Applies the index operation on the wrapped slice. /// /// Returns a shared `Volatile` reference to the resulting subslice. From aa93fb97f8eebd81fc01833c181652c7a6f510df Mon Sep 17 00:00:00 2001 From: Tom Dohrmann Date: Sat, 4 Jun 2022 17:12:31 +0200 Subject: [PATCH 13/16] add missing access where bounds --- src/lib.rs | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index fb8ad0b..bdecf16 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -535,6 +535,7 @@ impl<'a, T, R, W> VolatilePtr<'a, [T], Access> { pub fn copy_into_slice(&self, dst: &mut [T]) where T: Copy, + R: access::Safe, { let len = self.pointer.len(); assert_eq!( @@ -591,6 +592,7 @@ impl<'a, T, R, W> VolatilePtr<'a, [T], Access> { pub fn copy_from_slice(&mut self, src: &[T]) where T: Copy, + W: access::Safe, { let len = self.pointer.len(); assert_eq!( @@ -644,6 +646,8 @@ impl<'a, T, R, W> VolatilePtr<'a, [T], Access> { pub fn copy_within(&mut self, src: impl RangeBounds, dest: usize) where T: Copy, + R: access::Safe, + W: access::Safe, { let len = self.pointer.len(); // implementation taken from https://github.com/rust-lang/rust/blob/683d1bcd405727fcc9209f64845bd3b9104878b8/library/core/src/slice/mod.rs#L2726-L2738 @@ -819,7 +823,7 @@ impl<'a, T, R, W> VolatilePtr<'a, [T], Access> { /// Methods for volatile byte slices #[cfg(feature = "unstable")] -impl VolatilePtr<'_, [u8], A> { +impl VolatilePtr<'_, [u8], Access> { /// Sets all elements of the byte slice to the given `value` using a volatile `memset`. /// /// This method is similar to the `slice::fill` method of the standard library, with the @@ -841,7 +845,10 @@ impl VolatilePtr<'_, [u8], A> { /// buf.fill(1); /// assert_eq!(unsafe { buf.as_ptr().as_mut() }, &mut vec![1; 10]); /// ``` - pub fn fill(&mut self, value: u8) { + pub fn fill(&mut self, value: u8) + where + W: access::Safe, + { unsafe { intrinsics::volatile_set_memory(self.pointer.as_mut_ptr(), value, self.pointer.len()); } From 554c806c50613b0686640f881015e8d46ab11520 Mon Sep 17 00:00:00 2001 From: Tom Dohrmann Date: Sat, 4 Jun 2022 17:49:35 +0200 Subject: [PATCH 14/16] reject mapping to unaligned fields --- src/lib.rs | 39 +++++++++++++++++++++++++++++++++++---- 1 file changed, 35 insertions(+), 4 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index bdecf16..95ccf73 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -57,26 +57,57 @@ pub mod access; /// let field_2 = map_field!(volatile.field_2); /// assert_eq!(field_2.read(), 255); /// ``` +/// +/// Creating `VolatilePtr`s to unaligned field in packed structs is not allowed: +/// ```compile_fail +/// # extern crate core; +/// use volatile::{VolatilePtr, map_field}; +/// use core::ptr::NonNull; +/// +/// #[repr(packed)] +/// struct Example { field_1: u8, field_2: usize, } +/// let mut value = Example { field_1: 15, field_2: 255 }; +/// let mut volatile = unsafe { VolatilePtr::new_read_write(NonNull::from(&mut value)) }; +/// +/// // Constructing a volatile reference to an unaligned field doesn't compile. +/// let field_2 = map_field!(volatile.field_2); +/// ``` #[macro_export] macro_rules! map_field { - ($volatile:ident.$place:ident) => { + ($volatile:ident.$place:ident) => {{ + // Simulate creating a reference to the field. This is done to make + // sure that the field is not potentially unaligned. The body of the + // if statement will never be executed, so it can never cause any UB. + if false { + #[deny(unaligned_references)] + let _ref_to_field = &(unsafe { &*$volatile.as_ptr().as_ptr() }).$place; + } + unsafe { $volatile.map(|ptr| { core::ptr::NonNull::new(core::ptr::addr_of_mut!((*ptr.as_ptr()).$place)).unwrap() }) } - }; + }}; } #[macro_export] macro_rules! map_field_mut { - ($volatile:ident.$place:ident) => { + ($volatile:ident.$place:ident) => {{ + // Simulate creating a reference to the field. This is done to make + // sure that the field is not potentially unaligned. The body of the + // if statement will never be executed, so it can never cause any UB. + if false { + #[deny(unaligned_references)] + let _ref_to_field = &(unsafe { &*$volatile.as_ptr().as_ptr() }).$place; + } + unsafe { $volatile.map_mut(|ptr| { core::ptr::NonNull::new(core::ptr::addr_of_mut!((*ptr.as_ptr()).$place)).unwrap() }) } - }; + }}; } // this must be defined after the `map_field` macros From ff0ee4f9a77bfdb7f76af2b725f52011be422e53 Mon Sep 17 00:00:00 2001 From: Tom Dohrmann Date: Wed, 15 Jun 2022 13:30:49 +0200 Subject: [PATCH 15/16] WIP: owned with borrow --- src/lib.rs | 119 ++++++++++++++++++++++++--------------------------- src/tests.rs | 25 ++++++----- 2 files changed, 70 insertions(+), 74 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 95ccf73..90f285d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -21,7 +21,7 @@ #![warn(missing_docs)] #![deny(unsafe_op_in_unsafe_fn)] -use access::{Access, ReadOnly, ReadWrite, WriteOnly}; +use access::{Access, NoAccess, ReadOnly, ReadWrite, WriteOnly}; use core::{ fmt, marker::PhantomData, @@ -337,10 +337,20 @@ where } /// Transformation methods for accessing struct fields -impl VolatilePtr<'_, T, Access> +impl<'a, T, R, W> VolatilePtr<'a, T, Access> where T: ?Sized, { + // TODO: Add documentation + pub fn borrow(&self) -> VolatilePtr> { + unsafe { VolatilePtr::new_generic(self.pointer) } + } + + // TODO: Add documentation + pub fn borrow_mut(&mut self) -> VolatilePtr> { + unsafe { VolatilePtr::new_generic(self.pointer) } + } + /// Constructs a new `Volatile` reference by mapping the wrapped pointer. /// /// This method is useful for accessing only a part of a volatile value, e.g. a subslice or @@ -382,7 +392,7 @@ where /// value /// })}; /// ``` - pub unsafe fn map<'a, F, U>(&'a self, f: F) -> VolatilePtr<'a, U, Access> + pub unsafe fn map(self, f: F) -> VolatilePtr<'a, U, Access> where F: FnOnce(NonNull) -> NonNull, U: ?Sized, @@ -391,8 +401,8 @@ where } #[cfg(feature = "very_unstable")] - pub const unsafe fn map_const<'a, F, U>( - &'a self, + pub const unsafe fn map_const( + self, f: F, ) -> VolatilePtr<'a, U, Access> where @@ -402,7 +412,7 @@ where unsafe { VolatilePtr::new_generic(f(self.pointer)) } } - pub unsafe fn map_mut(&mut self, f: F) -> VolatilePtr> + pub unsafe fn map_mut(self, f: F) -> VolatilePtr<'a, U, Access> where F: FnOnce(NonNull) -> NonNull, U: ?Sized, @@ -411,7 +421,7 @@ where } #[cfg(feature = "very_unstable")] - pub const unsafe fn map_mut_const(&mut self, f: F) -> VolatilePtr> + pub const unsafe fn map_mut_const(self, f: F) -> VolatilePtr<'a, U, Access> where F: FnOnce(NonNull) -> NonNull, U: ?Sized, @@ -468,9 +478,9 @@ impl<'a, T, R, W> VolatilePtr<'a, [T], Access> { /// assert_eq!(subslice.index(0).read(), 2); /// ``` pub fn index( - &self, + self, index: I, - ) -> VolatilePtr<>::Output, Access> + ) -> VolatilePtr<'a, >::Output, Access> where I: SliceIndex<[T]> + SliceIndex<[()]> + Clone, { @@ -480,7 +490,10 @@ impl<'a, T, R, W> VolatilePtr<'a, [T], Access> { } #[cfg(feature = "very_unstable")] - pub const fn index_const(&self, index: usize) -> VolatilePtr> { + pub const fn index_const( + self, + index: usize, + ) -> VolatilePtr<'a, T, Access> { assert!(index < self.pointer.len(), "index out of bounds"); unsafe { self.map_const(|slice| { @@ -490,9 +503,9 @@ impl<'a, T, R, W> VolatilePtr<'a, [T], Access> { } pub fn index_mut( - &mut self, + self, index: I, - ) -> VolatilePtr<>::Output, Access> + ) -> VolatilePtr<'a, >::Output, Access> where I: SliceIndex<[T]> + SliceIndex<[()]> + Clone, { @@ -502,7 +515,7 @@ impl<'a, T, R, W> VolatilePtr<'a, [T], Access> { } #[cfg(feature = "very_unstable")] - pub const fn index_mut_const(&mut self, index: usize) -> VolatilePtr> { + pub const fn index_mut_const(self, index: usize) -> VolatilePtr<'a, T, Access> { assert!(index < self.pointer.len(), "index out of bounds"); unsafe { self.map_mut_const(|slice| { @@ -512,17 +525,15 @@ impl<'a, T, R, W> VolatilePtr<'a, [T], Access> { } /// Returns an iterator over the slice. - pub fn iter<'b>( - &'b self, - ) -> impl Iterator>> + 'b { + pub fn iter(self) -> impl Iterator>> { + let ptr = self.as_ptr().as_ptr() as *mut T; let len = self.len(); - (0..len).map(move |i| self.index(i)) + (0..len) + .map(move |i| unsafe { VolatilePtr::new_generic(NonNull::new_unchecked(ptr.add(i))) }) } /// Returns an iterator that allows modifying each value. - pub fn iter_mut<'b>( - &'b mut self, - ) -> impl Iterator>> + 'b { + pub fn iter_mut(self) -> impl Iterator>> { let ptr = self.as_ptr().as_ptr() as *mut T; let len = self.len(); (0..len) @@ -700,11 +711,11 @@ impl<'a, T, R, W> VolatilePtr<'a, [T], Access> { } pub fn split_at( - &self, + self, mid: usize, ) -> ( - VolatilePtr<[T], Access>, - VolatilePtr<[T], Access>, + VolatilePtr<'a, [T], Access>, + VolatilePtr<'a, [T], Access>, ) { assert!(mid <= self.pointer.len()); // SAFETY: `[ptr; mid]` and `[mid; len]` are inside `self`, which @@ -713,11 +724,11 @@ impl<'a, T, R, W> VolatilePtr<'a, [T], Access> { } pub fn split_at_mut( - &mut self, + self, mid: usize, ) -> ( - VolatilePtr<[T], Access>, - VolatilePtr<[T], Access>, + VolatilePtr<'a, [T], Access>, + VolatilePtr<'a, [T], Access>, ) { assert!(mid <= self.pointer.len()); // SAFETY: `[ptr; mid]` and `[mid; len]` are inside `self`, which @@ -726,11 +737,11 @@ impl<'a, T, R, W> VolatilePtr<'a, [T], Access> { } unsafe fn split_at_unchecked( - &self, + self, mid: usize, ) -> ( - VolatilePtr<[T], Access>, - VolatilePtr<[T], Access>, + VolatilePtr<'a, [T], Access>, + VolatilePtr<'a, [T], Access>, ) { // SAFETY: Caller has to check that `0 <= mid <= self.len()` unsafe { @@ -742,11 +753,11 @@ impl<'a, T, R, W> VolatilePtr<'a, [T], Access> { } unsafe fn split_at_mut_unchecked( - &mut self, + self, mid: usize, ) -> ( - VolatilePtr<[T], Access>, - VolatilePtr<[T], Access>, + VolatilePtr<'a, [T], Access>, + VolatilePtr<'a, [T], Access>, ) { let len = self.pointer.len(); let ptr = self.pointer.as_mut_ptr(); @@ -768,23 +779,23 @@ impl<'a, T, R, W> VolatilePtr<'a, [T], Access> { } pub fn as_chunks( - &self, + self, ) -> ( - VolatilePtr<[[T; N]], Access>, - VolatilePtr<[T], Access>, + VolatilePtr<'a, [[T; N]], Access>, + VolatilePtr<'a, [T], Access>, ) { assert_ne!(N, 0); let len = self.pointer.len() / N; let (multiple_of_n, remainder) = self.split_at(len * N); // SAFETY: We already panicked for zero, and ensured by construction // that the length of the subslice is a multiple of N. - let array_slice = unsafe { multiple_of_n.as_chunks_unchecked_by_val() }; + let array_slice = unsafe { multiple_of_n.as_chunks_unchecked() }; (array_slice, remainder) } pub unsafe fn as_chunks_unchecked( - &self, - ) -> VolatilePtr<[[T; N]], Access> { + self, + ) -> VolatilePtr<'a, [[T; N]], Access> { debug_assert_ne!(N, 0); debug_assert_eq!(self.pointer.len() % N, 0); let new_len = @@ -801,39 +812,21 @@ impl<'a, T, R, W> VolatilePtr<'a, [T], Access> { } pub fn as_chunks_mut( - &mut self, + self, ) -> ( - VolatilePtr<[[T; N]], Access>, - VolatilePtr<[T], Access>, + VolatilePtr<'a, [[T; N]], Access>, + VolatilePtr<'a, [T], Access>, ) { assert_ne!(N, 0); let len = self.pointer.len() / N; let (multiple_of_n, remainder) = self.split_at_mut(len * N); // SAFETY: We already panicked for zero, and ensured by construction // that the length of the subslice is a multiple of N. - let array_slice = unsafe { multiple_of_n.as_chunks_unchecked_by_val() }; + let array_slice = unsafe { multiple_of_n.as_chunks_unchecked_mut() }; (array_slice, remainder) } pub unsafe fn as_chunks_unchecked_mut( - &mut self, - ) -> VolatilePtr<[[T; N]], Access> { - debug_assert_ne!(N, 0); - debug_assert_eq!(self.pointer.len() % N, 0); - let new_len = - // SAFETY: Our precondition is exactly what's needed to call this - unsafe { core::intrinsics::exact_div(self.pointer.len(), N) }; - // SAFETY: We cast a slice of `new_len * N` elements into - // a slice of `new_len` many `N` elements chunks. - let pointer = NonNull::new(ptr::slice_from_raw_parts_mut( - self.pointer.as_mut_ptr().cast(), - new_len, - )) - .unwrap(); - unsafe { VolatilePtr::new_generic(pointer) } - } - - pub unsafe fn as_chunks_unchecked_by_val( self, ) -> VolatilePtr<'a, [[T; N]], Access> { debug_assert_ne!(N, 0); @@ -891,7 +884,7 @@ impl VolatilePtr<'_, [u8], Access> { /// These methods are only available with the `unstable` feature enabled (requires a nightly /// Rust compiler). #[cfg(feature = "unstable")] -impl VolatilePtr<'_, [T; N], Access> { +impl<'a, T, R, W, const N: usize> VolatilePtr<'a, [T; N], Access> { /// Converts an array reference to a shared slice. /// /// This makes it possible to use the methods defined on slices. @@ -916,7 +909,7 @@ impl VolatilePtr<'_, [T; N], Access> { /// /// assert_eq!(dst, [1, 2]); /// ``` - pub fn as_slice(&self) -> VolatilePtr<[T], Access> { + pub fn as_slice(self) -> VolatilePtr<'a, [T], Access> { unsafe { self.map(|array| { NonNull::new(ptr::slice_from_raw_parts_mut(array.as_ptr() as *mut T, N)).unwrap() @@ -948,7 +941,7 @@ impl VolatilePtr<'_, [T; N], Access> { /// /// assert_eq!(dst, [1, 2]); /// ``` - pub fn as_slice_mut<'a>(&'a mut self) -> VolatilePtr<'a, [T], Access> { + pub fn as_slice_mut(self) -> VolatilePtr<'a, [T], Access> { unsafe { self.map_mut(|array| { NonNull::new(ptr::slice_from_raw_parts_mut(array.as_ptr() as *mut T, N)).unwrap() diff --git a/src/tests.rs b/src/tests.rs index 840c7ff..bc22063 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -138,7 +138,9 @@ fn test_struct() { }; let mut volatile = unsafe { VolatilePtr::new_read_write(NonNull::from(&mut val)) }; unsafe { - volatile.map_mut(|s| NonNull::new(core::ptr::addr_of_mut!((*s.as_ptr()).field_1)).unwrap()) + volatile + .borrow_mut() + .map_mut(|s| NonNull::new(core::ptr::addr_of_mut!((*s.as_ptr()).field_1)).unwrap()) } .update(|v| *v += 1); let mut field_2 = unsafe { @@ -168,7 +170,8 @@ fn test_struct_macro() { field_2: true, }; let mut volatile = unsafe { VolatilePtr::new_read_write(NonNull::from(&mut val)) }; - let mut field_1 = map_field_mut!(volatile.field_1); + let volatile_borrowed = volatile.borrow_mut(); + let mut field_1 = map_field_mut!(volatile_borrowed.field_1); field_1.update(|v| *v += 1); let mut field_2 = map_field_mut!(volatile.field_2); assert!(field_2.read()); @@ -187,7 +190,7 @@ fn test_struct_macro() { fn test_slice() { let val: &mut [u32] = &mut [1, 2, 3]; let mut volatile = unsafe { VolatilePtr::new_read_write(NonNull::from(val)) }; - volatile.index_mut(0).update(|v| *v += 1); + volatile.borrow_mut().index_mut(0).update(|v| *v += 1); let mut dst = [0; 3]; volatile.copy_into_slice(&mut dst); @@ -199,7 +202,7 @@ fn test_slice() { #[should_panic] fn test_bounds_check_1() { let val: &mut [u32] = &mut [1, 2, 3]; - let mut volatile = unsafe { VolatilePtr::new_read_write(NonNull::from(val)) }; + let volatile = unsafe { VolatilePtr::new_read_write(NonNull::from(val)) }; volatile.index_mut(3); } @@ -208,7 +211,7 @@ fn test_bounds_check_1() { #[should_panic] fn test_bounds_check_2() { let val: &mut [u32] = &mut [1, 2, 3]; - let mut volatile = unsafe { VolatilePtr::new_read_write(NonNull::from(val)) }; + let volatile = unsafe { VolatilePtr::new_read_write(NonNull::from(val)) }; volatile.index_mut(2..1); } @@ -217,7 +220,7 @@ fn test_bounds_check_2() { #[should_panic] fn test_bounds_check_3() { let val: &mut [u32] = &mut [1, 2, 3]; - let mut volatile = unsafe { VolatilePtr::new_read_write(NonNull::from(val)) }; + let volatile = unsafe { VolatilePtr::new_read_write(NonNull::from(val)) }; volatile.index_mut(4..); // `3..` is is still ok (see next test) } @@ -225,7 +228,7 @@ fn test_bounds_check_3() { #[test] fn test_bounds_check_4() { let val: &mut [u32] = &mut [1, 2, 3]; - let mut volatile = unsafe { VolatilePtr::new_read_write(NonNull::from(val)) }; + let volatile = unsafe { VolatilePtr::new_read_write(NonNull::from(val)) }; assert_eq!(volatile.index_mut(3..).len(), 0); } @@ -234,7 +237,7 @@ fn test_bounds_check_4() { #[should_panic] fn test_bounds_check_5() { let val: &mut [u32] = &mut [1, 2, 3]; - let mut volatile = unsafe { VolatilePtr::new_read_write(NonNull::from(val)) }; + let volatile = unsafe { VolatilePtr::new_read_write(NonNull::from(val)) }; volatile.index_mut(..4); } @@ -242,10 +245,10 @@ fn test_bounds_check_5() { #[test] fn test_chunks() { let val: &mut [u32] = &mut [1, 2, 3, 4, 5, 6]; - let mut volatile = unsafe { VolatilePtr::new_read_write(NonNull::from(val)) }; + let volatile = unsafe { VolatilePtr::new_read_write(NonNull::from(val)) }; let mut chunks = volatile.as_chunks_mut().0; - chunks.index_mut(1).write([10, 11, 12]); - assert_eq!(chunks.index(0).read(), [1, 2, 3]); + chunks.borrow_mut().index_mut(1).write([10, 11, 12]); + assert_eq!(chunks.borrow().index(0).read(), [1, 2, 3]); assert_eq!(chunks.index(1).read(), [10, 11, 12]); } From cd57be929e4bdc722720fd71b91854ba487d3b60 Mon Sep 17 00:00:00 2001 From: Tom Dohrmann Date: Sun, 3 Jul 2022 20:32:59 +0200 Subject: [PATCH 16/16] fix UAF in doctest --- src/lib.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index 90f285d..8f0c749 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -865,7 +865,8 @@ impl VolatilePtr<'_, [u8], Access> { /// use volatile::VolatilePtr; /// use core::ptr::NonNull; /// - /// let mut buf = unsafe { VolatilePtr::new_read_write(NonNull::from(vec![0; 10].as_mut_slice())) }; + /// let mut vec = vec![0; 10]; + /// let mut buf = unsafe { VolatilePtr::new_read_write(NonNull::from(vec.as_mut_slice())) }; /// buf.fill(1); /// assert_eq!(unsafe { buf.as_ptr().as_mut() }, &mut vec![1; 10]); /// ```