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