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