token-cell 3.0.0

A more convenient GhostCell
Documentation
use std::fmt::Debug;

/// A trait for types that can serve as unique identifiers for runtime tokens.
///
/// An `Identifier` must be able to be represented by an atomic type that
/// can be incremented to produce the next identifier.
pub trait Identifier: Eq + Debug + Copy + Send + Sync {
    /// The atomic counterpart to this identifier type.
    type AtomicType: RollingCounter<NonAtomic = Self>;
    /// Whether or not comparison between two distinct token could yield `eq`.
    type ComparisonMaySpuriouslyEq: crate::core::sealed::Boolean;
}

/// A trait for types that can be used as rolling counters. It is only intended for the atomic counterparts to [`Identifier`] types.
pub trait RollingCounter {
    /// The starting value of the rolling counter.
    const DEFAULT: Self;
    /// The non-atomic identifier type associated with this atomic type.
    type NonAtomic;
    /// Atomically increments the value and returns the previous value.
    fn next(&self) -> Self::NonAtomic;
}

macro_rules! impl_identifier {
    (comp u64) => {
        crate::core::False
    };
    (comp u128) => {
        crate::core::False
    };
    (comp $id: ty) => {
        crate::core::False
    };
    ($id: ty, $atom: ty) => {
        impl Identifier for $id {
            type AtomicType = $atom;
            type ComparisonMaySpuriouslyEq = impl_identifier!(comp $id);
        }
        impl RollingCounter for $atom {
            const DEFAULT: Self = Self::new(0);
            type NonAtomic = $id;
            fn next(&self) -> Self::NonAtomic {
                self.fetch_add(1, core::sync::atomic::Ordering::Relaxed)
            }
        }
    };
}

impl_identifier!(u8, portable_atomic::AtomicU8);
impl_identifier!(u16, portable_atomic::AtomicU16);
impl_identifier!(u32, portable_atomic::AtomicU32);
impl_identifier!(u64, portable_atomic::AtomicU64);
impl_identifier!(u128, portable_atomic::AtomicU128);

/// The comparison error for runtime tokens.
///
/// Such an error being returned implies a massive bug in the calling code, as it implies that
/// said code failed to track which instance of a [`runtime_token`] was used to construct a cell,
/// and thus failed to ensure that the token was used correctly.
///
/// Unless the token is backed by an integer with sufficient size to guarantee that the program
/// never constructs multiple tokens with a same identifier, your program SHOULD NOT rely on
/// token identifiers being unique for soundness.
#[derive(Debug, Clone, Copy)]
pub struct IdMismatch<T: Identifier> {
    /// The identifier of the token the cell was expecting.
    pub cell: T,
    /// The identifier of the token that was used to attempt accessing the cell's contents.
    pub token: T,
}

impl<T: Identifier> ::core::fmt::Display for IdMismatch<T> {
    fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
        write!(f, "{:?}", self)
    }
}

/// Produces tokens that are also checked at runtime, ensuring that a [`TokenCell`] is never accidentally used with another instance of the same token type.
///
/// Each token of a given type generated by [`runtime_token`] being identified by a `u16` by default, constructing over 65536 instances may mean that distinct token instances share a same identifier,
/// and may unlock each other's cells.
///
/// This means that if you accidentally use the wrong instance of a runtime token on a cell, you are likely but not _guaranteed_ to be protected by the identifier
///
/// You can chose the size of your tokens by calling `runtime_token!(pub MyToken: uX)`.
/// `u64` has more overhead than the default `u16`, but guarantees that you will never accidentally use the wrong instance of a token undetected;
/// therefore, runtime tokens that use `u64` as their backing allow you to use the [safe](crate::core::TokenCellTrait) access methods to [`TokenCell`]
///
/// [`TokenCell`]: crate::core::TokenCell
#[macro_export]
macro_rules! runtime_token {
    ($(#[$meta: meta])* $vis: vis $id: ident) => {
        runtime_token!($(#[meta])* $vis $id: u16);
    };
    ($(#[$meta: meta])* $vis: vis $id: ident: $ty: ty) => {
        $crate::paste! {
            $vis use [<__ $id _mod__ >]::$id;
            #[allow(nonstandard_style)]
            mod [<__ $id _mod__ >] {
                use core::convert::Infallible;
                use $crate::core::UnscopedToken;
                use $crate::runtime_token_support::{Identifier, IdMismatch, RollingCounter};

                static COUNTER: <$ty as Identifier>::AtomicType = <<$ty as Identifier>::AtomicType as RollingCounter>::DEFAULT;

                $(#[$meta])*
                #[derive(::core::fmt::Debug)]
                pub struct $id($ty);

                impl UnscopedToken for $id {
                    type ConstructionError = Infallible;
                    fn try_new() -> Result<Self, Self::ConstructionError> {
                        let id = COUNTER.next();
                        Ok(Self(id))
                    }
                }
                impl $crate::core::TokenTrait for $id {
                    type ComparisonMaySpuriouslyEq = $crate::core::False;
                    type RunError = Infallible;
                    type Identifier = $ty;
                    type ComparisonError = IdMismatch<$ty>;
                    type Branded<'a> = Self;
                    fn with_token<R, F: for<'a> FnOnce(Self::Branded<'a>) -> R>(f: F) -> Result<R, Self::RunError> {
                        Ok(f(Self::new()))
                    }
                    fn identifier(&self) -> Self::Identifier { self.0 }
                    fn compare(&self, id: &Self::Identifier) -> Result<(), Self::ComparisonError> {
                        if *id == self.0 { Ok(()) } else { Err(IdMismatch { cell: *id, token: self.0 }) }
                    }
                }
            }
        }
    };
    ($($(#[$meta: meta])* $vis: vis $id: ident),*) => {
        $($crate::runtime_token!($(#[meta])* $vis $id);)*
    };
    ($($(#[$meta: meta])* $vis: vis $id: ident: $ty: ty),*) => {
        $($crate::runtime_token!($(#[meta])* $vis $id: $ty);)*
    };
}