use core::{
mem::MaybeUninit,
ops::Deref, };
use crate::prelude::*;
pub struct OutOfBound;
impl From<OutOfBound> for Error {
#[inline(always)]
fn from(_: OutOfBound) -> Self {
ERANGE
}
}
#[diagnostic::on_unimplemented(message = "`{Self}` cannot be used to index `{T}`")]
#[doc(hidden)]
pub unsafe trait ProjectIndex<T: ?Sized>: Sized {
type Output: ?Sized;
fn get(self, slice: *mut T) -> Option<*mut Self::Output>;
#[inline(always)]
fn index(self, slice: *mut T) -> *mut Self::Output {
Self::get(self, slice).unwrap_or_else(|| build_error!())
}
}
unsafe impl<T, I, const N: usize> ProjectIndex<[T; N]> for I
where
I: ProjectIndex<[T]>,
{
type Output = <I as ProjectIndex<[T]>>::Output;
#[inline(always)]
fn get(self, slice: *mut [T; N]) -> Option<*mut Self::Output> {
<I as ProjectIndex<[T]>>::get(self, slice)
}
#[inline(always)]
fn index(self, slice: *mut [T; N]) -> *mut Self::Output {
<I as ProjectIndex<[T]>>::index(self, slice)
}
}
unsafe impl<T> ProjectIndex<[T]> for usize {
type Output = T;
#[inline(always)]
fn get(self, slice: *mut [T]) -> Option<*mut T> {
if self >= slice.len() {
None
} else {
Some(slice.cast::<T>().wrapping_add(self))
}
}
}
unsafe impl<T> ProjectIndex<[T]> for core::ops::Range<usize> {
type Output = [T];
#[inline(always)]
fn get(self, slice: *mut [T]) -> Option<*mut [T]> {
let new_len = self.end.checked_sub(self.start)?;
if self.end > slice.len() {
return None;
}
Some(core::ptr::slice_from_raw_parts_mut(
slice.cast::<T>().wrapping_add(self.start),
new_len,
))
}
}
unsafe impl<T> ProjectIndex<[T]> for core::ops::RangeTo<usize> {
type Output = [T];
#[inline(always)]
fn get(self, slice: *mut [T]) -> Option<*mut [T]> {
(0..self.end).get(slice)
}
}
unsafe impl<T> ProjectIndex<[T]> for core::ops::RangeFrom<usize> {
type Output = [T];
#[inline(always)]
fn get(self, slice: *mut [T]) -> Option<*mut [T]> {
(self.start..slice.len()).get(slice)
}
}
unsafe impl<T> ProjectIndex<[T]> for core::ops::RangeFull {
type Output = [T];
#[inline(always)]
fn get(self, slice: *mut [T]) -> Option<*mut [T]> {
Some(slice)
}
}
#[doc(hidden)]
pub unsafe trait ProjectField<const DEREF: bool> {
unsafe fn proj<F>(base: *mut Self, f: impl FnOnce(*mut Self) -> *mut F) -> *mut F;
}
unsafe impl<T> ProjectField<false> for T {
#[inline(always)]
unsafe fn proj<F>(base: *mut Self, f: impl FnOnce(*mut Self) -> *mut F) -> *mut F {
let mut place = MaybeUninit::uninit();
let place_base = place.as_mut_ptr();
let field = f(place_base);
let offset = unsafe { field.byte_offset_from(place_base) };
base.wrapping_byte_offset(offset).cast()
}
}
unsafe impl<T: Deref> ProjectField<true> for T {
#[inline(always)]
unsafe fn proj<F>(_: *mut Self, _: impl FnOnce(*mut Self) -> *mut F) -> *mut F {
build_error!("this function is a guard against `Deref` impl and is never invoked");
}
}
#[macro_export]
macro_rules! project_pointer {
(@gen $ptr:ident, ) => {};
(@gen $ptr:ident, .$field:tt $($rest:tt)*) => {
let $ptr = unsafe {
$crate::ptr::projection::ProjectField::proj($ptr, #[inline(always)] |ptr| {
if false {
let _ = &(*ptr).$field;
}
&raw mut (*ptr).$field
})
};
$crate::ptr::project!(@gen $ptr, $($rest)*)
};
(@gen $ptr:ident, [$index:expr]? $($rest:tt)*) => {
let $ptr = $crate::ptr::projection::ProjectIndex::get($index, $ptr)
.ok_or($crate::ptr::projection::OutOfBound)?;
$crate::ptr::project!(@gen $ptr, $($rest)*)
};
(@gen $ptr:ident, [$index:expr] $($rest:tt)*) => {
let $ptr = $crate::ptr::projection::ProjectIndex::index($index, $ptr);
$crate::ptr::project!(@gen $ptr, $($rest)*)
};
(mut $ptr:expr, $($proj:tt)*) => {{
let ptr: *mut _ = $ptr;
$crate::ptr::project!(@gen ptr, $($proj)*);
ptr
}};
($ptr:expr, $($proj:tt)*) => {{
let ptr = <*const _>::cast_mut($ptr);
$crate::ptr::project!(@gen ptr, $($proj)*);
ptr.cast_const()
}};
}