pub mod owned;
pub mod validators;
use crate::{Archive, ArchivePointee, Fallible, RelPtr};
use bytecheck::CheckBytes;
use core::{alloc::Layout, any::TypeId, fmt};
use ptr_meta::Pointee;
#[cfg(feature = "std")]
use std::error::Error;
pub trait LayoutRaw {
fn layout_raw(value: *const Self) -> Layout;
}
impl<T> LayoutRaw for T {
#[inline]
fn layout_raw(_: *const Self) -> Layout {
Layout::new::<T>()
}
}
impl<T> LayoutRaw for [T] {
#[inline]
fn layout_raw(value: *const Self) -> Layout {
let metadata = ptr_meta::metadata(value);
Layout::array::<T>(metadata).unwrap()
}
}
impl LayoutRaw for str {
#[inline]
fn layout_raw(value: *const Self) -> Layout {
let metadata = ptr_meta::metadata(value);
Layout::array::<u8>(metadata).unwrap()
}
}
#[cfg(feature = "std")]
impl LayoutRaw for ::std::ffi::CStr {
#[inline]
fn layout_raw(value: *const Self) -> Layout {
let metadata = ptr_meta::metadata(value);
Layout::array::<::std::os::raw::c_char>(metadata).unwrap()
}
}
pub trait ArchiveContext: Fallible {
type PrefixRange: 'static;
type SuffixRange: 'static;
unsafe fn bounds_check_ptr(
&mut self,
base: *const u8,
offset: isize,
) -> Result<*const u8, Self::Error>;
unsafe fn bounds_check_layout(
&mut self,
data_address: *const u8,
layout: &Layout,
) -> Result<(), Self::Error>;
#[inline]
unsafe fn check_ptr<T: LayoutRaw + Pointee + ?Sized>(
&mut self,
base: *const u8,
offset: isize,
metadata: T::Metadata,
) -> Result<*const T, Self::Error> {
let data_address = self.bounds_check_ptr(base, offset)?;
let ptr = ptr_meta::from_raw_parts(data_address.cast(), metadata);
let layout = T::layout_raw(ptr);
self.bounds_check_layout(data_address, &layout)?;
Ok(ptr)
}
#[inline]
unsafe fn check_rel_ptr<T: ArchivePointee + LayoutRaw + ?Sized>(
&mut self,
rel_ptr: &RelPtr<T>,
) -> Result<*const T, Self::Error> {
let metadata = T::pointer_metadata(rel_ptr.metadata());
self.check_ptr(rel_ptr.base(), rel_ptr.offset(), metadata)
}
unsafe fn bounds_check_subtree_ptr_layout(
&mut self,
data_address: *const u8,
layout: &Layout,
) -> Result<(), Self::Error>;
#[inline]
unsafe fn bounds_check_subtree_ptr<T: LayoutRaw + ?Sized>(
&mut self,
ptr: *const T,
) -> Result<(), Self::Error> {
let layout = T::layout_raw(ptr);
self.bounds_check_subtree_ptr_layout(ptr.cast(), &layout)
}
#[inline]
unsafe fn check_subtree_ptr<T: LayoutRaw + Pointee + ?Sized>(
&mut self,
base: *const u8,
offset: isize,
metadata: T::Metadata,
) -> Result<*const T, Self::Error> {
let ptr = self.check_ptr(base, offset, metadata)?;
self.bounds_check_subtree_ptr(ptr)?;
Ok(ptr)
}
#[inline]
unsafe fn check_subtree_rel_ptr<T: ArchivePointee + LayoutRaw + ?Sized>(
&mut self,
rel_ptr: &RelPtr<T>,
) -> Result<*const T, Self::Error> {
let ptr = self.check_rel_ptr(rel_ptr)?;
self.bounds_check_subtree_ptr(ptr)?;
Ok(ptr)
}
unsafe fn push_prefix_subtree_range(
&mut self,
root: *const u8,
end: *const u8,
) -> Result<Self::PrefixRange, Self::Error>;
#[inline]
unsafe fn push_prefix_subtree<T: LayoutRaw + ?Sized>(
&mut self,
root: *const T,
) -> Result<Self::PrefixRange, Self::Error> {
let layout = T::layout_raw(root);
self.push_prefix_subtree_range(root as *const u8, (root as *const u8).add(layout.size()))
}
fn pop_prefix_range(&mut self, range: Self::PrefixRange) -> Result<(), Self::Error>;
unsafe fn push_suffix_subtree_range(
&mut self,
start: *const u8,
root: *const u8,
) -> Result<Self::SuffixRange, Self::Error>;
fn pop_suffix_range(&mut self, range: Self::SuffixRange) -> Result<(), Self::Error>;
fn finish(&mut self) -> Result<(), Self::Error>;
}
pub trait SharedContext: Fallible {
fn register_shared_ptr(&mut self, ptr: *const u8, type_id: TypeId)
-> Result<bool, Self::Error>;
}
#[derive(Debug)]
pub enum CheckArchiveError<T, C> {
CheckBytesError(T),
ContextError(C),
}
impl<T: fmt::Display, C: fmt::Display> fmt::Display for CheckArchiveError<T, C> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
CheckArchiveError::CheckBytesError(e) => write!(f, "check bytes error: {}", e),
CheckArchiveError::ContextError(e) => write!(f, "context error: {}", e),
}
}
}
#[cfg(feature = "std")]
impl<T: Error + 'static, C: Error + 'static> Error for CheckArchiveError<T, C> {
fn source(&self) -> Option<&(dyn Error + 'static)> {
match self {
CheckArchiveError::CheckBytesError(e) => Some(e as &dyn Error),
CheckArchiveError::ContextError(e) => Some(e as &dyn Error),
}
}
}
pub type CheckTypeError<T, C> =
CheckArchiveError<<T as CheckBytes<C>>::Error, <C as Fallible>::Error>;
#[inline]
fn internal_check_archived_value_with_context<'a, T, C>(
buf: &'a [u8],
pos: isize,
context: &mut C,
) -> Result<&'a T::Archived, CheckTypeError<T::Archived, C>>
where
T: Archive,
T::Archived: CheckBytes<C> + Pointee<Metadata = ()>,
C: ArchiveContext + ?Sized,
{
unsafe {
let ptr = context
.check_subtree_ptr(buf.as_ptr(), pos, ())
.map_err(CheckArchiveError::ContextError)?;
let range = context
.push_prefix_subtree(ptr)
.map_err(CheckArchiveError::ContextError)?;
let result =
CheckBytes::check_bytes(ptr, context).map_err(CheckArchiveError::CheckBytesError)?;
context
.pop_prefix_range(range)
.map_err(CheckArchiveError::ContextError)?;
context.finish().map_err(CheckArchiveError::ContextError)?;
Ok(result)
}
}
#[inline]
pub fn check_archived_value_with_context<'a, T, C>(
buf: &'a [u8],
pos: usize,
context: &mut C,
) -> Result<&'a T::Archived, CheckTypeError<T::Archived, C>>
where
T: Archive,
T::Archived: CheckBytes<C> + Pointee<Metadata = ()>,
C: ArchiveContext + ?Sized,
{
internal_check_archived_value_with_context::<T, C>(buf, pos as isize, context)
}
#[inline]
pub fn check_archived_root_with_context<'a, T, C>(
buf: &'a [u8],
context: &mut C,
) -> Result<&'a T::Archived, CheckTypeError<T::Archived, C>>
where
T: Archive,
T::Archived: CheckBytes<C> + Pointee<Metadata = ()>,
C: ArchiveContext + ?Sized,
{
internal_check_archived_value_with_context::<T, C>(
buf,
buf.len() as isize - core::mem::size_of::<T::Archived>() as isize,
context,
)
}