Function bytecheck::check_bytes_with_context

source ·
pub unsafe fn check_bytes_with_context<T, C, E>(
    value: *const T,
    context: &mut C,
) -> Result<(), E>
where T: CheckBytes<Strategy<C, E>> + ?Sized,
Expand description

Checks whether the given pointer points to a valid value within the given context.

§Safety

The passed pointer must be aligned and point to enough initialized bytes to represent the type.

§Example

use core::{error::Error, fmt};

use bytecheck::{check_bytes_with_context, CheckBytes, Verify};
use rancor::{fail, Failure, Fallible, Source, Strategy};

trait Context {
    fn is_allowed(&self, value: u8) -> bool;
}

impl<T: Context + ?Sized, E> Context for Strategy<T, E> {
    fn is_allowed(&self, value: u8) -> bool {
        T::is_allowed(self, value)
    }
}

struct Allowed(u8);

impl Context for Allowed {
    fn is_allowed(&self, value: u8) -> bool {
        value == self.0
    }
}

#[derive(CheckBytes)]
#[bytecheck(verify)]
#[repr(C)]
pub struct ContextualByte(u8);

unsafe impl<C: Context + Fallible + ?Sized> Verify<C> for ContextualByte
where
    C::Error: Source,
{
    fn verify(&self, context: &mut C) -> Result<(), C::Error> {
        #[derive(Debug)]
        struct InvalidByte(u8);

        impl fmt::Display for InvalidByte {
            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
                write!(f, "invalid contextual byte: {}", self.0)
            }
        }

        impl Error for InvalidByte {}

        if !context.is_allowed(self.0) {
            fail!(InvalidByte(self.0));
        }

        Ok(())
    }
}

let value = 45u8;
unsafe {
    // Checking passes when the context allows byte 45
    check_bytes_with_context::<ContextualByte, _, Failure>(
        (&value as *const u8).cast(),
        &mut Allowed(45),
    )
    .unwrap();

    // Checking fails when the conteext does not allow byte 45
    check_bytes_with_context::<ContextualByte, _, Failure>(
        (&value as *const u8).cast(),
        &mut Allowed(0),
    )
    .unwrap_err();
}