Skip to content

Commit

Permalink
Merge pull request #60 from mkroening/general-restrict
Browse files Browse the repository at this point in the history
feat: introduce `RestrictAccess<To>` and generalize `restrict` to all access types
  • Loading branch information
phil-opp committed Apr 26, 2024
2 parents e334f2a + c64c9af commit 53c30e7
Show file tree
Hide file tree
Showing 3 changed files with 90 additions and 47 deletions.
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

0 comments on commit 53c30e7

Please sign in to comment.