use core::{
error::Error,
fmt,
marker::{PhantomData, PhantomPinned},
ptr::addr_of_mut,
};
use munge::munge;
use rancor::{fail, Panic, ResultExt as _, Source};
use crate::{
primitive::{
ArchivedI16, ArchivedI32, ArchivedI64, ArchivedU16, ArchivedU32,
ArchivedU64,
},
seal::Seal,
traits::{ArchivePointee, NoUndef},
Place, Portable,
};
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
struct IsizeOverflow;
impl fmt::Display for IsizeOverflow {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "the offset overflowed the range of `isize`")
}
}
impl Error for IsizeOverflow {}
pub trait Offset: Copy + NoUndef {
fn from_isize<E: Source>(value: isize) -> Result<Self, E>;
fn to_isize(self) -> isize;
}
macro_rules! impl_offset_single_byte {
($ty:ty) => {
impl Offset for $ty {
fn from_isize<E: Source>(value: isize) -> Result<Self, E> {
Self::try_from(value).into_error()
}
#[inline]
fn to_isize(self) -> isize {
self as isize
}
}
};
}
impl_offset_single_byte!(i8);
impl_offset_single_byte!(u8);
macro_rules! impl_offset_multi_byte {
($ty:ty, $archived:ty) => {
impl Offset for $archived {
fn from_isize<E: Source>(value: isize) -> Result<Self, E> {
Ok(<$archived>::from_native(
<$ty>::try_from(value).into_error()?,
))
}
#[inline]
fn to_isize(self) -> isize {
self.to_native() as isize
}
}
};
}
impl_offset_multi_byte!(i16, ArchivedI16);
impl_offset_multi_byte!(i32, ArchivedI32);
impl_offset_multi_byte!(i64, ArchivedI64);
impl_offset_multi_byte!(u16, ArchivedU16);
impl_offset_multi_byte!(u32, ArchivedU32);
impl_offset_multi_byte!(u64, ArchivedU64);
#[cfg_attr(feature = "bytecheck", derive(bytecheck::CheckBytes))]
#[derive(Portable)]
#[rkyv(crate)]
#[repr(transparent)]
pub struct RawRelPtr<O> {
offset: O,
_phantom: PhantomPinned,
}
pub fn signed_offset<E: Source>(from: usize, to: usize) -> Result<isize, E> {
let (result, overflow) = to.overflowing_sub(from);
if (!overflow && result <= (isize::MAX as usize))
|| (overflow && result >= (isize::MIN as usize))
{
Ok(result as isize)
} else {
fail!(IsizeOverflow);
}
}
impl<O: Offset> RawRelPtr<O> {
pub fn try_emplace_invalid<E: Source>(out: Place<Self>) -> Result<(), E> {
Self::try_emplace::<E>(out.pos() + 1, out)
}
pub fn emplace_invalid(out: Place<Self>) {
Self::try_emplace_invalid::<Panic>(out).always_ok();
}
pub fn try_emplace<E: Source>(
to: usize,
out: Place<Self>,
) -> Result<(), E> {
let offset = O::from_isize(signed_offset(out.pos(), to)?)?;
munge!(let Self { offset: out_offset, _phantom: _ } = out);
out_offset.write(offset);
Ok(())
}
pub fn emplace(to: usize, out: Place<Self>) {
Self::try_emplace::<Panic>(to, out).always_ok()
}
pub fn base_raw(this: *mut Self) -> *mut u8 {
this.cast()
}
pub unsafe fn offset_raw(this: *mut Self) -> isize {
unsafe { addr_of_mut!((*this).offset).read().to_isize() }
}
pub unsafe fn as_ptr_raw(this: *mut Self) -> *mut () {
unsafe { Self::base_raw(this).offset(Self::offset_raw(this)).cast() }
}
pub unsafe fn as_ptr_wrapping_raw(this: *mut Self) -> *mut () {
let offset = unsafe { Self::offset_raw(this) };
Self::base_raw(this).wrapping_offset(offset).cast()
}
pub unsafe fn is_invalid_raw(this: *mut Self) -> bool {
unsafe { Self::offset_raw(this) == 1 }
}
pub fn base(&self) -> *const u8 {
Self::base_raw((self as *const Self).cast_mut()).cast_const()
}
pub fn base_mut(this: Seal<'_, Self>) -> *mut u8 {
let this = unsafe { Seal::unseal_unchecked(this) };
Self::base_raw(this as *mut Self)
}
pub fn offset(&self) -> isize {
let this = self as *const Self;
unsafe { Self::offset_raw(this.cast_mut()) }
}
pub fn is_invalid(&self) -> bool {
let this = self as *const Self;
unsafe { Self::is_invalid_raw(this.cast_mut()) }
}
pub unsafe fn as_ptr(&self) -> *const () {
let this = self as *const Self;
unsafe { Self::as_ptr_raw(this.cast_mut()).cast_const() }
}
pub unsafe fn as_mut_ptr(this: Seal<'_, Self>) -> *mut () {
let this = unsafe { Seal::unseal_unchecked(this) };
unsafe { Self::as_ptr_raw(this as *mut Self) }
}
pub fn as_ptr_wrapping(&self) -> *const () {
let this = self as *const Self;
unsafe { Self::as_ptr_wrapping_raw(this.cast_mut()).cast_const() }
}
pub fn as_mut_ptr_wrapping(this: Seal<'_, Self>) -> *mut () {
let this = unsafe { Seal::unseal_unchecked(this) };
unsafe { Self::as_ptr_wrapping_raw(this as *mut Self) }
}
}
impl<O: fmt::Debug> fmt::Debug for RawRelPtr<O> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("RawRelPtr")
.field("offset", &self.offset)
.finish()
}
}
impl<O: Offset> fmt::Pointer for RawRelPtr<O> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Pointer::fmt(&self.as_ptr_wrapping(), f)
}
}
pub type RawRelPtrI8 = RawRelPtr<i8>;
pub type RawRelPtrI16 = RawRelPtr<ArchivedI16>;
pub type RawRelPtrI32 = RawRelPtr<ArchivedI32>;
pub type RawRelPtrI64 = RawRelPtr<ArchivedI64>;
pub type RawRelPtrU8 = RawRelPtr<u8>;
pub type RawRelPtrU16 = RawRelPtr<ArchivedU16>;
pub type RawRelPtrU32 = RawRelPtr<ArchivedU32>;
pub type RawRelPtrU64 = RawRelPtr<ArchivedU64>;
#[derive(Portable)]
#[cfg_attr(feature = "bytecheck", derive(bytecheck::CheckBytes))]
#[rkyv(crate)]
#[repr(C)]
pub struct RelPtr<T: ArchivePointee + ?Sized, O> {
raw_ptr: RawRelPtr<O>,
metadata: T::ArchivedMetadata,
_phantom: PhantomData<T>,
}
impl<T, O: Offset> RelPtr<T, O> {
pub fn try_emplace<E: Source>(
to: usize,
out: Place<Self>,
) -> Result<(), E> {
munge!(let RelPtr { raw_ptr, metadata: _, _phantom: _ } = out);
RawRelPtr::try_emplace(to, raw_ptr)
}
pub fn emplace(to: usize, out: Place<Self>) {
Self::try_emplace::<Panic>(to, out).always_ok()
}
}
impl<T: ArchivePointee + ?Sized, O: Offset> RelPtr<T, O> {
pub fn try_emplace_invalid<E: Source>(out: Place<Self>) -> Result<(), E> {
munge!(let RelPtr { raw_ptr, metadata, _phantom: _ } = out);
RawRelPtr::try_emplace_invalid(raw_ptr)?;
metadata.write(Default::default());
Ok(())
}
pub fn emplace_invalid(out: Place<Self>) {
Self::try_emplace_invalid::<Panic>(out).always_ok()
}
pub fn try_emplace_unsized<E: Source>(
to: usize,
metadata: T::ArchivedMetadata,
out: Place<Self>,
) -> Result<(), E> {
munge!(let RelPtr { raw_ptr, metadata: out_meta, _phantom: _ } = out);
RawRelPtr::try_emplace(to, raw_ptr)?;
out_meta.write(metadata);
Ok(())
}
pub fn emplace_unsized(
to: usize,
metadata: T::ArchivedMetadata,
out: Place<Self>,
) {
Self::try_emplace_unsized::<Panic>(to, metadata, out).always_ok()
}
pub fn base_raw(this: *mut Self) -> *mut u8 {
RawRelPtr::<O>::base_raw(this.cast())
}
pub unsafe fn offset_raw(this: *mut Self) -> isize {
unsafe { RawRelPtr::<O>::offset_raw(this.cast()) }
}
pub unsafe fn as_ptr_raw(this: *mut Self) -> *mut T {
let data_address = unsafe { RawRelPtr::<O>::as_ptr_raw(this.cast()) };
let metadata = unsafe { T::pointer_metadata(&(*this).metadata) };
ptr_meta::from_raw_parts_mut(data_address, metadata)
}
pub unsafe fn as_ptr_wrapping_raw(this: *mut Self) -> *mut T {
let data_address =
unsafe { RawRelPtr::<O>::as_ptr_wrapping_raw(this.cast()) };
let metadata = unsafe { T::pointer_metadata(&(*this).metadata) };
ptr_meta::from_raw_parts_mut(data_address, metadata)
}
pub unsafe fn is_invalid_raw(this: *mut Self) -> bool {
unsafe { RawRelPtr::<O>::is_invalid_raw(this.cast()) }
}
pub fn base(&self) -> *const u8 {
self.raw_ptr.base()
}
pub fn base_mut(this: Seal<'_, Self>) -> *mut u8 {
munge!(let Self { raw_ptr, .. } = this);
RawRelPtr::base_mut(raw_ptr)
}
pub fn offset(&self) -> isize {
self.raw_ptr.offset()
}
pub fn is_invalid(&self) -> bool {
self.raw_ptr.is_invalid()
}
pub fn metadata(&self) -> &T::ArchivedMetadata {
&self.metadata
}
pub unsafe fn as_ptr(&self) -> *const T {
ptr_meta::from_raw_parts(
unsafe { self.raw_ptr.as_ptr() },
T::pointer_metadata(&self.metadata),
)
}
pub unsafe fn as_mut_ptr(this: Seal<'_, Self>) -> *mut T {
munge!(let Self { raw_ptr, metadata, _phantom: _ } = this);
let metadata = T::pointer_metadata(&*metadata);
ptr_meta::from_raw_parts_mut(
unsafe { RawRelPtr::as_mut_ptr(raw_ptr) },
metadata,
)
}
pub fn as_ptr_wrapping(&self) -> *const T {
ptr_meta::from_raw_parts(
self.raw_ptr.as_ptr_wrapping(),
T::pointer_metadata(&self.metadata),
)
}
pub fn as_mut_ptr_wrapping(this: Seal<'_, Self>) -> *mut T {
munge!(let Self { raw_ptr, metadata, _phantom: _ } = this);
let metadata = T::pointer_metadata(&*metadata);
ptr_meta::from_raw_parts_mut(
RawRelPtr::as_mut_ptr_wrapping(raw_ptr),
metadata,
)
}
}
impl<T: ArchivePointee + ?Sized, O: fmt::Debug> fmt::Debug for RelPtr<T, O>
where
T::ArchivedMetadata: fmt::Debug,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("RelPtr")
.field("raw_ptr", &self.raw_ptr)
.field("metadata", &self.metadata)
.finish()
}
}
impl<T: ArchivePointee + ?Sized, O: Offset> fmt::Pointer for RelPtr<T, O> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Pointer::fmt(&self.as_ptr_wrapping(), f)
}
}