flense 0.1.0

Purpose-oriented lensing
Documentation
//! Lensing trait definitions.

use core::ptr::{
    self,
    NonNull,
};

use crate::{
    Adapter,
    Field,
    lens::{
        Lens,
        LensMut,
        LensSlice,
        LensSliceMut,
    },
    type_lists::{
        ConsPtr,
        ConsSlice,
        Nil,
        TupleSet,
    },
};

/// Access for a set of [`Field`] through a [`Lens`].
pub trait Lenses<'a, T>
where
    T: TupleSet,
{
    /// Create a [`Lens`] to access the fields.
    fn lens(self) -> Lens<'a, T>;
}

/// Access for a set of [`Field`] through a [`LensMut`].
pub trait LensesMut<'a, T>
where
    T: TupleSet,
{
    /// Create a [`LensMut`] to access the fields.
    fn lens_mut(self) -> LensMut<'a, T>;
}

/// Access for a slice to a set of [`Field`] through a [`LensSlice`].
pub trait LensesSlice<'a, T>
where
    T: TupleSet,
{
    /// Create a [`LensSlice`] to access the fields.
    fn lens_slice(self) -> LensSlice<'a, T>;
}

/// Mutable access for a slice of [`Field`] through a [`LensSliceMut`].
pub trait LensesSliceMut<'a, T>
where
    T: TupleSet,
{
    /// Create a [`LensSliceMut`] to mutably access the value.
    fn lens_slice_mut(self) -> LensSliceMut<'a, T>;
}

macro_rules! impl_lenses {
    ($($ty:ident),+ $(,)?) => {
        impl<'a, T, $($ty),+> Lenses<'a, ($($ty,)+)> for &'a T
        where
            T: $(Adapter<$ty> +)+,
            $($ty: 'static + Field),+
        {
            #[inline]
            fn lens(self) -> Lens<'a, ($($ty,)+)> {
                let base: *const T = ptr::from_ref(self);
                Lens::new(
                    impl_lenses!(
                        @nest @ ConsPtr => { $(
                            // SAFETY: If `Adapter` is implemented correctly
                            // these adds on the base pointer produce correct
                            // pointers into self.
                            unsafe {
                                NonNull::new_unchecked(
                                    base
                                        .byte_add(<T as Adapter<$ty>>::OFFSET)
                                        .cast_mut()
                                        .cast()
                                )
                            }
                        ),+} => { } => { Nil }
                    )
                )
            }
        }

        impl<'a, T, $($ty),+> LensesMut<'a, ($($ty,)+)> for &'a mut T
        where
            T: $(Adapter<$ty> +)+,
            $($ty: 'static + Field),+
        {
            #[inline]
            fn lens_mut(self) -> LensMut<'a, ($($ty,)+)> {
                const {
                    assert!(
                        ranges_disjoint(&[$((
                            <T as Adapter<$ty>>::OFFSET,
                            size_of::<<$ty as Field>::Type>(),
                        )),+]),
                        "flense: fields in a `LensMut` must not alias overlapping bytes",
                    );
                }
                // Take the unique retag exactly once. Calling
                // `ptr::from_mut(self)` per field would invalidate every
                // earlier retag under stacked borrows, leaving all but the
                // last field pointer dangling.
                let base: *mut T = ptr::from_mut(self);
                LensMut::new(
                    impl_lenses!(
                        @nest @ ConsPtr => { $(
                            // SAFETY: If `Adapter` is implemented correctly
                            // these adds on the base pointer produce correct
                            // pointers into self. Field byte ranges are
                            // verified disjoint by the const block above.
                            unsafe {
                                NonNull::new_unchecked(
                                    base
                                        .byte_add(<T as Adapter<$ty>>::OFFSET)
                                        .cast()
                                )
                            }
                        ),+} => { } => { Nil }
                    )
                )
            }
        }

        impl<'a, T, $($ty),+> LensesSlice<'a, ($($ty,)+)> for &'a [T]
        where
            T: $(Adapter<$ty> +)+,
            $($ty: 'static + Field),+
        {
            #[inline]
            fn lens_slice(self) -> LensSlice<'a, ($($ty,)+)> {
                let base: *const T = self.as_ptr();
                let len = self.len();
                LensSlice::new(
                    impl_lenses!(
                        @nest @ ConsSlice => { $((
                            // SAFETY: If `Adapter` is implemented correctly
                            // these adds on the base pointer produce correct
                            // pointers into self.
                            unsafe {
                                NonNull::new_unchecked(
                                    base
                                        .byte_add(<T as Adapter<$ty>>::OFFSET)
                                        .cast_mut()
                                        .cast()
                                )
                            },
                            size_of::<T>()
                        )),+} => { } => { Nil }
                    ),
                    len,
                )
            }
        }

        impl<'a, T, $($ty),+> LensesSliceMut<'a, ($($ty,)+)> for &'a mut [T]
        where
            T: $(Adapter<$ty> +)+,
            $($ty: 'static + Field),+
        {
            #[inline]
            fn lens_slice_mut(self) -> LensSliceMut<'a, ($($ty,)+)> {
                const {
                    assert!(
                        ranges_disjoint(&[$((
                            <T as Adapter<$ty>>::OFFSET,
                            size_of::<<$ty as Field>::Type>(),
                        )),+]),
                        "flense: fields in a `LensSliceMut` must not alias overlapping bytes",
                    );
                }
                // Take the unique retag of the slice exactly once; see the
                // matching comment in `lens_mut`.
                let len = self.len();
                let base: *mut T = self.as_mut_ptr();
                LensSliceMut::new(
                    impl_lenses!(
                        @nest @ ConsSlice => { $((
                            // SAFETY: If `Adapter` is implemented correctly
                            // these adds on the base pointer produce correct
                            // pointers into self. Field byte ranges are
                            // verified disjoint by the const block above.
                            unsafe {
                                NonNull::new_unchecked(
                                    base
                                        .byte_add(<T as Adapter<$ty>>::OFFSET)
                                        .cast()
                                )
                            },
                            size_of::<T>()
                        )),+} => { } => { Nil }
                    ),
                    len,
                )
            }
        }
    };

    (@nest @ $ty:ident => { } => { } => { $($repr:tt)* }) => {
        $($repr)*
    };
    (@nest @ $ty:ident => { } => { $head:expr $(, $rev:expr)* } => { $($repr:tt)* }) => {
        impl_lenses!(@nest @ $ty => { } => { $($rev),* } => { $ty($head, $($repr)*) })
    };
    (@nest @ $ty:ident => { $head:expr $(, $items:expr)* } => { $($rev:expr),* } => { $($repr:tt)* }) => {
        impl_lenses!(@nest @ $ty => { $($items),* } => { $head $(, $rev)* } => { $($repr)* })
    }
}

impl_lenses!(A);
impl_lenses!(A, B);
impl_lenses!(A, B, C);
impl_lenses!(A, B, C, D);
impl_lenses!(A, B, C, D, E);
impl_lenses!(A, B, C, D, E, F);
impl_lenses!(A, B, C, D, E, F, G);
impl_lenses!(A, B, C, D, E, F, G, H);
impl_lenses!(A, B, C, D, E, F, G, H, I);
impl_lenses!(A, B, C, D, E, F, G, H, I, J);
impl_lenses!(A, B, C, D, E, F, G, H, I, J, K);
impl_lenses!(A, B, C, D, E, F, G, H, I, J, K, L);
impl_lenses!(A, B, C, D, E, F, G, H, I, J, K, L, M);
impl_lenses!(A, B, C, D, E, F, G, H, I, J, K, L, M, N);
impl_lenses!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O);
impl_lenses!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P);

/// Compile-time check that no two `(offset, size)` byte ranges overlap.
///
/// Used by the [`LensesMut`] and [`LensesSliceMut`] impls to reject
/// constructing a mutable lens whose fields would alias the same bytes, which
/// would be undefined behavior the moment two `&mut` were taken.
///
/// Returns `true` when all ranges are pairwise disjoint.
#[doc(hidden)]
#[must_use]
#[allow(
    clippy::indexing_slicing,
    reason = "indices are bounded by N at every site"
)]
pub const fn ranges_disjoint<const N: usize>(ranges: &[(usize, usize); N]) -> bool {
    let mut i = 0;
    while i < N {
        let (a_off, a_size) = ranges[i];
        let a = a_off..(a_off + a_size);
        let mut j = i + 1;
        while j < N {
            let (b_off, b_size) = ranges[j];
            let b = b_off..(b_off + b_size);
            // Two half-open ranges overlap iff a.start < b.end && b.start < a.end
            if a.start < b.end && b.start < a.end {
                return false;
            }
            j += 1;
        }
        i += 1;
    }
    true
}