bytecheck/
lib.rs

1//! # bytecheck
2//!
3//! bytecheck is a type validation framework for Rust.
4//!
5//! For some types, creating an invalid value immediately results in undefined
6//! behavior. This can cause some issues when trying to validate potentially
7//! invalid bytes, as just casting the bytes to your type can technically cause
8//! errors. This makes it difficult to write validation routines, because until
9//! you're certain that the bytes represent valid values you cannot cast them.
10//!
11//! bytecheck provides a framework for performing these byte-level validations
12//! and implements checks for basic types along with a derive macro to implement
13//! validation for custom structs and enums.
14//!
15//! ## Design
16//!
17//! [`CheckBytes`] is at the heart of bytecheck, and does the heavy lifting of
18//! verifying that some bytes represent a valid type. Implementing it can be
19//! done manually or automatically with the [derive macro](macro@CheckBytes).
20//!
21//! ## Examples
22//!
23//! ```
24//! use bytecheck::CheckBytes;
25//!
26//! #[derive(CheckBytes, Debug)]
27//! #[repr(C)]
28//! struct Test {
29//!     a: u32,
30//!     b: char,
31//!     c: bool,
32//! }
33//! #[repr(C, align(16))]
34//! struct Aligned<const N: usize>([u8; N]);
35//!
36//! macro_rules! bytes {
37//!     ($($byte:literal,)*) => {
38//!         (&Aligned([$($byte,)*]).0 as &[u8]).as_ptr()
39//!     };
40//!     ($($byte:literal),*) => {
41//!         bytes!($($byte,)*)
42//!     };
43//! }
44//!
45//! // This type is laid out as (u32, char, bool)
46//! // In this example, the architecture is assumed to be little-endian
47//! # #[cfg(target_endian = "little")]
48//! unsafe {
49//!     // These are valid bytes for (0, 'x', true)
50//!     Test::check_bytes(
51//!         bytes![
52//!             0u8, 0u8, 0u8, 0u8, 0x78u8, 0u8, 0u8, 0u8,
53//!             1u8, 255u8, 255u8, 255u8, 255u8, 255u8, 255u8, 255u8
54//!         ].cast(),
55//!         &mut ()
56//!     ).unwrap();
57//!
58//!     // Changing the bytes for the u32 is OK, any bytes are a valid u32
59//!     Test::check_bytes(
60//!         bytes![
61//!             42u8, 16u8, 20u8, 3u8, 0x78u8, 0u8, 0u8, 0u8,
62//!             1u8, 255u8, 255u8, 255u8, 255u8, 255u8, 255u8, 255u8
63//!         ].cast(),
64//!         &mut ()
65//!     ).unwrap();
66//!
67//!     // Characters outside the valid ranges are invalid
68//!     Test::check_bytes(
69//!         bytes![
70//!             0u8, 0u8, 0u8, 0u8, 0x00u8, 0xd8u8, 0u8, 0u8,
71//!             1u8, 255u8, 255u8, 255u8, 255u8, 255u8, 255u8, 255u8
72//!         ].cast(),
73//!         &mut ()
74//!     ).unwrap_err();
75//!     Test::check_bytes(
76//!         bytes![
77//!             0u8, 0u8, 0u8, 0u8, 0x00u8, 0x00u8, 0x11u8, 0u8,
78//!             1u8, 255u8, 255u8, 255u8, 255u8, 255u8, 255u8, 255u8
79//!         ].cast(),
80//!         &mut ()
81//!     ).unwrap_err();
82//!
83//!     // 0 is a valid boolean value (false) but 2 is not
84//!     Test::check_bytes(
85//!         bytes![
86//!             0u8, 0u8, 0u8, 0u8, 0x78u8, 0u8, 0u8, 0u8,
87//!             0u8, 255u8, 255u8, 255u8, 255u8, 255u8, 255u8, 255u8
88//!         ].cast(),
89//!         &mut ()
90//!     ).unwrap();
91//!     Test::check_bytes(
92//!         bytes![
93//!             0u8, 0u8, 0u8, 0u8, 0x78u8, 0u8, 0u8, 0u8,
94//!             2u8, 255u8, 255u8, 255u8, 255u8, 255u8, 255u8, 255u8
95//!         ].cast(),
96//!         &mut ()
97//!     ).unwrap_err();
98//! }
99//! ```
100//!
101//! ## Features
102//!
103//! - `verbose`: Some validation algorithms are optimized for speed and do not report full error
104//!   details by default. This feature provides full error information.
105//! - `std`: Enables standard library support (enabled by default). If the `std` feature is not
106//!   enabled, the `alloc` crate is required.
107//!
108//! ## Crate support
109//!
110//! Some common crates need to be supported by bytecheck before an official integration has been
111//! made. Support is provided by bytecheck for these crates, but in the future crates should depend
112//! on bytecheck and provide their own implementations. The crates that already have support
113//! provided by bytecheck should work toward integrating the implementations into themselves.
114//!
115//! Crates supported by bytecheck:
116//!
117//! - [`uuid`](https://docs.rs/uuid)
118
119#![deny(
120    rustdoc::broken_intra_doc_links,
121    rustdoc::missing_crate_level_docs,
122    missing_docs,
123    rust_2018_compatibility,
124    rust_2018_idioms,
125    future_incompatible,
126    nonstandard_style,
127    unused,
128    clippy::all
129)]
130#![cfg_attr(not(feature = "std"), no_std)]
131
132#[cfg(not(feature = "std"))]
133extern crate alloc;
134
135// Support for various common crates. These are primarily to get users off the ground and build some
136// momentum.
137
138// These are NOT PLANNED to remain in bytecheck for the final release. Much like serde, these
139// implementations should be moved into their respective crates over time. Before adding support for
140// another crate, please consider getting bytecheck support in the crate instead.
141
142#[cfg(feature = "uuid")]
143pub mod uuid;
144
145#[cfg(not(feature = "simdutf8"))]
146use core::str::{from_utf8, Utf8Error};
147#[cfg(has_atomics)]
148use core::sync::atomic::{
149    AtomicBool, AtomicI16, AtomicI32, AtomicI8, AtomicU16, AtomicU32, AtomicU8,
150};
151#[cfg(has_atomics_64)]
152use core::sync::atomic::{AtomicI64, AtomicU64};
153use core::{
154    convert::{Infallible, TryFrom},
155    fmt,
156    marker::{PhantomData, PhantomPinned},
157    mem::ManuallyDrop,
158    num::{
159        NonZeroI128, NonZeroI16, NonZeroI32, NonZeroI64, NonZeroI8, NonZeroU128, NonZeroU16,
160        NonZeroU32, NonZeroU64, NonZeroU8,
161    },
162    ops, ptr, slice,
163};
164use ptr_meta::PtrExt;
165#[cfg(all(feature = "simdutf8", not(feature = "verbose")))]
166use simdutf8::basic::{from_utf8, Utf8Error};
167#[cfg(all(feature = "simdutf8", feature = "verbose"))]
168use simdutf8::compat::{from_utf8, Utf8Error};
169
170pub use bytecheck_derive::CheckBytes;
171
172/// An error that can be debugged and displayed.
173///
174/// With the `std` feature, this also supports `std::error::Error`.
175#[cfg(not(feature = "std"))]
176pub trait Error: fmt::Debug + fmt::Display + 'static {}
177
178#[cfg(not(feature = "std"))]
179impl<T: fmt::Debug + fmt::Display + 'static> Error for T {}
180
181/// An error that can be debugged and displayed.
182///
183/// With the `std` feature, this also supports `std::error::Error`.
184#[cfg(feature = "std")]
185pub trait Error: std::error::Error + 'static {
186    /// Gets this error as an `std::error::Error`.
187    fn as_error(&self) -> &(dyn std::error::Error + 'static);
188}
189
190#[cfg(feature = "std")]
191impl<T: std::error::Error + 'static> Error for T {
192    fn as_error(&self) -> &(dyn std::error::Error + 'static) {
193        self
194    }
195}
196
197/// The type used for boxing errors.
198#[cfg(not(feature = "std"))]
199pub type ErrorBox<E> = alloc::boxed::Box<E>;
200
201/// The type used for boxing errors.
202#[cfg(feature = "std")]
203pub type ErrorBox<E> = std::boxed::Box<E>;
204
205/// A type that can check whether a pointer points to a valid value.
206///
207/// `CheckBytes` can be derived with [`CheckBytes`](macro@CheckBytes) or
208/// implemented manually for custom behavior.
209pub trait CheckBytes<C: ?Sized> {
210    /// The error that may result from checking the type.
211    type Error: Error + 'static;
212
213    /// Checks whether the given pointer points to a valid value within the
214    /// given context.
215    ///
216    /// # Safety
217    ///
218    /// The passed pointer must be aligned and point to enough bytes to
219    /// represent the type.
220    unsafe fn check_bytes<'a>(value: *const Self, context: &mut C)
221        -> Result<&'a Self, Self::Error>;
222}
223
224macro_rules! impl_primitive {
225    ($type:ty) => {
226        impl<C: ?Sized> CheckBytes<C> for $type {
227            type Error = Infallible;
228
229            #[inline]
230            unsafe fn check_bytes<'a>(
231                value: *const Self,
232                _: &mut C,
233            ) -> Result<&'a Self, Self::Error> {
234                Ok(&*value)
235            }
236        }
237    };
238}
239
240impl_primitive!(());
241impl_primitive!(i8);
242impl_primitive!(i16);
243impl_primitive!(i32);
244impl_primitive!(i64);
245impl_primitive!(i128);
246impl_primitive!(u8);
247impl_primitive!(u16);
248impl_primitive!(u32);
249impl_primitive!(u64);
250impl_primitive!(u128);
251impl_primitive!(f32);
252impl_primitive!(f64);
253#[cfg(has_atomics)]
254impl_primitive!(AtomicI8);
255#[cfg(has_atomics)]
256impl_primitive!(AtomicI16);
257#[cfg(has_atomics)]
258impl_primitive!(AtomicI32);
259#[cfg(has_atomics_64)]
260impl_primitive!(AtomicI64);
261#[cfg(has_atomics)]
262impl_primitive!(AtomicU8);
263#[cfg(has_atomics)]
264impl_primitive!(AtomicU16);
265#[cfg(has_atomics)]
266impl_primitive!(AtomicU32);
267#[cfg(has_atomics_64)]
268impl_primitive!(AtomicU64);
269
270impl<T: ?Sized, C: ?Sized> CheckBytes<C> for PhantomData<T> {
271    type Error = Infallible;
272
273    #[inline]
274    unsafe fn check_bytes<'a>(value: *const Self, _: &mut C) -> Result<&'a Self, Self::Error> {
275        Ok(&*value)
276    }
277}
278
279impl<C: ?Sized> CheckBytes<C> for PhantomPinned {
280    type Error = Infallible;
281
282    #[inline]
283    unsafe fn check_bytes<'a>(value: *const Self, _: &mut C) -> Result<&'a Self, Self::Error> {
284        Ok(&*value)
285    }
286}
287
288impl<C: ?Sized, T: CheckBytes<C> + ?Sized> CheckBytes<C> for ManuallyDrop<T> {
289    type Error = T::Error;
290
291    #[inline]
292    unsafe fn check_bytes<'a>(value: *const Self, c: &mut C) -> Result<&'a Self, Self::Error> {
293        let _ = T::check_bytes(value as *const T, c)?;
294        Ok(&*value)
295    }
296}
297
298/// An error resulting from an invalid boolean.
299///
300/// Booleans are one byte and may only have the value 0 or 1.
301#[derive(Debug)]
302pub struct BoolCheckError {
303    /// The byte value of the invalid boolean
304    pub invalid_value: u8,
305}
306
307impl fmt::Display for BoolCheckError {
308    #[inline]
309    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
310        write!(
311            f,
312            "check failed for bool: expected 0 or 1, found {}",
313            self.invalid_value
314        )
315    }
316}
317
318#[cfg(feature = "std")]
319impl std::error::Error for BoolCheckError {}
320
321impl<C: ?Sized> CheckBytes<C> for bool {
322    type Error = BoolCheckError;
323
324    #[inline]
325    unsafe fn check_bytes<'a>(value: *const Self, _: &mut C) -> Result<&'a Self, Self::Error> {
326        let byte = *value.cast::<u8>();
327        match byte {
328            0 | 1 => Ok(&*value),
329            _ => Err(BoolCheckError {
330                invalid_value: byte,
331            }),
332        }
333    }
334}
335
336#[cfg(has_atomics)]
337impl<C: ?Sized> CheckBytes<C> for AtomicBool {
338    type Error = BoolCheckError;
339
340    #[inline]
341    unsafe fn check_bytes<'a>(value: *const Self, _: &mut C) -> Result<&'a Self, Self::Error> {
342        let byte = *value.cast::<u8>();
343        match byte {
344            0 | 1 => Ok(&*value),
345            _ => Err(BoolCheckError {
346                invalid_value: byte,
347            }),
348        }
349    }
350}
351
352/// An error resulting from an invalid character.
353///
354/// Characters are four bytes and may only have values from `0x0` to `0xD7FF`
355/// and `0xE000` to `0x10FFFF` inclusive.
356#[derive(Debug)]
357pub struct CharCheckError {
358    /// The `u32` value of the invalid character
359    pub invalid_value: u32,
360}
361
362impl From<Infallible> for CharCheckError {
363    #[inline]
364    fn from(_: Infallible) -> Self {
365        unsafe { core::hint::unreachable_unchecked() }
366    }
367}
368
369impl fmt::Display for CharCheckError {
370    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
371        write!(
372            f,
373            "check failed for char: invalid value {}",
374            self.invalid_value
375        )
376    }
377}
378
379#[cfg(feature = "std")]
380impl std::error::Error for CharCheckError {}
381
382impl<C: ?Sized> CheckBytes<C> for char {
383    type Error = CharCheckError;
384
385    #[inline]
386    unsafe fn check_bytes<'a>(
387        value: *const Self,
388        context: &mut C,
389    ) -> Result<&'a Self, Self::Error> {
390        let c = *u32::check_bytes(value.cast(), context)?;
391        char::try_from(c).map_err(|_| CharCheckError { invalid_value: c })?;
392        Ok(&*value)
393    }
394}
395
396macro_rules! peel_tuple {
397    ([$($error_rest:ident,)*], [$type:ident $index:tt, $($type_rest:ident $index_rest:tt,)*]) => { impl_tuple! { [$($error_rest,)*], [$($type_rest $index_rest,)*] } };
398}
399
400macro_rules! impl_tuple {
401    ([], []) => {};
402    ([$error:ident, $($error_rest:ident,)*], [$($type:ident $index:tt,)+]) => {
403        /// An error resulting from an invalid tuple.
404        #[derive(Debug)]
405        pub enum $error<$($type),+> {
406            $(
407                /// The given tuple member was invalid.
408                $type($type),
409            )+
410        }
411
412        impl<$($type: fmt::Display),*> fmt::Display for $error<$($type),+> {
413            #[inline]
414            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
415                const SIZE: usize = [$($index,)+].len();
416                match self {
417                    $($error::$type(e) => write!(f, "check failed for {}-tuple index {}: {}", SIZE, SIZE - $index - 1, e),)+
418                }
419            }
420        }
421
422        #[cfg(feature = "std")]
423        impl<$($type: fmt::Display + fmt::Debug),*> std::error::Error for $error<$($type),+> {}
424
425        impl<$($type: CheckBytes<C>,)+ C: ?Sized> CheckBytes<C> for ($($type,)+) {
426            type Error = $error<$($type::Error),+>;
427
428            #[inline]
429            #[allow(clippy::unneeded_wildcard_pattern)]
430            unsafe fn check_bytes<'a>(value: *const Self, context: &mut C) -> Result<&'a Self, Self::Error> {
431                let field_bytes = ($(ptr::addr_of!((*value).$index),)+);
432                impl_tuple!(@check_fields field_bytes, $error, context, $($type $index,)*);
433                Ok(&*value)
434            }
435        }
436
437        peel_tuple! {
438            [$($error_rest,)*],
439            [$($type $index,)+]
440        }
441    };
442    (@check_fields $field_bytes:ident, $error:ident, $context:ident,) => {};
443    (@check_fields $field_bytes:ident, $error:ident, $context:ident, $type:ident $index:tt, $($type_rest:ident $index_rest:tt,)*) => {
444        impl_tuple!(@check_fields $field_bytes, $error, $context, $($type_rest $index_rest,)*);
445        $type::check_bytes($field_bytes.$index, $context).map_err($error::$type)?;
446    };
447}
448
449impl_tuple! {
450    [Tuple12CheckError, Tuple11CheckError, Tuple10CheckError, Tuple9CheckError, Tuple8CheckError, Tuple7CheckError, Tuple6CheckError, Tuple5CheckError, Tuple4CheckError, Tuple3CheckError, Tuple2CheckError, Tuple1CheckError, ],
451    [T11 11, T10 10, T9 9, T8 8, T7 7, T6 6, T5 5, T4 4, T3 3, T2 2, T1 1, T0 0, ]
452}
453
454/// An error resulting from an invalid array.
455#[derive(Debug)]
456pub struct ArrayCheckError<T> {
457    /// The index of the invalid element
458    pub index: usize,
459    /// The error that occured while validating the invalid element
460    pub error: T,
461}
462
463impl<T: fmt::Display> fmt::Display for ArrayCheckError<T> {
464    #[inline]
465    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
466        write!(
467            f,
468            "check failed for array index {}: {}",
469            self.index, self.error
470        )
471    }
472}
473
474#[cfg(feature = "std")]
475impl<T: fmt::Debug + fmt::Display> std::error::Error for ArrayCheckError<T> {}
476
477impl<T: CheckBytes<C>, C: ?Sized, const N: usize> CheckBytes<C> for [T; N] {
478    type Error = ArrayCheckError<T::Error>;
479
480    #[inline]
481    unsafe fn check_bytes<'a>(
482        value: *const Self,
483        context: &mut C,
484    ) -> Result<&'a Self, Self::Error> {
485        for index in 0..N {
486            let el = value.cast::<T>().add(index);
487            T::check_bytes(el, context).map_err(|error| ArrayCheckError { index, error })?;
488        }
489        Ok(&*value)
490    }
491}
492
493/// An error resulting from an invalid slice.
494#[derive(Debug)]
495pub enum SliceCheckError<T> {
496    /// An element of the slice failed to validate
497    CheckBytes {
498        /// The index of the invalid element
499        index: usize,
500        /// The error that occured while validating the invalid element
501        error: T,
502    },
503}
504
505impl<T: fmt::Display> fmt::Display for SliceCheckError<T> {
506    #[inline]
507    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
508        match self {
509            SliceCheckError::CheckBytes { index, error } => {
510                write!(f, "check failed for slice index {}: {}", index, error)
511            }
512        }
513    }
514}
515
516#[cfg(feature = "std")]
517impl<T: fmt::Debug + fmt::Display> std::error::Error for SliceCheckError<T> {}
518
519impl<T: CheckBytes<C>, C: ?Sized> CheckBytes<C> for [T] {
520    type Error = SliceCheckError<T::Error>;
521
522    #[inline]
523    unsafe fn check_bytes<'a>(
524        value: *const Self,
525        context: &mut C,
526    ) -> Result<&'a Self, Self::Error> {
527        let (data, len) = PtrExt::to_raw_parts(value);
528        for index in 0..len {
529            let el = data.cast::<T>().add(index);
530            T::check_bytes(el, context)
531                .map_err(|error| SliceCheckError::CheckBytes { index, error })?;
532        }
533        Ok(&*value)
534    }
535}
536
537/// An error resulting from an invalid str.
538#[derive(Debug)]
539pub enum StrCheckError {
540    /// The UTF-8 string failed to validate
541    Utf8Error(Utf8Error),
542}
543
544impl From<Utf8Error> for StrCheckError {
545    fn from(e: Utf8Error) -> Self {
546        Self::Utf8Error(e)
547    }
548}
549
550impl fmt::Display for StrCheckError {
551    #[inline]
552    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
553        match self {
554            StrCheckError::Utf8Error(e) => write!(f, "utf8 error: {}", e),
555        }
556    }
557}
558
559#[cfg(feature = "std")]
560impl std::error::Error for StrCheckError {
561    #[inline]
562    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
563        match self {
564            StrCheckError::Utf8Error(e) => Some(e),
565        }
566    }
567}
568
569impl<C: ?Sized> CheckBytes<C> for str {
570    type Error = StrCheckError;
571
572    #[inline]
573    unsafe fn check_bytes<'a>(value: *const Self, _: &mut C) -> Result<&'a Self, Self::Error> {
574        let (data, len) = PtrExt::to_raw_parts(value);
575        from_utf8(slice::from_raw_parts(data.cast(), len))?;
576        Ok(&*value)
577    }
578}
579
580/// An error resulting from an invalid `CStr`.
581#[cfg(feature = "std")]
582#[derive(Debug)]
583pub enum CStrCheckError {
584    /// The UTF-8 C string failed to validate
585    Utf8Error(Utf8Error),
586    /// The string did not end with a null terminator
587    MissingNullTerminator,
588}
589
590#[cfg(feature = "std")]
591impl From<Utf8Error> for CStrCheckError {
592    fn from(e: Utf8Error) -> Self {
593        Self::Utf8Error(e)
594    }
595}
596
597#[cfg(feature = "std")]
598impl fmt::Display for CStrCheckError {
599    #[inline]
600    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
601        match self {
602            CStrCheckError::Utf8Error(e) => write!(f, "utf8 error: {}", e),
603            CStrCheckError::MissingNullTerminator => write!(f, "missing null terminator"),
604        }
605    }
606}
607
608#[cfg(feature = "std")]
609impl std::error::Error for CStrCheckError {
610    #[inline]
611    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
612        match self {
613            CStrCheckError::Utf8Error(e) => Some(e),
614            CStrCheckError::MissingNullTerminator => None,
615        }
616    }
617}
618
619#[cfg(feature = "std")]
620impl<C: ?Sized> CheckBytes<C> for ::std::ffi::CStr {
621    type Error = CStrCheckError;
622
623    #[inline]
624    unsafe fn check_bytes<'a>(value: *const Self, _: &mut C) -> Result<&'a Self, Self::Error> {
625        let (data, len) = PtrExt::to_raw_parts(value);
626        if len == 0 {
627            Err(CStrCheckError::MissingNullTerminator)
628        } else {
629            from_utf8(slice::from_raw_parts(data.cast(), len - 1))?;
630            if *data.cast::<u8>().add(len - 1) != 0 {
631                Err(CStrCheckError::MissingNullTerminator)
632            } else {
633                Ok(&*value)
634            }
635        }
636    }
637}
638
639/// An error resulting from an invalid struct.
640#[derive(Debug)]
641pub struct StructCheckError {
642    /// The name of the struct field that was invalid
643    pub field_name: &'static str,
644    /// The error that occurred while validating the field
645    pub inner: ErrorBox<dyn Error>,
646}
647
648impl fmt::Display for StructCheckError {
649    #[inline]
650    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
651        write!(
652            f,
653            "check failed for struct member {}: {}",
654            self.field_name, self.inner
655        )
656    }
657}
658
659#[cfg(feature = "std")]
660impl std::error::Error for StructCheckError {}
661
662/// An error resulting from an invalid tuple struct.
663#[derive(Debug)]
664pub struct TupleStructCheckError {
665    /// The index of the struct field that was invalid
666    pub field_index: usize,
667    /// The error that occurred while validating the field
668    pub inner: ErrorBox<dyn Error>,
669}
670
671impl fmt::Display for TupleStructCheckError {
672    #[inline]
673    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
674        write!(
675            f,
676            "check failed for tuple struct member {}: {}",
677            self.field_index, self.inner
678        )
679    }
680}
681
682#[cfg(feature = "std")]
683impl std::error::Error for TupleStructCheckError {}
684
685/// An error resulting from an invalid enum.
686#[derive(Debug)]
687pub enum EnumCheckError<T> {
688    /// A struct variant was invalid
689    InvalidStruct {
690        /// The name of the variant that was invalid
691        variant_name: &'static str,
692        /// The error that occurred while validating the variant
693        inner: StructCheckError,
694    },
695    /// A tuple variant was invalid
696    InvalidTuple {
697        /// The name of the variant that was invalid
698        variant_name: &'static str,
699        /// The error that occurred while validating the variant
700        inner: TupleStructCheckError,
701    },
702    /// The enum tag was invalid
703    InvalidTag(
704        /// The invalid value of the tag
705        T,
706    ),
707}
708
709impl<T: fmt::Display> fmt::Display for EnumCheckError<T> {
710    #[inline]
711    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
712        match self {
713            EnumCheckError::InvalidStruct {
714                variant_name,
715                inner,
716            } => write!(
717                f,
718                "check failed for enum struct variant {}: {}",
719                variant_name, inner
720            ),
721            EnumCheckError::InvalidTuple {
722                variant_name,
723                inner,
724            } => write!(
725                f,
726                "check failed for enum tuple variant {}: {}",
727                variant_name, inner
728            ),
729            EnumCheckError::InvalidTag(tag) => write!(f, "invalid tag for enum: {}", tag),
730        }
731    }
732}
733
734#[cfg(feature = "std")]
735impl<T: fmt::Debug + fmt::Display> std::error::Error for EnumCheckError<T> {}
736
737// Range types
738impl<T: CheckBytes<C>, C: ?Sized> CheckBytes<C> for ops::Range<T> {
739    type Error = StructCheckError;
740
741    #[inline]
742    unsafe fn check_bytes<'a>(
743        value: *const Self,
744        context: &mut C,
745    ) -> Result<&'a Self, Self::Error> {
746        T::check_bytes(ptr::addr_of!((*value).start), context).map_err(|error| {
747            StructCheckError {
748                field_name: "start",
749                inner: ErrorBox::new(error),
750            }
751        })?;
752        T::check_bytes(ptr::addr_of!((*value).end), context).map_err(|error| StructCheckError {
753            field_name: "end",
754            inner: ErrorBox::new(error),
755        })?;
756        Ok(&*value)
757    }
758}
759
760impl<T: CheckBytes<C>, C: ?Sized> CheckBytes<C> for ops::RangeFrom<T> {
761    type Error = StructCheckError;
762
763    #[inline]
764    unsafe fn check_bytes<'a>(
765        value: *const Self,
766        context: &mut C,
767    ) -> Result<&'a Self, Self::Error> {
768        let bytes = value.cast::<u8>();
769        T::check_bytes(ptr::addr_of!((*value).start), context).map_err(|error| {
770            StructCheckError {
771                field_name: "start",
772                inner: ErrorBox::new(error),
773            }
774        })?;
775        Ok(&*bytes.cast())
776    }
777}
778
779impl<C: ?Sized> CheckBytes<C> for ops::RangeFull {
780    type Error = Infallible;
781
782    #[inline]
783    unsafe fn check_bytes<'a>(value: *const Self, _: &mut C) -> Result<&'a Self, Self::Error> {
784        Ok(&*value)
785    }
786}
787
788impl<T: CheckBytes<C>, C: ?Sized> CheckBytes<C> for ops::RangeTo<T> {
789    type Error = StructCheckError;
790
791    #[inline]
792    unsafe fn check_bytes<'a>(
793        value: *const Self,
794        context: &mut C,
795    ) -> Result<&'a Self, Self::Error> {
796        T::check_bytes(ptr::addr_of!((*value).end), context).map_err(|error| StructCheckError {
797            field_name: "end",
798            inner: ErrorBox::new(error),
799        })?;
800        Ok(&*value)
801    }
802}
803
804impl<T: CheckBytes<C>, C: ?Sized> CheckBytes<C> for ops::RangeToInclusive<T> {
805    type Error = StructCheckError;
806
807    #[inline]
808    unsafe fn check_bytes<'a>(
809        value: *const Self,
810        context: &mut C,
811    ) -> Result<&'a Self, Self::Error> {
812        T::check_bytes(ptr::addr_of!((*value).end), context).map_err(|error| StructCheckError {
813            field_name: "end",
814            inner: ErrorBox::new(error),
815        })?;
816        Ok(&*value)
817    }
818}
819
820/// An error resulting from an invalid `NonZero` integer.
821#[derive(Debug)]
822pub enum NonZeroCheckError {
823    /// The integer was zero
824    IsZero,
825}
826
827impl From<Infallible> for NonZeroCheckError {
828    #[inline]
829    fn from(_: Infallible) -> Self {
830        unsafe { core::hint::unreachable_unchecked() }
831    }
832}
833
834impl fmt::Display for NonZeroCheckError {
835    #[inline]
836    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
837        match self {
838            NonZeroCheckError::IsZero => write!(f, "nonzero integer is zero"),
839        }
840    }
841}
842
843#[cfg(feature = "std")]
844impl std::error::Error for NonZeroCheckError {}
845
846macro_rules! impl_nonzero {
847    ($nonzero:ident, $underlying:ident) => {
848        impl<C: ?Sized> CheckBytes<C> for $nonzero {
849            type Error = NonZeroCheckError;
850
851            #[inline]
852            unsafe fn check_bytes<'a>(
853                value: *const Self,
854                context: &mut C,
855            ) -> Result<&'a Self, Self::Error> {
856                if *$underlying::check_bytes(value.cast(), context)? == 0 {
857                    Err(NonZeroCheckError::IsZero)
858                } else {
859                    Ok(&*value)
860                }
861            }
862        }
863    };
864}
865
866impl_nonzero!(NonZeroI8, i8);
867impl_nonzero!(NonZeroI16, i16);
868impl_nonzero!(NonZeroI32, i32);
869impl_nonzero!(NonZeroI64, i64);
870impl_nonzero!(NonZeroI128, i128);
871impl_nonzero!(NonZeroU8, u8);
872impl_nonzero!(NonZeroU16, u16);
873impl_nonzero!(NonZeroU32, u32);
874impl_nonzero!(NonZeroU64, u64);
875impl_nonzero!(NonZeroU128, u128);