/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 | | } |