use core::{
marker::PhantomData,
ptr::{self, NonNull},
};
use crate::{
access::{Access, ReadOnly, ReadWrite, Readable, RestrictAccess, Writable, WriteOnly},
VolatilePtr,
};
impl<'a, T> VolatilePtr<'a, T>
where
T: ?Sized,
{
pub unsafe fn new(pointer: NonNull<T>) -> VolatilePtr<'a, T, ReadWrite> {
unsafe { VolatilePtr::new_restricted(ReadWrite, pointer) }
}
pub const unsafe fn new_read_only(pointer: NonNull<T>) -> VolatilePtr<'a, T, ReadOnly> {
unsafe { Self::new_restricted(ReadOnly, pointer) }
}
pub const unsafe fn new_restricted<A>(access: A, pointer: NonNull<T>) -> VolatilePtr<'a, T, A>
where
A: Access,
{
let _ = access;
unsafe { Self::new_generic(pointer) }
}
pub(super) const unsafe fn new_generic<A>(pointer: NonNull<T>) -> VolatilePtr<'a, T, A> {
VolatilePtr {
pointer,
reference: PhantomData,
access: PhantomData,
}
}
}
impl<'a, T, A> VolatilePtr<'a, T, A>
where
T: ?Sized,
{
#[must_use]
pub fn read(self) -> T
where
T: Copy,
A: Readable,
{
unsafe { ptr::read_volatile(self.pointer.as_ptr()) }
}
pub fn write(self, value: T)
where
T: Copy,
A: Writable,
{
unsafe { ptr::write_volatile(self.pointer.as_ptr(), value) };
}
pub fn update<F>(self, f: F)
where
T: Copy,
A: Readable + Writable,
F: FnOnce(T) -> T,
{
let new = f(self.read());
self.write(new);
}
#[must_use]
pub fn as_raw_ptr(self) -> NonNull<T> {
self.pointer
}
pub unsafe fn map<F, U>(self, f: F) -> VolatilePtr<'a, U, A>
where
F: FnOnce(NonNull<T>) -> NonNull<U>,
A: Access,
U: ?Sized,
{
unsafe { VolatilePtr::new_restricted(A::default(), f(self.pointer)) }
}
}
impl<'a, T, A> VolatilePtr<'a, T, A>
where
T: ?Sized,
{
pub fn restrict<To>(self) -> VolatilePtr<'a, T, A::Restricted>
where
A: RestrictAccess<To>,
{
unsafe { VolatilePtr::new_restricted(Default::default(), self.pointer) }
}
}
impl<'a, T> VolatilePtr<'a, T, ReadWrite>
where
T: ?Sized,
{
pub fn read_only(self) -> VolatilePtr<'a, T, ReadOnly> {
self.restrict()
}
pub fn write_only(self) -> VolatilePtr<'a, T, WriteOnly> {
self.restrict()
}
}