rkyv/collections/hash_map/
validation.rs

1//! Validation implementation for ArchiveHashMap.
2
3use crate::{
4    collections::{
5        hash_index::validation::HashIndexError,
6        hash_map::ArchivedHashMap,
7        util::{validation::ArchivedEntryError, Entry},
8        ArchivedHashIndex,
9    },
10    validation::ArchiveContext,
11    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/// Errors that can occur while checking an archived hash map.
23#[derive(Debug)]
24pub enum HashMapError<K, V, C> {
25    /// An error occurred while checking the hash index
26    HashIndexError(HashIndexError<C>),
27    /// An error occurred while checking the layouts of displacements or entries
28    LayoutError(LayoutError),
29    /// An error occurred while checking the entries
30    CheckEntryError(SliceCheckError<ArchivedEntryError<K, V>>),
31    /// A key is not located at the correct position
32    InvalidKeyPosition {
33        /// The index of the key when iterating
34        index: usize,
35    },
36    /// A bounds error occurred
37    ContextError(C),
38}
39
40impl<K: fmt::Display, V: fmt::Display, E: fmt::Display> fmt::Display for HashMapError<K, V, E> {
41    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
42        match self {
43            HashMapError::HashIndexError(e) => write!(f, "hash index check error: {}", e),
44            HashMapError::LayoutError(e) => write!(f, "layout error: {}", e),
45            HashMapError::CheckEntryError(e) => write!(f, "entry check error: {}", e),
46            HashMapError::InvalidKeyPosition { index } => {
47                write!(f, "invalid key position: at index {}", index)
48            }
49            HashMapError::ContextError(e) => e.fmt(f),
50        }
51    }
52}
53
54#[cfg(feature = "std")]
55const _: () = {
56    use std::error::Error;
57
58    impl<K, V, C> Error for HashMapError<K, V, C>
59    where
60        K: Error + 'static,
61        V: Error + 'static,
62        C: Error + 'static,
63    {
64        fn source(&self) -> Option<&(dyn Error + 'static)> {
65            match self {
66                HashMapError::HashIndexError(e) => Some(e as &dyn Error),
67                HashMapError::LayoutError(e) => Some(e as &dyn Error),
68                HashMapError::CheckEntryError(e) => Some(e as &dyn Error),
69                HashMapError::InvalidKeyPosition { .. } => None,
70                HashMapError::ContextError(e) => Some(e as &dyn Error),
71            }
72        }
73    }
74};
75
76impl<K, V, C> From<Infallible> for HashMapError<K, V, C> {
77    fn from(_: Infallible) -> Self {
78        unsafe { core::hint::unreachable_unchecked() }
79    }
80}
81
82impl<K, V, C> From<SliceCheckError<Infallible>> for HashMapError<K, V, C> {
83    #[inline]
84    fn from(_: SliceCheckError<Infallible>) -> Self {
85        unsafe { core::hint::unreachable_unchecked() }
86    }
87}
88
89impl<K, V, C> From<HashIndexError<C>> for HashMapError<K, V, C> {
90    #[inline]
91    fn from(e: HashIndexError<C>) -> Self {
92        Self::HashIndexError(e)
93    }
94}
95
96impl<K, V, C> From<LayoutError> for HashMapError<K, V, C> {
97    #[inline]
98    fn from(e: LayoutError) -> Self {
99        Self::LayoutError(e)
100    }
101}
102
103impl<K, V, C> From<SliceCheckError<ArchivedEntryError<K, V>>> for HashMapError<K, V, C> {
104    #[inline]
105    fn from(e: SliceCheckError<ArchivedEntryError<K, V>>) -> Self {
106        Self::CheckEntryError(e)
107    }
108}
109
110impl<K, V, C> CheckBytes<C> for ArchivedHashMap<K, V>
111where
112    K: CheckBytes<C> + Eq + Hash,
113    V: CheckBytes<C>,
114    C: ArchiveContext + ?Sized,
115    C::Error: Error,
116{
117    type Error = HashMapError<K::Error, V::Error, C::Error>;
118
119    unsafe fn check_bytes<'a>(
120        value: *const Self,
121        context: &mut C,
122    ) -> Result<&'a Self, Self::Error> {
123        let index = ArchivedHashIndex::check_bytes(ptr::addr_of!((*value).index), context)?;
124        Layout::array::<Entry<K, V>>(index.len())?;
125
126        let entries_rel_ptr = RelPtr::manual_check_bytes(ptr::addr_of!((*value).entries), context)?;
127        let entries_ptr = context
128            .check_subtree_ptr::<[Entry<K, V>]>(
129                entries_rel_ptr.base(),
130                entries_rel_ptr.offset(),
131                index.len(),
132            )
133            .map_err(HashMapError::ContextError)?;
134
135        let range = context
136            .push_prefix_subtree(entries_ptr)
137            .map_err(HashMapError::ContextError)?;
138        let entries = <[Entry<K, V>]>::check_bytes(entries_ptr, context)?;
139        context
140            .pop_prefix_range(range)
141            .map_err(HashMapError::ContextError)?;
142
143        for (i, entry) in entries.iter().enumerate() {
144            if index.index(&entry.key) != Some(i) {
145                return Err(HashMapError::InvalidKeyPosition { index: i });
146            }
147        }
148
149        Ok(&*value)
150    }
151}