flense 0.1.0

Purpose-oriented lensing
Documentation
use core::marker::PhantomData;

use crate::{
    Field,
    type_lists::{
        ConsSet,
        GetPtr,
        GetStride,
        SculptSlice,
        TupleSet,
    },
};

/// Mutable reified lens into contiguous memory.
#[derive(Debug, PartialEq, Eq)]
pub struct LensSliceMut<'a, T>
where
    T: TupleSet,
{
    storage: T::ConsSlice,
    len: usize,
    _phantom: PhantomData<&'a ()>,
}

impl<'a, T> LensSliceMut<'a, T>
where
    T: TupleSet,
{
    #[inline]
    pub(crate) const fn new(storage: T::ConsSlice, len: usize) -> Self {
        Self {
            storage,
            len,
            _phantom: PhantomData,
        }
    }

    /// Split `self` into two mutable slice lenses.
    ///
    /// `Lhs` is the tuple of fields that should appear in the left-hand
    /// returned lens; the right-hand lens contains the remaining fields.
    ///
    /// `Indices` is a single tuple-nested list of shrinking-list positions (one
    /// per element of `Lhs`) and should be inferred; pass `_` at the call site.
    #[expect(clippy::type_complexity, reason = "necessary type inference")]
    #[inline]
    pub fn split<Lhs, Indices>(
        self,
    ) -> (
        LensSliceMut<'a, Lhs>,
        LensSliceMut<
            'a,
            <<T::ConsSlice as SculptSlice<Lhs::ConsSlice, Indices>>::Remainder as ConsSet>::Tuple,
        >,
    )
    where
        Lhs: TupleSet,
        T::ConsSlice: SculptSlice<Lhs::ConsSlice, Indices>,
        <T::ConsSlice as SculptSlice<Lhs::ConsSlice, Indices>>::Remainder: ConsSet<
            Tuple: TupleSet<
                ConsSlice = <T::ConsSlice as SculptSlice<Lhs::ConsSlice, Indices>>::Remainder,
            >,
        >,
    {
        let (lhs_storage, rem_storage) = self.storage.sculpt_slice();
        (
            LensSliceMut {
                storage: lhs_storage,
                len: self.len,
                _phantom: PhantomData,
            },
            LensSliceMut {
                storage: rem_storage,
                len: self.len,
                _phantom: PhantomData,
            },
        )
    }

    /// Returns a pointer to the first lensed element of a field.
    #[inline]
    pub fn as_ptr<Elt, Index>(&self) -> *const Elt::Type
    where
        Elt: Field + 'static,
        T::ConsSlice: GetPtr<Elt, Index>,
    {
        self.storage.get_ptr().as_ptr()
    }

    /// Returns a mutable pointer to the first lensed element of a field.
    #[inline]
    pub fn as_mut_ptr<Elt, Index>(&mut self) -> *mut Elt::Type
    where
        Elt: Field + 'static,
        T::ConsSlice: GetPtr<Elt, Index>,
    {
        self.storage.get_ptr().as_ptr()
    }

    /// Returns a shared reference to the `i`-th lensed element of a field, or
    /// `None` if `i >= self.len()`.
    #[inline]
    #[must_use]
    pub fn get<Elt, Index>(&self, i: usize) -> Option<&Elt::Type>
    where
        Elt: Field + 'static,
        T::ConsSlice: GetPtr<Elt, Index> + GetStride<Elt, Index>,
    {
        if i < self.len {
            // SAFETY: `i < self.len` checked above.
            Some(unsafe { self.get_unchecked::<Elt, _>(i) })
        } else {
            None
        }
    }

    /// Returns a shared reference to the `i`-th lensed element of a field,
    /// without bounds checking.
    ///
    /// # Safety
    /// `i` must be `< self.len()`.
    #[inline]
    #[must_use]
    pub unsafe fn get_unchecked<Elt, Index>(&self, i: usize) -> &Elt::Type
    where
        Elt: Field + 'static,
        T::ConsSlice: GetPtr<Elt, Index> + GetStride<Elt, Index>,
    {
        let base: *const Elt::Type = self.storage.get_ptr().as_ptr();
        let stride = self.storage.get_stride();
        // SAFETY: By the caller's contract `i < self.len()`, so the offset
        // `stride * i` lands inside the original slice. `Adapter` guarantees
        // the resulting pointer is valid for reads of `Elt::Type` and
        // properly aligned. Tying the returned reference to `&self`
        // reborrows the lens shared for the call's lifetime, so no
        // `&mut Elt::Type` to the same field can coexist.
        unsafe { &*base.byte_add(stride * i) }
    }

    /// Returns a unique reference to the `i`-th lensed element of a field, or
    /// `None` if `i >= self.len()`.
    #[inline]
    #[must_use]
    pub fn get_mut<Elt, Index>(&mut self, i: usize) -> Option<&mut Elt::Type>
    where
        Elt: Field + 'static,
        T::ConsSlice: GetPtr<Elt, Index> + GetStride<Elt, Index>,
    {
        if i < self.len {
            // SAFETY: `i < self.len` checked above.
            Some(unsafe { self.get_mut_unchecked::<Elt, _>(i) })
        } else {
            None
        }
    }

    /// Returns a unique reference to the `i`-th lensed element of a field,
    /// without bounds checking.
    ///
    /// # Safety
    /// `i` must be `< self.len()`.
    #[inline]
    #[must_use]
    pub unsafe fn get_mut_unchecked<Elt, Index>(&mut self, i: usize) -> &mut Elt::Type
    where
        Elt: Field + 'static,
        T::ConsSlice: GetPtr<Elt, Index> + GetStride<Elt, Index>,
    {
        let base: *mut Elt::Type = self.storage.get_ptr().as_ptr();
        let stride = self.storage.get_stride();
        // SAFETY: By the caller's contract `i < self.len()`, so the offset
        // `stride * i` lands inside the original slice. `Adapter` guarantees
        // the resulting pointer is valid for reads and writes of `Elt::Type`
        // and properly aligned. `&mut self` reborrows the lens uniquely for
        // the call's lifetime, so the returned `&mut` cannot alias any
        // other reference reachable through the lens; the disjointness
        // check at construction rules out aliasing through other fields.
        unsafe { &mut *base.byte_add(stride * i) }
    }

    /// Returns the number of lensed elements.
    #[inline]
    pub const fn len(&self) -> usize {
        self.len
    }

    /// Returns the number of bytes between lensed elements of a field.
    #[inline]
    pub fn stride<Elt, Index>(&self) -> usize
    where
        Elt: Field + 'static,
        T::ConsSlice: GetStride<Elt, Index>,
    {
        self.storage.get_stride()
    }

    /// Returns `true` if the lens has a length of 0.
    #[inline]
    pub const fn is_empty(&self) -> bool {
        self.len == 0
    }
}