use crate::metadata::PointerRecomposition;
use crate::offset::{Nullable, Offset, Ptr};
use crate::pointer::unreachable::UncheckedOptionExt as _;
use core::marker::PhantomData;
use core::mem::MaybeUninit;
use std::ptr::NonNull;
type GuardPayload<T> = Option<NonNull<T>>;
#[inline]
fn guard_payload_from<T: ?Sized>(target: Option<NonNull<T>>) -> GuardPayload<T> {
#[cfg(feature = "debug-guards")]
{
target
}
#[cfg(not(feature = "debug-guards"))]
{
let _ = target;
None
}
}
#[inline]
fn guard_payload_empty<T: ?Sized>() -> GuardPayload<T> {
guard_payload_from::<T>(None)
}
#[inline]
fn guard_extract_target<T: ?Sized>(payload: GuardPayload<T>) -> Option<NonNull<T>> {
#[cfg(feature = "debug-guards")]
{
payload
}
#[cfg(not(feature = "debug-guards"))]
{
let _ = payload;
None
}
}
#[inline]
fn guard_assert_target<T: ?Sized>(payload: GuardPayload<T>, target: *mut u8) {
#[cfg(feature = "debug-guards")]
{
if let Some(expected) = payload {
debug_assert_eq!(expected.as_ptr() as *mut u8, target);
}
}
#[cfg(not(feature = "debug-guards"))]
{
let _ = (payload, target);
}
}
enum RefState<T: ?Sized> {
Unset,
Ready(GuardPayload<T>),
}
impl<T: ?Sized> Copy for RefState<T> {}
impl<T: ?Sized> Clone for RefState<T> {
fn clone(&self) -> Self {
*self
}
}
#[inline(always)]
fn nn_to_ptr<T: ?Sized>(nn: Ptr<T>) -> *mut T {
unsafe { core::mem::transmute(nn) }
}
pub struct SelfRef<T: ?Sized + PointerRecomposition, I: Offset = isize>(
I,
MaybeUninit<T::Components>,
PhantomData<*mut T>,
RefState<T>,
);
impl<T: ?Sized + PointerRecomposition, I: Offset> Copy for SelfRef<T, I> {}
impl<T: ?Sized + PointerRecomposition, I: Offset> Clone for SelfRef<T, I> {
fn clone(&self) -> Self {
*self
}
}
impl<T: ?Sized + PointerRecomposition, I: Offset> Eq for SelfRef<T, I> {}
impl<T: ?Sized + PointerRecomposition, I: Offset> PartialEq for SelfRef<T, I> {
fn eq(&self, other: &Self) -> bool {
match (self.components_if_ready(), other.components_if_ready()) {
(None, None) => true,
(Some(lhs), Some(rhs)) => self.0 == other.0 && lhs == rhs,
_ => false,
}
}
}
impl<T: ?Sized + PointerRecomposition, I: Nullable> SelfRef<T, I> {
#[inline(always)]
pub fn null() -> Self {
Self(I::NULL, MaybeUninit::uninit(), PhantomData, RefState::Unset)
}
#[inline(always)]
pub fn is_null(&self) -> bool {
self.0 == I::NULL
}
}
impl<T: ?Sized + PointerRecomposition, I: Offset> SelfRef<T, I> {
#[inline]
pub fn is_ready(&self) -> bool {
matches!(self.3, RefState::Ready(_))
}
#[inline]
pub fn components_if_ready(&self) -> Option<T::Components> {
match self.3 {
RefState::Ready(_) => Some(unsafe { self.components_unchecked() }),
RefState::Unset => None,
}
}
#[inline]
unsafe fn components_unchecked(&self) -> T::Components {
*self.1.assume_init_ref()
}
#[inline]
pub fn offset(&self) -> I {
self.0
}
#[inline]
pub fn from_parts(offset: I, components: T::Components) -> Self {
Self(
offset,
MaybeUninit::new(components),
PhantomData,
RefState::Ready(guard_payload_empty::<T>()),
)
}
#[inline]
pub fn from_parts_with_target(
offset: I,
components: T::Components,
target: Option<NonNull<T>>,
) -> Self {
Self(
offset,
MaybeUninit::new(components),
PhantomData,
RefState::Ready(guard_payload_from::<T>(target)),
)
}
#[inline]
pub fn parts_if_ready(&self) -> Option<(I, T::Components)> {
self.components_if_ready()
.map(|components| (self.0, components))
}
#[inline]
pub fn parts_with_target_if_ready(&self) -> Option<(I, T::Components, Option<NonNull<T>>)> {
self.components_if_ready().map(|components| match self.3 {
RefState::Ready(payload) => (self.0, components, guard_extract_target::<T>(payload)),
RefState::Unset => unreachable!(),
})
}
#[inline]
pub fn set(&mut self, value: &mut T) -> Result<(), I::Error> {
self.0 = I::sub(value as *mut T as _, self as *mut Self as _)?;
self.1 = MaybeUninit::new(T::decompose(value));
self.3 = RefState::Ready(guard_payload_empty::<T>());
Ok(())
}
#[inline]
pub unsafe fn set_unchecked(&mut self, value: *mut T) {
debug_assert!(!value.is_null());
self.0 = I::sub_unchecked(value as _, self as *mut Self as _);
self.1 = MaybeUninit::new(T::decompose(&*value));
self.3 = RefState::Ready(guard_payload_empty::<T>());
}
#[inline]
unsafe fn as_raw_unchecked_impl(&mut self) -> *mut T {
debug_assert!(self.is_ready());
let base = self as *mut Self as *const u8;
let target = self.0.add(base);
let components = unsafe { self.components_unchecked() };
nn_to_ptr(T::recompose(NonNull::new(target), components))
}
#[inline]
pub unsafe fn as_raw_unchecked(&mut self) -> *mut T {
self.as_raw_unchecked_impl()
}
#[inline]
pub unsafe fn as_non_null_unchecked(&mut self) -> NonNull<T> {
debug_assert!(self.is_ready());
let base = self as *mut Self as *const u8;
let target = self.0.add(base);
let components = unsafe { self.components_unchecked() };
if let RefState::Ready(payload) = self.3 {
guard_assert_target::<T>(payload, target);
}
T::recompose(NonNull::new(target), components)
.unchecked_unwrap("Tried to use an unset relative pointer, this is UB in release mode!")
}
#[inline]
pub unsafe fn as_ref_unchecked(&mut self) -> &T {
&*self.as_raw_unchecked_impl()
}
#[inline]
pub unsafe fn get_ref_from_base_unchecked<'a>(&self, base: *const u8) -> &'a T {
debug_assert!(self.is_ready());
let self_ptr = self as *const Self as *const u8;
let d_self = self_ptr.offset_from(base);
let at_self = base.wrapping_offset(d_self);
let components = unsafe { self.components_unchecked() };
let target = self.0.add(at_self);
if let RefState::Ready(payload) = self.3 {
guard_assert_target::<T>(payload, target);
}
let p = nn_to_ptr(T::recompose(NonNull::new(target), components));
&*p
}
#[inline]
pub unsafe fn get_mut_from_base_unchecked<'a>(&self, base: *mut u8) -> &'a mut T {
debug_assert!(self.is_ready());
let base_ptr = base.cast_const();
let self_ptr = self as *const Self as *const u8;
let d_self = self_ptr.offset_from(base_ptr);
let at_self = base_ptr.wrapping_offset(d_self);
let components = unsafe { self.components_unchecked() };
let target = self.0.add(at_self);
if let RefState::Ready(payload) = self.3 {
guard_assert_target::<T>(payload, target);
}
let p = nn_to_ptr(T::recompose(NonNull::new(target), components));
&mut *p
}
#[inline]
pub unsafe fn as_mut_unchecked(&mut self) -> &mut T {
&mut *self.as_raw_unchecked()
}
}
impl<T: ?Sized + PointerRecomposition, I: Nullable> SelfRef<T, I> {
#[inline]
pub unsafe fn as_raw(&mut self) -> *mut T {
nn_to_ptr(self.as_non_null())
}
#[inline]
pub unsafe fn as_non_null(&mut self) -> Ptr<T> {
if !self.is_ready() {
return None;
}
let base = self as *mut Self as *const u8;
let target = self.0.add(base);
let components = unsafe { self.components_unchecked() };
if let RefState::Ready(payload) = self.3 {
guard_assert_target::<T>(payload, target);
}
T::recompose(NonNull::new(target), components)
}
#[inline]
pub unsafe fn as_ref(&mut self) -> Option<&T> {
self.as_non_null().map(|ptr| unsafe { &*ptr.as_ptr() })
}
#[inline]
pub unsafe fn as_mut(&mut self) -> Option<&mut T> {
self.as_non_null()
.map(|mut_ptr| unsafe { &mut *mut_ptr.as_ptr() })
}
}