rkyv/collections/index_map/
validation.rs1use crate::{
4 collections::{
5 hash_index::validation::HashIndexError,
6 index_map::ArchivedIndexMap,
7 util::{validation::ArchivedEntryError, Entry},
8 ArchivedHashIndex,
9 },
10 validation::ArchiveContext,
11 Archived, RelPtr,
12};
13use bytecheck::{CheckBytes, Error, SliceCheckError};
14use core::{
15 alloc::{Layout, LayoutError},
16 convert::Infallible,
17 fmt,
18 hash::Hash,
19 ptr,
20};
21
22#[derive(Debug)]
24pub enum IndexMapError<K, V, C> {
25 HashIndexError(HashIndexError<C>),
27 LayoutError(LayoutError),
29 PivotOutOfBounds {
31 index: usize,
33 pivot: usize,
35 },
36 CheckEntryError(SliceCheckError<ArchivedEntryError<K, V>>),
38 InvalidKeyPosition {
43 index: usize,
45 },
46 ContextError(C),
48}
49
50impl<K: fmt::Display, V: fmt::Display, E: fmt::Display> fmt::Display for IndexMapError<K, V, E> {
51 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
52 match self {
53 IndexMapError::HashIndexError(e) => write!(f, "hash index check error: {}", e),
54 IndexMapError::LayoutError(e) => write!(f, "layout error: {}", e),
55 IndexMapError::PivotOutOfBounds { index, pivot } => {
56 write!(f, "pivot out of bounds: {} at index {}", pivot, index)
57 }
58 IndexMapError::CheckEntryError(e) => write!(f, "entry check error: {}", e),
59 IndexMapError::InvalidKeyPosition { index } => {
60 write!(f, "invalid key position: at index {}", index)
61 }
62 IndexMapError::ContextError(e) => e.fmt(f),
63 }
64 }
65}
66
67#[cfg(feature = "std")]
68const _: () = {
69 use std::error::Error;
70
71 impl<K, V, C> Error for IndexMapError<K, V, C>
72 where
73 K: Error + 'static,
74 V: Error + 'static,
75 C: Error + 'static,
76 {
77 fn source(&self) -> Option<&(dyn Error + 'static)> {
78 match self {
79 IndexMapError::HashIndexError(e) => Some(e as &dyn Error),
80 IndexMapError::LayoutError(e) => Some(e as &dyn Error),
81 IndexMapError::PivotOutOfBounds { .. } => None,
82 IndexMapError::CheckEntryError(e) => Some(e as &dyn Error),
83 IndexMapError::InvalidKeyPosition { .. } => None,
84 IndexMapError::ContextError(e) => Some(e as &dyn Error),
85 }
86 }
87 }
88};
89
90impl<K, V, C> From<Infallible> for IndexMapError<K, V, C> {
91 fn from(_: Infallible) -> Self {
92 unsafe { core::hint::unreachable_unchecked() }
93 }
94}
95
96impl<K, V, C> From<SliceCheckError<Infallible>> for IndexMapError<K, V, C> {
97 #[inline]
98 fn from(_: SliceCheckError<Infallible>) -> Self {
99 unsafe { core::hint::unreachable_unchecked() }
100 }
101}
102
103impl<K, V, C> From<HashIndexError<C>> for IndexMapError<K, V, C> {
104 #[inline]
105 fn from(e: HashIndexError<C>) -> Self {
106 Self::HashIndexError(e)
107 }
108}
109
110impl<K, V, C> From<LayoutError> for IndexMapError<K, V, C> {
111 #[inline]
112 fn from(e: LayoutError) -> Self {
113 Self::LayoutError(e)
114 }
115}
116
117impl<K, V, C> From<SliceCheckError<ArchivedEntryError<K, V>>> for IndexMapError<K, V, C> {
118 #[inline]
119 fn from(e: SliceCheckError<ArchivedEntryError<K, V>>) -> Self {
120 Self::CheckEntryError(e)
121 }
122}
123
124impl<K, V, C> CheckBytes<C> for ArchivedIndexMap<K, V>
125where
126 K: CheckBytes<C> + Eq + Hash,
127 V: CheckBytes<C>,
128 C: ArchiveContext + ?Sized,
129 C::Error: Error,
130{
131 type Error = IndexMapError<K::Error, V::Error, C::Error>;
132
133 unsafe fn check_bytes<'a>(
134 value: *const Self,
135 context: &mut C,
136 ) -> Result<&'a Self, Self::Error> {
137 let index = ArchivedHashIndex::check_bytes(ptr::addr_of!((*value).index), context)?;
138
139 Layout::array::<Entry<K, V>>(index.len())?;
141 let entries_rel_ptr = RelPtr::manual_check_bytes(ptr::addr_of!((*value).entries), context)?;
142 let entries_ptr = context
143 .check_subtree_ptr::<[Entry<K, V>]>(
144 entries_rel_ptr.base(),
145 entries_rel_ptr.offset(),
146 index.len(),
147 )
148 .map_err(IndexMapError::ContextError)?;
149
150 let range = context
151 .push_prefix_subtree(entries_ptr)
152 .map_err(IndexMapError::ContextError)?;
153 let entries = <[Entry<K, V>]>::check_bytes(entries_ptr, context)?;
154 context
155 .pop_prefix_range(range)
156 .map_err(IndexMapError::ContextError)?;
157
158 Layout::array::<Archived<usize>>(index.len())?;
160 let pivots_rel_ptr = RelPtr::manual_check_bytes(ptr::addr_of!((*value).pivots), context)?;
161 let pivots_ptr = context
162 .check_subtree_ptr::<[Archived<usize>]>(
163 pivots_rel_ptr.base(),
164 pivots_rel_ptr.offset(),
165 index.len(),
166 )
167 .map_err(IndexMapError::ContextError)?;
168
169 let range = context
170 .push_prefix_subtree(pivots_ptr)
171 .map_err(IndexMapError::ContextError)?;
172 let pivots = <[Archived<usize>]>::check_bytes(pivots_ptr, context)?;
173 context
174 .pop_prefix_range(range)
175 .map_err(IndexMapError::ContextError)?;
176
177 for (i, pivot) in pivots.iter().enumerate() {
178 let pivot = from_archived!(*pivot) as usize;
179 if pivot >= index.len() {
180 return Err(IndexMapError::PivotOutOfBounds { index: i, pivot });
181 }
182 }
183
184 for (i, entry) in entries.iter().enumerate() {
185 if let Some(pivot_index) = index.index(&entry.key) {
186 let pivot = from_archived!(pivots[pivot_index]) as usize;
187 if pivot != i {
188 return Err(IndexMapError::InvalidKeyPosition { index: i });
189 }
190 } else {
191 return Err(IndexMapError::InvalidKeyPosition { index: i });
192 }
193 }
194
195 Ok(&*value)
196 }
197}