diff --git a/bytecheck/src/lib.rs b/bytecheck/src/lib.rs index 2ef7b32..39df7e1 100644 --- a/bytecheck/src/lib.rs +++ b/bytecheck/src/lib.rs @@ -112,6 +112,48 @@ use simdutf8::basic::from_utf8; /// `Self`. Because `value` must always be properly aligned for `Self` and point /// to enough bytes to represent the type, this implies that `value` may be /// dereferenced safely. +/// +/// # Example +/// +/// ``` +/// use core::{error::Error, fmt}; +/// +/// use bytecheck::CheckBytes; +/// use rancor::{fail, Fallible, Source}; +/// +/// #[repr(C, align(4))] +/// pub struct NonMaxU32(u32); +/// +/// unsafe impl CheckBytes for NonMaxU32 +/// where +/// C::Error: Source, +/// { +/// unsafe fn check_bytes( +/// value: *const Self, +/// context: &mut C, +/// ) -> Result<(), C::Error> { +/// #[derive(Debug)] +/// struct NonMaxCheckError; +/// +/// impl fmt::Display for NonMaxCheckError { +/// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { +/// write!(f, "non-max u32 was set to u32::MAX") +/// } +/// } +/// +/// impl Error for NonMaxCheckError {} +/// +/// let value = unsafe { value.read() }; +/// if value.0 == u32::MAX { +/// fail!(NonMaxCheckError); +/// } +/// +/// Ok(()) +/// } +/// } +/// ``` +/// +/// See [`Verify`] for an example which uses less unsafe. pub unsafe trait CheckBytes { /// Checks whether the given pointer points to a valid value within the /// given context. @@ -128,12 +170,54 @@ pub unsafe trait CheckBytes { /// A type that can check whether its invariants are upheld. /// +/// When using [the derive](macro@CheckBytes), adding `#[bytecheck(verify)]` +/// allows implementing `Verify` for the derived type. [`Verify::verify`] will +/// be called after the type is checked and all fields are known to be valid. +/// /// # Safety /// /// - `verify` must only return `Ok` if all of the invariants of this type are /// upheld by `self`. /// - `verify` may not assume that its type invariants are upheld by the given -/// `self.` +/// `self` (the invariants of each field are guaranteed to be upheld). +/// +/// # Example +/// +/// ``` +/// use core::{error::Error, fmt}; +/// +/// use bytecheck::{CheckBytes, Verify}; +/// use rancor::{fail, Fallible, Source}; +/// +/// #[derive(CheckBytes)] +/// #[bytecheck(verify)] +/// #[repr(C, align(4))] +/// pub struct NonMaxU32(u32); +/// +/// unsafe impl Verify for NonMaxU32 +/// where +/// C::Error: Source, +/// { +/// fn verify(&self, context: &mut C) -> Result<(), C::Error> { +/// #[derive(Debug)] +/// struct NonMaxCheckError; +/// +/// impl fmt::Display for NonMaxCheckError { +/// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { +/// write!(f, "non-max u32 was set to u32::MAX") +/// } +/// } +/// +/// impl Error for NonMaxCheckError {} +/// +/// if self.0 == u32::MAX { +/// fail!(NonMaxCheckError); +/// } +/// +/// Ok(()) +/// } +/// } +/// ``` pub unsafe trait Verify { /// Checks whether the invariants of this type are upheld by `self`. fn verify(&self, context: &mut C) -> Result<(), C::Error>; @@ -145,6 +229,22 @@ pub unsafe trait Verify { /// /// The passed pointer must be aligned and point to enough initialized bytes to /// represent the type. +/// +/// # Example +/// +/// ``` +/// use bytecheck::check_bytes; +/// use rancor::Failure; +/// +/// unsafe { +/// // 0 and 1 are valid values for bools +/// check_bytes::((&0u8 as *const u8).cast()).unwrap(); +/// check_bytes::((&1u8 as *const u8).cast()).unwrap(); +/// +/// // 2 is not a valid value +/// check_bytes::((&2u8 as *const u8).cast()).unwrap_err(); +/// } +/// ``` #[inline] pub unsafe fn check_bytes(value: *const T) -> Result<(), E> where @@ -162,6 +262,79 @@ where /// /// 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 Context for Strategy { +/// 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 Verify 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::( +/// (&value as *const u8).cast(), +/// &mut Allowed(45), +/// ) +/// .unwrap(); +/// +/// // Checking fails when the conteext does not allow byte 45 +/// check_bytes_with_context::( +/// (&value as *const u8).cast(), +/// &mut Allowed(0), +/// ) +/// .unwrap_err(); +/// } +/// ``` pub unsafe fn check_bytes_with_context( value: *const T, context: &mut C, @@ -606,6 +779,9 @@ where // Generic contexts used by the derive. /// Context for errors resulting from invalid structs. +/// +/// This context is used by the derive macro to trace which field of a struct +/// failed validation. #[derive(Debug)] pub struct StructCheckContext { /// The name of the struct with an invalid field. @@ -625,6 +801,9 @@ impl fmt::Display for StructCheckContext { } /// Context for errors resulting from invalid tuple structs. +/// +/// This context is used by the derive macro to trace which field of a tuple +/// struct failed validation. #[derive(Debug)] pub struct TupleStructCheckContext { /// The name of the tuple struct with an invalid field. @@ -644,6 +823,9 @@ impl fmt::Display for TupleStructCheckContext { } /// An error resulting from an invalid enum tag. +/// +/// This context is used by the derive macro to trace what the invalid +/// discriminant for an enum is. #[derive(Debug)] pub struct InvalidEnumDiscriminantError { /// The name of the enum with an invalid discriminant. @@ -668,6 +850,9 @@ impl Error for InvalidEnumDiscriminantError where } /// Context for errors resulting from checking enum variants with named fields. +/// +/// This context is used by the derive macro to trace which field of an enum +/// variant failed validation. #[derive(Debug)] pub struct NamedEnumVariantCheckContext { /// The name of the enum with an invalid variant. @@ -690,6 +875,9 @@ impl fmt::Display for NamedEnumVariantCheckContext { /// Context for errors resulting from checking enum variants with unnamed /// fields. +/// +/// This context is used by the derive macro to trace which field of an enum +/// variant failed validation. #[derive(Debug)] pub struct UnnamedEnumVariantCheckContext { /// The name of the enum with an invalid variant. @@ -908,7 +1096,7 @@ mod tests { #[derive(Debug)] #[repr(transparent)] - pub struct CharLE(u32); + struct CharLE(u32); impl From for CharLE { fn from(c: char) -> Self { @@ -946,7 +1134,7 @@ mod tests { } #[repr(C, align(16))] - pub struct Aligned(pub [u8; N]); + struct Aligned(pub [u8; N]); macro_rules! bytes { ($($byte:literal),* $(,)?) => {