Skip to main content

rkyv/validation/shared/
validator.rs

1//! Validators add validation capabilities by wrapping and extending basic
2//! validators.
3
4use core::{any::TypeId, error::Error, fmt, hash::BuildHasherDefault};
5#[cfg(feature = "std")]
6use std::collections::hash_map;
7
8#[cfg(not(feature = "std"))]
9use hashbrown::hash_map;
10use rancor::{fail, Source};
11
12use crate::{
13    hash::FxHasher64,
14    validation::{shared::ValidationState, SharedContext},
15};
16
17/// A validator that can verify shared pointers.
18#[derive(Debug, Default)]
19pub struct SharedValidator {
20    shared: hash_map::HashMap<
21        usize,
22        (TypeId, bool),
23        BuildHasherDefault<FxHasher64>,
24    >,
25}
26
27impl SharedValidator {
28    /// Creates a new shared pointer validator.
29    #[inline]
30    pub fn new() -> Self {
31        Self::default()
32    }
33
34    /// Creates a new shared pointer validator with specific capacity.
35    #[inline]
36    pub fn with_capacity(capacity: usize) -> Self {
37        Self {
38            shared: hash_map::HashMap::with_capacity_and_hasher(
39                capacity,
40                Default::default(),
41            ),
42        }
43    }
44}
45
46#[derive(Debug)]
47struct TypeMismatch {
48    previous: TypeId,
49    current: TypeId,
50}
51
52impl fmt::Display for TypeMismatch {
53    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
54        write!(
55            f,
56            "the same memory region has been claimed as two different types: \
57             {:?} and {:?}",
58            self.previous, self.current,
59        )
60    }
61}
62
63impl Error for TypeMismatch {}
64
65#[derive(Debug)]
66struct NotStarted;
67
68impl fmt::Display for NotStarted {
69    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
70        write!(f, "shared pointer was not started validation")
71    }
72}
73
74impl Error for NotStarted {}
75
76#[derive(Debug)]
77struct AlreadyFinished;
78
79impl fmt::Display for AlreadyFinished {
80    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
81        write!(f, "shared pointer was already finished validation")
82    }
83}
84
85impl Error for AlreadyFinished {}
86
87impl<E: Source> SharedContext<E> for SharedValidator {
88    fn start_shared(
89        &mut self,
90        address: usize,
91        type_id: TypeId,
92    ) -> Result<ValidationState, E> {
93        match self.shared.entry(address) {
94            hash_map::Entry::Vacant(vacant) => {
95                vacant.insert((type_id, false));
96                Ok(ValidationState::Started)
97            }
98            hash_map::Entry::Occupied(occupied) => {
99                let (previous_type_id, finished) = occupied.get();
100                if previous_type_id != &type_id {
101                    fail!(TypeMismatch {
102                        previous: *previous_type_id,
103                        current: type_id,
104                    })
105                } else if !finished {
106                    Ok(ValidationState::Pending)
107                } else {
108                    Ok(ValidationState::Finished)
109                }
110            }
111        }
112    }
113
114    fn finish_shared(
115        &mut self,
116        address: usize,
117        type_id: TypeId,
118    ) -> Result<(), E> {
119        match self.shared.entry(address) {
120            hash_map::Entry::Vacant(_) => fail!(NotStarted),
121            hash_map::Entry::Occupied(mut occupied) => {
122                let (previous_type_id, finished) = occupied.get_mut();
123                if previous_type_id != &type_id {
124                    fail!(TypeMismatch {
125                        previous: *previous_type_id,
126                        current: type_id,
127                    });
128                } else if *finished {
129                    fail!(AlreadyFinished);
130                } else {
131                    *finished = true;
132                    Ok(())
133                }
134            }
135        }
136    }
137}