volatile/volatile_ptr/macros.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49
/// Provides safe field projection for volatile pointers referencing structs.
///
/// ## Examples
///
/// Accessing 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 reference to a field
/// 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
/// 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((&mut value).into()) };
///
/// // 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) => {{
// 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 {
let _ref_to_field = &(unsafe { &*$volatile.as_raw_ptr().as_ptr() }).$place;
}
unsafe {
$volatile.map(|ptr| {
core::ptr::NonNull::new(core::ptr::addr_of_mut!((*ptr.as_ptr()).$place)).unwrap()
})
}
}};
}