use core::ptr::{
self,
NonNull,
};
use crate::{
Adapter,
Field,
lens::{
Lens,
LensMut,
LensSlice,
LensSliceMut,
},
type_lists::{
ConsPtr,
ConsSlice,
Nil,
TupleSet,
},
};
pub trait Lenses<'a, T>
where
T: TupleSet,
{
fn lens(self) -> Lens<'a, T>;
}
pub trait LensesMut<'a, T>
where
T: TupleSet,
{
fn lens_mut(self) -> LensMut<'a, T>;
}
pub trait LensesSlice<'a, T>
where
T: TupleSet,
{
fn lens_slice(self) -> LensSlice<'a, T>;
}
pub trait LensesSliceMut<'a, T>
where
T: TupleSet,
{
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 => { $(
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",
);
}
let base: *mut T = ptr::from_mut(self);
LensMut::new(
impl_lenses!(
@nest @ ConsPtr => { $(
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 => { $((
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",
);
}
let len = self.len();
let base: *mut T = self.as_mut_ptr();
LensSliceMut::new(
impl_lenses!(
@nest @ ConsSlice => { $((
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);
#[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);
if a.start < b.end && b.start < a.end {
return false;
}
j += 1;
}
i += 1;
}
true
}