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::{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
17/// A deserializable shared pointer type.
18///
19/// # Safety
20///
21/// `alloc` and `from_value` must return pointers which are non-null, writeable,
22/// and properly aligned for `T`.
23pub unsafe trait SharedPointer<T: Pointee + ?Sized> {
24    /// Allocates space for a value with the given metadata.
25    fn alloc(metadata: T::Metadata) -> Result<*mut T, LayoutError>;
26
27    /// Creates a new `Self` from a pointer to a valid `T`.
28    ///
29    /// # Safety
30    ///
31    /// `ptr` must have been allocated via `alloc`. `from_value` must not have
32    /// been called on `ptr` yet.
33    unsafe fn from_value(ptr: *mut T) -> *mut T;
34
35    /// Drops a pointer created by `from_value`.
36    ///
37    /// # Safety
38    ///
39    /// - `ptr` must have been created using `from_value`.
40    /// - `drop` must only be called once per `ptr`.
41    unsafe fn drop(ptr: *mut T);
42}
43
44/// The result of starting to deserialize a shared pointer.
45pub enum PoolingState {
46    /// The caller started pooling this value. They should proceed to
47    /// deserialize the shared value and call `finish_pooling`.
48    Started,
49    /// Another caller started pooling this value, but has not finished yet.
50    /// This can only occur with cyclic shared pointer structures, and so rkyv
51    /// treats this as an error by default.
52    Pending,
53    /// This value has already been pooled. The caller should use the returned
54    /// pointer to pool its value.
55    Finished(ErasedPtr),
56}
57
58/// A shared pointer deserialization strategy.
59///
60/// This trait is required to deserialize `Rc` and `Arc`.
61pub trait Pooling<E = <Self as Fallible>::Error> {
62    /// Starts pooling the value associated with the given address.
63    fn start_pooling(&mut self, address: usize) -> PoolingState;
64
65    /// Finishes pooling the value associated with the given address.
66    ///
67    /// Returns an error if the given address was not pending.
68    ///
69    /// # Safety
70    ///
71    /// The given `drop` function must be valid to call with `ptr`.
72    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        // SAFETY: The safety requirements for `finish_pooling` are the same as
95        // the requirements for calling this function.
96        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
116/// Helper methods for [`Pooling`].
117pub trait PoolingExt<E>: Pooling<E> {
118    /// Checks whether the given reference has been deserialized and either uses
119    /// the existing shared pointer to it, or deserializes it and converts
120    /// it to a shared pointer with `to_shared`.
121    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 {}