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