Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: introduce RestrictAccess<To> and generalize restrict to all access types #60

Merged
merged 11 commits into from
Apr 26, 2024
Merged
89 changes: 56 additions & 33 deletions src/access.rs
Original file line number Diff line number Diff line change
@@ -1,61 +1,84 @@
//! Marker types for limiting access.

/// Sealed trait that is implemented for the types in this module.
pub trait Access: Copy + Default + private::Sealed {
/// Reduced access level to safely share the corresponding value.
type RestrictShared: Access;
/// A trait for restricting one [`Access`] type to another [`Access`] type.
///
/// Restricting `Self` to `To` results in [`Self::Restricted`].
///
/// Restriction is a symmetric operation which is denoted by ∩, as it is the intersection of permissions.
/// The following table holds:
///
/// | `Self` | `To` | `Self` ∩ `To` |
/// | ------------- | ------------- | ------------- |
/// | `T` | `T` | `T` |
/// | [`ReadWrite`] | `T` | `T` |
/// | [`NoAccess`] | `T` | [`NoAccess`] |
/// | [`ReadOnly`] | [`WriteOnly`] | [`NoAccess`] |
pub trait RestrictAccess<To>: Access {
/// The resulting [`Access`] type of `Self` restricted to `To`.
type Restricted: Access;
}

/// Helper trait that is implemented by [`ReadWrite`] and [`ReadOnly`].
pub trait Readable: Copy + Default + private::Sealed {
/// Reduced access level to safely share the corresponding value.
type RestrictShared: Readable + Access;
impl<To: Access> RestrictAccess<To> for ReadWrite {
type Restricted = To;
}

impl<To> RestrictAccess<To> for NoAccess {
type Restricted = Self;
}

// Sadly, we cannot provide more generic implementations, since they would overlap.
macro_rules! restrict_impl {
($SelfT:ty, $To:ty, $Restricted:ty) => {
impl RestrictAccess<$To> for $SelfT {
type Restricted = $Restricted;
}
};
}

restrict_impl!(ReadOnly, ReadWrite, ReadOnly);
restrict_impl!(ReadOnly, ReadOnly, ReadOnly);
restrict_impl!(ReadOnly, WriteOnly, NoAccess);
restrict_impl!(ReadOnly, NoAccess, NoAccess);

restrict_impl!(WriteOnly, ReadWrite, WriteOnly);
restrict_impl!(WriteOnly, ReadOnly, NoAccess);
restrict_impl!(WriteOnly, WriteOnly, WriteOnly);
restrict_impl!(WriteOnly, NoAccess, NoAccess);

/// Sealed trait that is implemented for the types in this module.
pub trait Access: Copy + Default + private::Sealed {}

/// Helper trait that is implemented by [`ReadWrite`] and [`ReadOnly`].
pub trait Readable: Access {}
impl<A: RestrictAccess<ReadOnly, Restricted = ReadOnly>> Readable for A {}

/// Helper trait that is implemented by [`ReadWrite`] and [`WriteOnly`].
pub trait Writable: Access + private::Sealed {}
pub trait Writable: Access {}
impl<A: RestrictAccess<WriteOnly, Restricted = WriteOnly>> Writable for A {}

/// Implemented for access types that permit copying of `VolatileRef`.
pub trait Copyable: private::Sealed {}

impl<T> Access for T
where
T: Readable + Default + Copy,
{
type RestrictShared = <T as Readable>::RestrictShared;
}
pub trait Copyable: Access {}
impl<A: RestrictAccess<ReadOnly, Restricted = Self>> Copyable for A {}

/// Zero-sized marker type for allowing both read and write access.
#[derive(Debug, Default, Copy, Clone)]
pub struct ReadWrite;
impl Readable for ReadWrite {
type RestrictShared = ReadOnly;
}
impl Writable for ReadWrite {}
impl Access for ReadWrite {}

/// Zero-sized marker type for allowing only read access.
#[derive(Debug, Default, Copy, Clone)]
pub struct ReadOnly;
impl Readable for ReadOnly {
type RestrictShared = ReadOnly;
}
impl Copyable for ReadOnly {}
impl Access for ReadOnly {}

/// Zero-sized marker type for allowing only write access.
#[derive(Debug, Default, Copy, Clone)]
pub struct WriteOnly;
impl Access for WriteOnly {
type RestrictShared = NoAccess;
}
impl Writable for WriteOnly {}
impl Access for WriteOnly {}

/// Zero-sized marker type that grants no access.
#[derive(Debug, Default, Copy, Clone)]
pub struct NoAccess;
impl Access for NoAccess {
type RestrictShared = NoAccess;
}
impl Copyable for NoAccess {}
impl Access for NoAccess {}

mod private {
pub trait Sealed {}
Expand Down
20 changes: 15 additions & 5 deletions src/volatile_ptr/operations.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use core::{
};

use crate::{
access::{Access, ReadOnly, ReadWrite, Readable, Writable, WriteOnly},
access::{Access, ReadOnly, ReadWrite, Readable, RestrictAccess, Writable, WriteOnly},
VolatilePtr,
};

Expand Down Expand Up @@ -211,7 +211,7 @@ where
}

/// Methods for restricting access.
impl<'a, T> VolatilePtr<'a, T, ReadWrite>
impl<'a, T, A> VolatilePtr<'a, T, A>
where
T: ?Sized,
{
Expand All @@ -220,7 +220,7 @@ where
/// ## Example
///
/// ```
/// use volatile::access::ReadOnly;
/// use volatile::access::{ReadOnly, WriteOnly};
/// use volatile::VolatilePtr;
///
/// let mut value: i16 = -4;
Expand All @@ -229,14 +229,24 @@ where
/// let read_only = volatile.restrict::<ReadOnly>();
/// assert_eq!(read_only.read(), -4);
/// // read_only.write(10); // compile-time error
///
/// let no_access = read_only.restrict::<WriteOnly>();
/// // no_access.read(); // compile-time error
/// // no_access.write(10); // compile-time error
/// ```
pub fn restrict<A>(self) -> VolatilePtr<'a, T, A>
pub fn restrict<To>(self) -> VolatilePtr<'a, T, A::Restricted>
where
A: Access,
A: RestrictAccess<To>,
{
unsafe { VolatilePtr::new_restricted(Default::default(), self.pointer) }
}
}

/// Methods for restricting access.
impl<'a, T> VolatilePtr<'a, T, ReadWrite>
where
T: ?Sized,
{
/// Restricts access permissions to read-only.
///
/// ## Example
Expand Down
28 changes: 19 additions & 9 deletions src/volatile_ref.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use crate::{
access::{Access, Copyable, ReadOnly, ReadWrite, WriteOnly},
access::{Access, Copyable, ReadOnly, ReadWrite, RestrictAccess, WriteOnly},
volatile_ptr::VolatilePtr,
};
use core::{cmp::Ordering, fmt, hash, marker::PhantomData, ptr::NonNull};
Expand Down Expand Up @@ -139,9 +139,9 @@ where
/// This method creates a `VolatileRef` tied to the lifetime of the `&VolatileRef` it is created from.
/// This is useful for providing a volatile reference without moving the original `VolatileRef`.
/// In comparison with creating a `&VolatileRef<'a, T>`, this avoids the additional indirection and lifetime.
pub fn borrow(&self) -> VolatileRef<'_, T, A::RestrictShared>
pub fn borrow(&self) -> VolatileRef<'_, T, A::Restricted>
where
A: Access,
A: RestrictAccess<ReadOnly>,
{
unsafe { VolatileRef::new_restricted(Default::default(), self.pointer) }
}
Expand All @@ -161,9 +161,9 @@ where
/// Borrows this `VolatileRef` as a read-only [`VolatilePtr`].
///
/// Use this method to do (partial) volatile reads of the referenced data.
pub fn as_ptr(&self) -> VolatilePtr<'_, T, A::RestrictShared>
pub fn as_ptr(&self) -> VolatilePtr<'_, T, A::Restricted>
where
A: Access,
A: RestrictAccess<ReadOnly>,
{
unsafe { VolatilePtr::new_restricted(Default::default(), self.pointer) }
}
Expand Down Expand Up @@ -194,7 +194,7 @@ where
}

/// Methods for restricting access.
impl<'a, T> VolatileRef<'a, T, ReadWrite>
impl<'a, T, A> VolatileRef<'a, T, A>
where
T: ?Sized,
{
Expand All @@ -203,7 +203,7 @@ where
/// ## Example
///
/// ```
/// use volatile::access::ReadOnly;
/// use volatile::access::{ReadOnly, WriteOnly};
/// use volatile::VolatileRef;
///
/// let mut value: i16 = -4;
Expand All @@ -212,14 +212,24 @@ where
/// let read_only = volatile.restrict::<ReadOnly>();
/// assert_eq!(read_only.as_ptr().read(), -4);
/// // read_only.as_ptr().write(10); // compile-time error
///
/// let no_access = read_only.restrict::<WriteOnly>();
/// // no_access.read(); // compile-time error
/// // no_access.write(10); // compile-time error
/// ```
pub fn restrict<A>(self) -> VolatileRef<'a, T, A>
pub fn restrict<To>(self) -> VolatileRef<'a, T, A::Restricted>
where
A: Access,
A: RestrictAccess<To>,
{
unsafe { VolatileRef::new_restricted(Default::default(), self.pointer) }
}
}

/// Methods for restricting access.
impl<'a, T> VolatileRef<'a, T, ReadWrite>
where
T: ?Sized,
{
/// Restricts access permissions to read-only.
///
/// ## Example
Expand Down
Loading