bytecheck-0.6.11/.cargo_vcs_info.json0000644000000001470000000000100130640ustar { "git": { "sha1": "86e9c7446a5125a21748b111fcbeb92e09617e62" }, "path_in_vcs": "bytecheck" }bytecheck-0.6.11/Cargo.toml0000644000000024000000000000100110540ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2021" name = "bytecheck" version = "0.6.11" authors = ["David Koloski "] description = "Derive macro for bytecheck" documentation = "https://docs.rs/bytecheck" readme = "crates-io.md" keywords = [ "bytecheck", "validation", "zero-copy", "rkyv", ] categories = ["encoding"] license = "MIT" repository = "https://github.com/djkoloski/bytecheck" resolver = "1" [dependencies.bytecheck_derive] version = "0.6.11" [dependencies.ptr_meta] version = "0.1" default-features = false [dependencies.simdutf8] version = "0.1" optional = true default-features = false [dependencies.uuid] version = "1.3" optional = true [features] default = [ "simdutf8", "std", ] std = [ "ptr_meta/std", "bytecheck_derive/std", "simdutf8/std", ] verbose = [] bytecheck-0.6.11/Cargo.toml.orig000064400000000000000000000023770072674642500146020ustar 00000000000000[package] name = "bytecheck" description = "Derive macro for bytecheck" documentation = "https://docs.rs/bytecheck" keywords = ["bytecheck", "validation", "zero-copy", "rkyv"] categories = ["encoding"] readme = "crates-io.md" version.workspace = true edition.workspace = true authors.workspace = true license.workspace = true repository.workspace = true # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] bytecheck_derive = { workspace = true, path = "../bytecheck_derive", default-features = false } ptr_meta = { version = "0.1", default-features = false } simdutf8 = { version = "0.1", default-features = false, optional = true } # Support for various common crates. These are primarily to get users off the ground and build some # momentum. # These are NOT PLANNED to remain in bytecheck for the final release. Much like serde, these # implementations should be moved into their respective crates over time. Before adding support for # another crate, please consider getting bytecheck support in the crate instead. uuid = { version = "1.3", optional = true } [features] default = ["simdutf8", "std"] verbose = [] std = ["ptr_meta/std", "bytecheck_derive/std", "simdutf8/std"] bytecheck-0.6.11/LICENSE000064400000000000000000000020350072674642500127070ustar 00000000000000Copyright 2020 David Koloski Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. bytecheck-0.6.11/README.md000064400000000000000000000052240072674642500131640ustar 00000000000000# bytecheck   [![Latest Version]][crates.io] [![License]][license path] [![requires: rustc 1.52+]][Rust 1.52] [Latest Version]: https://img.shields.io/crates/v/bytecheck.svg [crates.io]: https://crates.io/crates/bytecheck [License]: https://img.shields.io/badge/license-MIT-blue.svg [license path]: https://github.com/djkoloski/bytecheck/blob/master/LICENSE [requires: rustc 1.52+]: https://img.shields.io/badge/rustc-1.52+-lightgray.svg [Rust 1.52]: https://blog.rust-lang.org/2021/05/06/Rust-1.52.0.html bytecheck is a type validation framework for Rust. ## bytecheck in action ```rust use bytecheck::CheckBytes; #[derive(CheckBytes, Debug)] struct Test { a: u32, b: bool, c: char, } #[repr(C, align(16))] struct Aligned([u8; N]); macro_rules! bytes { ($($byte:literal,)*) => { (&Aligned([$($byte,)*]).0 as &[u8]).as_ptr() }; ($($byte:literal),*) => { bytes!($($byte,)*) }; } fn main() { // This type is laid out as (u32, char, bool) unsafe { // These are valid bytes for (0, 'x', true) Test::check_bytes( bytes![ 0u8, 0u8, 0u8, 0u8, 0x78u8, 0u8, 0u8, 0u8, 1u8, 255u8, 255u8, 255u8, 255u8, 255u8, 255u8, 255u8 ].cast(), &() ).unwrap(); // Changing the bytes for the u32 is OK, any bytes are a valid u32 Test::check_bytes( bytes![ 42u8, 16u8, 20u8, 3u8, 0x78u8, 0u8, 0u8, 0u8, 1u8, 255u8, 255u8, 255u8, 255u8, 255u8, 255u8, 255u8 ].cast(), &() ).unwrap(); // Characters outside the valid ranges are invalid Test::check_bytes( bytes![ 0u8, 0u8, 0u8, 0u8, 0x00u8, 0xd8u8, 0u8, 0u8, 1u8, 255u8, 255u8, 255u8, 255u8, 255u8, 255u8, 255u8 ].cast(), &() ).unwrap_err(); Test::check_bytes( bytes![ 0u8, 0u8, 0u8, 0u8, 0x00u8, 0x00u8, 0x11u8, 0u8, 1u8, 255u8, 255u8, 255u8, 255u8, 255u8, 255u8, 255u8 ].cast(), &() ).unwrap_err(); // 0 is a valid boolean value (false) but 2 is not Test::check_bytes( bytes![ 0u8, 0u8, 0u8, 0u8, 0x78u8, 0u8, 0u8, 0u8, 0u8, 255u8, 255u8, 255u8, 255u8, 255u8, 255u8, 255u8 ].cast(), &() ).unwrap(); Test::check_bytes( bytes![ 0u8, 0u8, 0u8, 0u8, 0x78u8, 0u8, 0u8, 0u8, 2u8, 255u8, 255u8, 255u8, 255u8, 255u8, 255u8, 255u8 ].cast(), &() ).unwrap_err(); } } ``` bytecheck-0.6.11/build.rs000064400000000000000000000023650072674642500133550ustar 00000000000000use std::env; fn main() { let mut has_atomic32 = true; let mut has_atomic64 = true; let target = env::var("TARGET").unwrap(); // Full target triples that have specific limitations: match target.as_str() { "arm-linux-androideabi" | "asmjs-unknown-emscripten" | "wasm32-unknown-emscripten" | "wasm32-unknown-unknown" => has_atomic64 = false, _ => {} } // Architecture-specific limitations: let arch = target.split('-').next().unwrap_or(&target); match arch { // NOTE: Not all ARMv7 variants are listed here, as certain variants do actually provide // 64-bit atomics. (`armv7`, `armv7a`, and `armv7s`, specifically) "armv5te" | "mips" | "mipsel" | "powerpc" | "riscv32imac" | "thumbv7em" | "thumbv7m" | "thumbv8m.base" | "thumbv8m.main" | "armebv7r" | "armv7r" => has_atomic64 = false, "avr" | "riscv32i" | "riscv32im" | "riscv32imc" | "thumbv6m" => { has_atomic32 = false; has_atomic64 = false; } _ => {} } if has_atomic64 { println!("cargo:rustc-cfg=has_atomics_64"); } if has_atomic32 { println!("cargo:rustc-cfg=has_atomics"); } } bytecheck-0.6.11/crates-io.md000064400000000000000000000042230072674642500141130ustar 00000000000000bytecheck is a type validation framework for Rust. ## bytecheck in action ```rust use bytecheck::CheckBytes; #[derive(CheckBytes, Debug)] struct Test { a: u32, b: bool, c: char, } #[repr(C, align(16))] struct Aligned([u8; N]); macro_rules! bytes { ($($byte:literal,)*) => { (&Aligned([$($byte,)*]).0 as &[u8]).as_ptr() }; ($($byte:literal),*) => { bytes!($($byte,)*) }; } fn main() { // This type is laid out as (u32, char, bool) unsafe { // These are valid bytes for (0, 'x', true) Test::check_bytes( bytes![ 0u8, 0u8, 0u8, 0u8, 0x78u8, 0u8, 0u8, 0u8, 1u8, 255u8, 255u8, 255u8, 255u8, 255u8, 255u8, 255u8 ].cast(), &() ).unwrap(); // Changing the bytes for the u32 is OK, any bytes are a valid u32 Test::check_bytes( bytes![ 42u8, 16u8, 20u8, 3u8, 0x78u8, 0u8, 0u8, 0u8, 1u8, 255u8, 255u8, 255u8, 255u8, 255u8, 255u8, 255u8 ].cast(), &() ).unwrap(); // Characters outside the valid ranges are invalid Test::check_bytes( bytes![ 0u8, 0u8, 0u8, 0u8, 0x00u8, 0xd8u8, 0u8, 0u8, 1u8, 255u8, 255u8, 255u8, 255u8, 255u8, 255u8, 255u8 ].cast(), &() ).unwrap_err(); Test::check_bytes( bytes![ 0u8, 0u8, 0u8, 0u8, 0x00u8, 0x00u8, 0x11u8, 0u8, 1u8, 255u8, 255u8, 255u8, 255u8, 255u8, 255u8, 255u8 ].cast(), &() ).unwrap_err(); // 0 is a valid boolean value (false) but 2 is not Test::check_bytes( bytes![ 0u8, 0u8, 0u8, 0u8, 0x78u8, 0u8, 0u8, 0u8, 0u8, 255u8, 255u8, 255u8, 255u8, 255u8, 255u8, 255u8 ].cast(), &() ).unwrap(); Test::check_bytes( bytes![ 0u8, 0u8, 0u8, 0u8, 0x78u8, 0u8, 0u8, 0u8, 2u8, 255u8, 255u8, 255u8, 255u8, 255u8, 255u8, 255u8 ].cast(), &() ).unwrap_err(); } } ``` bytecheck-0.6.11/src/lib.rs000064400000000000000000000644070072674642500136200ustar 00000000000000//! # bytecheck //! //! bytecheck is a type validation framework for Rust. //! //! For some types, creating an invalid value immediately results in undefined //! behavior. This can cause some issues when trying to validate potentially //! invalid bytes, as just casting the bytes to your type can technically cause //! errors. This makes it difficult to write validation routines, because until //! you're certain that the bytes represent valid values you cannot cast them. //! //! bytecheck provides a framework for performing these byte-level validations //! and implements checks for basic types along with a derive macro to implement //! validation for custom structs and enums. //! //! ## Design //! //! [`CheckBytes`] is at the heart of bytecheck, and does the heavy lifting of //! verifying that some bytes represent a valid type. Implementing it can be //! done manually or automatically with the [derive macro](macro@CheckBytes). //! //! ## Examples //! //! ``` //! use bytecheck::CheckBytes; //! //! #[derive(CheckBytes, Debug)] //! struct Test { //! a: u32, //! b: bool, //! c: char, //! } //! #[repr(C, align(16))] //! struct Aligned([u8; N]); //! //! macro_rules! bytes { //! ($($byte:literal,)*) => { //! (&Aligned([$($byte,)*]).0 as &[u8]).as_ptr() //! }; //! ($($byte:literal),*) => { //! bytes!($($byte,)*) //! }; //! } //! //! // This type is laid out as (u32, char, bool) //! // In this example, the architecture is assumed to be little-endian //! # #[cfg(target_endian = "little")] //! unsafe { //! // These are valid bytes for (0, 'x', true) //! Test::check_bytes( //! bytes![ //! 0u8, 0u8, 0u8, 0u8, 0x78u8, 0u8, 0u8, 0u8, //! 1u8, 255u8, 255u8, 255u8, 255u8, 255u8, 255u8, 255u8 //! ].cast(), //! &mut () //! ).unwrap(); //! //! // Changing the bytes for the u32 is OK, any bytes are a valid u32 //! Test::check_bytes( //! bytes![ //! 42u8, 16u8, 20u8, 3u8, 0x78u8, 0u8, 0u8, 0u8, //! 1u8, 255u8, 255u8, 255u8, 255u8, 255u8, 255u8, 255u8 //! ].cast(), //! &mut () //! ).unwrap(); //! //! // Characters outside the valid ranges are invalid //! Test::check_bytes( //! bytes![ //! 0u8, 0u8, 0u8, 0u8, 0x00u8, 0xd8u8, 0u8, 0u8, //! 1u8, 255u8, 255u8, 255u8, 255u8, 255u8, 255u8, 255u8 //! ].cast(), //! &mut () //! ).unwrap_err(); //! Test::check_bytes( //! bytes![ //! 0u8, 0u8, 0u8, 0u8, 0x00u8, 0x00u8, 0x11u8, 0u8, //! 1u8, 255u8, 255u8, 255u8, 255u8, 255u8, 255u8, 255u8 //! ].cast(), //! &mut () //! ).unwrap_err(); //! //! // 0 is a valid boolean value (false) but 2 is not //! Test::check_bytes( //! bytes![ //! 0u8, 0u8, 0u8, 0u8, 0x78u8, 0u8, 0u8, 0u8, //! 0u8, 255u8, 255u8, 255u8, 255u8, 255u8, 255u8, 255u8 //! ].cast(), //! &mut () //! ).unwrap(); //! Test::check_bytes( //! bytes![ //! 0u8, 0u8, 0u8, 0u8, 0x78u8, 0u8, 0u8, 0u8, //! 2u8, 255u8, 255u8, 255u8, 255u8, 255u8, 255u8, 255u8 //! ].cast(), //! &mut () //! ).unwrap_err(); //! } //! ``` //! //! ## Features //! //! - `verbose`: Some validation algorithms are optimized for speed and do not report full error //! details by default. This feature provides full error information. //! - `std`: Enables standard library support (enabled by default). If the `std` feature is not //! enabled, the `alloc` crate is required. //! //! ## Crate support //! //! Some common crates need to be supported by bytecheck before an official integration has been //! made. Support is provided by bytecheck for these crates, but in the future crates should depend //! on bytecheck and provide their own implementations. The crates that already have support //! provided by bytecheck should work toward integrating the implementations into themselves. //! //! Crates supported by bytecheck: //! //! - [`uuid`](https://docs.rs/uuid) #![deny( rustdoc::broken_intra_doc_links, rustdoc::missing_crate_level_docs, missing_docs, rust_2018_compatibility, rust_2018_idioms, future_incompatible, nonstandard_style, unused, clippy::all )] #![cfg_attr(not(feature = "std"), no_std)] #[cfg(not(feature = "std"))] extern crate alloc; // Support for various common crates. These are primarily to get users off the ground and build some // momentum. // These are NOT PLANNED to remain in bytecheck for the final release. Much like serde, these // implementations should be moved into their respective crates over time. Before adding support for // another crate, please consider getting bytecheck support in the crate instead. #[cfg(feature = "uuid")] pub mod uuid; #[cfg(not(feature = "simdutf8"))] use core::str::{from_utf8, Utf8Error}; #[cfg(has_atomics)] use core::sync::atomic::{ AtomicBool, AtomicI16, AtomicI32, AtomicI8, AtomicU16, AtomicU32, AtomicU8, }; #[cfg(has_atomics_64)] use core::sync::atomic::{AtomicI64, AtomicU64}; use core::{ convert::{Infallible, TryFrom}, fmt, marker::{PhantomData, PhantomPinned}, mem::ManuallyDrop, num::{ NonZeroI128, NonZeroI16, NonZeroI32, NonZeroI64, NonZeroI8, NonZeroU128, NonZeroU16, NonZeroU32, NonZeroU64, NonZeroU8, }, ops, ptr, slice, }; use ptr_meta::PtrExt; #[cfg(all(feature = "simdutf8", not(feature = "verbose")))] use simdutf8::basic::{from_utf8, Utf8Error}; #[cfg(all(feature = "simdutf8", feature = "verbose"))] use simdutf8::compat::{from_utf8, Utf8Error}; pub use bytecheck_derive::CheckBytes; /// An error that can be debugged and displayed. /// /// With the `std` feature, this also supports `std::error::Error`. #[cfg(not(feature = "std"))] pub trait Error: fmt::Debug + fmt::Display + 'static {} #[cfg(not(feature = "std"))] impl Error for T {} /// An error that can be debugged and displayed. /// /// With the `std` feature, this also supports `std::error::Error`. #[cfg(feature = "std")] pub trait Error: std::error::Error + 'static { /// Gets this error as an `std::error::Error`. fn as_error(&self) -> &(dyn std::error::Error + 'static); } #[cfg(feature = "std")] impl Error for T { fn as_error(&self) -> &(dyn std::error::Error + 'static) { self } } /// The type used for boxing errors. #[cfg(not(feature = "std"))] pub type ErrorBox = alloc::boxed::Box; /// The type used for boxing errors. #[cfg(feature = "std")] pub type ErrorBox = std::boxed::Box; /// A type that can check whether a pointer points to a valid value. /// /// `CheckBytes` can be derived with [`CheckBytes`](macro@CheckBytes) or /// implemented manually for custom behavior. pub trait CheckBytes { /// The error that may result from checking the type. type Error: Error + 'static; /// 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 bytes to /// represent the type. unsafe fn check_bytes<'a>(value: *const Self, context: &mut C) -> Result<&'a Self, Self::Error>; } macro_rules! impl_primitive { ($type:ty) => { impl CheckBytes for $type { type Error = Infallible; #[inline] unsafe fn check_bytes<'a>( value: *const Self, _: &mut C, ) -> Result<&'a Self, Self::Error> { Ok(&*value) } } }; } impl_primitive!(()); impl_primitive!(i8); impl_primitive!(i16); impl_primitive!(i32); impl_primitive!(i64); impl_primitive!(i128); impl_primitive!(u8); impl_primitive!(u16); impl_primitive!(u32); impl_primitive!(u64); impl_primitive!(u128); impl_primitive!(f32); impl_primitive!(f64); #[cfg(has_atomics)] impl_primitive!(AtomicI8); #[cfg(has_atomics)] impl_primitive!(AtomicI16); #[cfg(has_atomics)] impl_primitive!(AtomicI32); #[cfg(has_atomics_64)] impl_primitive!(AtomicI64); #[cfg(has_atomics)] impl_primitive!(AtomicU8); #[cfg(has_atomics)] impl_primitive!(AtomicU16); #[cfg(has_atomics)] impl_primitive!(AtomicU32); #[cfg(has_atomics_64)] impl_primitive!(AtomicU64); impl CheckBytes for PhantomData { type Error = Infallible; #[inline] unsafe fn check_bytes<'a>(value: *const Self, _: &mut C) -> Result<&'a Self, Self::Error> { Ok(&*value) } } impl CheckBytes for PhantomPinned { type Error = Infallible; #[inline] unsafe fn check_bytes<'a>(value: *const Self, _: &mut C) -> Result<&'a Self, Self::Error> { Ok(&*value) } } impl + ?Sized> CheckBytes for ManuallyDrop { type Error = T::Error; #[inline] unsafe fn check_bytes<'a>(value: *const Self, c: &mut C) -> Result<&'a Self, Self::Error> { let _ = T::check_bytes(value as *const T, c)?; Ok(&*value) } } /// An error resulting from an invalid boolean. /// /// Booleans are one byte and may only have the value 0 or 1. #[derive(Debug)] pub struct BoolCheckError { /// The byte value of the invalid boolean pub invalid_value: u8, } impl fmt::Display for BoolCheckError { #[inline] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!( f, "check failed for bool: expected 0 or 1, found {}", self.invalid_value ) } } #[cfg(feature = "std")] impl std::error::Error for BoolCheckError {} impl CheckBytes for bool { type Error = BoolCheckError; #[inline] unsafe fn check_bytes<'a>(value: *const Self, _: &mut C) -> Result<&'a Self, Self::Error> { let byte = *value.cast::(); match byte { 0 | 1 => Ok(&*value), _ => Err(BoolCheckError { invalid_value: byte, }), } } } #[cfg(has_atomics)] impl CheckBytes for AtomicBool { type Error = BoolCheckError; #[inline] unsafe fn check_bytes<'a>(value: *const Self, _: &mut C) -> Result<&'a Self, Self::Error> { let byte = *value.cast::(); match byte { 0 | 1 => Ok(&*value), _ => Err(BoolCheckError { invalid_value: byte, }), } } } /// An error resulting from an invalid character. /// /// Characters are four bytes and may only have values from `0x0` to `0xD7FF` /// and `0xE000` to `0x10FFFF` inclusive. #[derive(Debug)] pub struct CharCheckError { /// The `u32` value of the invalid character pub invalid_value: u32, } impl From for CharCheckError { #[inline] fn from(_: Infallible) -> Self { unsafe { core::hint::unreachable_unchecked() } } } impl fmt::Display for CharCheckError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!( f, "check failed for char: invalid value {}", self.invalid_value ) } } #[cfg(feature = "std")] impl std::error::Error for CharCheckError {} impl CheckBytes for char { type Error = CharCheckError; #[inline] unsafe fn check_bytes<'a>( value: *const Self, context: &mut C, ) -> Result<&'a Self, Self::Error> { let c = *u32::check_bytes(value.cast(), context)?; char::try_from(c).map_err(|_| CharCheckError { invalid_value: c })?; Ok(&*value) } } macro_rules! peel_tuple { ([$($error_rest:ident,)*], [$type:ident $index:tt, $($type_rest:ident $index_rest:tt,)*]) => { impl_tuple! { [$($error_rest,)*], [$($type_rest $index_rest,)*] } }; } macro_rules! impl_tuple { ([], []) => {}; ([$error:ident, $($error_rest:ident,)*], [$($type:ident $index:tt,)+]) => { /// An error resulting from an invalid tuple. #[derive(Debug)] pub enum $error<$($type),+> { $( /// The given tuple member was invalid. $type($type), )+ } impl<$($type: fmt::Display),*> fmt::Display for $error<$($type),+> { #[inline] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { const SIZE: usize = [$($index,)+].len(); match self { $($error::$type(e) => write!(f, "check failed for {}-tuple index {}: {}", SIZE, SIZE - $index - 1, e),)+ } } } #[cfg(feature = "std")] impl<$($type: fmt::Display + fmt::Debug),*> std::error::Error for $error<$($type),+> {} impl<$($type: CheckBytes,)+ C: ?Sized> CheckBytes for ($($type,)+) { type Error = $error<$($type::Error),+>; #[inline] #[allow(clippy::unneeded_wildcard_pattern)] unsafe fn check_bytes<'a>(value: *const Self, context: &mut C) -> Result<&'a Self, Self::Error> { let field_bytes = ($(ptr::addr_of!((*value).$index),)+); impl_tuple!(@check_fields field_bytes, $error, context, $($type $index,)*); Ok(&*value) } } peel_tuple! { [$($error_rest,)*], [$($type $index,)+] } }; (@check_fields $field_bytes:ident, $error:ident, $context:ident,) => {}; (@check_fields $field_bytes:ident, $error:ident, $context:ident, $type:ident $index:tt, $($type_rest:ident $index_rest:tt,)*) => { impl_tuple!(@check_fields $field_bytes, $error, $context, $($type_rest $index_rest,)*); $type::check_bytes($field_bytes.$index, $context).map_err($error::$type)?; }; } impl_tuple! { [Tuple12CheckError, Tuple11CheckError, Tuple10CheckError, Tuple9CheckError, Tuple8CheckError, Tuple7CheckError, Tuple6CheckError, Tuple5CheckError, Tuple4CheckError, Tuple3CheckError, Tuple2CheckError, Tuple1CheckError, ], [T11 11, T10 10, T9 9, T8 8, T7 7, T6 6, T5 5, T4 4, T3 3, T2 2, T1 1, T0 0, ] } /// An error resulting from an invalid array. #[derive(Debug)] pub struct ArrayCheckError { /// The index of the invalid element pub index: usize, /// The error that occured while validating the invalid element pub error: T, } impl fmt::Display for ArrayCheckError { #[inline] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!( f, "check failed for array index {}: {}", self.index, self.error ) } } #[cfg(feature = "std")] impl std::error::Error for ArrayCheckError {} impl, C: ?Sized, const N: usize> CheckBytes for [T; N] { type Error = ArrayCheckError; #[inline] unsafe fn check_bytes<'a>( value: *const Self, context: &mut C, ) -> Result<&'a Self, Self::Error> { for index in 0..N { let el = value.cast::().add(index); T::check_bytes(el, context).map_err(|error| ArrayCheckError { index, error })?; } Ok(&*value) } } /// An error resulting from an invalid slice. #[derive(Debug)] pub enum SliceCheckError { /// An element of the slice failed to validate CheckBytes { /// The index of the invalid element index: usize, /// The error that occured while validating the invalid element error: T, }, } impl fmt::Display for SliceCheckError { #[inline] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { SliceCheckError::CheckBytes { index, error } => { write!(f, "check failed for slice index {index}: {error}") } } } } #[cfg(feature = "std")] impl std::error::Error for SliceCheckError {} impl, C: ?Sized> CheckBytes for [T] { type Error = SliceCheckError; #[inline] unsafe fn check_bytes<'a>( value: *const Self, context: &mut C, ) -> Result<&'a Self, Self::Error> { let (data, len) = PtrExt::to_raw_parts(value); for index in 0..len { let el = data.cast::().add(index); T::check_bytes(el, context) .map_err(|error| SliceCheckError::CheckBytes { index, error })?; } Ok(&*value) } } /// An error resulting from an invalid str. #[derive(Debug)] pub enum StrCheckError { /// The UTF-8 string failed to validate Utf8Error(Utf8Error), } impl From for StrCheckError { fn from(e: Utf8Error) -> Self { Self::Utf8Error(e) } } impl fmt::Display for StrCheckError { #[inline] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { StrCheckError::Utf8Error(e) => write!(f, "utf8 error: {e}"), } } } #[cfg(feature = "std")] impl std::error::Error for StrCheckError { #[inline] fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { match self { StrCheckError::Utf8Error(e) => Some(e), } } } impl CheckBytes for str { type Error = StrCheckError; #[inline] unsafe fn check_bytes<'a>(value: *const Self, _: &mut C) -> Result<&'a Self, Self::Error> { let (data, len) = PtrExt::to_raw_parts(value); from_utf8(slice::from_raw_parts(data.cast(), len))?; Ok(&*value) } } /// An error resulting from an invalid `CStr`. #[cfg(feature = "std")] #[derive(Debug)] pub enum CStrCheckError { /// The UTF-8 C string failed to validate Utf8Error(Utf8Error), /// The string did not end with a null terminator MissingNullTerminator, } #[cfg(feature = "std")] impl From for CStrCheckError { fn from(e: Utf8Error) -> Self { Self::Utf8Error(e) } } #[cfg(feature = "std")] impl fmt::Display for CStrCheckError { #[inline] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { CStrCheckError::Utf8Error(e) => write!(f, "utf8 error: {e}"), CStrCheckError::MissingNullTerminator => write!(f, "missing null terminator"), } } } #[cfg(feature = "std")] impl std::error::Error for CStrCheckError { #[inline] fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { match self { CStrCheckError::Utf8Error(e) => Some(e), CStrCheckError::MissingNullTerminator => None, } } } #[cfg(feature = "std")] impl CheckBytes for ::std::ffi::CStr { type Error = CStrCheckError; #[inline] unsafe fn check_bytes<'a>(value: *const Self, _: &mut C) -> Result<&'a Self, Self::Error> { let (data, len) = PtrExt::to_raw_parts(value); if len == 0 { Err(CStrCheckError::MissingNullTerminator) } else { from_utf8(slice::from_raw_parts(data.cast(), len - 1))?; if *data.cast::().add(len - 1) != 0 { Err(CStrCheckError::MissingNullTerminator) } else { Ok(&*value) } } } } /// An error resulting from an invalid struct. #[derive(Debug)] pub struct StructCheckError { /// The name of the struct field that was invalid pub field_name: &'static str, /// The error that occurred while validating the field pub inner: ErrorBox, } impl fmt::Display for StructCheckError { #[inline] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!( f, "check failed for struct member {}: {}", self.field_name, self.inner ) } } #[cfg(feature = "std")] impl std::error::Error for StructCheckError {} /// An error resulting from an invalid tuple struct. #[derive(Debug)] pub struct TupleStructCheckError { /// The index of the struct field that was invalid pub field_index: usize, /// The error that occurred while validating the field pub inner: ErrorBox, } impl fmt::Display for TupleStructCheckError { #[inline] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!( f, "check failed for tuple struct member {}: {}", self.field_index, self.inner ) } } #[cfg(feature = "std")] impl std::error::Error for TupleStructCheckError {} /// An error resulting from an invalid enum. #[derive(Debug)] pub enum EnumCheckError { /// A struct variant was invalid InvalidStruct { /// The name of the variant that was invalid variant_name: &'static str, /// The error that occurred while validating the variant inner: StructCheckError, }, /// A tuple variant was invalid InvalidTuple { /// The name of the variant that was invalid variant_name: &'static str, /// The error that occurred while validating the variant inner: TupleStructCheckError, }, /// The enum tag was invalid InvalidTag( /// The invalid value of the tag T, ), } impl fmt::Display for EnumCheckError { #[inline] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { EnumCheckError::InvalidStruct { variant_name, inner, } => write!( f, "check failed for enum struct variant {variant_name}: {inner}" ), EnumCheckError::InvalidTuple { variant_name, inner, } => write!( f, "check failed for enum tuple variant {variant_name}: {inner}" ), EnumCheckError::InvalidTag(tag) => write!(f, "invalid tag for enum: {tag}"), } } } #[cfg(feature = "std")] impl std::error::Error for EnumCheckError {} // Range types impl, C: ?Sized> CheckBytes for ops::Range { type Error = StructCheckError; #[inline] unsafe fn check_bytes<'a>( value: *const Self, context: &mut C, ) -> Result<&'a Self, Self::Error> { T::check_bytes(ptr::addr_of!((*value).start), context).map_err(|error| { StructCheckError { field_name: "start", inner: ErrorBox::new(error), } })?; T::check_bytes(ptr::addr_of!((*value).end), context).map_err(|error| StructCheckError { field_name: "end", inner: ErrorBox::new(error), })?; Ok(&*value) } } impl, C: ?Sized> CheckBytes for ops::RangeFrom { type Error = StructCheckError; #[inline] unsafe fn check_bytes<'a>( value: *const Self, context: &mut C, ) -> Result<&'a Self, Self::Error> { let bytes = value.cast::(); T::check_bytes(ptr::addr_of!((*value).start), context).map_err(|error| { StructCheckError { field_name: "start", inner: ErrorBox::new(error), } })?; Ok(&*bytes.cast()) } } impl CheckBytes for ops::RangeFull { type Error = Infallible; #[inline] unsafe fn check_bytes<'a>(value: *const Self, _: &mut C) -> Result<&'a Self, Self::Error> { Ok(&*value) } } impl, C: ?Sized> CheckBytes for ops::RangeTo { type Error = StructCheckError; #[inline] unsafe fn check_bytes<'a>( value: *const Self, context: &mut C, ) -> Result<&'a Self, Self::Error> { T::check_bytes(ptr::addr_of!((*value).end), context).map_err(|error| StructCheckError { field_name: "end", inner: ErrorBox::new(error), })?; Ok(&*value) } } impl, C: ?Sized> CheckBytes for ops::RangeToInclusive { type Error = StructCheckError; #[inline] unsafe fn check_bytes<'a>( value: *const Self, context: &mut C, ) -> Result<&'a Self, Self::Error> { T::check_bytes(ptr::addr_of!((*value).end), context).map_err(|error| StructCheckError { field_name: "end", inner: ErrorBox::new(error), })?; Ok(&*value) } } /// An error resulting from an invalid `NonZero` integer. #[derive(Debug)] pub enum NonZeroCheckError { /// The integer was zero IsZero, } impl From for NonZeroCheckError { #[inline] fn from(_: Infallible) -> Self { unsafe { core::hint::unreachable_unchecked() } } } impl fmt::Display for NonZeroCheckError { #[inline] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { NonZeroCheckError::IsZero => write!(f, "nonzero integer is zero"), } } } #[cfg(feature = "std")] impl std::error::Error for NonZeroCheckError {} macro_rules! impl_nonzero { ($nonzero:ident, $underlying:ident) => { impl CheckBytes for $nonzero { type Error = NonZeroCheckError; #[inline] unsafe fn check_bytes<'a>( value: *const Self, context: &mut C, ) -> Result<&'a Self, Self::Error> { if *$underlying::check_bytes(value.cast(), context)? == 0 { Err(NonZeroCheckError::IsZero) } else { Ok(&*value) } } } }; } impl_nonzero!(NonZeroI8, i8); impl_nonzero!(NonZeroI16, i16); impl_nonzero!(NonZeroI32, i32); impl_nonzero!(NonZeroI64, i64); impl_nonzero!(NonZeroI128, i128); impl_nonzero!(NonZeroU8, u8); impl_nonzero!(NonZeroU16, u16); impl_nonzero!(NonZeroU32, u32); impl_nonzero!(NonZeroU64, u64); impl_nonzero!(NonZeroU128, u128); bytecheck-0.6.11/src/uuid.rs000064400000000000000000000017030072674642500140060ustar 00000000000000//! [`CheckBytes`](crate::CheckBytes) implementations for uuid. use crate::CheckBytes; use uuid::{Bytes, Uuid}; impl CheckBytes for Uuid { type Error = >::Error; unsafe fn check_bytes<'a>( value: *const Self, context: &mut C, ) -> Result<&'a Self, Self::Error> { // Safety: cast is OK because Uuid is repr(transparent) Bytes::check_bytes(value.cast(), context)?; Ok(&*value) } } #[cfg(test)] mod bytecheck_tests { use crate::CheckBytes; use uuid::Uuid; #[test] fn test_check_bytes() { let uuid_str = "f9168c5e-ceb2-4faa-b6bf-329bf39fa1e4"; let u = Uuid::parse_str(uuid_str).unwrap(); // Safety: the pointer is aligned and points to enough bytes to represent a Uuid unsafe { Uuid::check_bytes(&u as *const Uuid, &mut ()).expect("failed to check uuid"); } } }