rkyv/collections/hash_index/
validation.rs

1//! Validation implementation for ArchivedHashIndex.
2
3use crate::{collections::ArchivedHashIndex, validation::ArchiveContext, Archived, RelPtr};
4use bytecheck::{CheckBytes, Error, SliceCheckError};
5use core::{
6    alloc::{Layout, LayoutError},
7    convert::Infallible,
8    fmt, ptr,
9};
10
11/// Errors that can occur while checking an archived hash index.
12#[derive(Debug)]
13pub enum HashIndexError<C> {
14    /// An error occurred while checking the layouts of displacements or entries
15    LayoutError(LayoutError),
16    /// A displacement value was invalid
17    InvalidDisplacement {
18        /// The index of the entry with an invalid displacement
19        index: usize,
20        /// The value of the entry at the invalid location
21        value: u32,
22    },
23    /// A bounds error occurred
24    ContextError(C),
25}
26
27impl<C> From<LayoutError> for HashIndexError<C> {
28    #[inline]
29    fn from(e: LayoutError) -> Self {
30        Self::LayoutError(e)
31    }
32}
33
34impl<C> From<Infallible> for HashIndexError<C> {
35    #[inline]
36    fn from(_: Infallible) -> Self {
37        unsafe { core::hint::unreachable_unchecked() }
38    }
39}
40
41impl<C> From<SliceCheckError<Infallible>> for HashIndexError<C> {
42    #[inline]
43    fn from(_: SliceCheckError<Infallible>) -> Self {
44        unsafe { core::hint::unreachable_unchecked() }
45    }
46}
47
48impl<C: fmt::Display> fmt::Display for HashIndexError<C> {
49    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
50        match self {
51            HashIndexError::LayoutError(e) => write!(f, "layout error: {}", e),
52            HashIndexError::InvalidDisplacement { index, value } => write!(
53                f,
54                "invalid displacement: value {} at index {}",
55                value, index,
56            ),
57            HashIndexError::ContextError(e) => e.fmt(f),
58        }
59    }
60}
61
62#[cfg(feature = "std")]
63const _: () = {
64    use std::error::Error;
65
66    impl<C: Error + 'static> Error for HashIndexError<C> {
67        fn source(&self) -> Option<&(dyn Error + 'static)> {
68            match self {
69                HashIndexError::LayoutError(e) => Some(e as &dyn Error),
70                HashIndexError::InvalidDisplacement { .. } => None,
71                HashIndexError::ContextError(e) => Some(e as &dyn Error),
72            }
73        }
74    }
75};
76
77impl<C: ArchiveContext + ?Sized> CheckBytes<C> for ArchivedHashIndex
78where
79    C::Error: Error,
80{
81    type Error = HashIndexError<C::Error>;
82
83    unsafe fn check_bytes<'a>(
84        value: *const Self,
85        context: &mut C,
86    ) -> Result<&'a Self, Self::Error> {
87        let len = from_archived!(*Archived::<usize>::check_bytes(
88            ptr::addr_of!((*value).len),
89            context,
90        )?) as usize;
91        Layout::array::<Archived<u32>>(len)?;
92
93        let displace_rel_ptr =
94            RelPtr::manual_check_bytes(ptr::addr_of!((*value).displace), context)?;
95        let displace_ptr = context
96            .check_subtree_ptr::<[Archived<u32>]>(
97                displace_rel_ptr.base(),
98                displace_rel_ptr.offset(),
99                len,
100            )
101            .map_err(HashIndexError::ContextError)?;
102
103        let range = context
104            .push_prefix_subtree(displace_ptr)
105            .map_err(HashIndexError::ContextError)?;
106        let displace = <[Archived<u32>]>::check_bytes(displace_ptr, context)?;
107        context
108            .pop_prefix_range(range)
109            .map_err(HashIndexError::ContextError)?;
110
111        for (i, &d) in displace.iter().enumerate() {
112            let d = from_archived!(d);
113            if d as usize >= len && d < 0x80_00_00_00 {
114                return Err(HashIndexError::InvalidDisplacement { index: i, value: d });
115            }
116        }
117
118        Ok(&*value)
119    }
120}