zerocopy/
error.rs

1// Copyright 2024 The Fuchsia Authors
2//
3// Licensed under the 2-Clause BSD License <LICENSE-BSD or
4// https://opensource.org/license/bsd-2-clause>, Apache License, Version 2.0
5// <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT
6// license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option.
7// This file may not be copied, modified, or distributed except according to
8// those terms.
9
10//! Types related to error reporting.
11//!
12//! ## Single failure mode errors
13//!
14//! Generally speaking, zerocopy's conversions may fail for one of up to three
15//! reasons:
16//! - [`AlignmentError`]: the conversion source was improperly aligned
17//! - [`SizeError`]: the conversion source was of incorrect size
18//! - [`ValidityError`]: the conversion source contained invalid data
19//!
20//! Methods that only have one failure mode, like
21//! [`FromBytes::read_from_bytes`], return that mode's corresponding error type
22//! directly.
23//!
24//! ## Compound errors
25//!
26//! Conversion methods that have either two or three possible failure modes
27//! return one of these error types:
28//! - [`CastError`]: the error type of reference conversions
29//! - [`TryCastError`]: the error type of fallible reference conversions
30//! - [`TryReadError`]: the error type of fallible read conversions
31//!
32//! ## [`Unaligned`] destination types
33//!
34//! For [`Unaligned`] destination types, alignment errors are impossible. All
35//! compound error types support infallibly discarding the alignment error via
36//! [`From`] so long as `Dst: Unaligned`. For example, see [`<SizeError as
37//! From<ConvertError>>::from`][size-error-from].
38//!
39//! [size-error-from]: struct.SizeError.html#method.from-1
40//!
41//! ## Accessing the conversion source
42//!
43//! All error types provide an `into_src` method that converts the error into
44//! the source value underlying the failed conversion.
45//!
46//! ## Display formatting
47//!
48//! All error types provide a `Display` implementation that produces a
49//! human-readable error message. When `debug_assertions` are enabled, these
50//! error messages are verbose and may include potentially sensitive
51//! information, including:
52//!
53//! - the names of the involved types
54//! - the sizes of the involved types
55//! - the addresses of the involved types
56//! - the contents of the involved types
57//!
58//! When `debug_assertions` are disabled (as is default for `release` builds),
59//! such potentially sensitive information is excluded.
60//!
61//! In the future, we may support manually configuring this behavior. If you are
62//! interested in this feature, [let us know on GitHub][issue-1457] so we know
63//! to prioritize it.
64//!
65//! [issue-1457]: https://github.com/google/zerocopy/issues/1457
66//!
67//! ## Validation order
68//!
69//! Our conversion methods typically check alignment, then size, then bit
70//! validity. However, we do not guarantee that this is always the case, and
71//! this behavior may change between releases.
72//!
73//! ## `Send`, `Sync`, and `'static`
74//!
75//! Our error types are `Send`, `Sync`, and `'static` when their `Src` parameter
76//! is `Send`, `Sync`, or `'static`, respectively. This can cause issues when an
77//! error is sent or synchronized across threads; e.g.:
78//!
79//! ```compile_fail,E0515
80//! use zerocopy::*;
81//!
82//! let result: SizeError<&[u8], u32> = std::thread::spawn(|| {
83//!     let source = &mut [0u8, 1, 2][..];
84//!     // Try (and fail) to read a `u32` from `source`.
85//!     u32::read_from_bytes(source).unwrap_err()
86//! }).join().unwrap();
87//! ```
88//!
89//! To work around this, use [`map_src`][CastError::map_src] to convert the
90//! source parameter to an unproblematic type e.g.:
91//!
92//! ```
93//! use zerocopy::*;
94//!
95//! let result: SizeError<(), u32> = std::thread::spawn(|| {
96//!     let source = &mut [0u8, 1, 2][..];
97//!     // Try (and fail) to read a `u32` from `source`.
98//!     u32::read_from_bytes(source).unwrap_err()
99//!         // Erase the error source.
100//!         .map_src(drop)
101//! }).join().unwrap();
102//! ```
103
104use core::{
105    convert::Infallible,
106    fmt::{self, Debug, Write},
107    ops::Deref,
108};
109
110#[cfg(zerocopy_core_error)]
111use core::error::Error;
112#[cfg(all(not(zerocopy_core_error), any(feature = "std", test)))]
113use std::error::Error;
114
115use crate::{util::SendSyncPhantomData, KnownLayout, TryFromBytes, Unaligned};
116#[cfg(doc)]
117use crate::{FromBytes, Ref};
118
119/// Zerocopy's generic error type.
120///
121/// Generally speaking, zerocopy's conversions may fail for one of up to three
122/// reasons:
123/// - [`AlignmentError`]: the conversion source was improperly aligned
124/// - [`SizeError`]: the conversion source was of incorrect size
125/// - [`ValidityError`]: the conversion source contained invalid data
126///
127/// However, not all conversions produce all errors. For instance,
128/// [`FromBytes::ref_from_bytes`] may fail due to alignment or size issues, but
129/// not validity issues. This generic error type captures these
130/// (im)possibilities via parameterization: `A` is parameterized with
131/// [`AlignmentError`], `S` is parameterized with [`SizeError`], and `V` is
132/// parameterized with [`Infallible`].
133///
134/// Zerocopy never uses this type directly in its API. Rather, we provide three
135/// pre-parameterized aliases:
136/// - [`CastError`]: the error type of reference conversions
137/// - [`TryCastError`]: the error type of fallible reference conversions
138/// - [`TryReadError`]: the error type of fallible read conversions
139#[derive(PartialEq, Eq)]
140pub enum ConvertError<A, S, V> {
141    /// The conversion source was improperly aligned.
142    Alignment(A),
143    /// The conversion source was of incorrect size.
144    Size(S),
145    /// The conversion source contained invalid data.
146    Validity(V),
147}
148
149impl<Src, Dst: ?Sized + Unaligned, S, V> From<ConvertError<AlignmentError<Src, Dst>, S, V>>
150    for ConvertError<Infallible, S, V>
151{
152    /// Infallibly discards the alignment error from this `ConvertError` since
153    /// `Dst` is unaligned.
154    ///
155    /// Since [`Dst: Unaligned`], it is impossible to encounter an alignment
156    /// error. This method permits discarding that alignment error infallibly
157    /// and replacing it with [`Infallible`].
158    ///
159    /// [`Dst: Unaligned`]: crate::Unaligned
160    ///
161    /// # Examples
162    ///
163    /// ```
164    /// use core::convert::Infallible;
165    /// use zerocopy::*;
166    /// # use zerocopy_derive::*;
167    ///
168    /// #[derive(TryFromBytes, KnownLayout, Unaligned, Immutable)]
169    /// #[repr(C, packed)]
170    /// struct Bools {
171    ///     one: bool,
172    ///     two: bool,
173    ///     many: [bool],
174    /// }
175    ///
176    /// impl Bools {
177    ///     fn parse(bytes: &[u8]) -> Result<&Bools, AlignedTryCastError<&[u8], Bools>> {
178    ///         // Since `Bools: Unaligned`, we can infallibly discard
179    ///         // the alignment error.
180    ///         Bools::try_ref_from_bytes(bytes).map_err(Into::into)
181    ///     }
182    /// }
183    /// ```
184    #[inline]
185    fn from(err: ConvertError<AlignmentError<Src, Dst>, S, V>) -> ConvertError<Infallible, S, V> {
186        match err {
187            ConvertError::Alignment(e) => ConvertError::Alignment(Infallible::from(e)),
188            ConvertError::Size(e) => ConvertError::Size(e),
189            ConvertError::Validity(e) => ConvertError::Validity(e),
190        }
191    }
192}
193
194impl<A: fmt::Debug, S: fmt::Debug, V: fmt::Debug> fmt::Debug for ConvertError<A, S, V> {
195    #[inline]
196    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
197        match self {
198            Self::Alignment(e) => f.debug_tuple("Alignment").field(e).finish(),
199            Self::Size(e) => f.debug_tuple("Size").field(e).finish(),
200            Self::Validity(e) => f.debug_tuple("Validity").field(e).finish(),
201        }
202    }
203}
204
205/// Produces a human-readable error message.
206///
207/// The message differs between debug and release builds. When
208/// `debug_assertions` are enabled, this message is verbose and includes
209/// potentially sensitive information.
210impl<A: fmt::Display, S: fmt::Display, V: fmt::Display> fmt::Display for ConvertError<A, S, V> {
211    #[inline]
212    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
213        match self {
214            Self::Alignment(e) => e.fmt(f),
215            Self::Size(e) => e.fmt(f),
216            Self::Validity(e) => e.fmt(f),
217        }
218    }
219}
220
221#[cfg(any(zerocopy_core_error, feature = "std", test))]
222impl<A, S, V> Error for ConvertError<A, S, V>
223where
224    A: fmt::Display + fmt::Debug,
225    S: fmt::Display + fmt::Debug,
226    V: fmt::Display + fmt::Debug,
227{
228}
229
230/// The error emitted if the conversion source is improperly aligned.
231#[derive(PartialEq, Eq)]
232pub struct AlignmentError<Src, Dst: ?Sized> {
233    /// The source value involved in the conversion.
234    src: Src,
235    /// The inner destination type inolved in the conversion.
236    ///
237    /// INVARIANT: An `AlignmentError` may only be constructed if `Dst`'s
238    /// alignment requirement is greater than one.
239    dst: SendSyncPhantomData<Dst>,
240}
241
242impl<Src, Dst: ?Sized> AlignmentError<Src, Dst> {
243    /// # Safety
244    ///
245    /// The caller must ensure that `Dst`'s alignment requirement is greater
246    /// than one.
247    pub(crate) unsafe fn new_unchecked(src: Src) -> Self {
248        // INVARIANT: The caller guarantees that `Dst`'s alignment requirement
249        // is greater than one.
250        Self { src, dst: SendSyncPhantomData::default() }
251    }
252
253    /// Produces the source underlying the failed conversion.
254    #[inline]
255    pub fn into_src(self) -> Src {
256        self.src
257    }
258
259    pub(crate) fn with_src<NewSrc>(self, new_src: NewSrc) -> AlignmentError<NewSrc, Dst> {
260        // INVARIANT: `with_src` doesn't change the type of `Dst`, so the
261        // invariant that `Dst`'s alignment requirement is greater than one is
262        // preserved.
263        AlignmentError { src: new_src, dst: SendSyncPhantomData::default() }
264    }
265
266    /// Maps the source value associated with the conversion error.
267    ///
268    /// This can help mitigate [issues with `Send`, `Sync` and `'static`
269    /// bounds][self#send-sync-and-static].
270    ///
271    /// # Examples
272    ///
273    /// ```
274    /// use zerocopy::*;
275    ///
276    /// let unaligned = Unalign::new(0u16);
277    ///
278    /// // Attempt to deref `unaligned`. This might fail with an alignment error.
279    /// let maybe_n: Result<&u16, AlignmentError<&Unalign<u16>, u16>> = unaligned.try_deref();
280    ///
281    /// // Map the error's source to its address as a usize.
282    /// let maybe_n: Result<&u16, AlignmentError<usize, u16>> = maybe_n.map_err(|err| {
283    ///     err.map_src(|src| src as *const _ as usize)
284    /// });
285    /// ```
286    #[inline]
287    pub fn map_src<NewSrc>(self, f: impl Fn(Src) -> NewSrc) -> AlignmentError<NewSrc, Dst> {
288        AlignmentError { src: f(self.src), dst: SendSyncPhantomData::default() }
289    }
290
291    pub(crate) fn into<S, V>(self) -> ConvertError<Self, S, V> {
292        ConvertError::Alignment(self)
293    }
294
295    /// Format extra details for a verbose, human-readable error message.
296    ///
297    /// This formatting may include potentially sensitive information.
298    fn display_verbose_extras(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
299    where
300        Src: Deref,
301        Dst: KnownLayout,
302    {
303        #[allow(clippy::as_conversions)]
304        let addr = self.src.deref() as *const _ as *const ();
305        let addr_align = 2usize.pow((crate::util::AsAddress::addr(addr)).trailing_zeros());
306
307        f.write_str("\n\nSource type: ")?;
308        f.write_str(core::any::type_name::<Src>())?;
309
310        f.write_str("\nSource address: ")?;
311        addr.fmt(f)?;
312        f.write_str(" (a multiple of ")?;
313        addr_align.fmt(f)?;
314        f.write_str(")")?;
315
316        f.write_str("\nDestination type: ")?;
317        f.write_str(core::any::type_name::<Dst>())?;
318
319        f.write_str("\nDestination alignment: ")?;
320        <Dst as KnownLayout>::LAYOUT.align.get().fmt(f)?;
321
322        Ok(())
323    }
324}
325
326impl<Src, Dst: ?Sized + Unaligned> From<AlignmentError<Src, Dst>> for Infallible {
327    #[inline(always)]
328    fn from(_: AlignmentError<Src, Dst>) -> Infallible {
329        // SAFETY: `AlignmentError`s can only be constructed when `Dst`'s
330        // alignment requirement is greater than one. In this block, `Dst:
331        // Unaligned`, which means that its alignment requirement is equal to
332        // one. Thus, it's not possible to reach here at runtime.
333        unsafe { core::hint::unreachable_unchecked() }
334    }
335}
336
337#[cfg(test)]
338impl<Src, Dst> AlignmentError<Src, Dst> {
339    // A convenience constructor so that test code doesn't need to write
340    // `unsafe`.
341    fn new_checked(src: Src) -> AlignmentError<Src, Dst> {
342        assert_ne!(core::mem::align_of::<Dst>(), 1);
343        // SAFETY: The preceding assertion guarantees that `Dst`'s alignment
344        // requirement is greater than one.
345        unsafe { AlignmentError::new_unchecked(src) }
346    }
347}
348
349impl<Src, Dst: ?Sized> fmt::Debug for AlignmentError<Src, Dst> {
350    #[inline]
351    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
352        f.debug_struct("AlignmentError").finish()
353    }
354}
355
356/// Produces a human-readable error message.
357///
358/// The message differs between debug and release builds. When
359/// `debug_assertions` are enabled, this message is verbose and includes
360/// potentially sensitive information.
361impl<Src, Dst: ?Sized> fmt::Display for AlignmentError<Src, Dst>
362where
363    Src: Deref,
364    Dst: KnownLayout,
365{
366    #[inline]
367    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
368        f.write_str("The conversion failed because the address of the source is not a multiple of the alignment of the destination type.")?;
369
370        if cfg!(debug_assertions) {
371            self.display_verbose_extras(f)
372        } else {
373            Ok(())
374        }
375    }
376}
377
378#[cfg(any(zerocopy_core_error, feature = "std", test))]
379impl<Src, Dst: ?Sized> Error for AlignmentError<Src, Dst>
380where
381    Src: Deref,
382    Dst: KnownLayout,
383{
384}
385
386impl<Src, Dst: ?Sized, S, V> From<AlignmentError<Src, Dst>>
387    for ConvertError<AlignmentError<Src, Dst>, S, V>
388{
389    #[inline(always)]
390    fn from(err: AlignmentError<Src, Dst>) -> Self {
391        Self::Alignment(err)
392    }
393}
394
395/// The error emitted if the conversion source is of incorrect size.
396#[derive(PartialEq, Eq)]
397pub struct SizeError<Src, Dst: ?Sized> {
398    /// The source value involved in the conversion.
399    src: Src,
400    /// The inner destination type inolved in the conversion.
401    dst: SendSyncPhantomData<Dst>,
402}
403
404impl<Src, Dst: ?Sized> SizeError<Src, Dst> {
405    pub(crate) fn new(src: Src) -> Self {
406        Self { src, dst: SendSyncPhantomData::default() }
407    }
408
409    /// Produces the source underlying the failed conversion.
410    #[inline]
411    pub fn into_src(self) -> Src {
412        self.src
413    }
414
415    /// Sets the source value associated with the conversion error.
416    pub(crate) fn with_src<NewSrc>(self, new_src: NewSrc) -> SizeError<NewSrc, Dst> {
417        SizeError { src: new_src, dst: SendSyncPhantomData::default() }
418    }
419
420    /// Maps the source value associated with the conversion error.
421    ///
422    /// This can help mitigate [issues with `Send`, `Sync` and `'static`
423    /// bounds][self#send-sync-and-static].
424    ///
425    /// # Examples
426    ///
427    /// ```
428    /// use zerocopy::*;
429    ///
430    /// let source: [u8; 3] = [0, 1, 2];
431    ///
432    /// // Try to read a `u32` from `source`. This will fail because there are insufficient
433    /// // bytes in `source`.
434    /// let maybe_u32: Result<u32, SizeError<&[u8], u32>> = u32::read_from_bytes(&source[..]);
435    ///
436    /// // Map the error's source to its size.
437    /// let maybe_u32: Result<u32, SizeError<usize, u32>> = maybe_u32.map_err(|err| {
438    ///     err.map_src(|src| src.len())
439    /// });
440    /// ```
441    #[inline]
442    pub fn map_src<NewSrc>(self, f: impl Fn(Src) -> NewSrc) -> SizeError<NewSrc, Dst> {
443        SizeError { src: f(self.src), dst: SendSyncPhantomData::default() }
444    }
445
446    /// Sets the destination type associated with the conversion error.
447    pub(crate) fn with_dst<NewDst: ?Sized>(self) -> SizeError<Src, NewDst> {
448        SizeError { src: self.src, dst: SendSyncPhantomData::default() }
449    }
450
451    /// Converts the error into a general [`ConvertError`].
452    pub(crate) fn into<A, V>(self) -> ConvertError<A, Self, V> {
453        ConvertError::Size(self)
454    }
455
456    /// Format extra details for a verbose, human-readable error message.
457    ///
458    /// This formatting may include potentially sensitive information.
459    fn display_verbose_extras(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
460    where
461        Src: Deref,
462        Dst: KnownLayout,
463    {
464        // include the source type
465        f.write_str("\nSource type: ")?;
466        f.write_str(core::any::type_name::<Src>())?;
467
468        // include the source.deref() size
469        let src_size = core::mem::size_of_val(&*self.src);
470        f.write_str("\nSource size: ")?;
471        src_size.fmt(f)?;
472        f.write_str(" byte")?;
473        if src_size != 1 {
474            f.write_char('s')?;
475        }
476
477        // if `Dst` is `Sized`, include the `Dst` size
478        if let crate::SizeInfo::Sized { size } = Dst::LAYOUT.size_info {
479            f.write_str("\nDestination size: ")?;
480            size.fmt(f)?;
481            f.write_str(" byte")?;
482            if size != 1 {
483                f.write_char('s')?;
484            }
485        }
486
487        // include the destination type
488        f.write_str("\nDestination type: ")?;
489        f.write_str(core::any::type_name::<Dst>())?;
490
491        Ok(())
492    }
493}
494
495impl<Src, Dst: ?Sized> fmt::Debug for SizeError<Src, Dst> {
496    #[inline]
497    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
498        f.debug_struct("SizeError").finish()
499    }
500}
501
502/// Produces a human-readable error message.
503///
504/// The message differs between debug and release builds. When
505/// `debug_assertions` are enabled, this message is verbose and includes
506/// potentially sensitive information.
507impl<Src, Dst: ?Sized> fmt::Display for SizeError<Src, Dst>
508where
509    Src: Deref,
510    Dst: KnownLayout,
511{
512    #[inline]
513    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
514        f.write_str("The conversion failed because the source was incorrectly sized to complete the conversion into the destination type.")?;
515        if cfg!(debug_assertions) {
516            f.write_str("\n")?;
517            self.display_verbose_extras(f)?;
518        }
519        Ok(())
520    }
521}
522
523#[cfg(any(zerocopy_core_error, feature = "std", test))]
524impl<Src, Dst: ?Sized> Error for SizeError<Src, Dst>
525where
526    Src: Deref,
527    Dst: KnownLayout,
528{
529}
530
531impl<Src, Dst: ?Sized, A, V> From<SizeError<Src, Dst>> for ConvertError<A, SizeError<Src, Dst>, V> {
532    #[inline(always)]
533    fn from(err: SizeError<Src, Dst>) -> Self {
534        Self::Size(err)
535    }
536}
537
538/// The error emitted if the conversion source contains invalid data.
539#[derive(PartialEq, Eq)]
540pub struct ValidityError<Src, Dst: ?Sized + TryFromBytes> {
541    /// The source value involved in the conversion.
542    pub(crate) src: Src,
543    /// The inner destination type inolved in the conversion.
544    dst: SendSyncPhantomData<Dst>,
545}
546
547impl<Src, Dst: ?Sized + TryFromBytes> ValidityError<Src, Dst> {
548    pub(crate) fn new(src: Src) -> Self {
549        Self { src, dst: SendSyncPhantomData::default() }
550    }
551
552    /// Produces the source underlying the failed conversion.
553    #[inline]
554    pub fn into_src(self) -> Src {
555        self.src
556    }
557
558    /// Maps the source value associated with the conversion error.
559    ///
560    /// This can help mitigate [issues with `Send`, `Sync` and `'static`
561    /// bounds][self#send-sync-and-static].
562    ///
563    /// # Examples
564    ///
565    /// ```
566    /// use zerocopy::*;
567    ///
568    /// let source: u8 = 42;
569    ///
570    /// // Try to transmute the `source` to a `bool`. This will fail.
571    /// let maybe_bool: Result<bool, ValidityError<u8, bool>> = try_transmute!(source);
572    ///
573    /// // Drop the error's source.
574    /// let maybe_bool: Result<bool, ValidityError<(), bool>> = maybe_bool.map_err(|err| {
575    ///     err.map_src(drop)
576    /// });
577    /// ```
578    #[inline]
579    pub fn map_src<NewSrc>(self, f: impl Fn(Src) -> NewSrc) -> ValidityError<NewSrc, Dst> {
580        ValidityError { src: f(self.src), dst: SendSyncPhantomData::default() }
581    }
582
583    /// Converts the error into a general [`ConvertError`].
584    pub(crate) fn into<A, S>(self) -> ConvertError<A, S, Self> {
585        ConvertError::Validity(self)
586    }
587
588    /// Format extra details for a verbose, human-readable error message.
589    ///
590    /// This formatting may include potentially sensitive information.
591    fn display_verbose_extras(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
592    where
593        Src: Deref,
594        Dst: KnownLayout,
595    {
596        f.write_str("Destination type: ")?;
597        f.write_str(core::any::type_name::<Dst>())?;
598        Ok(())
599    }
600}
601
602impl<Src, Dst: ?Sized + TryFromBytes> fmt::Debug for ValidityError<Src, Dst> {
603    #[inline]
604    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
605        f.debug_struct("ValidityError").finish()
606    }
607}
608
609/// Produces a human-readable error message.
610///
611/// The message differs between debug and release builds. When
612/// `debug_assertions` are enabled, this message is verbose and includes
613/// potentially sensitive information.
614impl<Src, Dst: ?Sized> fmt::Display for ValidityError<Src, Dst>
615where
616    Src: Deref,
617    Dst: KnownLayout + TryFromBytes,
618{
619    #[inline]
620    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
621        f.write_str("The conversion failed because the source bytes are not a valid value of the destination type.")?;
622        if cfg!(debug_assertions) {
623            f.write_str("\n\n")?;
624            self.display_verbose_extras(f)?;
625        }
626        Ok(())
627    }
628}
629
630#[cfg(any(zerocopy_core_error, feature = "std", test))]
631impl<Src, Dst: ?Sized> Error for ValidityError<Src, Dst>
632where
633    Src: Deref,
634    Dst: KnownLayout + TryFromBytes,
635{
636}
637
638impl<Src, Dst: ?Sized + TryFromBytes, A, S> From<ValidityError<Src, Dst>>
639    for ConvertError<A, S, ValidityError<Src, Dst>>
640{
641    #[inline(always)]
642    fn from(err: ValidityError<Src, Dst>) -> Self {
643        Self::Validity(err)
644    }
645}
646
647/// The error type of reference conversions.
648///
649/// Reference conversions, like [`FromBytes::ref_from_bytes`] may emit
650/// [alignment](AlignmentError) and [size](SizeError) errors.
651// Bounds on generic parameters are not enforced in type aliases, but they do
652// appear in rustdoc.
653#[allow(type_alias_bounds)]
654pub type CastError<Src, Dst: ?Sized> =
655    ConvertError<AlignmentError<Src, Dst>, SizeError<Src, Dst>, Infallible>;
656
657impl<Src, Dst: ?Sized> CastError<Src, Dst> {
658    /// Produces the source underlying the failed conversion.
659    #[inline]
660    pub fn into_src(self) -> Src {
661        match self {
662            Self::Alignment(e) => e.src,
663            Self::Size(e) => e.src,
664            Self::Validity(i) => match i {},
665        }
666    }
667
668    /// Sets the source value associated with the conversion error.
669    pub(crate) fn with_src<NewSrc>(self, new_src: NewSrc) -> CastError<NewSrc, Dst> {
670        match self {
671            Self::Alignment(e) => CastError::Alignment(e.with_src(new_src)),
672            Self::Size(e) => CastError::Size(e.with_src(new_src)),
673            Self::Validity(i) => match i {},
674        }
675    }
676
677    /// Maps the source value associated with the conversion error.
678    ///
679    /// This can help mitigate [issues with `Send`, `Sync` and `'static`
680    /// bounds][self#send-sync-and-static].
681    ///
682    /// # Examples
683    ///
684    /// ```
685    /// use zerocopy::*;
686    ///
687    /// let source: [u8; 3] = [0, 1, 2];
688    ///
689    /// // Try to read a `u32` from `source`. This will fail because there are insufficient
690    /// // bytes in `source`.
691    /// let maybe_u32: Result<&u32, CastError<&[u8], u32>> = u32::ref_from_bytes(&source[..]);
692    ///
693    /// // Map the error's source to its size and address.
694    /// let maybe_u32: Result<&u32, CastError<(usize, usize), u32>> = maybe_u32.map_err(|err| {
695    ///     err.map_src(|src| (src.len(), src.as_ptr() as usize))
696    /// });
697    /// ```
698    #[inline]
699    pub fn map_src<NewSrc>(self, f: impl Fn(Src) -> NewSrc) -> CastError<NewSrc, Dst> {
700        match self {
701            Self::Alignment(e) => CastError::Alignment(e.map_src(f)),
702            Self::Size(e) => CastError::Size(e.map_src(f)),
703            Self::Validity(i) => match i {},
704        }
705    }
706
707    /// Converts the error into a general [`ConvertError`].
708    pub(crate) fn into(self) -> TryCastError<Src, Dst>
709    where
710        Dst: TryFromBytes,
711    {
712        match self {
713            Self::Alignment(e) => TryCastError::Alignment(e),
714            Self::Size(e) => TryCastError::Size(e),
715            Self::Validity(i) => match i {},
716        }
717    }
718}
719
720impl<Src, Dst: ?Sized + Unaligned> From<CastError<Src, Dst>> for SizeError<Src, Dst> {
721    /// Infallibly extracts the [`SizeError`] from this `CastError` since `Dst`
722    /// is unaligned.
723    ///
724    /// Since [`Dst: Unaligned`], it is impossible to encounter an alignment
725    /// error, and so the only error that can be encountered at runtime is a
726    /// [`SizeError`]. This method permits extracting that `SizeError`
727    /// infallibly.
728    ///
729    /// [`Dst: Unaligned`]: crate::Unaligned
730    ///
731    /// # Examples
732    ///
733    /// ```rust
734    /// use zerocopy::*;
735    /// # use zerocopy_derive::*;
736    ///
737    /// #[derive(FromBytes, IntoBytes, KnownLayout, Immutable, Unaligned)]
738    /// #[repr(C)]
739    /// struct UdpHeader {
740    ///     src_port: [u8; 2],
741    ///     dst_port: [u8; 2],
742    ///     length: [u8; 2],
743    ///     checksum: [u8; 2],
744    /// }
745    ///
746    /// #[derive(FromBytes, IntoBytes, KnownLayout, Immutable, Unaligned)]
747    /// #[repr(C, packed)]
748    /// struct UdpPacket {
749    ///     header: UdpHeader,
750    ///     body: [u8],
751    /// }
752    ///
753    /// impl UdpPacket {
754    ///     pub fn parse(bytes: &[u8]) -> Result<&UdpPacket, SizeError<&[u8], UdpPacket>> {
755    ///         // Since `UdpPacket: Unaligned`, we can map the `CastError` to a `SizeError`.
756    ///         UdpPacket::ref_from_bytes(bytes).map_err(Into::into)
757    ///     }
758    /// }
759    /// ```
760    #[inline(always)]
761    fn from(err: CastError<Src, Dst>) -> SizeError<Src, Dst> {
762        match err {
763            #[allow(unreachable_code)]
764            CastError::Alignment(e) => match Infallible::from(e) {},
765            CastError::Size(e) => e,
766            CastError::Validity(i) => match i {},
767        }
768    }
769}
770
771/// The error type of fallible reference conversions.
772///
773/// Fallible reference conversions, like [`TryFromBytes::try_ref_from_bytes`]
774/// may emit [alignment](AlignmentError), [size](SizeError), and
775/// [validity](ValidityError) errors.
776// Bounds on generic parameters are not enforced in type aliases, but they do
777// appear in rustdoc.
778#[allow(type_alias_bounds)]
779pub type TryCastError<Src, Dst: ?Sized + TryFromBytes> =
780    ConvertError<AlignmentError<Src, Dst>, SizeError<Src, Dst>, ValidityError<Src, Dst>>;
781
782// TODO(#1139): Remove the `TryFromBytes` here and in other downstream locations
783// (all the way to `ValidityError`) if we determine it's not necessary for rich
784// validity errors.
785impl<Src, Dst: ?Sized + TryFromBytes> TryCastError<Src, Dst> {
786    /// Produces the source underlying the failed conversion.
787    #[inline]
788    pub fn into_src(self) -> Src {
789        match self {
790            Self::Alignment(e) => e.src,
791            Self::Size(e) => e.src,
792            Self::Validity(e) => e.src,
793        }
794    }
795
796    /// Maps the source value associated with the conversion error.
797    ///
798    /// This can help mitigate [issues with `Send`, `Sync` and `'static`
799    /// bounds][self#send-sync-and-static].
800    ///
801    /// # Examples
802    ///
803    /// ```
804    /// use core::num::NonZeroU32;
805    /// use zerocopy::*;
806    ///
807    /// let source: [u8; 3] = [0, 0, 0];
808    ///
809    /// // Try to read a `NonZeroU32` from `source`.
810    /// let maybe_u32: Result<&NonZeroU32, TryCastError<&[u8], NonZeroU32>>
811    ///     = NonZeroU32::try_ref_from_bytes(&source[..]);
812    ///
813    /// // Map the error's source to its size and address.
814    /// let maybe_u32: Result<&NonZeroU32, TryCastError<(usize, usize), NonZeroU32>> =
815    ///     maybe_u32.map_err(|err| {
816    ///         err.map_src(|src| (src.len(), src.as_ptr() as usize))
817    ///     });
818    /// ```
819    #[inline]
820    pub fn map_src<NewSrc>(self, f: impl Fn(Src) -> NewSrc) -> TryCastError<NewSrc, Dst> {
821        match self {
822            Self::Alignment(e) => TryCastError::Alignment(e.map_src(f)),
823            Self::Size(e) => TryCastError::Size(e.map_src(f)),
824            Self::Validity(e) => TryCastError::Validity(e.map_src(f)),
825        }
826    }
827}
828
829impl<Src, Dst: ?Sized + TryFromBytes> From<CastError<Src, Dst>> for TryCastError<Src, Dst> {
830    #[inline]
831    fn from(value: CastError<Src, Dst>) -> Self {
832        match value {
833            CastError::Alignment(e) => Self::Alignment(e),
834            CastError::Size(e) => Self::Size(e),
835            CastError::Validity(i) => match i {},
836        }
837    }
838}
839
840/// The error type of fallible read-conversions.
841///
842/// Fallible read-conversions, like [`TryFromBytes::try_read_from_bytes`] may emit
843/// [size](SizeError) and [validity](ValidityError) errors, but not alignment errors.
844// Bounds on generic parameters are not enforced in type aliases, but they do
845// appear in rustdoc.
846#[allow(type_alias_bounds)]
847pub type TryReadError<Src, Dst: ?Sized + TryFromBytes> =
848    ConvertError<Infallible, SizeError<Src, Dst>, ValidityError<Src, Dst>>;
849
850impl<Src, Dst: ?Sized + TryFromBytes> TryReadError<Src, Dst> {
851    /// Produces the source underlying the failed conversion.
852    #[inline]
853    pub fn into_src(self) -> Src {
854        match self {
855            Self::Alignment(i) => match i {},
856            Self::Size(e) => e.src,
857            Self::Validity(e) => e.src,
858        }
859    }
860
861    /// Maps the source value associated with the conversion error.
862    ///
863    /// This can help mitigate [issues with `Send`, `Sync` and `'static`
864    /// bounds][self#send-sync-and-static].
865    ///
866    /// # Examples
867    ///
868    /// ```
869    /// use core::num::NonZeroU32;
870    /// use zerocopy::*;
871    ///
872    /// let source: [u8; 3] = [0, 0, 0];
873    ///
874    /// // Try to read a `NonZeroU32` from `source`.
875    /// let maybe_u32: Result<NonZeroU32, TryReadError<&[u8], NonZeroU32>>
876    ///     = NonZeroU32::try_read_from_bytes(&source[..]);
877    ///
878    /// // Map the error's source to its size.
879    /// let maybe_u32: Result<NonZeroU32, TryReadError<usize, NonZeroU32>> =
880    ///     maybe_u32.map_err(|err| {
881    ///         err.map_src(|src| src.len())
882    ///     });
883    /// ```
884    #[inline]
885    pub fn map_src<NewSrc>(self, f: impl Fn(Src) -> NewSrc) -> TryReadError<NewSrc, Dst> {
886        match self {
887            Self::Alignment(i) => match i {},
888            Self::Size(e) => TryReadError::Size(e.map_src(f)),
889            Self::Validity(e) => TryReadError::Validity(e.map_src(f)),
890        }
891    }
892}
893
894/// The error type of well-aligned, fallible casts.
895///
896/// This is like [`TryCastError`], but for casts that are always well-aligned.
897/// It is identical to `TryCastError`, except that its alignment error is
898/// [`Infallible`].
899///
900/// As of this writing, none of zerocopy's API produces this error directly.
901/// However, it is useful since it permits users to infallibly discard alignment
902/// errors when they can prove statically that alignment errors are impossible.
903///
904/// # Examples
905///
906/// ```
907/// use core::convert::Infallible;
908/// use zerocopy::*;
909/// # use zerocopy_derive::*;
910///
911/// #[derive(TryFromBytes, KnownLayout, Unaligned, Immutable)]
912/// #[repr(C, packed)]
913/// struct Bools {
914///     one: bool,
915///     two: bool,
916///     many: [bool],
917/// }
918///
919/// impl Bools {
920///     fn parse(bytes: &[u8]) -> Result<&Bools, AlignedTryCastError<&[u8], Bools>> {
921///         // Since `Bools: Unaligned`, we can infallibly discard
922///         // the alignment error.
923///         Bools::try_ref_from_bytes(bytes).map_err(Into::into)
924///     }
925/// }
926/// ```
927#[allow(type_alias_bounds)]
928pub type AlignedTryCastError<Src, Dst: ?Sized + TryFromBytes> =
929    ConvertError<Infallible, SizeError<Src, Dst>, ValidityError<Src, Dst>>;
930
931/// The error type of a failed allocation.
932///
933/// This type is intended to be deprecated in favor of the standard library's
934/// [`AllocError`] type once it is stabilized. When that happens, this type will
935/// be replaced by a type alias to the standard library type. We do not intend
936/// to treat this as a breaking change; users who wish to avoid breakage should
937/// avoid writing code which assumes that this is *not* such an alias. For
938/// example, implementing the same trait for both types will result in an impl
939/// conflict once this type is an alias.
940///
941/// [`AllocError`]: https://doc.rust-lang.org/alloc/alloc/struct.AllocError.html
942#[derive(Copy, Clone, PartialEq, Eq, Debug)]
943pub struct AllocError;
944
945#[cfg(test)]
946mod tests {
947    use super::*;
948
949    #[test]
950    fn test_send_sync() {
951        // Test that all error types are `Send + Sync` even if `Dst: !Send +
952        // !Sync`.
953
954        #[allow(dead_code)]
955        fn is_send_sync<T: Send + Sync>(_t: T) {}
956
957        #[allow(dead_code)]
958        fn alignment_err_is_send_sync<Src: Send + Sync, Dst>(err: AlignmentError<Src, Dst>) {
959            is_send_sync(err)
960        }
961
962        #[allow(dead_code)]
963        fn size_err_is_send_sync<Src: Send + Sync, Dst>(err: SizeError<Src, Dst>) {
964            is_send_sync(err)
965        }
966
967        #[allow(dead_code)]
968        fn validity_err_is_send_sync<Src: Send + Sync, Dst: TryFromBytes>(
969            err: ValidityError<Src, Dst>,
970        ) {
971            is_send_sync(err)
972        }
973
974        #[allow(dead_code)]
975        fn convert_error_is_send_sync<Src: Send + Sync, Dst: TryFromBytes>(
976            err: ConvertError<
977                AlignmentError<Src, Dst>,
978                SizeError<Src, Dst>,
979                ValidityError<Src, Dst>,
980            >,
981        ) {
982            is_send_sync(err)
983        }
984    }
985
986    #[test]
987    fn alignment_display() {
988        #[repr(C, align(128))]
989        struct Aligned {
990            bytes: [u8; 128],
991        }
992
993        impl_known_layout!(elain::Align::<8>);
994
995        let aligned = Aligned { bytes: [0; 128] };
996
997        let bytes = &aligned.bytes[1..];
998        let addr = crate::util::AsAddress::addr(bytes);
999        assert_eq!(
1000            AlignmentError::<_, elain::Align::<8>>::new_checked(bytes).to_string(),
1001            format!("The conversion failed because the address of the source is not a multiple of the alignment of the destination type.\n\
1002            \nSource type: &[u8]\
1003            \nSource address: 0x{:x} (a multiple of 1)\
1004            \nDestination type: elain::Align<8>\
1005            \nDestination alignment: 8", addr)
1006        );
1007
1008        let bytes = &aligned.bytes[2..];
1009        let addr = crate::util::AsAddress::addr(bytes);
1010        assert_eq!(
1011            AlignmentError::<_, elain::Align::<8>>::new_checked(bytes).to_string(),
1012            format!("The conversion failed because the address of the source is not a multiple of the alignment of the destination type.\n\
1013            \nSource type: &[u8]\
1014            \nSource address: 0x{:x} (a multiple of 2)\
1015            \nDestination type: elain::Align<8>\
1016            \nDestination alignment: 8", addr)
1017        );
1018
1019        let bytes = &aligned.bytes[3..];
1020        let addr = crate::util::AsAddress::addr(bytes);
1021        assert_eq!(
1022            AlignmentError::<_, elain::Align::<8>>::new_checked(bytes).to_string(),
1023            format!("The conversion failed because the address of the source is not a multiple of the alignment of the destination type.\n\
1024            \nSource type: &[u8]\
1025            \nSource address: 0x{:x} (a multiple of 1)\
1026            \nDestination type: elain::Align<8>\
1027            \nDestination alignment: 8", addr)
1028        );
1029
1030        let bytes = &aligned.bytes[4..];
1031        let addr = crate::util::AsAddress::addr(bytes);
1032        assert_eq!(
1033            AlignmentError::<_, elain::Align::<8>>::new_checked(bytes).to_string(),
1034            format!("The conversion failed because the address of the source is not a multiple of the alignment of the destination type.\n\
1035            \nSource type: &[u8]\
1036            \nSource address: 0x{:x} (a multiple of 4)\
1037            \nDestination type: elain::Align<8>\
1038            \nDestination alignment: 8", addr)
1039        );
1040    }
1041
1042    #[test]
1043    fn size_display() {
1044        assert_eq!(
1045            SizeError::<_, [u8]>::new(&[0u8; 2][..]).to_string(),
1046            "The conversion failed because the source was incorrectly sized to complete the conversion into the destination type.\n\
1047            \nSource type: &[u8]\
1048            \nSource size: 2 bytes\
1049            \nDestination type: [u8]"
1050        );
1051
1052        assert_eq!(
1053            SizeError::<_, [u8; 2]>::new(&[0u8; 1][..]).to_string(),
1054            "The conversion failed because the source was incorrectly sized to complete the conversion into the destination type.\n\
1055            \nSource type: &[u8]\
1056            \nSource size: 1 byte\
1057            \nDestination size: 2 bytes\
1058            \nDestination type: [u8; 2]"
1059        );
1060    }
1061
1062    #[test]
1063    fn validity_display() {
1064        assert_eq!(
1065            ValidityError::<_, bool>::new(&[2u8; 1][..]).to_string(),
1066            "The conversion failed because the source bytes are not a valid value of the destination type.\n\
1067            \n\
1068            Destination type: bool"
1069        );
1070    }
1071}