#[cfg(feature = "alloc")]
mod alloc;
mod core;
use ::core::{
alloc::LayoutError, error::Error, fmt, mem::transmute, ptr::NonNull,
};
use ptr_meta::{from_raw_parts_mut, metadata, DynMetadata, Pointee};
use rancor::{fail, Fallible, ResultExt as _, Source, Strategy};
#[cfg(feature = "alloc")]
pub use self::alloc::*;
pub use self::core::*;
use crate::{traits::LayoutRaw, ArchiveUnsized, DeserializeUnsized};
#[derive(Clone, Copy)]
pub union Metadata {
unit: (),
usize: usize,
vtable: DynMetadata<()>,
}
impl fmt::Debug for Metadata {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "<metadata>")
}
}
impl From<()> for Metadata {
fn from(value: ()) -> Self {
Self { unit: value }
}
}
impl From<usize> for Metadata {
fn from(value: usize) -> Self {
Self { usize: value }
}
}
impl<T: ?Sized> From<DynMetadata<T>> for Metadata {
fn from(value: DynMetadata<T>) -> Self {
Self {
vtable: unsafe {
transmute::<DynMetadata<T>, DynMetadata<()>>(value)
},
}
}
}
pub trait FromMetadata {
unsafe fn from_metadata(metadata: Metadata) -> Self;
}
impl FromMetadata for () {
unsafe fn from_metadata(metadata: Metadata) -> Self {
unsafe { metadata.unit }
}
}
impl FromMetadata for usize {
unsafe fn from_metadata(metadata: Metadata) -> Self {
unsafe { metadata.usize }
}
}
impl<T: ?Sized> FromMetadata for DynMetadata<T> {
unsafe fn from_metadata(metadata: Metadata) -> Self {
unsafe { transmute::<DynMetadata<()>, DynMetadata<T>>(metadata.vtable) }
}
}
#[derive(Clone, Copy, Debug)]
pub struct ErasedPtr {
data_address: NonNull<()>,
metadata: Metadata,
}
impl ErasedPtr {
#[inline]
pub fn new<T>(ptr: NonNull<T>) -> Self
where
T: Pointee + ?Sized,
T::Metadata: Into<Metadata>,
{
Self {
data_address: ptr.cast(),
metadata: metadata(ptr.as_ptr()).into(),
}
}
#[inline]
pub fn data_address(&self) -> *mut () {
self.data_address.as_ptr()
}
#[inline]
unsafe fn downcast_unchecked<T>(&self) -> *mut T
where
T: Pointee + ?Sized,
T::Metadata: FromMetadata,
{
from_raw_parts_mut(self.data_address.as_ptr(), unsafe {
T::Metadata::from_metadata(self.metadata)
})
}
}
pub unsafe trait SharedPointer<T: Pointee + ?Sized> {
fn alloc(metadata: T::Metadata) -> Result<*mut T, LayoutError>;
unsafe fn from_value(ptr: *mut T) -> *mut T;
unsafe fn drop(ptr: *mut T);
}
pub enum PoolingState {
Started,
Pending,
Finished(ErasedPtr),
}
pub trait Pooling<E = <Self as Fallible>::Error> {
fn start_pooling(&mut self, address: usize) -> PoolingState;
unsafe fn finish_pooling(
&mut self,
address: usize,
ptr: ErasedPtr,
drop: unsafe fn(ErasedPtr),
) -> Result<(), E>;
}
impl<T, E> Pooling<E> for Strategy<T, E>
where
T: Pooling<E>,
{
fn start_pooling(&mut self, address: usize) -> PoolingState {
T::start_pooling(self, address)
}
unsafe fn finish_pooling(
&mut self,
address: usize,
ptr: ErasedPtr,
drop: unsafe fn(ErasedPtr),
) -> Result<(), E> {
unsafe { T::finish_pooling(self, address, ptr, drop) }
}
}
#[derive(Debug)]
struct CyclicSharedPointerError;
impl fmt::Display for CyclicSharedPointerError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"encountered cyclic shared pointers while deserializing\nhelp: \
change your deserialization strategy to `Unpool` or use the \
`Unpool` wrapper type to break the cycle",
)
}
}
impl Error for CyclicSharedPointerError {}
pub trait PoolingExt<E>: Pooling<E> {
fn deserialize_shared<T, P>(
&mut self,
value: &T::Archived,
) -> Result<*mut T, Self::Error>
where
T: ArchiveUnsized + Pointee + LayoutRaw + ?Sized,
T::Metadata: Into<Metadata> + FromMetadata,
T::Archived: DeserializeUnsized<T, Self>,
P: SharedPointer<T>,
Self: Fallible<Error = E>,
E: Source,
{
unsafe fn drop_shared<T, P>(ptr: ErasedPtr)
where
T: Pointee + ?Sized,
T::Metadata: FromMetadata,
P: SharedPointer<T>,
{
unsafe { P::drop(ptr.downcast_unchecked::<T>()) }
}
let address = value as *const T::Archived as *const () as usize;
let metadata = T::Archived::deserialize_metadata(value);
match self.start_pooling(address) {
PoolingState::Started => {
let out = P::alloc(metadata).into_error()?;
unsafe { value.deserialize_unsized(self, out)? };
let ptr = unsafe { NonNull::new_unchecked(P::from_value(out)) };
unsafe {
self.finish_pooling(
address,
ErasedPtr::new(ptr),
drop_shared::<T, P>,
)?;
}
Ok(ptr.as_ptr())
}
PoolingState::Pending => fail!(CyclicSharedPointerError),
PoolingState::Finished(ptr) => {
Ok(from_raw_parts_mut(ptr.data_address.as_ptr(), metadata))
}
}
}
}
impl<T, E> PoolingExt<E> for T where T: Pooling<E> + ?Sized {}