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}