use core::ptr::{
self,
NonNull,
};
use crate::{
Adapter,
Field,
dim::Dim,
lens::{
Lens,
LensBase,
LensBaseMut,
LensCube,
LensCubeMut,
LensMut,
LensRect,
LensRectMut,
LensSlice,
LensSliceMut,
},
type_lists::{
Cons,
Nil,
TupleSet,
},
};
pub trait LensesBase<'a, T, const N: usize>
where
T: TupleSet,
T::Cons<N>: 'a,
{
#[must_use]
fn lens_base(self) -> LensBase<'a, T, N>;
}
pub trait LensesBaseMut<'a, T, const N: usize>
where
T: TupleSet,
T::Cons<N>: 'a,
{
#[must_use]
fn lens_base_mut(self) -> LensBaseMut<'a, T, N>;
}
pub trait Lenses<'a, T>: LensesBase<'a, T, 0>
where
T: TupleSet,
T::Cons<0>: 'a,
{
#[inline]
#[must_use]
fn lens(self) -> Lens<'a, T>
where
Self: Sized,
{
self.lens_base()
}
}
impl<'a, T, U> Lenses<'a, U> for T
where
T: LensesBase<'a, U, 0>,
U: TupleSet,
U::Cons<0>: 'a,
{
}
pub trait LensesMut<'a, T>: LensesBaseMut<'a, T, 0>
where
T: TupleSet,
T::Cons<0>: 'a,
{
#[inline]
#[must_use]
fn lens_mut(self) -> LensMut<'a, T>
where
Self: Sized,
{
self.lens_base_mut()
}
}
impl<'a, T, U> LensesMut<'a, U> for T
where
T: LensesBaseMut<'a, U, 0>,
U: TupleSet,
U::Cons<0>: 'a,
{
}
pub trait LensesSlice<'a, T>: LensesBase<'a, T, 1>
where
T: TupleSet,
T::Cons<1>: 'a,
{
#[inline]
#[must_use]
fn lens_slice(self) -> LensSlice<'a, T>
where
Self: Sized,
{
self.lens_base()
}
}
impl<'a, T, U> LensesSlice<'a, U> for T
where
T: LensesBase<'a, U, 1>,
U: TupleSet,
U::Cons<1>: 'a,
{
}
pub trait LensesSliceMut<'a, T>: LensesBaseMut<'a, T, 1>
where
T: TupleSet,
T::Cons<1>: 'a,
{
#[inline]
#[must_use]
fn lens_slice_mut(self) -> LensSliceMut<'a, T>
where
Self: Sized,
{
self.lens_base_mut()
}
}
impl<'a, T, U> LensesSliceMut<'a, U> for T
where
T: LensesBaseMut<'a, U, 1>,
U: TupleSet,
U::Cons<1>: 'a,
{
}
pub trait LensesRect<'a, T>: LensesBase<'a, T, 2>
where
T: TupleSet,
T::Cons<2>: 'a,
{
#[inline]
#[must_use]
fn lens_rect(self) -> LensRect<'a, T>
where
Self: Sized,
{
self.lens_base()
}
}
impl<'a, T, U> LensesRect<'a, U> for T
where
T: LensesBase<'a, U, 2>,
U: TupleSet,
U::Cons<2>: 'a,
{
}
pub trait LensesRectMut<'a, T>: LensesBaseMut<'a, T, 2>
where
T: TupleSet,
T::Cons<2>: 'a,
{
#[inline]
#[must_use]
fn lens_rect_mut(self) -> LensRectMut<'a, T>
where
Self: Sized,
{
self.lens_base_mut()
}
}
impl<'a, T, U> LensesRectMut<'a, U> for T
where
T: LensesBaseMut<'a, U, 2>,
U: TupleSet,
U::Cons<2>: 'a,
{
}
pub trait LensesCube<'a, T>: LensesBase<'a, T, 3>
where
T: TupleSet,
T::Cons<3>: 'a,
{
#[inline]
#[must_use]
fn lens_cube(self) -> LensCube<'a, T>
where
Self: Sized,
{
self.lens_base()
}
}
impl<'a, T, U> LensesCube<'a, U> for T
where
T: LensesBase<'a, U, 3>,
U: TupleSet,
U::Cons<3>: 'a,
{
}
pub trait LensesCubeMut<'a, T>: LensesBaseMut<'a, T, 3>
where
T: TupleSet,
T::Cons<3>: 'a,
{
#[inline]
#[must_use]
fn lens_cube_mut(self) -> LensCubeMut<'a, T>
where
Self: Sized,
{
self.lens_base_mut()
}
}
impl<'a, T, U> LensesCubeMut<'a, U> for T
where
T: LensesBaseMut<'a, U, 3>,
U: TupleSet,
U::Cons<3>: 'a,
{
}
macro_rules! impl_lenses {
($N:literal $(@ $($ty:ident),+ $(,)?)?) => {
impl<'a, T, $($($ty),+)?> LensesBase<'a, ($($($ty,)+)?), 0> for &'a T
where
T: $($(Adapter<$ty> +)+)?,
$($($ty: 'a + Field),+)?
{
#[inline]
fn lens_base(self) -> LensBase<'a, ($($($ty,)+)?), 0> {
#[allow(unused_variables, reason = "unused in arity 0 implementation")]
let base: *mut T = ptr::from_ref(self).cast_mut();
LensBase::new(
impl_lenses!(
@nest @ Cons => { $($((
Dim([]),
// 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()
)
}
)),+)? } => { } => { Nil }
),
Dim([])
)
}
}
impl<'a, T, $($($ty),+)?> LensesBaseMut<'a, ($($($ty,)+)?), 0> for &'a mut T
where
T: $($(Adapter<$ty> +)+)?,
$($($ty: 'a + Field),+)?
{
#[inline]
fn lens_base_mut(self) -> LensBaseMut<'a, ($($($ty,)+)?), 0> {
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",
);
}
#[allow(unused_variables, reason = "unused in arity 0 implementation")]
let base: *mut T = ptr::from_mut(self);
LensBaseMut::new(
impl_lenses!(
@nest @ Cons => { $($((
Dim([]),
unsafe {
NonNull::new_unchecked(
base
.byte_add(<T as Adapter<$ty>>::OFFSET)
.cast()
)
}
)),+)? } => { } => { Nil }
),
Dim([]),
)
}
}
impl_lenses!(@slice $N @ 'a T => { $($($ty),+)? } => {
&'a [T]
} => {
#[cfg(feature = "alloc")]
&'a alloc::boxed::Box<[T]>
} => {
#[cfg(feature = "alloc")]
&'a alloc::rc::Rc<[T]>
} => {
#[cfg(feature = "alloc")]
&'a alloc::sync::Arc<[T]>
} => {
#[cfg(feature = "alloc")]
&'a alloc::vec::Vec<T>
});
impl_lenses!(@slice_mut $N @ 'a T => { $($($ty),+)? } => {
&'a mut [T]
} => {
#[cfg(feature = "alloc")]
&'a mut alloc::boxed::Box<[T]>
} => {
#[cfg(feature = "alloc")]
&'a mut alloc::vec::Vec<T>
});
impl_lenses!(@ndarray @ 'a T => {
$($($ty),+)?
} => 1 [(d0, s0)] => {
#[cfg(feature = "ndarray")]
ndarray::ArrayView1<'a, T>
} => 2 [(d0, s0), (d1, s1)] => {
#[cfg(feature = "ndarray")]
ndarray::ArrayView2<'a, T>
} => 3 [(d0, s0), (d1, s1), (d2, s2)] => {
#[cfg(feature = "ndarray")]
ndarray::ArrayView3<'a, T>
} => 4 [(d0, s0), (d1, s1), (d2, s2), (d3, s3)] => {
#[cfg(feature = "ndarray")]
ndarray::ArrayView4<'a, T>
} => 5 [(d0, s0), (d1, s1), (d2, s2), (d3, s3), (d4, s4)] => {
#[cfg(feature = "ndarray")]
ndarray::ArrayView5<'a, T>
} => 6 [(d0, s0), (d1, s1), (d2, s2), (d3, s3), (d4, s4), (d5, s5)] => {
#[cfg(feature = "ndarray")]
ndarray::ArrayView6<'a, T>
});
impl_lenses!(@ndarray_mut @ 'a T => {
$($($ty),+)?
} => 1 [(d0, s0)] => {
#[cfg(feature = "ndarray")]
ndarray::ArrayViewMut1<'a, T>
} => 2 [(d0, s0), (d1, s1)] => {
#[cfg(feature = "ndarray")]
ndarray::ArrayViewMut2<'a, T>
} => 3 [(d0, s0), (d1, s1), (d2, s2)] => {
#[cfg(feature = "ndarray")]
ndarray::ArrayViewMut3<'a, T>
} => 4 [(d0, s0), (d1, s1), (d2, s2), (d3, s3)] => {
#[cfg(feature = "ndarray")]
ndarray::ArrayViewMut4<'a, T>
} => 5 [(d0, s0), (d1, s1), (d2, s2), (d3, s3), (d4, s4)] => {
#[cfg(feature = "ndarray")]
ndarray::ArrayViewMut5<'a, T>
} => 6 [(d0, s0), (d1, s1), (d2, s2), (d3, s3), (d4, s4), (d5, s5)] => {
#[cfg(feature = "ndarray")]
ndarray::ArrayViewMut6<'a, T>
});
};
(@slice $N:literal @ $lt:lifetime $var:ident => { $($ty:ident),* }) => {};
(@slice $N:literal @ $lt:lifetime $var:ident => { $($ty:ident),* } => { $(#[$impl_meta:meta])* $impl_ty:ty } $(=> { $(#[$impl_metas:meta])* $impl_tys:ty })*) => {
$(#[$impl_meta])*
impl<$lt, $var, $($ty),*> LensesBase<$lt, ($($ty,)*), 1> for $impl_ty
where
$var: $(Adapter<$ty> +)*,
$($ty: $lt + Field),*
{
#[inline]
fn lens_base(self) -> LensBase<$lt, ($($ty,)*), 1> {
let len = self.len();
assert!(
isize::try_from(len).is_ok(),
"flense: lens length must fit in `isize`; guarantee from https://doc.rust-lang.org/stable/reference/types/numeric.html#r-type.numeric.int.size.isize is invalid?",
);
#[allow(unused_variables, reason = "unused in arity 0 implementation")]
let base: *mut $var = self.as_ptr().cast_mut();
#[expect(
clippy::cast_possible_wrap,
reason = "performs const-time check on cast"
)]
LensBase::new(
impl_lenses!(
@nest @ Cons => { $((
const {
assert!(
size_of::<$var>() <= isize::MAX as usize,
"flense cannot lens elements larger than isize::MAX"
);
Dim([size_of::<$var>() as isize])
},
unsafe {
NonNull::new_unchecked(
base
.wrapping_byte_add(<$var as Adapter<$ty>>::OFFSET)
.cast()
)
}
)),* } => { } => { Nil }
),
Dim([len]),
)
}
}
impl_lenses!(@slice $N @ $lt $var => { $($ty),* } $(=> { $(#[$impl_metas])* $impl_tys })*);
};
(@slice_mut $N:literal @ $lt:lifetime $var:ident => { $($ty:ident),* }) => {};
(@slice_mut $N:literal @ $lt:lifetime $var:ident => { $($ty:ident),* } => { $(#[$impl_meta:meta])* $impl_ty:ty } $(=> { $(#[$impl_metas:meta])* $impl_tys:ty })*) => {
$(#[$impl_meta])*
impl<$lt, $var, $($ty),*> LensesBaseMut<$lt, ($($ty,)*), 1> for $impl_ty
where
$var: $(Adapter<$ty> +)*,
$($ty: $lt + Field),*
{
#[inline]
fn lens_base_mut(self) -> LensBaseMut<$lt, ($($ty,)*), 1> {
const {
assert!(
ranges_disjoint(&[$((
<$var as Adapter<$ty>>::OFFSET,
size_of::<<$ty as Field>::Type>(),
)),*]),
"flense: fields in a `LensSliceMut` must not alias overlapping bytes",
);
}
let len = self.len();
assert!(
isize::try_from(len).is_ok(),
"flense: lens length must fit in `isize`; guarantee from https://doc.rust-lang.org/stable/reference/types/numeric.html#r-type.numeric.int.size.isize is invalid?",
);
#[allow(unused_variables, reason = "unused in arity 0 implementation")]
let base: *mut $var = self.as_mut_ptr();
#[expect(
clippy::cast_possible_wrap,
reason = "performs const-time check on cast"
)]
LensBaseMut::new(
impl_lenses!(
@nest @ Cons => { $((
const {
assert!(
size_of::<$var>() <= isize::MAX as usize,
"flense cannot lens elements larger than isize::MAX"
);
Dim([size_of::<$var>() as isize])
},
unsafe {
NonNull::new_unchecked(
base
.wrapping_byte_add(<$var as Adapter<$ty>>::OFFSET)
.cast()
)
}
)),* } => { } => { Nil }
),
Dim([len]),
)
}
}
impl_lenses!(@slice_mut $N @ $lt $var => { $($ty),* } $(=> { $(#[$impl_metas])* $impl_tys })*);
};
(@ndarray @ $lt:lifetime $var:ident => { $($ty:ident),* }) => {};
(@ndarray @ $lt:lifetime $var:ident => { $($ty:ident),* }
=> $dimn:literal [$(($dim:ident, $stride:ident)),+]
=> { $(#[$impl_meta:meta])* $impl_ty:ty }
$($rest:tt)*
) => {
$(#[$impl_meta])*
impl<$lt, $var, $($ty),*> LensesBase<$lt, ($($ty,)*), $dimn> for $impl_ty
where
$var: $(Adapter<$ty> +)*,
$($ty: $lt + Field),*
{
#[inline]
fn lens_base(self) -> LensBase<$lt, ($($ty,)*), $dimn> {
let &[$($dim),+] = self.shape() else {
unreachable!("an `ndarray` view's shape matches its dimensionality")
};
let &[$($stride),+] = self.strides() else {
unreachable!("an `ndarray` view's strides match its dimensionality")
};
$(
assert!(
isize::try_from($dim).is_ok(),
"flense: lens length must fit in `isize`; guarantee from ndarray invalid?",
);
)+
let base: *mut $var = self.as_ptr().cast_mut();
#[expect(
clippy::cast_possible_wrap,
reason = "performs const-time check on cast"
)]
let strides = {
let elem = const {
assert!(
size_of::<$var>() <= isize::MAX as usize,
"flense cannot lens elements larger than isize::MAX"
);
size_of::<$var>() as isize
};
Dim([$($stride * elem),+])
};
LensBase::new(
impl_lenses!(
@nest @ Cons => { $((
strides,
unsafe {
NonNull::new_unchecked(
base
.wrapping_byte_add(<$var as Adapter<$ty>>::OFFSET)
.cast()
)
}
)),* } => { } => { Nil }
),
Dim([$($dim),+]),
)
}
}
impl_lenses!(@ndarray @ $lt $var => { $($ty),* } $($rest)*);
};
(@ndarray_mut @ $lt:lifetime $var:ident => { $($ty:ident),* }) => {};
(@ndarray_mut @ $lt:lifetime $var:ident => { $($ty:ident),* }
=> $dimn:literal [$(($dim:ident, $stride:ident)),+]
=> { $(#[$impl_meta:meta])* $impl_ty:ty }
$($rest:tt)*
) => {
$(#[$impl_meta])*
impl<$lt, $var, $($ty),*> LensesBaseMut<$lt, ($($ty,)*), $dimn> for $impl_ty
where
$var: $(Adapter<$ty> +)*,
$($ty: $lt + Field),*
{
#[inline]
fn lens_base_mut(mut self) -> LensBaseMut<$lt, ($($ty,)*), $dimn> {
const {
assert!(
ranges_disjoint(&[$((
<$var as Adapter<$ty>>::OFFSET,
size_of::<<$ty as Field>::Type>(),
)),*]),
"flense: fields in a mutable `ndarray` lens must not alias overlapping bytes",
);
}
let &[$($dim),+] = self.shape() else {
unreachable!("an `ndarray` view's shape matches its dimensionality")
};
let &[$($stride),+] = self.strides() else {
unreachable!("an `ndarray` view's strides match its dimensionality")
};
$(
assert!(
isize::try_from($dim).is_ok(),
"flense: lens length must fit in `isize`; guarantee from ndarray invalid?",
);
)+
let base: *mut $var = self.as_mut_ptr();
#[expect(
clippy::cast_possible_wrap,
reason = "performs const-time check on cast"
)]
let strides = {
let elem = const {
assert!(
size_of::<$var>() <= isize::MAX as usize,
"flense cannot lens elements larger than isize::MAX"
);
size_of::<$var>() as isize
};
Dim([$($stride * elem),+])
};
LensBaseMut::new(
impl_lenses!(
@nest @ Cons => { $((
strides,
unsafe {
NonNull::new_unchecked(
base
.wrapping_byte_add(<$var as Adapter<$ty>>::OFFSET)
.cast()
)
}
)),* } => { } => { Nil }
),
Dim([$($dim),+]),
)
}
}
impl_lenses!(@ndarray_mut @ $lt $var => { $($ty),* } $($rest)*);
};
(@nest @ $ty:ident => { } => { } => { $($repr:tt)* }) => {
$($repr)*
};
(@nest @ $ty:ident => { } => { ($metadata:expr, $ptr:expr) $(, $rev:tt)* } => { $($repr:tt)* }) => {
impl_lenses!(@nest @ $ty => { } => { $($rev),* } => { $ty { dimensions: $metadata, ptr: $ptr, tail: $($repr)* } })
};
(@nest @ $ty:ident => { $head:tt $(, $items:tt)* } => { $($rev:tt),* } => { $($repr:tt)* }) => {
impl_lenses!(@nest @ $ty => { $($items),* } => { $head $(, $rev)* } => { $($repr)* })
}
}
impl_lenses!(1 @ A);
impl_lenses!(2 @ A, B);
impl_lenses!(3 @ A, B, C);
impl_lenses!(4 @ A, B, C, D);
impl_lenses!(5 @ A, B, C, D, E);
impl_lenses!(6 @ A, B, C, D, E, F);
impl_lenses!(7 @ A, B, C, D, E, F, G);
impl_lenses!(8 @ A, B, C, D, E, F, G, H);
impl_lenses!(9 @ A, B, C, D, E, F, G, H, I);
impl_lenses!(10 @ A, B, C, D, E, F, G, H, I, J);
impl_lenses!(11 @ A, B, C, D, E, F, G, H, I, J, K);
impl_lenses!(12 @ A, B, C, D, E, F, G, H, I, J, K, L);
impl_lenses!(13 @ A, B, C, D, E, F, G, H, I, J, K, L, M);
impl_lenses!(14 @ A, B, C, D, E, F, G, H, I, J, K, L, M, N);
impl_lenses!(15 @ A, B, C, D, E, F, G, H, I, J, K, L, M, N, O);
impl_lenses!(16 @ A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P);
#[must_use]
#[allow(
clippy::indexing_slicing,
reason = "indices are bounded by N at every site"
)]
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);
if a.start < b.end && b.start < a.end {
return false;
}
j += 1;
}
i += 1;
}
true
}