Skip to main content

rkyv/ser/sharing/
mod.rs

1//! Shared pointer serialization.
2
3#[cfg(feature = "alloc")]
4mod alloc;
5mod core;
6
7use ::core::{error::Error, fmt};
8use rancor::{fail, Fallible, Source, Strategy};
9
10#[cfg(feature = "alloc")]
11pub use self::alloc::*;
12pub use self::core::*;
13use crate::SerializeUnsized;
14
15/// The result of starting to serialize a shared pointer.
16pub enum SharingState {
17    /// The caller started sharing this value. They should proceed to serialize
18    /// the shared value and call `finish_sharing`.
19    Started,
20    /// Another caller started sharing this value, but has not finished yet.
21    /// This can only occur with cyclic shared pointer structures, and so rkyv
22    /// treats this as an error by default.
23    Pending,
24    /// This value has already been shared. The caller should use the returned
25    /// address to share its value.
26    Finished(usize),
27}
28
29/// A shared pointer serialization strategy.
30///
31/// This trait is required to serialize `Rc` and `Arc`.
32pub trait Sharing<E = <Self as Fallible>::Error> {
33    /// Starts sharing the value associated with the given address.
34    fn start_sharing(&mut self, address: usize) -> SharingState;
35
36    /// Finishes sharing the value associated with the given address.
37    ///
38    /// Returns an error if the given address was not pending.
39    fn finish_sharing(&mut self, address: usize, pos: usize) -> Result<(), E>;
40}
41
42impl<T, E> Sharing<E> for &mut T
43where
44    T: Sharing<E> + ?Sized,
45{
46    fn start_sharing(&mut self, address: usize) -> SharingState {
47        T::start_sharing(*self, address)
48    }
49
50    fn finish_sharing(&mut self, address: usize, pos: usize) -> Result<(), E> {
51        T::finish_sharing(*self, address, pos)
52    }
53}
54
55impl<T, E> Sharing<E> for Strategy<T, E>
56where
57    T: Sharing<E> + ?Sized,
58{
59    fn start_sharing(&mut self, address: usize) -> SharingState {
60        T::start_sharing(self, address)
61    }
62
63    fn finish_sharing(&mut self, address: usize, pos: usize) -> Result<(), E> {
64        T::finish_sharing(self, address, pos)
65    }
66}
67
68#[derive(Debug)]
69struct CyclicSharedPointerError;
70
71impl fmt::Display for CyclicSharedPointerError {
72    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
73        write!(
74            f,
75            "encountered cyclic shared pointers while serializing\nhelp: \
76             change your serialization strategy to `Unshare` or use the \
77             `Unshare` wrapper type to break the cycle",
78        )
79    }
80}
81
82impl Error for CyclicSharedPointerError {}
83
84/// Helper methods for [`Sharing`].
85pub trait SharingExt<E>: Sharing<E> {
86    /// Serializes the given shared value and returns its position. If the value
87    /// has already been serialized then it returns the position of the
88    /// previously added value.
89    ///
90    /// Returns an error if cyclic shared pointers are encountered.
91    fn serialize_shared<T: SerializeUnsized<Self> + ?Sized>(
92        &mut self,
93        value: &T,
94    ) -> Result<usize, <Self as Fallible>::Error>
95    where
96        Self: Fallible<Error = E>,
97        E: Source,
98    {
99        let addr = value as *const T as *const () as usize;
100        match self.start_sharing(addr) {
101            SharingState::Started => {
102                let pos = value.serialize_unsized(self)?;
103                self.finish_sharing(addr, pos)?;
104                Ok(pos)
105            }
106            SharingState::Pending => fail!(CyclicSharedPointerError),
107            SharingState::Finished(pos) => Ok(pos),
108        }
109    }
110}
111
112impl<S, E> SharingExt<E> for S where S: Sharing<E> + ?Sized {}