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