Skip to main content

rkyv/ser/sharing/
alloc.rs

1use core::{error::Error, fmt, hash::BuildHasherDefault};
2
3use hashbrown::hash_map::{Entry, HashMap};
4use rancor::{fail, Source};
5
6use crate::{
7    hash::FxHasher64,
8    ser::{sharing::SharingState, Sharing},
9};
10
11/// A shared pointer strategy that shares serializations of the same shared
12/// pointer.
13#[derive(Debug, Default)]
14pub struct Share {
15    shared_address_to_pos:
16        HashMap<usize, Option<usize>, BuildHasherDefault<FxHasher64>>,
17}
18
19impl Share {
20    /// Creates a new shared pointer unifier.
21    #[inline]
22    pub fn new() -> Self {
23        Self::default()
24    }
25
26    /// Creates a new shared pointer unifier with initial capacity.
27    #[inline]
28    pub fn with_capacity(capacity: usize) -> Self {
29        Self {
30            shared_address_to_pos: HashMap::with_capacity_and_hasher(
31                capacity,
32                Default::default(),
33            ),
34        }
35    }
36
37    /// Clears the shared pointer unifier for reuse.
38    pub fn clear(&mut self) {
39        self.shared_address_to_pos.clear();
40    }
41}
42
43#[derive(Debug)]
44struct NotStarted;
45
46impl fmt::Display for NotStarted {
47    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
48        write!(f, "shared pointer was not started sharing")
49    }
50}
51
52impl Error for NotStarted {}
53
54#[derive(Debug)]
55struct AlreadyFinished;
56
57impl fmt::Display for AlreadyFinished {
58    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
59        write!(f, "shared pointer was already finished sharing")
60    }
61}
62
63impl Error for AlreadyFinished {}
64
65impl<E: Source> Sharing<E> for Share {
66    fn start_sharing(&mut self, address: usize) -> SharingState {
67        match self.shared_address_to_pos.entry(address) {
68            Entry::Vacant(vacant) => {
69                vacant.insert(None);
70                SharingState::Started
71            }
72            Entry::Occupied(occupied) => {
73                if let Some(pos) = occupied.get() {
74                    SharingState::Finished(*pos)
75                } else {
76                    SharingState::Pending
77                }
78            }
79        }
80    }
81
82    fn finish_sharing(&mut self, address: usize, pos: usize) -> Result<(), E> {
83        match self.shared_address_to_pos.entry(address) {
84            Entry::Vacant(_) => fail!(NotStarted),
85            Entry::Occupied(mut occupied) => {
86                let inner = occupied.get_mut();
87                if inner.is_some() {
88                    fail!(AlreadyFinished);
89                } else {
90                    *inner = Some(pos);
91                    Ok(())
92                }
93            }
94        }
95    }
96}