airlock 0.0.8

no_std async communication
Documentation
use core::sync::atomic::{AtomicU8, AtomicUsize, Ordering};

pub(crate) const ATOMIC_UPDATE_MAX_ITERATIONS: usize = 1024;

pub trait AtomicValue {
    type Value: Copy;

    fn load(&self, ordering: Ordering) -> Self::Value;
    fn compare_exchange(
        &self,
        old: Self::Value,
        new: Self::Value,
        success: Ordering,
        failure: Ordering,
    ) -> Result<Self::Value, Self::Value>;
}

pub(crate) enum AtomicUpdate<T> {
    Retry,
    Set(T),
}

pub(crate) fn compare_exchange_loop<A, F, E>(
    atomic_value: &A,
    max_attempts: usize,
    old_value: Option<A::Value>,
    mut map_value: F,
) -> Result<A::Value, Option<E>>
where
    A: AtomicValue,
    F: FnMut(A::Value) -> Result<AtomicUpdate<A::Value>, E>,
{
    let mut old_value = old_value.unwrap_or_else(|| atomic_value.load(Ordering::SeqCst));
    for _ in 0..max_attempts {
        let AtomicUpdate::Set(new_value) = map_value(old_value).map_err(Some)? else { continue };

        match atomic_value.compare_exchange(
            old_value,
            new_value,
            Ordering::SeqCst,
            Ordering::SeqCst,
        ) {
            Ok(_) => return Ok(new_value),
            Err(v) => old_value = v,
        }
    }
    Err(None)
}

macro_rules! impl_atomic_value {
    ($atomic: ty, $prim: ty) => {
        impl $crate::utils::AtomicValue for $atomic {
            type Value = $prim;

            #[inline(always)]
            fn load(&self, ordering: Ordering) -> Self::Value {
                Self::load(self, ordering)
            }

            #[inline(always)]
            fn compare_exchange(
                &self,
                old: Self::Value,
                new: Self::Value,
                success: Ordering,
                failure: Ordering,
            ) -> Result<Self::Value, Self::Value> {
                Self::compare_exchange(self, old, new, success, failure)
            }
        }
    };
}

impl_atomic_value!(AtomicU8, u8);
impl_atomic_value!(AtomicUsize, usize);

pub mod bits {
    use core::ops::{BitAnd, BitOr, BitXor, Not, Shl, Shr};
    pub(crate) trait BitOps:
        From<u8>
        + Copy
        + Sized
        + Not<Output = Self>
        + BitAnd<Output = Self>
        + BitOr<Output = Self>
        + BitXor<Output = Self>
        + Shl<u8, Output = Self>
        + Shr<u8, Output = Self>
    {
    }
    impl<T> BitOps for T where
        T: From<u8>
            + Copy
            + Sized
            + Not<Output = Self>
            + BitAnd<Output = Self>
            + BitOr<Output = Self>
            + BitXor<Output = Self>
            + Shl<u8, Output = Self>
            + Shr<u8, Output = Self>
    {
    }

    pub(crate) fn zeroes<F: BitOps>() -> F {
        0b0u8.into()
    }
    pub(crate) fn ones<F: BitOps>() -> F {
        !zeroes::<F>()
    }

    pub(crate) fn flag<F: BitOps, const POS: u8>(flags: F) -> F {
        let one: F = 0b1u8.into();
        let mask = one << POS;
        flags & mask
    }

    pub(crate) fn unpack<F: BitOps, const START: u8, const LEN: u8>(packed: F) -> F {
        let ones: F = ones::<F>();
        let mask = !(ones << LEN);
        (packed >> START) & mask
    }

    pub(crate) fn pack<F: BitOps, const START: u8, const LEN: u8>(packed: F, value: F) -> F {
        let ones: F = ones::<F>();
        let mask = !(ones << LEN) << START;
        let bits = (value << START) & mask;
        (packed & !mask) | bits
    }

    #[cfg(test)]
    mod tests {
        use super::*;

        #[test]
        fn tests() {
            assert_eq!(ones::<u8>(), 0b1111_1111);
            assert_eq!(flag::<u8, 0>(ones()), 0b0000_0001);
            assert_eq!(flag::<u8, 4>(ones()), 0b0001_0000);
            assert_eq!(flag::<u8, 7>(ones()), 0b1000_0000);

            assert_eq!(zeroes::<u8>(), 0b0000_0000);
            assert_eq!(flag::<u8, 0>(zeroes()), 0b0000_0000);
            assert_eq!(flag::<u8, 4>(zeroes()), 0b0000_0000);
            assert_eq!(flag::<u8, 7>(zeroes()), 0b0000_0000);

            assert_eq!(unpack::<u8, 1, 2>(ones()), 0b11);
            assert_eq!(pack::<u8, 1, 2>(0b0000_0000, 0b11), 0b0000_0110);
            assert_eq!(pack::<u8, 1, 2>(0b1111_1111, 0b11), 0b1111_1111);
            assert_eq!(pack::<u8, 1, 2>(0b0000_0000, 0b00), 0b0000_0000);
            assert_eq!(pack::<u8, 1, 2>(0b1111_1111, 0b00), 0b1111_1001);
        }
    }
}