Struct volatile::VolatilePtr

source ·
#[repr(transparent)]
pub struct VolatilePtr<'a, T, A = ReadWrite>where T: ?Sized,{ /* private fields */ }
Expand description

Wraps a pointer to make accesses to the referenced value volatile.

Allows volatile reads and writes on the referenced value. The referenced value needs to be Copy for reading and writing, as volatile reads and writes take and return copies of the value.

Since not all volatile resources (e.g. memory mapped device registers) are both readable and writable, this type supports limiting the allowed access types through an optional second generic parameter A that can be one of ReadWrite, ReadOnly, or WriteOnly. It defaults to ReadWrite, which allows all operations.

The size of this struct is the same as the size of the contained reference.

Implementations§

source§

impl<'a, T> VolatilePtr<'a, T>where T: ?Sized,

Constructor functions.

These functions construct new VolatilePtr values. While the new function creates a VolatilePtr instance with unrestricted access, there are also functions for creating read-only or write-only instances.

source

pub unsafe fn new(pointer: NonNull<T>) -> VolatilePtr<'a, T, ReadWrite>

Turns the given pointer into a VolatilePtr.

Safety
  • The given pointer must be valid.
  • No other thread must have access to the given pointer. This must remain true for the whole lifetime of the VolatilePtr.
source

pub const unsafe fn new_read_only( pointer: NonNull<T> ) -> VolatilePtr<'a, T, ReadOnly>

Creates a new read-only volatile pointer from the given raw pointer.

Safety

The requirements for Self::new apply to this function too.

source

pub const unsafe fn new_restricted<A>( access: A, pointer: NonNull<T> ) -> VolatilePtr<'a, T, A>where A: Access,

Creates a new volatile pointer with restricted access from the given raw pointer.

Safety

The requirements for Self::new apply to this function too.

source§

impl<'a, T, A> VolatilePtr<'a, T, A>where T: ?Sized,

source

pub fn read(self) -> Twhere T: Copy, A: Readable,

Performs a volatile read of the contained value.

Returns a copy of the read value. Volatile reads are guaranteed not to be optimized away by the compiler, but by themselves do not have atomic ordering guarantees. To also get atomicity, consider looking at the Atomic wrapper types of the standard/core library.

Examples
use volatile::{VolatilePtr, access};
use core::ptr::NonNull;

let value = 42;
let pointer = unsafe {
    VolatilePtr::new_restricted(access::ReadOnly, NonNull::from(&value))
};
assert_eq!(pointer.read(), 42);
source

pub fn write(self, value: T)where T: Copy, A: Writable,

Performs a volatile write, setting the contained value to the given value.

Volatile writes are guaranteed to not be optimized away by the compiler, but by themselves do not have atomic ordering guarantees. To also get atomicity, consider looking at the Atomic wrapper types of the standard/core library.

Example
use volatile::VolatilePtr;
use core::ptr::NonNull;

let mut value = 42;
let mut volatile = unsafe { VolatilePtr::new((&mut value).into()) };
volatile.write(50);

assert_eq!(volatile.read(), 50);
source

pub fn update<F>(self, f: F)where T: Copy, A: Readable + Writable, F: FnOnce(T) -> T,

Updates the contained value using the given closure and volatile instructions.

Performs a volatile read of the contained value, passes it to the function f, and then performs a volatile write of the returned value back to the target.

use volatile::VolatilePtr;
use core::ptr::NonNull;

let mut value = 42;
let mut volatile = unsafe { VolatilePtr::new((&mut value).into()) };
volatile.update(|val| val + 1);

assert_eq!(volatile.read(), 43);
source

pub fn as_raw_ptr(self) -> NonNull<T>

Extracts the wrapped raw pointer.

Example
use volatile::VolatilePtr;
use core::ptr::NonNull;

let mut value = 42;
let mut volatile = unsafe { VolatilePtr::new((&mut value).into()) };
volatile.write(50);
let unwrapped: *mut i32 = volatile.as_raw_ptr().as_ptr();

assert_eq!(unsafe { *unwrapped }, 50); // non volatile access, be careful!
source

pub unsafe fn map<F, U>(self, f: F) -> VolatilePtr<'a, U, A>where F: FnOnce(NonNull<T>) -> NonNull<U>, A: Access, U: ?Sized,

Constructs a new VolatilePtr by mapping the wrapped pointer.

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

Accessing a struct field:

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 { VolatilePtr::new((&mut value).into()) };

// construct a volatile pointer to a field
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:

use volatile::VolatilePtr;
use core::ptr::NonNull;

let mut value = 5;
let mut volatile = unsafe { VolatilePtr::new((&mut value).into()) };

// DON'T DO THIS:
let mut readout = 0;
unsafe { volatile.map(|value| {
   readout = *value.as_ptr(); // non-volatile read, might lead to bugs
   value
})};
Safety

The pointer returned by f must satisfy the requirements of Self::new.

source§

impl<'a, T> VolatilePtr<'a, T, ReadWrite>where T: ?Sized,

Methods for restricting access.

source

pub fn read_only(self) -> VolatilePtr<'a, T, ReadOnly>

Restricts access permissions to read-only.

Example
use volatile::VolatilePtr;
use core::ptr::NonNull;

let mut value: i16 = -4;
let mut volatile = unsafe { VolatilePtr::new((&mut value).into()) };

let read_only = volatile.read_only();
assert_eq!(read_only.read(), -4);
// read_only.write(10); // compile-time error
source

pub fn write_only(self) -> VolatilePtr<'a, T, WriteOnly>

Restricts access permissions to write-only.

Example

Creating a write-only pointer to a struct 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 { VolatilePtr::new((&mut value).into()) };

// construct a volatile write-only pointer to `field_2`
let mut field_2 = map_field!(volatile.field_2).write_only();
field_2.write(14);
// field_2.read(); // compile-time error
source§

impl<'a, T, A> VolatilePtr<'a, [T], A>

source

pub fn len(self) -> usize

Returns the length of the slice.

source

pub fn is_empty(self) -> bool

Returns whether the slice is empty.

source

pub fn index<I>( self, index: I ) -> VolatilePtr<'a, <I as SliceIndex<[T]>>::Output, A>where I: SliceIndex<[T]> + SliceIndex<[()]> + Clone, A: Access,

Applies the index operation on the wrapped slice.

Returns a shared Volatile reference to the resulting subslice.

This is a convenience method for the map(|slice| slice.index(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::VolatilePtr;
use core::ptr::NonNull;

let array = [1, 2, 3];
let slice = &array[..];
let volatile = unsafe { VolatilePtr::new_read_only(NonNull::from(slice)) };
assert_eq!(volatile.index(1).read(), 2);

Accessing a subslice:

use volatile::VolatilePtr;
use core::ptr::NonNull;

let array = [1, 2, 3];
let slice = &array[..];
let volatile = unsafe { VolatilePtr::new_read_only(NonNull::from(slice)) };
let subslice = volatile.index(1..);
assert_eq!(subslice.index(0).read(), 2);
source

pub fn iter(self) -> impl Iterator<Item = VolatilePtr<'a, T, A>>where A: Access,

Returns an iterator over the slice.

source

pub fn copy_into_slice(self, dst: &mut [T])where T: Copy, A: Readable,

Copies all elements from self into dst, using a volatile memcpy.

The length of dst must be the same as self.

The method is only available with the unstable feature enabled (requires a nightly Rust compiler).

Panics

This function will panic if the two slices have different lengths.

Examples

Copying two elements from a volatile slice:

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 { VolatilePtr::new_read_only(NonNull::from(slice)) };
let mut dst = [5, 0, 0];

// Because the slices have to be the same length,
// we slice the destination slice from three elements
// to two. It will panic if we don't do this.
volatile.copy_into_slice(&mut dst[1..]);

assert_eq!(src, [1, 2]);
assert_eq!(dst, [5, 1, 2]);
source

pub fn copy_from_slice(self, src: &[T])where T: Copy, A: Writable,

Copies all elements from src into self, using a volatile memcpy.

The length of src must be the same as self.

This method is similar to the slice::copy_from_slice method of the standard library. The difference is that this method performs a volatile copy.

The method is only available with the unstable feature enabled (requires a nightly Rust compiler).

Panics

This function will panic if the two slices have different lengths.

Examples

Copying two elements from a slice into a volatile slice:

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 { VolatilePtr::new(NonNull::from(slice)) };
// Because the slices have to be the same length,
// we slice the source slice from four elements
// to two. It will panic if we don't do this.
volatile.copy_from_slice(&src[2..]);

assert_eq!(src, [1, 2, 3, 4]);
assert_eq!(dst, [3, 4]);
source

pub fn copy_within(self, src: impl RangeBounds<usize>, dest: usize)where T: Copy, A: Readable + Writable,

Copies elements from one part of the slice to another part of itself, using a volatile memmove.

src is the range within self to copy from. dest is the starting index of the range within self to copy to, which will have the same length as src. The two ranges may overlap. The ends of the two ranges must be less than or equal to self.len().

This method is similar to the slice::copy_within method of the standard library. The difference is that this method performs a volatile copy.

This method is only available with the unstable feature enabled (requires a nightly Rust compiler).

Panics

This function will panic if either range exceeds the end of the slice, or if the end of src is before the start.

Examples

Copying four bytes within a slice:

extern crate core;
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 { VolatilePtr::new(NonNull::from(slice)) };
volatile.copy_within(1..5, 8);

assert_eq!(&byte_array, b"Hello, Wello!");
source

pub fn split_at( self, mid: usize ) -> (VolatilePtr<'a, [T], A>, VolatilePtr<'a, [T], A>)where A: Access,

Divides one slice into two at an index.

The first will contain all indices from [0, mid) (excluding the index mid itself) and the second will contain all indices from [mid, len) (excluding the index len itself).

Panics

Panics if mid > len.

source

pub fn as_chunks<const N: usize>( self ) -> (VolatilePtr<'a, [[T; N]], A>, VolatilePtr<'a, [T], A>)where A: Access,

Splits the slice into a slice of N-element arrays, starting at the beginning of the slice, and a remainder slice with length strictly less than N.

Panics

Panics if N is 0.

source

pub unsafe fn as_chunks_unchecked<const N: usize>( self ) -> VolatilePtr<'a, [[T; N]], A>where A: Access,

Splits the slice into a slice of N-element arrays, assuming that there’s no remainder.

Safety

This may only be called when

  • The slice splits exactly into N-element chunks (aka self.len() % N == 0).
  • N != 0.
source§

impl<A> VolatilePtr<'_, [u8], A>

Methods for volatile byte slices

source

pub fn fill(self, value: u8)where A: Writable,

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 difference that this method performs a volatile write operation. Another difference is that this method is only available for byte slices (not general &mut [T] slices) because there currently isn’t a instrinsic function that allows non-u8 values.

This method is only available with the unstable feature enabled (requires a nightly Rust compiler).

Example
use volatile::VolatilePtr;
use core::ptr::NonNull;

let mut vec = vec![0; 10];
let mut buf = unsafe { VolatilePtr::new(NonNull::from(vec.as_mut_slice())) };
buf.fill(1);
assert_eq!(unsafe { buf.as_raw_ptr().as_mut() }, &mut vec![1; 10]);
source§

impl<'a, T, A, const N: usize> VolatilePtr<'a, [T; N], A>

Methods for converting arrays to slices

These methods are only available with the unstable feature enabled (requires a nightly Rust compiler).

source

pub fn as_slice(self) -> VolatilePtr<'a, [T], A>where A: Access,

Converts an array pointer to a slice pointer.

This makes it possible to use the methods defined on slices.

Example

Copying two elements from a volatile array reference using copy_into_slice:

use volatile::VolatilePtr;
use core::ptr::NonNull;

let src = [1, 2];
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
let volatile_slice = volatile.as_slice();
// we can now use the slice methods
volatile_slice.copy_into_slice(&mut dst);

assert_eq!(dst, [1, 2]);

Trait Implementations§

source§

impl<T, A> Clone for VolatilePtr<'_, T, A>where T: ?Sized,

source§

fn clone(&self) -> Self

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl<T, A> Debug for VolatilePtr<'_, T, A>where T: Copy + Debug + ?Sized,

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl<'a, T, A> Copy for VolatilePtr<'a, T, A>where T: ?Sized,

Auto Trait Implementations§

§

impl<'a, T: ?Sized, A> RefUnwindSafe for VolatilePtr<'a, T, A>where A: RefUnwindSafe, T: RefUnwindSafe,

§

impl<'a, T, A = ReadWrite> !Send for VolatilePtr<'a, T, A>

§

impl<'a, T, A = ReadWrite> !Sync for VolatilePtr<'a, T, A>

§

impl<'a, T: ?Sized, A> Unpin for VolatilePtr<'a, T, A>where A: Unpin,

§

impl<'a, T: ?Sized, A> UnwindSafe for VolatilePtr<'a, T, A>where A: UnwindSafe, T: RefUnwindSafe,

Blanket Implementations§

source§

impl<T> Any for Twhere T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for Twhere T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for Twhere T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

source§

impl<T, U> Into<U> for Twhere U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

source§

impl<T, U> TryFrom<U> for Twhere U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for Twhere U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.