Coverage Report

Created: 2025-10-12 08:06

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/rust/registry/src/index.crates.io-1949cf8c6b5b557f/zerocopy-0.8.27/src/error.rs
Line
Count
Source
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)]
119
use core::error::Error;
120
use 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)))]
126
use std::error::Error;
127
128
use crate::{util::SendSyncPhantomData, KnownLayout, TryFromBytes, Unaligned};
129
#[cfg(doc)]
130
use 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)]
153
pub 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
162
impl<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
0
    fn from(err: ConvertError<AlignmentError<Src, Dst>, S, V>) -> ConvertError<Infallible, S, V> {
199
0
        match err {
200
0
            ConvertError::Alignment(e) => ConvertError::Alignment(Infallible::from(e)),
201
0
            ConvertError::Size(e) => ConvertError::Size(e),
202
0
            ConvertError::Validity(e) => ConvertError::Validity(e),
203
        }
204
0
    }
205
}
206
207
impl<A: fmt::Debug, S: fmt::Debug, V: fmt::Debug> fmt::Debug for ConvertError<A, S, V> {
208
    #[inline]
209
0
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
210
0
        match self {
211
0
            Self::Alignment(e) => f.debug_tuple("Alignment").field(e).finish(),
212
0
            Self::Size(e) => f.debug_tuple("Size").field(e).finish(),
213
0
            Self::Validity(e) => f.debug_tuple("Validity").field(e).finish(),
214
        }
215
0
    }
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.
223
impl<A: fmt::Display, S: fmt::Display, V: fmt::Display> fmt::Display for ConvertError<A, S, V> {
224
    #[inline]
225
0
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
226
0
        match self {
227
0
            Self::Alignment(e) => e.fmt(f),
228
0
            Self::Size(e) => e.fmt(f),
229
0
            Self::Validity(e) => e.fmt(f),
230
        }
231
0
    }
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"))))]
236
impl<A, S, V> Error for ConvertError<A, S, V>
237
where
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)]
246
pub 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
256
impl<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
0
    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
0
        Self { src, dst: SendSyncPhantomData::default() }
265
0
    }
266
267
    /// Produces the source underlying the failed conversion.
268
    #[inline]
269
0
    pub fn into_src(self) -> Src {
270
0
        self.src
271
0
    }
272
273
0
    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
0
        AlignmentError { src: new_src, dst: SendSyncPhantomData::default() }
278
0
    }
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
0
    pub fn map_src<NewSrc>(self, f: impl FnOnce(Src) -> NewSrc) -> AlignmentError<NewSrc, Dst> {
302
0
        AlignmentError { src: f(self.src), dst: SendSyncPhantomData::default() }
303
0
    }
304
305
0
    pub(crate) fn into<S, V>(self) -> ConvertError<Self, S, V> {
306
0
        ConvertError::Alignment(self)
307
0
    }
308
309
    /// Format extra details for a verbose, human-readable error message.
310
    ///
311
    /// This formatting may include potentially sensitive information.
312
0
    fn display_verbose_extras(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
313
0
    where
314
0
        Src: Deref,
315
0
        Dst: KnownLayout,
316
    {
317
        #[allow(clippy::as_conversions)]
318
0
        let addr = self.src.deref() as *const _ as *const ();
319
0
        let addr_align = 2usize.pow((crate::util::AsAddress::addr(addr)).trailing_zeros());
320
321
0
        f.write_str("\n\nSource type: ")?;
322
0
        f.write_str(core::any::type_name::<Src>())?;
323
324
0
        f.write_str("\nSource address: ")?;
325
0
        addr.fmt(f)?;
326
0
        f.write_str(" (a multiple of ")?;
327
0
        addr_align.fmt(f)?;
328
0
        f.write_str(")")?;
329
330
0
        f.write_str("\nDestination type: ")?;
331
0
        f.write_str(core::any::type_name::<Dst>())?;
332
333
0
        f.write_str("\nDestination alignment: ")?;
334
0
        <Dst as KnownLayout>::LAYOUT.align.get().fmt(f)?;
335
336
0
        Ok(())
337
0
    }
338
}
339
340
impl<Src, Dst: ?Sized + Unaligned> From<AlignmentError<Src, Dst>> for Infallible {
341
    #[inline(always)]
342
0
    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
0
        unsafe { core::hint::unreachable_unchecked() }
348
    }
349
}
350
351
#[cfg(test)]
352
impl<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
363
impl<Src, Dst: ?Sized> fmt::Debug for AlignmentError<Src, Dst> {
364
    #[inline]
365
0
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
366
0
        f.debug_struct("AlignmentError").finish()
367
0
    }
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.
375
impl<Src, Dst: ?Sized> fmt::Display for AlignmentError<Src, Dst>
376
where
377
    Src: Deref,
378
    Dst: KnownLayout,
379
{
380
    #[inline]
381
0
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
382
0
        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
0
        if cfg!(debug_assertions) {
385
0
            self.display_verbose_extras(f)
386
        } else {
387
0
            Ok(())
388
        }
389
0
    }
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"))))]
394
impl<Src, Dst: ?Sized> Error for AlignmentError<Src, Dst>
395
where
396
    Src: Deref,
397
    Dst: KnownLayout,
398
{
399
}
400
401
impl<Src, Dst: ?Sized, S, V> From<AlignmentError<Src, Dst>>
402
    for ConvertError<AlignmentError<Src, Dst>, S, V>
403
{
404
    #[inline(always)]
405
0
    fn from(err: AlignmentError<Src, Dst>) -> Self {
406
0
        Self::Alignment(err)
407
0
    }
408
}
409
410
/// The error emitted if the conversion source is of incorrect size.
411
#[derive(PartialEq, Eq)]
412
pub 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
419
impl<Src, Dst: ?Sized> SizeError<Src, Dst> {
420
0
    pub(crate) fn new(src: Src) -> Self {
421
0
        Self { src, dst: SendSyncPhantomData::default() }
422
0
    }
423
424
    /// Produces the source underlying the failed conversion.
425
    #[inline]
426
0
    pub fn into_src(self) -> Src {
427
0
        self.src
428
0
    }
429
430
    /// Sets the source value associated with the conversion error.
431
0
    pub(crate) fn with_src<NewSrc>(self, new_src: NewSrc) -> SizeError<NewSrc, Dst> {
432
0
        SizeError { src: new_src, dst: SendSyncPhantomData::default() }
433
0
    }
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
0
    pub fn map_src<NewSrc>(self, f: impl FnOnce(Src) -> NewSrc) -> SizeError<NewSrc, Dst> {
458
0
        SizeError { src: f(self.src), dst: SendSyncPhantomData::default() }
459
0
    }
460
461
    /// Sets the destination type associated with the conversion error.
462
0
    pub(crate) fn with_dst<NewDst: ?Sized>(self) -> SizeError<Src, NewDst> {
463
0
        SizeError { src: self.src, dst: SendSyncPhantomData::default() }
464
0
    }
465
466
    /// Converts the error into a general [`ConvertError`].
467
0
    pub(crate) fn into<A, V>(self) -> ConvertError<A, Self, V> {
468
0
        ConvertError::Size(self)
469
0
    }
470
471
    /// Format extra details for a verbose, human-readable error message.
472
    ///
473
    /// This formatting may include potentially sensitive information.
474
0
    fn display_verbose_extras(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
475
0
    where
476
0
        Src: Deref,
477
0
        Dst: KnownLayout,
478
    {
479
        // include the source type
480
0
        f.write_str("\nSource type: ")?;
481
0
        f.write_str(core::any::type_name::<Src>())?;
482
483
        // include the source.deref() size
484
0
        let src_size = core::mem::size_of_val(&*self.src);
485
0
        f.write_str("\nSource size: ")?;
486
0
        src_size.fmt(f)?;
487
0
        f.write_str(" byte")?;
488
0
        if src_size != 1 {
489
0
            f.write_char('s')?;
490
0
        }
491
492
        // if `Dst` is `Sized`, include the `Dst` size
493
0
        if let crate::SizeInfo::Sized { size } = Dst::LAYOUT.size_info {
494
0
            f.write_str("\nDestination size: ")?;
495
0
            size.fmt(f)?;
496
0
            f.write_str(" byte")?;
497
0
            if size != 1 {
498
0
                f.write_char('s')?;
499
0
            }
500
0
        }
501
502
        // include the destination type
503
0
        f.write_str("\nDestination type: ")?;
504
0
        f.write_str(core::any::type_name::<Dst>())?;
505
506
0
        Ok(())
507
0
    }
508
}
509
510
impl<Src, Dst: ?Sized> fmt::Debug for SizeError<Src, Dst> {
511
    #[inline]
512
0
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
513
0
        f.debug_struct("SizeError").finish()
514
0
    }
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.
522
impl<Src, Dst: ?Sized> fmt::Display for SizeError<Src, Dst>
523
where
524
    Src: Deref,
525
    Dst: KnownLayout,
526
{
527
    #[inline]
528
0
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
529
0
        f.write_str("The conversion failed because the source was incorrectly sized to complete the conversion into the destination type.")?;
530
0
        if cfg!(debug_assertions) {
531
0
            f.write_str("\n")?;
532
0
            self.display_verbose_extras(f)?;
533
0
        }
534
0
        Ok(())
535
0
    }
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"))))]
540
impl<Src, Dst: ?Sized> Error for SizeError<Src, Dst>
541
where
542
    Src: Deref,
543
    Dst: KnownLayout,
544
{
545
}
546
547
impl<Src, Dst: ?Sized, A, V> From<SizeError<Src, Dst>> for ConvertError<A, SizeError<Src, Dst>, V> {
548
    #[inline(always)]
549
0
    fn from(err: SizeError<Src, Dst>) -> Self {
550
0
        Self::Size(err)
551
0
    }
552
}
553
554
/// The error emitted if the conversion source contains invalid data.
555
#[derive(PartialEq, Eq)]
556
pub 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
563
impl<Src, Dst: ?Sized + TryFromBytes> ValidityError<Src, Dst> {
564
0
    pub(crate) fn new(src: Src) -> Self {
565
0
        Self { src, dst: SendSyncPhantomData::default() }
566
0
    }
567
568
    /// Produces the source underlying the failed conversion.
569
    #[inline]
570
0
    pub fn into_src(self) -> Src {
571
0
        self.src
572
0
    }
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
0
    pub fn map_src<NewSrc>(self, f: impl FnOnce(Src) -> NewSrc) -> ValidityError<NewSrc, Dst> {
596
0
        ValidityError { src: f(self.src), dst: SendSyncPhantomData::default() }
597
0
    }
598
599
    /// Converts the error into a general [`ConvertError`].
600
0
    pub(crate) fn into<A, S>(self) -> ConvertError<A, S, Self> {
601
0
        ConvertError::Validity(self)
602
0
    }
603
604
    /// Format extra details for a verbose, human-readable error message.
605
    ///
606
    /// This formatting may include potentially sensitive information.
607
0
    fn display_verbose_extras(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
608
0
    where
609
0
        Dst: KnownLayout,
610
    {
611
0
        f.write_str("Destination type: ")?;
612
0
        f.write_str(core::any::type_name::<Dst>())?;
613
0
        Ok(())
614
0
    }
615
}
616
617
impl<Src, Dst: ?Sized + TryFromBytes> fmt::Debug for ValidityError<Src, Dst> {
618
    #[inline]
619
0
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
620
0
        f.debug_struct("ValidityError").finish()
621
0
    }
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.
629
impl<Src, Dst: ?Sized> fmt::Display for ValidityError<Src, Dst>
630
where
631
    Dst: KnownLayout + TryFromBytes,
632
{
633
    #[inline]
634
0
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
635
0
        f.write_str("The conversion failed because the source bytes are not a valid value of the destination type.")?;
636
0
        if cfg!(debug_assertions) {
637
0
            f.write_str("\n\n")?;
638
0
            self.display_verbose_extras(f)?;
639
0
        }
640
0
        Ok(())
641
0
    }
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"))))]
646
impl<Src, Dst: ?Sized> Error for ValidityError<Src, Dst> where Dst: KnownLayout + TryFromBytes {}
647
648
impl<Src, Dst: ?Sized + TryFromBytes, A, S> From<ValidityError<Src, Dst>>
649
    for ConvertError<A, S, ValidityError<Src, Dst>>
650
{
651
    #[inline(always)]
652
0
    fn from(err: ValidityError<Src, Dst>) -> Self {
653
0
        Self::Validity(err)
654
0
    }
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)]
664
pub type CastError<Src, Dst: ?Sized> =
665
    ConvertError<AlignmentError<Src, Dst>, SizeError<Src, Dst>, Infallible>;
666
667
impl<Src, Dst: ?Sized> CastError<Src, Dst> {
668
    /// Produces the source underlying the failed conversion.
669
    #[inline]
670
0
    pub fn into_src(self) -> Src {
671
0
        match self {
672
0
            Self::Alignment(e) => e.src,
673
0
            Self::Size(e) => e.src,
674
            Self::Validity(i) => match i {},
675
        }
676
0
    }
677
678
    /// Sets the source value associated with the conversion error.
679
0
    pub(crate) fn with_src<NewSrc>(self, new_src: NewSrc) -> CastError<NewSrc, Dst> {
680
0
        match self {
681
0
            Self::Alignment(e) => CastError::Alignment(e.with_src(new_src)),
682
0
            Self::Size(e) => CastError::Size(e.with_src(new_src)),
683
            Self::Validity(i) => match i {},
684
        }
685
0
    }
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
0
    pub fn map_src<NewSrc>(self, f: impl FnOnce(Src) -> NewSrc) -> CastError<NewSrc, Dst> {
710
0
        match self {
711
0
            Self::Alignment(e) => CastError::Alignment(e.map_src(f)),
712
0
            Self::Size(e) => CastError::Size(e.map_src(f)),
713
            Self::Validity(i) => match i {},
714
        }
715
0
    }
716
717
    /// Converts the error into a general [`ConvertError`].
718
0
    pub(crate) fn into(self) -> TryCastError<Src, Dst>
719
0
    where
720
0
        Dst: TryFromBytes,
721
    {
722
0
        match self {
723
0
            Self::Alignment(e) => TryCastError::Alignment(e),
724
0
            Self::Size(e) => TryCastError::Size(e),
725
            Self::Validity(i) => match i {},
726
        }
727
0
    }
728
}
729
730
impl<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
0
    fn from(err: CastError<Src, Dst>) -> SizeError<Src, Dst> {
772
0
        match err {
773
            #[allow(unreachable_code)]
774
0
            CastError::Alignment(e) => match Infallible::from(e) {},
775
0
            CastError::Size(e) => e,
776
            CastError::Validity(i) => match i {},
777
        }
778
0
    }
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)]
789
pub 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.
795
impl<Src, Dst: ?Sized + TryFromBytes> TryCastError<Src, Dst> {
796
    /// Produces the source underlying the failed conversion.
797
    #[inline]
798
0
    pub fn into_src(self) -> Src {
799
0
        match self {
800
0
            Self::Alignment(e) => e.src,
801
0
            Self::Size(e) => e.src,
802
0
            Self::Validity(e) => e.src,
803
        }
804
0
    }
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
0
    pub fn map_src<NewSrc>(self, f: impl FnOnce(Src) -> NewSrc) -> TryCastError<NewSrc, Dst> {
831
0
        match self {
832
0
            Self::Alignment(e) => TryCastError::Alignment(e.map_src(f)),
833
0
            Self::Size(e) => TryCastError::Size(e.map_src(f)),
834
0
            Self::Validity(e) => TryCastError::Validity(e.map_src(f)),
835
        }
836
0
    }
837
}
838
839
impl<Src, Dst: ?Sized + TryFromBytes> From<CastError<Src, Dst>> for TryCastError<Src, Dst> {
840
    #[inline]
841
0
    fn from(value: CastError<Src, Dst>) -> Self {
842
0
        match value {
843
0
            CastError::Alignment(e) => Self::Alignment(e),
844
0
            CastError::Size(e) => Self::Size(e),
845
            CastError::Validity(i) => match i {},
846
        }
847
0
    }
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)]
857
pub type TryReadError<Src, Dst: ?Sized + TryFromBytes> =
858
    ConvertError<Infallible, SizeError<Src, Dst>, ValidityError<Src, Dst>>;
859
860
impl<Src, Dst: ?Sized + TryFromBytes> TryReadError<Src, Dst> {
861
    /// Produces the source underlying the failed conversion.
862
    #[inline]
863
0
    pub fn into_src(self) -> Src {
864
0
        match self {
865
            Self::Alignment(i) => match i {},
866
0
            Self::Size(e) => e.src,
867
0
            Self::Validity(e) => e.src,
868
        }
869
0
    }
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
0
    pub fn map_src<NewSrc>(self, f: impl FnOnce(Src) -> NewSrc) -> TryReadError<NewSrc, Dst> {
896
0
        match self {
897
            Self::Alignment(i) => match i {},
898
0
            Self::Size(e) => TryReadError::Size(e.map_src(f)),
899
0
            Self::Validity(e) => TryReadError::Validity(e.map_src(f)),
900
        }
901
0
    }
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)]
938
pub 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)]
953
pub struct AllocError;
954
955
#[cfg(test)]
956
mod 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
}