rkyv/validation/shared/
validator.rs1use 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#[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 #[inline]
30 pub fn new() -> Self {
31 Self::default()
32 }
33
34 #[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}