use core::cell::UnsafeCell;
#[repr(transparent)]
pub struct Rw<T: Copy>(UnsafeCell<T>);
impl<T: Copy> Rw<T> {
#[inline(always)]
pub fn read(&self) -> T {
unsafe { core::ptr::read_volatile(self.0.get()) }
}
#[inline(always)]
pub fn write(&self, value: T) {
unsafe { core::ptr::write_volatile(self.0.get(), value) }
}
#[inline(always)]
pub fn modify<F: FnOnce(T) -> T>(&self, f: F) {
let v = self.read();
self.write(f(v));
}
}
#[repr(transparent)]
pub struct Ro<T: Copy>(UnsafeCell<T>);
impl<T: Copy> Ro<T> {
#[inline(always)]
pub fn read(&self) -> T {
unsafe { core::ptr::read_volatile(self.0.get()) }
}
}
#[repr(transparent)]
pub struct Wo<T: Copy>(UnsafeCell<T>);
impl<T: Copy> Wo<T> {
#[inline(always)]
pub fn write(&self, value: T) {
unsafe { core::ptr::write_volatile(self.0.get(), value) }
}
}
unsafe impl<T: Copy + Sync> Sync for Rw<T> {}
unsafe impl<T: Copy + Sync> Sync for Ro<T> {}
unsafe impl<T: Copy + Sync> Sync for Wo<T> {}
#[cfg(test)]
mod tests {
use super::*;
use core::mem::size_of;
#[test]
fn wrappers_are_zero_overhead() {
assert_eq!(size_of::<Rw<u32>>(), size_of::<u32>());
assert_eq!(size_of::<Ro<u32>>(), size_of::<u32>());
assert_eq!(size_of::<Wo<u32>>(), size_of::<u32>());
}
#[test]
fn rw_round_trip_through_unsafe_cell() {
let cell = Rw(UnsafeCell::new(0u32));
cell.write(0xDEAD_BEEF);
assert_eq!(cell.read(), 0xDEAD_BEEF);
cell.modify(|v| v ^ 0xFFFF_FFFF);
assert_eq!(cell.read(), 0x2152_4110);
}
}