rkyv/validation/validators/
shared.rs

1//! Validators add validation capabilities by wrapping and extending basic validators.
2
3use crate::{validation::SharedContext, Fallible};
4use core::{any::TypeId, fmt};
5
6#[cfg(not(feature = "std"))]
7use hashbrown::HashMap;
8#[cfg(feature = "std")]
9use std::collections::HashMap;
10
11/// Errors that can occur when checking shared memory.
12#[derive(Debug)]
13pub enum SharedError {
14    /// Multiple pointers exist to the same location with different types
15    TypeMismatch {
16        /// A previous type that the location was checked as
17        previous: TypeId,
18        /// The current type that the location is checked as
19        current: TypeId,
20    },
21}
22
23impl fmt::Display for SharedError {
24    #[inline]
25    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
26        match self {
27            SharedError::TypeMismatch { previous, current } => write!(
28                f,
29                "the same memory region has been claimed as two different types ({:?} and {:?})",
30                previous, current
31            ),
32        }
33    }
34}
35
36#[cfg(feature = "std")]
37const _: () = {
38    use std::error::Error;
39
40    impl Error for SharedError {
41        fn source(&self) -> Option<&(dyn Error + 'static)> {
42            match self {
43                SharedError::TypeMismatch { .. } => None,
44            }
45        }
46    }
47};
48
49/// A validator that can verify shared memory.
50#[derive(Debug)]
51pub struct SharedValidator {
52    shared: HashMap<*const u8, TypeId>,
53}
54
55// SAFETY: SharedValidator is safe to send to another thread
56// This trait is not automatically implemented because the struct contains a pointer
57unsafe impl Send for SharedValidator {}
58
59// SAFETY: SharedValidator is safe to share between threads
60// This trait is not automatically implemented because the struct contains a pointer
61unsafe impl Sync for SharedValidator {}
62
63impl SharedValidator {
64    /// Wraps the given context and adds shared memory validation.
65    #[inline]
66    pub fn new() -> Self {
67        Self {
68            // TODO: consider deferring this to avoid the overhead of constructing
69            shared: HashMap::new(),
70        }
71    }
72
73    /// Shared memory validator with specific capacity.
74    #[inline]
75    pub fn with_capacity(capacity: usize) -> Self {
76        Self {
77            shared: HashMap::with_capacity(capacity),
78        }
79    }
80}
81
82impl Default for SharedValidator {
83    #[inline]
84    fn default() -> Self {
85        Self::new()
86    }
87}
88
89impl Fallible for SharedValidator {
90    type Error = SharedError;
91}
92
93impl SharedContext for SharedValidator {
94    #[inline]
95    fn register_shared_ptr(
96        &mut self,
97        ptr: *const u8,
98        type_id: TypeId,
99    ) -> Result<bool, Self::Error> {
100        #[cfg(not(feature = "std"))]
101        use hashbrown::hash_map::Entry;
102        #[cfg(feature = "std")]
103        use std::collections::hash_map::Entry;
104
105        match self.shared.entry(ptr) {
106            Entry::Occupied(previous_type_entry) => {
107                let previous_type_id = previous_type_entry.get();
108                if previous_type_id != &type_id {
109                    Err(SharedError::TypeMismatch {
110                        previous: *previous_type_id,
111                        current: type_id,
112                    })
113                } else {
114                    Ok(false)
115                }
116            }
117            Entry::Vacant(ent) => {
118                ent.insert(type_id);
119                Ok(true)
120            }
121        }
122    }
123}