Skip to main content

rkyv/de/pooling/
mod.rs

1//! Deserializers that can be used standalone and provide basic capabilities.
2
3#[cfg(feature = "alloc")]
4mod alloc;
5mod core;
6
7use ::core::{
8    alloc::LayoutError, error::Error, fmt, mem::transmute, ptr::NonNull,
9};
10use ptr_meta::{from_raw_parts_mut, metadata, DynMetadata, Pointee};
11use rancor::{fail, Fallible, ResultExt as _, Source, Strategy};
12
13#[cfg(feature = "alloc")]
14pub use self::alloc::*;
15pub use self::core::*;
16use crate::{traits::LayoutRaw, ArchiveUnsized, DeserializeUnsized};
17
18/// Type-erased pointer metadata.
19#[derive(Clone, Copy)]
20pub union Metadata {
21    unit: (),
22    usize: usize,
23    vtable: DynMetadata<()>,
24}
25
26impl fmt::Debug for Metadata {
27    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
28        write!(f, "<metadata>")
29    }
30}
31
32impl From<()> for Metadata {
33    fn from(value: ()) -> Self {
34        Self { unit: value }
35    }
36}
37
38impl From<usize> for Metadata {
39    fn from(value: usize) -> Self {
40        Self { usize: value }
41    }
42}
43
44impl<T: ?Sized> From<DynMetadata<T>> for Metadata {
45    fn from(value: DynMetadata<T>) -> Self {
46        Self {
47            vtable: unsafe {
48                transmute::<DynMetadata<T>, DynMetadata<()>>(value)
49            },
50        }
51    }
52}
53
54/// A type which can be extracted from `Metadata`.
55pub trait FromMetadata {
56    /// Extracts this type from [`Metadata`].
57    ///
58    /// # Safety
59    ///
60    /// The metadata must have been created by calling `Metadata::from` on a
61    /// value of this type.
62    unsafe fn from_metadata(metadata: Metadata) -> Self;
63}
64
65// These impls are sound because `Metadata` has the type-level invariant that
66// `From` will only be called on `Metadata` created from pointers with the
67// corresponding metadata.
68
69impl FromMetadata for () {
70    unsafe fn from_metadata(metadata: Metadata) -> Self {
71        unsafe { metadata.unit }
72    }
73}
74
75impl FromMetadata for usize {
76    unsafe fn from_metadata(metadata: Metadata) -> Self {
77        unsafe { metadata.usize }
78    }
79}
80
81impl<T: ?Sized> FromMetadata for DynMetadata<T> {
82    unsafe fn from_metadata(metadata: Metadata) -> Self {
83        unsafe { transmute::<DynMetadata<()>, DynMetadata<T>>(metadata.vtable) }
84    }
85}
86
87/// A type-erased pointer.
88#[derive(Clone, Copy, Debug)]
89pub struct ErasedPtr {
90    data_address: NonNull<()>,
91    metadata: Metadata,
92}
93
94impl ErasedPtr {
95    /// Returns an erased pointer corresponding to the given pointer.
96    #[inline]
97    pub fn new<T>(ptr: NonNull<T>) -> Self
98    where
99        T: Pointee + ?Sized,
100        T::Metadata: Into<Metadata>,
101    {
102        Self {
103            data_address: ptr.cast(),
104            metadata: metadata(ptr.as_ptr()).into(),
105        }
106    }
107
108    /// Returns the data address corresponding to this erased pointer.
109    #[inline]
110    pub fn data_address(&self) -> *mut () {
111        self.data_address.as_ptr()
112    }
113
114    /// # Safety
115    ///
116    /// `self` must be created from a valid pointer to `T`.
117    #[inline]
118    unsafe fn downcast_unchecked<T>(&self) -> *mut T
119    where
120        T: Pointee + ?Sized,
121        T::Metadata: FromMetadata,
122    {
123        from_raw_parts_mut(self.data_address.as_ptr(), unsafe {
124            T::Metadata::from_metadata(self.metadata)
125        })
126    }
127}
128
129/// A deserializable shared pointer type.
130///
131/// # Safety
132///
133/// `alloc` and `from_value` must return pointers which are non-null, writeable,
134/// and properly aligned for `T`.
135pub unsafe trait SharedPointer<T: Pointee + ?Sized> {
136    /// Allocates space for a value with the given metadata.
137    fn alloc(metadata: T::Metadata) -> Result<*mut T, LayoutError>;
138
139    /// Creates a new `Self` from a pointer to a valid `T`.
140    ///
141    /// # Safety
142    ///
143    /// `ptr` must have been allocated via `alloc`. `from_value` must not have
144    /// been called on `ptr` yet.
145    unsafe fn from_value(ptr: *mut T) -> *mut T;
146
147    /// Drops a pointer created by `from_value`.
148    ///
149    /// # Safety
150    ///
151    /// - `ptr` must have been created using `from_value`.
152    /// - `drop` must only be called once per `ptr`.
153    unsafe fn drop(ptr: *mut T);
154}
155
156/// The result of starting to deserialize a shared pointer.
157pub enum PoolingState {
158    /// The caller started pooling this value. They should proceed to
159    /// deserialize the shared value and call `finish_pooling`.
160    Started,
161    /// Another caller started pooling this value, but has not finished yet.
162    /// This can only occur with cyclic shared pointer structures, and so rkyv
163    /// treats this as an error by default.
164    Pending,
165    /// This value has already been pooled. The caller should use the returned
166    /// pointer to pool its value.
167    Finished(ErasedPtr),
168}
169
170/// A shared pointer deserialization strategy.
171///
172/// This trait is required to deserialize `Rc` and `Arc`.
173pub trait Pooling<E = <Self as Fallible>::Error> {
174    /// Starts pooling the value associated with the given address.
175    fn start_pooling(&mut self, address: usize) -> PoolingState;
176
177    /// Finishes pooling the value associated with the given address.
178    ///
179    /// Returns an error if the given address was not pending.
180    ///
181    /// # Safety
182    ///
183    /// The given `drop` function must be valid to call with `ptr`.
184    unsafe fn finish_pooling(
185        &mut self,
186        address: usize,
187        ptr: ErasedPtr,
188        drop: unsafe fn(ErasedPtr),
189    ) -> Result<(), E>;
190}
191
192impl<T, E> Pooling<E> for Strategy<T, E>
193where
194    T: Pooling<E>,
195{
196    fn start_pooling(&mut self, address: usize) -> PoolingState {
197        T::start_pooling(self, address)
198    }
199
200    unsafe fn finish_pooling(
201        &mut self,
202        address: usize,
203        ptr: ErasedPtr,
204        drop: unsafe fn(ErasedPtr),
205    ) -> Result<(), E> {
206        // SAFETY: The safety requirements for `finish_pooling` are the same as
207        // the requirements for calling this function.
208        unsafe { T::finish_pooling(self, address, ptr, drop) }
209    }
210}
211
212#[derive(Debug)]
213struct CyclicSharedPointerError;
214
215impl fmt::Display for CyclicSharedPointerError {
216    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
217        write!(
218            f,
219            "encountered cyclic shared pointers while deserializing\nhelp: \
220             change your deserialization strategy to `Unpool` or use the \
221             `Unpool` wrapper type to break the cycle",
222        )
223    }
224}
225
226impl Error for CyclicSharedPointerError {}
227
228/// Helper methods for [`Pooling`].
229pub trait PoolingExt<E>: Pooling<E> {
230    /// Checks whether the given reference has been deserialized and either uses
231    /// the existing shared pointer to it, or deserializes it and converts
232    /// it to a shared pointer with `to_shared`.
233    fn deserialize_shared<T, P>(
234        &mut self,
235        value: &T::Archived,
236    ) -> Result<*mut T, Self::Error>
237    where
238        T: ArchiveUnsized + Pointee + LayoutRaw + ?Sized,
239        T::Metadata: Into<Metadata> + FromMetadata,
240        T::Archived: DeserializeUnsized<T, Self>,
241        P: SharedPointer<T>,
242        Self: Fallible<Error = E>,
243        E: Source,
244    {
245        unsafe fn drop_shared<T, P>(ptr: ErasedPtr)
246        where
247            T: Pointee + ?Sized,
248            T::Metadata: FromMetadata,
249            P: SharedPointer<T>,
250        {
251            unsafe { P::drop(ptr.downcast_unchecked::<T>()) }
252        }
253
254        let address = value as *const T::Archived as *const () as usize;
255        let metadata = T::Archived::deserialize_metadata(value);
256
257        match self.start_pooling(address) {
258            PoolingState::Started => {
259                let out = P::alloc(metadata).into_error()?;
260                unsafe { value.deserialize_unsized(self, out)? };
261                let ptr = unsafe { NonNull::new_unchecked(P::from_value(out)) };
262
263                unsafe {
264                    self.finish_pooling(
265                        address,
266                        ErasedPtr::new(ptr),
267                        drop_shared::<T, P>,
268                    )?;
269                }
270
271                Ok(ptr.as_ptr())
272            }
273            PoolingState::Pending => fail!(CyclicSharedPointerError),
274            PoolingState::Finished(ptr) => {
275                Ok(from_raw_parts_mut(ptr.data_address.as_ptr(), metadata))
276            }
277        }
278    }
279}
280
281impl<T, E> PoolingExt<E> for T where T: Pooling<E> + ?Sized {}