1#[cfg(feature = "alloc")]
4mod alloc;
5mod core;
6
7use ::core::{alloc::LayoutError, error::Error, fmt, ptr::NonNull};
8use ptr_meta::{from_raw_parts_mut, Pointee};
9use rancor::{fail, Fallible, ResultExt as _, Source, Strategy};
10
11#[cfg(feature = "alloc")]
12pub use self::alloc::*;
13pub use self::core::*;
14pub use crate::erased::{ErasedPtr, FromMetadata, Metadata};
15use crate::{traits::LayoutRaw, ArchiveUnsized, DeserializeUnsized};
16
17pub unsafe trait SharedPointer<T: Pointee + ?Sized> {
24 fn alloc(metadata: T::Metadata) -> Result<*mut T, LayoutError>;
26
27 unsafe fn from_value(ptr: *mut T) -> *mut T;
34
35 unsafe fn drop(ptr: *mut T);
42}
43
44pub enum PoolingState {
46 Started,
49 Pending,
53 Finished(ErasedPtr),
56}
57
58pub trait Pooling<E = <Self as Fallible>::Error> {
62 fn start_pooling(&mut self, address: usize) -> PoolingState;
64
65 unsafe fn finish_pooling(
73 &mut self,
74 address: usize,
75 ptr: ErasedPtr,
76 drop: unsafe fn(ErasedPtr),
77 ) -> Result<(), E>;
78}
79
80impl<T, E> Pooling<E> for Strategy<T, E>
81where
82 T: Pooling<E>,
83{
84 fn start_pooling(&mut self, address: usize) -> PoolingState {
85 T::start_pooling(self, address)
86 }
87
88 unsafe fn finish_pooling(
89 &mut self,
90 address: usize,
91 ptr: ErasedPtr,
92 drop: unsafe fn(ErasedPtr),
93 ) -> Result<(), E> {
94 unsafe { T::finish_pooling(self, address, ptr, drop) }
97 }
98}
99
100#[derive(Debug)]
101struct CyclicSharedPointerError;
102
103impl fmt::Display for CyclicSharedPointerError {
104 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
105 write!(
106 f,
107 "encountered cyclic shared pointers while deserializing\nhelp: \
108 change your deserialization strategy to `Unpool` or use the \
109 `Unpool` wrapper type to break the cycle",
110 )
111 }
112}
113
114impl Error for CyclicSharedPointerError {}
115
116pub trait PoolingExt<E>: Pooling<E> {
118 fn deserialize_shared<T, P>(
122 &mut self,
123 value: &T::Archived,
124 ) -> Result<*mut T, Self::Error>
125 where
126 T: ArchiveUnsized + Pointee + LayoutRaw + ?Sized,
127 T::Metadata: Into<Metadata> + FromMetadata,
128 T::Archived: DeserializeUnsized<T, Self>,
129 P: SharedPointer<T>,
130 Self: Fallible<Error = E>,
131 E: Source,
132 {
133 unsafe fn drop_shared<T, P>(ptr: ErasedPtr)
134 where
135 T: Pointee + ?Sized,
136 T::Metadata: FromMetadata,
137 P: SharedPointer<T>,
138 {
139 unsafe { P::drop(ptr.downcast_unchecked::<T>()) }
140 }
141
142 let address = value as *const T::Archived as *const () as usize;
143 let metadata = T::Archived::deserialize_metadata(value);
144
145 match self.start_pooling(address) {
146 PoolingState::Started => {
147 let out = P::alloc(metadata).into_error()?;
148 unsafe { value.deserialize_unsized(self, out)? };
149 let ptr = unsafe { NonNull::new_unchecked(P::from_value(out)) };
150
151 unsafe {
152 self.finish_pooling(
153 address,
154 ErasedPtr::new(ptr.as_ptr()),
155 drop_shared::<T, P>,
156 )?;
157 }
158
159 Ok(ptr.as_ptr())
160 }
161 PoolingState::Pending => fail!(CyclicSharedPointerError),
162 PoolingState::Finished(ptr) => {
163 Ok(from_raw_parts_mut(ptr.data_address(), metadata))
164 }
165 }
166 }
167}
168
169impl<T, E> PoolingExt<E> for T where T: Pooling<E> + ?Sized {}