// SPDX-License-Identifier: (BSD-2-Clause OR Apache-2.0) OR MIT//// Copyright 2024 The Fuchsia Authors//// Licensed under the 2-Clause BSD License <LICENSE-BSD or// https://opensource.org/license/bsd-2-clause>, Apache License, Version 2.0// <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT// license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option.// This file may not be copied, modified, or distributed except according to// those terms. //! Types related to error reporting.//!//! ## Single failure mode errors//!//! Generally speaking, zerocopy's conversions may fail for one of up to three//! reasons://! - [`AlignmentError`]: the conversion source was improperly aligned//! - [`SizeError`]: the conversion source was of incorrect size//! - [`ValidityError`]: the conversion source contained invalid data//!//! Methods that only have one failure mode, like//! [`FromBytes::read_from_bytes`], return that mode's corresponding error type//! directly.//!//! ## Compound errors//!//! Conversion methods that have either two or three possible failure modes//! return one of these error types://! - [`CastError`]: the error type of reference conversions//! - [`TryCastError`]: the error type of fallible reference conversions//! - [`TryReadError`]: the error type of fallible read conversions//!//! ## [`Unaligned`] destination types//!//! For [`Unaligned`] destination types, alignment errors are impossible. All//! compound error types support infallibly discarding the alignment error via//! [`From`] so long as `Dst: Unaligned`. For example, see [`<SizeError as//! From<ConvertError>>::from`][size-error-from].//!//! [size-error-from]: struct.SizeError.html#method.from-1//!//! ## Accessing the conversion source//!//! All error types provide an `into_src` method that converts the error into//! the source value underlying the failed conversion.//!//! ## Display formatting//!//! All error types provide a `Display` implementation that produces a//! human-readable error message. When `debug_assertions` are enabled, these//! error messages are verbose and may include potentially sensitive//! information, including://!//! - the names of the involved types//! - the sizes of the involved types//! - the addresses of the involved types//! - the contents of the involved types//!//! When `debug_assertions` are disabled (as is default for `release` builds),//! such potentially sensitive information is excluded.//!//! In the future, we may support manually configuring this behavior. If you are//! interested in this feature, [let us know on GitHub][issue-1457] so we know//! to prioritize it.//!//! [issue-1457]: https://github.com/google/zerocopy/issues/1457//!//! ## Validation order//!//! Our conversion methods typically check alignment, then size, then bit//! validity. However, we do not guarantee that this is always the case, and//! this behavior may change between releases.//!//! ## `Send`, `Sync`, and `'static`//!//! Our error types are `Send`, `Sync`, and `'static` when their `Src` parameter//! is `Send`, `Sync`, or `'static`, respectively. This can cause issues when an//! error is sent or synchronized across threads; e.g.://!//! ```compile_fail,E0515//! use zerocopy::*;//!//! let result: SizeError<&[u8], u32> = std::thread::spawn(|| {//! let source = &mut [0u8, 1, 2][..];//! // Try (and fail) to read a `u32` from `source`.//! u32::read_from_bytes(source).unwrap_err()//! }).join().unwrap();//! ```//!//! To work around this, use [`map_src`][CastError::map_src] to convert the//! source parameter to an unproblematic type; e.g.://!//! ```//! use zerocopy::*;//!//! let result: SizeError<(), u32> = std::thread::spawn(|| {//! let source = &mut [0u8, 1, 2][..];//! // Try (and fail) to read a `u32` from `source`.//! u32::read_from_bytes(source).unwrap_err()//! // Erase the error source.//! .map_src(drop)//! }).join().unwrap();//! ```//!//! Alternatively, use `.to_string()` to eagerly convert the error into a//! human-readable message; e.g.://!//! ```//! use zerocopy::*;//!//! let result: Result<u32, String> = std::thread::spawn(|| {//! let source = &mut [0u8, 1, 2][..];//! // Try (and fail) to read a `u32` from `source`.//! u32::read_from_bytes(source)//! // Eagerly render the error message.//! .map_err(|err| err.to_string())//! }).join().unwrap();//! ```#[cfg(not(no_zerocopy_core_error_1_81_0))]use core::error::Error;use core::{ convert::Infallible, fmt::{self, Debug, Write}, ops::Deref,};#[cfg(all(no_zerocopy_core_error_1_81_0, any(feature = "std", test)))]use std::error::Error; use crate::{util::SendSyncPhantomData, KnownLayout, TryFromBytes, Unaligned};#[cfg(doc)]use crate::{FromBytes, Ref}; /// Zerocopy's generic error type.////// Generally speaking, zerocopy's conversions may fail for one of up to three/// reasons:/// - [`AlignmentError`]: the conversion source was improperly aligned/// - [`SizeError`]: the conversion source was of incorrect size/// - [`ValidityError`]: the conversion source contained invalid data////// However, not all conversions produce all errors. For instance,/// [`FromBytes::ref_from_bytes`] may fail due to alignment or size issues, but/// not validity issues. This generic error type captures these/// (im)possibilities via parameterization: `A` is parameterized with/// [`AlignmentError`], `S` is parameterized with [`SizeError`], and `V` is/// parameterized with [`Infallible`].////// Zerocopy never uses this type directly in its API. Rather, we provide three/// pre-parameterized aliases:/// - [`CastError`]: the error type of reference conversions/// - [`TryCastError`]: the error type of fallible reference conversions/// - [`TryReadError`]: the error type of fallible read conversions#[derive(PartialEq, Eq, Clone)]pub enum ConvertError<A, S, V> { /// The conversion source was improperly aligned. Alignment(A), /// The conversion source was of incorrect size. Size(S), /// The conversion source contained invalid data. Validity(V),} impl<Src, Dst: ?Sized + Unaligned, S, V> From<ConvertError<AlignmentError<Src, Dst>, S, V>> for ConvertError<Infallible, S, V>{ /// Infallibly discards the alignment error from this `ConvertError` since /// `Dst` is unaligned. /// /// Since [`Dst: Unaligned`], it is impossible to encounter an alignment /// error. This method permits discarding that alignment error infallibly /// and replacing it with [`Infallible`]. /// /// [`Dst: Unaligned`]: crate::Unaligned /// /// # Examples /// /// ``` /// use core::convert::Infallible; /// use zerocopy::*; /// # use zerocopy_derive::*; /// /// #[derive(TryFromBytes, KnownLayout, Unaligned, Immutable)] /// #[repr(C, packed)] /// struct Bools { /// one: bool, /// two: bool, /// many: [bool], /// } /// /// impl Bools { /// fn parse(bytes: &[u8]) -> Result<&Bools, AlignedTryCastError<&[u8], Bools>> { /// // Since `Bools: Unaligned`, we can infallibly discard /// // the alignment error. /// Bools::try_ref_from_bytes(bytes).map_err(Into::into) /// } /// } /// ``` #[inline] fn from(err: ConvertError<AlignmentError<Src, Dst>, S, V>) -> ConvertError<Infallible, S, V> { match err { ConvertError::Alignment(e) => { #[allow(unreachable_code)] return ConvertError::Alignment(Infallible::from(e)); } ConvertError::Size(e) => ConvertError::Size(e), ConvertError::Validity(e) => ConvertError::Validity(e), } }} impl<A: fmt::Debug, S: fmt::Debug, V: fmt::Debug> fmt::Debug for ConvertError<A, S, V> { #[inline] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Self::Alignment(e) => f.debug_tuple("Alignment").field(e).finish(), Self::Size(e) => f.debug_tuple("Size").field(e).finish(), Self::Validity(e) => f.debug_tuple("Validity").field(e).finish(), } }} /// Produces a human-readable error message.////// The message differs between debug and release builds. When/// `debug_assertions` are enabled, this message is verbose and includes/// potentially sensitive information.impl<A: fmt::Display, S: fmt::Display, V: fmt::Display> fmt::Display for ConvertError<A, S, V> { #[inline] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Self::Alignment(e) => e.fmt(f), Self::Size(e) => e.fmt(f), Self::Validity(e) => e.fmt(f), } }} #[cfg(any(not(no_zerocopy_core_error_1_81_0), feature = "std", test))]#[cfg_attr(doc_cfg, doc(cfg(all(rust = "1.81.0", feature = "std"))))]impl<A, S, V> Error for ConvertError<A, S, V>where A: fmt::Display + fmt::Debug, S: fmt::Display + fmt::Debug, V: fmt::Display + fmt::Debug,{} /// The error emitted if the conversion source is improperly aligned.pub struct AlignmentError<Src, Dst: ?Sized> { /// The source value involved in the conversion. src: Src, /// The inner destination type involved in the conversion. /// /// INVARIANT: An `AlignmentError` may only be constructed if `Dst`'s /// alignment requirement is greater than one. _dst: SendSyncPhantomData<Dst>,} impl<Src, Dst: ?Sized> AlignmentError<Src, Dst> { /// # Safety /// /// The caller must ensure that `Dst`'s alignment requirement is greater /// than one. pub(crate) unsafe fn new_unchecked(src: Src) -> Self { // INVARIANT: The caller guarantees that `Dst`'s alignment requirement // is greater than one. Self { src, _dst: SendSyncPhantomData::default() } } /// Produces the source underlying the failed conversion. #[inline] pub fn into_src(self) -> Src { self.src } pub(crate) fn with_src<NewSrc>(self, new_src: NewSrc) -> AlignmentError<NewSrc, Dst> { // INVARIANT: `with_src` doesn't change the type of `Dst`, so the // invariant that `Dst`'s alignment requirement is greater than one is // preserved. AlignmentError { src: new_src, _dst: SendSyncPhantomData::default() } } /// Maps the source value associated with the conversion error. /// /// This can help mitigate [issues with `Send`, `Sync` and `'static` /// bounds][self#send-sync-and-static]. /// /// # Examples /// /// ``` /// use zerocopy::*; /// /// let unaligned = Unalign::new(0u16); /// /// // Attempt to deref `unaligned`. This might fail with an alignment error. /// let maybe_n: Result<&u16, AlignmentError<&Unalign<u16>, u16>> = unaligned.try_deref(); /// /// // Map the error's source to its address as a usize. /// let maybe_n: Result<&u16, AlignmentError<usize, u16>> = maybe_n.map_err(|err| { /// err.map_src(|src| src as *const _ as usize) /// }); /// ``` #[inline] pub fn map_src<NewSrc>(self, f: impl FnOnce(Src) -> NewSrc) -> AlignmentError<NewSrc, Dst> { AlignmentError { src: f(self.src), _dst: SendSyncPhantomData::default() } } pub(crate) fn into<S, V>(self) -> ConvertError<Self, S, V> { ConvertError::Alignment(self) } /// Format extra details for a verbose, human-readable error message. /// /// This formatting may include potentially sensitive information. fn display_verbose_extras(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result where Src: Deref, Dst: KnownLayout, { #[allow(clippy::as_conversions)] let addr = self.src.deref() as *const _ as *const (); let addr_align = 2usize.pow((crate::util::AsAddress::addr(addr)).trailing_zeros()); f.write_str("\n\nSource type: ")?; f.write_str(core::any::type_name::<Src>())?; f.write_str("\nSource address: ")?; addr.fmt(f)?; f.write_str(" (a multiple of ")?; addr_align.fmt(f)?; f.write_str(")")?; f.write_str("\nDestination type: ")?; f.write_str(core::any::type_name::<Dst>())?; f.write_str("\nDestination alignment: ")?; <Dst as KnownLayout>::LAYOUT.align.get().fmt(f)?; Ok(()) }} impl<Src: Clone, Dst: ?Sized> Clone for AlignmentError<Src, Dst> { #[inline] fn clone(&self) -> Self { Self { src: self.src.clone(), _dst: SendSyncPhantomData::default() } }} impl<Src: PartialEq, Dst: ?Sized> PartialEq for AlignmentError<Src, Dst> { #[inline] fn eq(&self, other: &Self) -> bool { self.src == other.src }} impl<Src: Eq, Dst: ?Sized> Eq for AlignmentError<Src, Dst> {} impl<Src, Dst: ?Sized + Unaligned> From<AlignmentError<Src, Dst>> for Infallible { #[inline(always)] fn from(_: AlignmentError<Src, Dst>) -> Infallible { // SAFETY: `AlignmentError`s can only be constructed when `Dst`'s // alignment requirement is greater than one. In this block, `Dst: // Unaligned`, which means that its alignment requirement is equal to // one. Thus, it's not possible to reach here at runtime. unsafe { core::hint::unreachable_unchecked() } }} #[cfg(test)]impl<Src, Dst> AlignmentError<Src, Dst> { // A convenience constructor so that test code doesn't need to write // `unsafe`. fn new_checked(src: Src) -> AlignmentError<Src, Dst> { assert_ne!(core::mem::align_of::<Dst>(), 1); // SAFETY: The preceding assertion guarantees that `Dst`'s alignment // requirement is greater than one. unsafe { AlignmentError::new_unchecked(src) } }} impl<Src, Dst: ?Sized> fmt::Debug for AlignmentError<Src, Dst> { #[inline] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("AlignmentError").finish() }} /// Produces a human-readable error message.////// The message differs between debug and release builds. When/// `debug_assertions` are enabled, this message is verbose and includes/// potentially sensitive information.impl<Src, Dst: ?Sized> fmt::Display for AlignmentError<Src, Dst>where Src: Deref, Dst: KnownLayout,{ #[inline] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.write_str("The conversion failed because the address of the source is not a multiple of the alignment of the destination type.")?; if cfg!(debug_assertions) { self.display_verbose_extras(f) } else { Ok(()) } }} #[cfg(any(not(no_zerocopy_core_error_1_81_0), feature = "std", test))]#[cfg_attr(doc_cfg, doc(cfg(all(rust = "1.81.0", feature = "std"))))]impl<Src, Dst: ?Sized> Error for AlignmentError<Src, Dst>where Src: Deref, Dst: KnownLayout,{} impl<Src, Dst: ?Sized, S, V> From<AlignmentError<Src, Dst>> for ConvertError<AlignmentError<Src, Dst>, S, V>{ #[inline(always)] fn from(err: AlignmentError<Src, Dst>) -> Self { Self::Alignment(err) }} /// The error emitted if the conversion source is of incorrect size.pub struct SizeError<Src, Dst: ?Sized> { /// The source value involved in the conversion. src: Src, /// The inner destination type involved in the conversion. _dst: SendSyncPhantomData<Dst>,} impl<Src, Dst: ?Sized> SizeError<Src, Dst> { pub(crate) fn new(src: Src) -> Self { Self { src, _dst: SendSyncPhantomData::default() } } /// Produces the source underlying the failed conversion. #[inline] pub fn into_src(self) -> Src { self.src } /// Sets the source value associated with the conversion error. pub(crate) fn with_src<NewSrc>(self, new_src: NewSrc) -> SizeError<NewSrc, Dst> { SizeError { src: new_src, _dst: SendSyncPhantomData::default() } } /// Maps the source value associated with the conversion error. /// /// This can help mitigate [issues with `Send`, `Sync` and `'static` /// bounds][self#send-sync-and-static]. /// /// # Examples /// /// ``` /// use zerocopy::*; /// /// let source: [u8; 3] = [0, 1, 2]; /// /// // Try to read a `u32` from `source`. This will fail because there are insufficient /// // bytes in `source`. /// let maybe_u32: Result<u32, SizeError<&[u8], u32>> = u32::read_from_bytes(&source[..]); /// /// // Map the error's source to its size. /// let maybe_u32: Result<u32, SizeError<usize, u32>> = maybe_u32.map_err(|err| { /// err.map_src(|src| src.len()) /// }); /// ``` #[inline] pub fn map_src<NewSrc>(self, f: impl FnOnce(Src) -> NewSrc) -> SizeError<NewSrc, Dst> { SizeError { src: f(self.src), _dst: SendSyncPhantomData::default() } } /// Sets the destination type associated with the conversion error. pub(crate) fn with_dst<NewDst: ?Sized>(self) -> SizeError<Src, NewDst> { SizeError { src: self.src, _dst: SendSyncPhantomData::default() } } /// Converts the error into a general [`ConvertError`]. pub(crate) fn into<A, V>(self) -> ConvertError<A, Self, V> { ConvertError::Size(self) } /// Format extra details for a verbose, human-readable error message. /// /// This formatting may include potentially sensitive information. fn display_verbose_extras(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result where Src: Deref, Dst: KnownLayout, { // include the source type f.write_str("\nSource type: ")?; f.write_str(core::any::type_name::<Src>())?;Lines 1–500 of 1350. Load more
// SPDX-License-Identifier: (BSD-2-Clause OR Apache-2.0) OR MIT // // Copyright 2024 The Fuchsia Authors // // Licensed under the 2-Clause BSD License <LICENSE-BSD or // https://opensource.org/license/bsd-2-clause>, Apache License, Version 2.0 // <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT // license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option. // This file may not be copied, modified, or distributed except according to // those terms. //! Types related to error reporting. //! //! ## Single failure mode errors //! //! Generally speaking, zerocopy's conversions may fail for one of up to three //! reasons: //! - [`AlignmentError`]: the conversion source was improperly aligned //! - [`SizeError`]: the conversion source was of incorrect size //! - [`ValidityError`]: the conversion source contained invalid data //! //! Methods that only have one failure mode, like //! [`FromBytes::read_from_bytes`], return that mode's corresponding error type //! directly. //! //! ## Compound errors //! //! Conversion methods that have either two or three possible failure modes //! return one of these error types: //! - [`CastError`]: the error type of reference conversions //! - [`TryCastError`]: the error type of fallible reference conversions //! - [`TryReadError`]: the error type of fallible read conversions //! //! ## [`Unaligned`] destination types //! //! For [`Unaligned`] destination types, alignment errors are impossible. All //! compound error types support infallibly discarding the alignment error via //! [`From`] so long as `Dst: Unaligned`. For example, see [`<SizeError as //! From<ConvertError>>::from`][size-error-from]. //! //! [size-error-from]: struct.SizeError.html#method.from-1 //! //! ## Accessing the conversion source //! //! All error types provide an `into_src` method that converts the error into //! the source value underlying the failed conversion. //! //! ## Display formatting //! //! All error types provide a `Display` implementation that produces a //! human-readable error message. When `debug_assertions` are enabled, these //! error messages are verbose and may include potentially sensitive //! information, including: //! //! - the names of the involved types //! - the sizes of the involved types //! - the addresses of the involved types //! - the contents of the involved types //! //! When `debug_assertions` are disabled (as is default for `release` builds), //! such potentially sensitive information is excluded. //! //! In the future, we may support manually configuring this behavior. If you are //! interested in this feature, [let us know on GitHub][issue-1457] so we know //! to prioritize it. //! //! [issue-1457]: https://github.com/google/zerocopy/issues/1457 //! //! ## Validation order //! //! Our conversion methods typically check alignment, then size, then bit //! validity. However, we do not guarantee that this is always the case, and //! this behavior may change between releases. //! //! ## `Send`, `Sync`, and `'static` //! //! Our error types are `Send`, `Sync`, and `'static` when their `Src` parameter //! is `Send`, `Sync`, or `'static`, respectively. This can cause issues when an //! error is sent or synchronized across threads; e.g.: //! //! ```compile_fail,E0515 //! use zerocopy::*; //! //! let result: SizeError<&[u8], u32> = std::thread::spawn(|| { //! let source = &mut [0u8, 1, 2][..]; //! // Try (and fail) to read a `u32` from `source`. //! u32::read_from_bytes(source).unwrap_err() //! }).join().unwrap(); //! ``` //! //! To work around this, use [`map_src`][CastError::map_src] to convert the //! source parameter to an unproblematic type; e.g.: //! //! ``` //! use zerocopy::*; //! //! let result: SizeError<(), u32> = std::thread::spawn(|| { //! let source = &mut [0u8, 1, 2][..]; //! // Try (and fail) to read a `u32` from `source`. //! u32::read_from_bytes(source).unwrap_err() //! // Erase the error source. //! .map_src(drop) //! }).join().unwrap(); //! ``` //! //! Alternatively, use `.to_string()` to eagerly convert the error into a //! human-readable message; e.g.: //! //! ``` //! use zerocopy::*; //! //! let result: Result<u32, String> = std::thread::spawn(|| { //! let source = &mut [0u8, 1, 2][..]; //! // Try (and fail) to read a `u32` from `source`. //! u32::read_from_bytes(source) //! // Eagerly render the error message. //! .map_err(|err| err.to_string()) //! }).join().unwrap(); //! ``` #[cfg(not(no_zerocopy_core_error_1_81_0))] use core::error::Error; use core::{ convert::Infallible, fmt::{self, Debug, Write}, ops::Deref, }; #[cfg(all(no_zerocopy_core_error_1_81_0, any(feature = "std", test)))] use std::error::Error; use crate::{util::SendSyncPhantomData, KnownLayout, TryFromBytes, Unaligned}; #[cfg(doc)] use crate::{FromBytes, Ref}; /// Zerocopy's generic error type. /// /// Generally speaking, zerocopy's conversions may fail for one of up to three /// reasons: /// - [`AlignmentError`]: the conversion source was improperly aligned /// - [`SizeError`]: the conversion source was of incorrect size /// - [`ValidityError`]: the conversion source contained invalid data /// /// However, not all conversions produce all errors. For instance, /// [`FromBytes::ref_from_bytes`] may fail due to alignment or size issues, but /// not validity issues. This generic error type captures these /// (im)possibilities via parameterization: `A` is parameterized with /// [`AlignmentError`], `S` is parameterized with [`SizeError`], and `V` is /// parameterized with [`Infallible`]. /// /// Zerocopy never uses this type directly in its API. Rather, we provide three /// pre-parameterized aliases: /// - [`CastError`]: the error type of reference conversions /// - [`TryCastError`]: the error type of fallible reference conversions /// - [`TryReadError`]: the error type of fallible read conversions #[derive(PartialEq, Eq, Clone)] pub enum ConvertError<A, S, V> { /// The conversion source was improperly aligned. Alignment(A), /// The conversion source was of incorrect size. Size(S), /// The conversion source contained invalid data. Validity(V), } impl<Src, Dst: ?Sized + Unaligned, S, V> From<ConvertError<AlignmentError<Src, Dst>, S, V>> for ConvertError<Infallible, S, V> { /// Infallibly discards the alignment error from this `ConvertError` since /// `Dst` is unaligned. /// /// Since [`Dst: Unaligned`], it is impossible to encounter an alignment /// error. This method permits discarding that alignment error infallibly /// and replacing it with [`Infallible`]. /// /// [`Dst: Unaligned`]: crate::Unaligned /// /// # Examples /// /// ``` /// use core::convert::Infallible; /// use zerocopy::*; /// # use zerocopy_derive::*; /// /// #[derive(TryFromBytes, KnownLayout, Unaligned, Immutable)] /// #[repr(C, packed)] /// struct Bools { /// one: bool, /// two: bool, /// many: [bool], /// } /// /// impl Bools { /// fn parse(bytes: &[u8]) -> Result<&Bools, AlignedTryCastError<&[u8], Bools>> { /// // Since `Bools: Unaligned`, we can infallibly discard /// // the alignment error. /// Bools::try_ref_from_bytes(bytes).map_err(Into::into) /// } /// } /// ``` #[inline] fn from(err: ConvertError<AlignmentError<Src, Dst>, S, V>) -> ConvertError<Infallible, S, V> { match err { ConvertError::Alignment(e) => { #[allow(unreachable_code)] return ConvertError::Alignment(Infallible::from(e)); } ConvertError::Size(e) => ConvertError::Size(e), ConvertError::Validity(e) => ConvertError::Validity(e), } } } impl<A: fmt::Debug, S: fmt::Debug, V: fmt::Debug> fmt::Debug for ConvertError<A, S, V> { #[inline] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Self::Alignment(e) => f.debug_tuple("Alignment").field(e).finish(), Self::Size(e) => f.debug_tuple("Size").field(e).finish(), Self::Validity(e) => f.debug_tuple("Validity").field(e).finish(), } } } /// Produces a human-readable error message. /// /// The message differs between debug and release builds. When /// `debug_assertions` are enabled, this message is verbose and includes /// potentially sensitive information. impl<A: fmt::Display, S: fmt::Display, V: fmt::Display> fmt::Display for ConvertError<A, S, V> { #[inline] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Self::Alignment(e) => e.fmt(f), Self::Size(e) => e.fmt(f), Self::Validity(e) => e.fmt(f), } } } #[cfg(any(not(no_zerocopy_core_error_1_81_0), feature = "std", test))] #[cfg_attr(doc_cfg, doc(cfg(all(rust = "1.81.0", feature = "std"))))] impl<A, S, V> Error for ConvertError<A, S, V> where A: fmt::Display + fmt::Debug, S: fmt::Display + fmt::Debug, V: fmt::Display + fmt::Debug, { } /// The error emitted if the conversion source is improperly aligned. pub struct AlignmentError<Src, Dst: ?Sized> { /// The source value involved in the conversion. src: Src, /// The inner destination type involved in the conversion. /// /// INVARIANT: An `AlignmentError` may only be constructed if `Dst`'s /// alignment requirement is greater than one. _dst: SendSyncPhantomData<Dst>, } impl<Src, Dst: ?Sized> AlignmentError<Src, Dst> { /// # Safety /// /// The caller must ensure that `Dst`'s alignment requirement is greater /// than one. pub(crate) unsafe fn new_unchecked(src: Src) -> Self { // INVARIANT: The caller guarantees that `Dst`'s alignment requirement // is greater than one. Self { src, _dst: SendSyncPhantomData::default() } } /// Produces the source underlying the failed conversion. #[inline] pub fn into_src(self) -> Src { self.src } pub(crate) fn with_src<NewSrc>(self, new_src: NewSrc) -> AlignmentError<NewSrc, Dst> { // INVARIANT: `with_src` doesn't change the type of `Dst`, so the // invariant that `Dst`'s alignment requirement is greater than one is // preserved. AlignmentError { src: new_src, _dst: SendSyncPhantomData::default() } } /// Maps the source value associated with the conversion error. /// /// This can help mitigate [issues with `Send`, `Sync` and `'static` /// bounds][self#send-sync-and-static]. /// /// # Examples /// /// ``` /// use zerocopy::*; /// /// let unaligned = Unalign::new(0u16); /// /// // Attempt to deref `unaligned`. This might fail with an alignment error. /// let maybe_n: Result<&u16, AlignmentError<&Unalign<u16>, u16>> = unaligned.try_deref(); /// /// // Map the error's source to its address as a usize. /// let maybe_n: Result<&u16, AlignmentError<usize, u16>> = maybe_n.map_err(|err| { /// err.map_src(|src| src as *const _ as usize) /// }); /// ``` #[inline] pub fn map_src<NewSrc>(self, f: impl FnOnce(Src) -> NewSrc) -> AlignmentError<NewSrc, Dst> { AlignmentError { src: f(self.src), _dst: SendSyncPhantomData::default() } } pub(crate) fn into<S, V>(self) -> ConvertError<Self, S, V> { ConvertError::Alignment(self) } /// Format extra details for a verbose, human-readable error message. /// /// This formatting may include potentially sensitive information. fn display_verbose_extras(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result where Src: Deref, Dst: KnownLayout, { #[allow(clippy::as_conversions)] let addr = self.src.deref() as *const _ as *const (); let addr_align = 2usize.pow((crate::util::AsAddress::addr(addr)).trailing_zeros()); f.write_str("\n\nSource type: ")?; f.write_str(core::any::type_name::<Src>())?; f.write_str("\nSource address: ")?; addr.fmt(f)?; f.write_str(" (a multiple of ")?; addr_align.fmt(f)?; f.write_str(")")?; f.write_str("\nDestination type: ")?; f.write_str(core::any::type_name::<Dst>())?; f.write_str("\nDestination alignment: ")?; <Dst as KnownLayout>::LAYOUT.align.get().fmt(f)?; Ok(()) } } impl<Src: Clone, Dst: ?Sized> Clone for AlignmentError<Src, Dst> { #[inline] fn clone(&self) -> Self { Self { src: self.src.clone(), _dst: SendSyncPhantomData::default() } } } impl<Src: PartialEq, Dst: ?Sized> PartialEq for AlignmentError<Src, Dst> { #[inline] fn eq(&self, other: &Self) -> bool { self.src == other.src } } impl<Src: Eq, Dst: ?Sized> Eq for AlignmentError<Src, Dst> {} impl<Src, Dst: ?Sized + Unaligned> From<AlignmentError<Src, Dst>> for Infallible { #[inline(always)] fn from(_: AlignmentError<Src, Dst>) -> Infallible { // SAFETY: `AlignmentError`s can only be constructed when `Dst`'s // alignment requirement is greater than one. In this block, `Dst: // Unaligned`, which means that its alignment requirement is equal to // one. Thus, it's not possible to reach here at runtime. unsafe { core::hint::unreachable_unchecked() } } } #[cfg(test)] impl<Src, Dst> AlignmentError<Src, Dst> { // A convenience constructor so that test code doesn't need to write // `unsafe`. fn new_checked(src: Src) -> AlignmentError<Src, Dst> { assert_ne!(core::mem::align_of::<Dst>(), 1); // SAFETY: The preceding assertion guarantees that `Dst`'s alignment // requirement is greater than one. unsafe { AlignmentError::new_unchecked(src) } } } impl<Src, Dst: ?Sized> fmt::Debug for AlignmentError<Src, Dst> { #[inline] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("AlignmentError").finish() } } /// Produces a human-readable error message. /// /// The message differs between debug and release builds. When /// `debug_assertions` are enabled, this message is verbose and includes /// potentially sensitive information. impl<Src, Dst: ?Sized> fmt::Display for AlignmentError<Src, Dst> where Src: Deref, Dst: KnownLayout, { #[inline] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.write_str("The conversion failed because the address of the source is not a multiple of the alignment of the destination type.")?; if cfg!(debug_assertions) { self.display_verbose_extras(f) } else { Ok(()) } } } #[cfg(any(not(no_zerocopy_core_error_1_81_0), feature = "std", test))] #[cfg_attr(doc_cfg, doc(cfg(all(rust = "1.81.0", feature = "std"))))] impl<Src, Dst: ?Sized> Error for AlignmentError<Src, Dst> where Src: Deref, Dst: KnownLayout, { } impl<Src, Dst: ?Sized, S, V> From<AlignmentError<Src, Dst>> for ConvertError<AlignmentError<Src, Dst>, S, V> { #[inline(always)] fn from(err: AlignmentError<Src, Dst>) -> Self { Self::Alignment(err) } } /// The error emitted if the conversion source is of incorrect size. pub struct SizeError<Src, Dst: ?Sized> { /// The source value involved in the conversion. src: Src, /// The inner destination type involved in the conversion. _dst: SendSyncPhantomData<Dst>, } impl<Src, Dst: ?Sized> SizeError<Src, Dst> { pub(crate) fn new(src: Src) -> Self { Self { src, _dst: SendSyncPhantomData::default() } } /// Produces the source underlying the failed conversion. #[inline] pub fn into_src(self) -> Src { self.src } /// Sets the source value associated with the conversion error. pub(crate) fn with_src<NewSrc>(self, new_src: NewSrc) -> SizeError<NewSrc, Dst> { SizeError { src: new_src, _dst: SendSyncPhantomData::default() } } /// Maps the source value associated with the conversion error. /// /// This can help mitigate [issues with `Send`, `Sync` and `'static` /// bounds][self#send-sync-and-static]. /// /// # Examples /// /// ``` /// use zerocopy::*; /// /// let source: [u8; 3] = [0, 1, 2]; /// /// // Try to read a `u32` from `source`. This will fail because there are insufficient /// // bytes in `source`. /// let maybe_u32: Result<u32, SizeError<&[u8], u32>> = u32::read_from_bytes(&source[..]); /// /// // Map the error's source to its size. /// let maybe_u32: Result<u32, SizeError<usize, u32>> = maybe_u32.map_err(|err| { /// err.map_src(|src| src.len()) /// }); /// ``` #[inline] pub fn map_src<NewSrc>(self, f: impl FnOnce(Src) -> NewSrc) -> SizeError<NewSrc, Dst> { SizeError { src: f(self.src), _dst: SendSyncPhantomData::default() } } /// Sets the destination type associated with the conversion error. pub(crate) fn with_dst<NewDst: ?Sized>(self) -> SizeError<Src, NewDst> { SizeError { src: self.src, _dst: SendSyncPhantomData::default() } } /// Converts the error into a general [`ConvertError`]. pub(crate) fn into<A, V>(self) -> ConvertError<A, Self, V> { ConvertError::Size(self) } /// Format extra details for a verbose, human-readable error message. /// /// This formatting may include potentially sensitive information. fn display_verbose_extras(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result where Src: Deref, Dst: KnownLayout, { // include the source type f.write_str("\nSource type: ")?; f.write_str(core::any::type_name::<Src>())?;