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 fn from(err: ConvertError<AlignmentError<Src, Dst>, S, V>) -> ConvertError<Infallible, S, V> { 199 match err { 200 ConvertError::Alignment(e) => { 201 #[allow(unreachable_code)] 202 return ConvertError::Alignment(Infallible::from(e)); 203 } 204 ConvertError::Size(e) => ConvertError::Size(e), 205 ConvertError::Validity(e) => ConvertError::Validity(e), 206 } 207 } 208 } 209 210 impl<A: fmt::Debug, S: fmt::Debug, V: fmt::Debug> fmt::Debug for ConvertError<A, S, V> { 211 #[inline] 212 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 213 match self { 214 Self::Alignment(e) => f.debug_tuple("Alignment").field(e).finish(), 215 Self::Size(e) => f.debug_tuple("Size").field(e).finish(), 216 Self::Validity(e) => f.debug_tuple("Validity").field(e).finish(), 217 } 218 } 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 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 229 match self { 230 Self::Alignment(e) => e.fmt(f), 231 Self::Size(e) => e.fmt(f), 232 Self::Validity(e) => e.fmt(f), 233 } 234 } 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 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 Self { src, _dst: SendSyncPhantomData::default() } 267 } 268 269 /// Produces the source underlying the failed conversion. 270 #[inline] 271 pub fn into_src(self) -> Src { 272 self.src 273 } 274 275 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 AlignmentError { src: new_src, _dst: SendSyncPhantomData::default() } 280 } 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 pub fn map_src<NewSrc>(self, f: impl FnOnce(Src) -> NewSrc) -> AlignmentError<NewSrc, Dst> { 304 AlignmentError { src: f(self.src), _dst: SendSyncPhantomData::default() } 305 } 306 307 pub(crate) fn into<S, V>(self) -> ConvertError<Self, S, V> { 308 ConvertError::Alignment(self) 309 } 310 311 /// Format extra details for a verbose, human-readable error message. 312 /// 313 /// This formatting may include potentially sensitive information. 314 fn display_verbose_extras(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result 315 where 316 Src: Deref, 317 Dst: KnownLayout, 318 { 319 #[allow(clippy::as_conversions)] 320 let addr = self.src.deref() as *const _ as *const (); 321 let addr_align = 2usize.pow((crate::util::AsAddress::addr(addr)).trailing_zeros()); 322 323 f.write_str("\n\nSource type: ")?; 324 f.write_str(core::any::type_name::<Src>())?; 325 326 f.write_str("\nSource address: ")?; 327 addr.fmt(f)?; 328 f.write_str(" (a multiple of ")?; 329 addr_align.fmt(f)?; 330 f.write_str(")")?; 331 332 f.write_str("\nDestination type: ")?; 333 f.write_str(core::any::type_name::<Dst>())?; 334 335 f.write_str("\nDestination alignment: ")?; 336 <Dst as KnownLayout>::LAYOUT.align.get().fmt(f)?; 337 338 Ok(()) 339 } 340 } 341 342 impl<Src: Clone, Dst: ?Sized> Clone for AlignmentError<Src, Dst> { 343 #[inline] 344 fn clone(&self) -> Self { 345 Self { src: self.src.clone(), _dst: SendSyncPhantomData::default() } 346 } 347 } 348 349 impl<Src: PartialEq, Dst: ?Sized> PartialEq for AlignmentError<Src, Dst> { 350 #[inline] 351 fn eq(&self, other: &Self) -> bool { 352 self.src == other.src 353 } 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 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 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 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 384 f.debug_struct("AlignmentError").finish() 385 } 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 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 400 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 if cfg!(debug_assertions) { 403 self.display_verbose_extras(f) 404 } else { 405 Ok(()) 406 } 407 } 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 fn from(err: AlignmentError<Src, Dst>) -> Self { 424 Self::Alignment(err) 425 } 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 pub(crate) fn new(src: Src) -> Self { 438 Self { src, _dst: SendSyncPhantomData::default() } 439 } 440 441 /// Produces the source underlying the failed conversion. 442 #[inline] 443 pub fn into_src(self) -> Src { 444 self.src 445 } 446 447 /// Sets the source value associated with the conversion error. 448 pub(crate) fn with_src<NewSrc>(self, new_src: NewSrc) -> SizeError<NewSrc, Dst> { 449 SizeError { src: new_src, _dst: SendSyncPhantomData::default() } 450 } 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 pub fn map_src<NewSrc>(self, f: impl FnOnce(Src) -> NewSrc) -> SizeError<NewSrc, Dst> { 475 SizeError { src: f(self.src), _dst: SendSyncPhantomData::default() } 476 } 477 478 /// Sets the destination type associated with the conversion error. 479 pub(crate) fn with_dst<NewDst: ?Sized>(self) -> SizeError<Src, NewDst> { 480 SizeError { src: self.src, _dst: SendSyncPhantomData::default() } 481 } 482 483 /// Converts the error into a general [`ConvertError`]. 484 pub(crate) fn into<A, V>(self) -> ConvertError<A, Self, V> { 485 ConvertError::Size(self) 486 } 487 488 /// Format extra details for a verbose, human-readable error message. 489 /// 490 /// This formatting may include potentially sensitive information. 491 fn display_verbose_extras(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result 492 where 493 Src: Deref, 494 Dst: KnownLayout, 495 { 496 // include the source type 497 f.write_str("\nSource type: ")?; 498 f.write_str(core::any::type_name::<Src>())?; 499 500 // include the source.deref() size 501 let src_size = core::mem::size_of_val(&*self.src); 502 f.write_str("\nSource size: ")?; 503 src_size.fmt(f)?; 504 f.write_str(" byte")?; 505 if src_size != 1 { 506 f.write_char('s')?; 507 } 508 509 // if `Dst` is `Sized`, include the `Dst` size 510 if let crate::SizeInfo::Sized { size } = Dst::LAYOUT.size_info { 511 f.write_str("\nDestination size: ")?; 512 size.fmt(f)?; 513 f.write_str(" byte")?; 514 if size != 1 { 515 f.write_char('s')?; 516 } 517 } 518 519 // include the destination type 520 f.write_str("\nDestination type: ")?; 521 f.write_str(core::any::type_name::<Dst>())?; 522 523 Ok(()) 524 } 525 } 526 527 impl<Src: Clone, Dst: ?Sized> Clone for SizeError<Src, Dst> { 528 #[inline] 529 fn clone(&self) -> Self { 530 Self { src: self.src.clone(), _dst: SendSyncPhantomData::default() } 531 } 532 } 533 534 impl<Src: PartialEq, Dst: ?Sized> PartialEq for SizeError<Src, Dst> { 535 #[inline] 536 fn eq(&self, other: &Self) -> bool { 537 self.src == other.src 538 } 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 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 546 f.debug_struct("SizeError").finish() 547 } 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 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 562 f.write_str("The conversion failed because the source was incorrectly sized to complete the conversion into the destination type.")?; 563 if cfg!(debug_assertions) { 564 f.write_str("\n")?; 565 self.display_verbose_extras(f)?; 566 } 567 Ok(()) 568 } 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 fn from(err: SizeError<Src, Dst>) -> Self { 583 Self::Size(err) 584 } 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 pub(crate) fn new(src: Src) -> Self { 597 Self { src, _dst: SendSyncPhantomData::default() } 598 } 599 600 /// Produces the source underlying the failed conversion. 601 #[inline] 602 pub fn into_src(self) -> Src { 603 self.src 604 } 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 pub fn map_src<NewSrc>(self, f: impl FnOnce(Src) -> NewSrc) -> ValidityError<NewSrc, Dst> { 628 ValidityError { src: f(self.src), _dst: SendSyncPhantomData::default() } 629 } 630 631 /// Converts the error into a general [`ConvertError`]. 632 pub(crate) fn into<A, S>(self) -> ConvertError<A, S, Self> { 633 ConvertError::Validity(self) 634 } 635 636 /// Format extra details for a verbose, human-readable error message. 637 /// 638 /// This formatting may include potentially sensitive information. 639 fn display_verbose_extras(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result 640 where 641 Dst: KnownLayout, 642 { 643 f.write_str("Destination type: ")?; 644 f.write_str(core::any::type_name::<Dst>())?; 645 Ok(()) 646 } 647 } 648 649 impl<Src: Clone, Dst: ?Sized + TryFromBytes> Clone for ValidityError<Src, Dst> { 650 #[inline] 651 fn clone(&self) -> Self { 652 Self { src: self.src.clone(), _dst: SendSyncPhantomData::default() } 653 } 654 } 655 656 // SAFETY: `ValidityError` contains a single `Self::Inner = Src`, and no other 657 // non-ZST fields. `map` passes ownership of `self`'s sole `Self::Inner` to `f`. 658 unsafe impl<Src, NewSrc, Dst> crate::pointer::TryWithError<NewSrc> 659 for crate::ValidityError<Src, Dst> 660 where 661 Dst: TryFromBytes + ?Sized, 662 { 663 type Inner = Src; 664 type Mapped = crate::ValidityError<NewSrc, Dst>; 665 #[inline] 666 fn map<F: FnOnce(Src) -> NewSrc>(self, f: F) -> Self::Mapped { 667 self.map_src(f) 668 } 669 } 670 671 impl<Src: PartialEq, Dst: ?Sized + TryFromBytes> PartialEq for ValidityError<Src, Dst> { 672 #[inline] 673 fn eq(&self, other: &Self) -> bool { 674 self.src == other.src 675 } 676 } 677 678 impl<Src: Eq, Dst: ?Sized + TryFromBytes> Eq for ValidityError<Src, Dst> {} 679 680 impl<Src, Dst: ?Sized + TryFromBytes> fmt::Debug for ValidityError<Src, Dst> { 681 #[inline] 682 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 683 f.debug_struct("ValidityError").finish() 684 } 685 } 686 687 /// Produces a human-readable error message. 688 /// 689 /// The message differs between debug and release builds. When 690 /// `debug_assertions` are enabled, this message is verbose and includes 691 /// potentially sensitive information. 692 impl<Src, Dst: ?Sized> fmt::Display for ValidityError<Src, Dst> 693 where 694 Dst: KnownLayout + TryFromBytes, 695 { 696 #[inline] 697 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 698 f.write_str("The conversion failed because the source bytes are not a valid value of the destination type.")?; 699 if cfg!(debug_assertions) { 700 f.write_str("\n\n")?; 701 self.display_verbose_extras(f)?; 702 } 703 Ok(()) 704 } 705 } 706 707 #[cfg(any(not(no_zerocopy_core_error_1_81_0), feature = "std", test))] 708 #[cfg_attr(doc_cfg, doc(cfg(all(rust = "1.81.0", feature = "std"))))] 709 impl<Src, Dst: ?Sized> Error for ValidityError<Src, Dst> where Dst: KnownLayout + TryFromBytes {} 710 711 impl<Src, Dst: ?Sized + TryFromBytes, A, S> From<ValidityError<Src, Dst>> 712 for ConvertError<A, S, ValidityError<Src, Dst>> 713 { 714 #[inline(always)] 715 fn from(err: ValidityError<Src, Dst>) -> Self { 716 Self::Validity(err) 717 } 718 } 719 720 /// The error type of reference conversions. 721 /// 722 /// Reference conversions, like [`FromBytes::ref_from_bytes`] may emit 723 /// [alignment](AlignmentError) and [size](SizeError) errors. 724 // Bounds on generic parameters are not enforced in type aliases, but they do 725 // appear in rustdoc. 726 #[allow(type_alias_bounds)] 727 pub type CastError<Src, Dst: ?Sized> = 728 ConvertError<AlignmentError<Src, Dst>, SizeError<Src, Dst>, Infallible>; 729 730 impl<Src, Dst: ?Sized> CastError<Src, Dst> { 731 /// Produces the source underlying the failed conversion. 732 #[inline] 733 pub fn into_src(self) -> Src { 734 match self { 735 Self::Alignment(e) => e.src, 736 Self::Size(e) => e.src, 737 Self::Validity(i) => match i {}, 738 } 739 } 740 741 /// Sets the source value associated with the conversion error. 742 pub(crate) fn with_src<NewSrc>(self, new_src: NewSrc) -> CastError<NewSrc, Dst> { 743 match self { 744 Self::Alignment(e) => CastError::Alignment(e.with_src(new_src)), 745 Self::Size(e) => CastError::Size(e.with_src(new_src)), 746 Self::Validity(i) => match i {}, 747 } 748 } 749 750 /// Maps the source value associated with the conversion error. 751 /// 752 /// This can help mitigate [issues with `Send`, `Sync` and `'static` 753 /// bounds][self#send-sync-and-static]. 754 /// 755 /// # Examples 756 /// 757 /// ``` 758 /// use zerocopy::*; 759 /// 760 /// let source: [u8; 3] = [0, 1, 2]; 761 /// 762 /// // Try to read a `u32` from `source`. This will fail because there are insufficient 763 /// // bytes in `source`. 764 /// let maybe_u32: Result<&u32, CastError<&[u8], u32>> = u32::ref_from_bytes(&source[..]); 765 /// 766 /// // Map the error's source to its size and address. 767 /// let maybe_u32: Result<&u32, CastError<(usize, usize), u32>> = maybe_u32.map_err(|err| { 768 /// err.map_src(|src| (src.len(), src.as_ptr() as usize)) 769 /// }); 770 /// ``` 771 #[inline] 772 pub fn map_src<NewSrc>(self, f: impl FnOnce(Src) -> NewSrc) -> CastError<NewSrc, Dst> { 773 match self { 774 Self::Alignment(e) => CastError::Alignment(e.map_src(f)), 775 Self::Size(e) => CastError::Size(e.map_src(f)), 776 Self::Validity(i) => match i {}, 777 } 778 } 779 780 /// Converts the error into a general [`ConvertError`]. 781 pub(crate) fn into(self) -> TryCastError<Src, Dst> 782 where 783 Dst: TryFromBytes, 784 { 785 match self { 786 Self::Alignment(e) => TryCastError::Alignment(e), 787 Self::Size(e) => TryCastError::Size(e), 788 Self::Validity(i) => match i {}, 789 } 790 } 791 } 792 793 // SAFETY: `CastError` is either a single `AlignmentError` or a single 794 // `SizeError`. In either case, it contains a single `Self::Inner = Src`, and no 795 // other non-ZST fields. `map` passes ownership of `self`'s sole `Self::Inner` 796 // to `f`. 797 unsafe impl<Src, NewSrc, Dst> crate::pointer::TryWithError<NewSrc> for crate::CastError<Src, Dst> 798 where 799 Dst: ?Sized, 800 { 801 type Inner = Src; 802 type Mapped = crate::CastError<NewSrc, Dst>; 803 804 #[inline] 805 fn map<F: FnOnce(Src) -> NewSrc>(self, f: F) -> Self::Mapped { 806 self.map_src(f) 807 } 808 } 809 810 impl<Src, Dst: ?Sized + Unaligned> From<CastError<Src, Dst>> for SizeError<Src, Dst> { 811 /// Infallibly extracts the [`SizeError`] from this `CastError` since `Dst` 812 /// is unaligned. 813 /// 814 /// Since [`Dst: Unaligned`], it is impossible to encounter an alignment 815 /// error, and so the only error that can be encountered at runtime is a 816 /// [`SizeError`]. This method permits extracting that `SizeError` 817 /// infallibly. 818 /// 819 /// [`Dst: Unaligned`]: crate::Unaligned 820 /// 821 /// # Examples 822 /// 823 /// ```rust 824 /// use zerocopy::*; 825 /// # use zerocopy_derive::*; 826 /// 827 /// #[derive(FromBytes, IntoBytes, KnownLayout, Immutable, Unaligned)] 828 /// #[repr(C)] 829 /// struct UdpHeader { 830 /// src_port: [u8; 2], 831 /// dst_port: [u8; 2], 832 /// length: [u8; 2], 833 /// checksum: [u8; 2], 834 /// } 835 /// 836 /// #[derive(FromBytes, IntoBytes, KnownLayout, Immutable, Unaligned)] 837 /// #[repr(C, packed)] 838 /// struct UdpPacket { 839 /// header: UdpHeader, 840 /// body: [u8], 841 /// } 842 /// 843 /// impl UdpPacket { 844 /// pub fn parse(bytes: &[u8]) -> Result<&UdpPacket, SizeError<&[u8], UdpPacket>> { 845 /// // Since `UdpPacket: Unaligned`, we can map the `CastError` to a `SizeError`. 846 /// UdpPacket::ref_from_bytes(bytes).map_err(Into::into) 847 /// } 848 /// } 849 /// ``` 850 #[inline(always)] 851 fn from(err: CastError<Src, Dst>) -> SizeError<Src, Dst> { 852 match err { 853 #[allow(unreachable_code)] 854 CastError::Alignment(e) => match Infallible::from(e) {}, 855 CastError::Size(e) => e, 856 CastError::Validity(i) => match i {}, 857 } 858 } 859 } 860 861 /// The error type of fallible reference conversions. 862 /// 863 /// Fallible reference conversions, like [`TryFromBytes::try_ref_from_bytes`] 864 /// may emit [alignment](AlignmentError), [size](SizeError), and 865 /// [validity](ValidityError) errors. 866 // Bounds on generic parameters are not enforced in type aliases, but they do 867 // appear in rustdoc. 868 #[allow(type_alias_bounds)] 869 pub type TryCastError<Src, Dst: ?Sized + TryFromBytes> = 870 ConvertError<AlignmentError<Src, Dst>, SizeError<Src, Dst>, ValidityError<Src, Dst>>; 871 872 // FIXME(#1139): Remove the `TryFromBytes` here and in other downstream 873 // locations (all the way to `ValidityError`) if we determine it's not necessary 874 // for rich validity errors. 875 impl<Src, Dst: ?Sized + TryFromBytes> TryCastError<Src, Dst> { 876 /// Produces the source underlying the failed conversion. 877 #[inline] 878 pub fn into_src(self) -> Src { 879 match self { 880 Self::Alignment(e) => e.src, 881 Self::Size(e) => e.src, 882 Self::Validity(e) => e.src, 883 } 884 } 885 886 /// Maps the source value associated with the conversion error. 887 /// 888 /// This can help mitigate [issues with `Send`, `Sync` and `'static` 889 /// bounds][self#send-sync-and-static]. 890 /// 891 /// # Examples 892 /// 893 /// ``` 894 /// use core::num::NonZeroU32; 895 /// use zerocopy::*; 896 /// 897 /// let source: [u8; 3] = [0, 0, 0]; 898 /// 899 /// // Try to read a `NonZeroU32` from `source`. 900 /// let maybe_u32: Result<&NonZeroU32, TryCastError<&[u8], NonZeroU32>> 901 /// = NonZeroU32::try_ref_from_bytes(&source[..]); 902 /// 903 /// // Map the error's source to its size and address. 904 /// let maybe_u32: Result<&NonZeroU32, TryCastError<(usize, usize), NonZeroU32>> = 905 /// maybe_u32.map_err(|err| { 906 /// err.map_src(|src| (src.len(), src.as_ptr() as usize)) 907 /// }); 908 /// ``` 909 #[inline] 910 pub fn map_src<NewSrc>(self, f: impl FnOnce(Src) -> NewSrc) -> TryCastError<NewSrc, Dst> { 911 match self { 912 Self::Alignment(e) => TryCastError::Alignment(e.map_src(f)), 913 Self::Size(e) => TryCastError::Size(e.map_src(f)), 914 Self::Validity(e) => TryCastError::Validity(e.map_src(f)), 915 } 916 } 917 } 918 919 impl<Src, Dst: ?Sized + TryFromBytes> From<CastError<Src, Dst>> for TryCastError<Src, Dst> { 920 #[inline] 921 fn from(value: CastError<Src, Dst>) -> Self { 922 match value { 923 CastError::Alignment(e) => Self::Alignment(e), 924 CastError::Size(e) => Self::Size(e), 925 CastError::Validity(i) => match i {}, 926 } 927 } 928 } 929 930 /// The error type of fallible read-conversions. 931 /// 932 /// Fallible read-conversions, like [`TryFromBytes::try_read_from_bytes`] may 933 /// emit [size](SizeError) and [validity](ValidityError) errors, but not 934 /// alignment errors. 935 // Bounds on generic parameters are not enforced in type aliases, but they do 936 // appear in rustdoc. 937 #[allow(type_alias_bounds)] 938 pub type TryReadError<Src, Dst: ?Sized + TryFromBytes> = 939 ConvertError<Infallible, SizeError<Src, Dst>, ValidityError<Src, Dst>>; 940 941 impl<Src, Dst: ?Sized + TryFromBytes> TryReadError<Src, Dst> { 942 /// Produces the source underlying the failed conversion. 943 #[inline] 944 pub fn into_src(self) -> Src { 945 match self { 946 Self::Alignment(i) => match i {}, 947 Self::Size(e) => e.src, 948 Self::Validity(e) => e.src, 949 } 950 } 951 952 /// Maps the source value associated with the conversion error. 953 /// 954 /// This can help mitigate [issues with `Send`, `Sync` and `'static` 955 /// bounds][self#send-sync-and-static]. 956 /// 957 /// # Examples 958 /// 959 /// ``` 960 /// use core::num::NonZeroU32; 961 /// use zerocopy::*; 962 /// 963 /// let source: [u8; 3] = [0, 0, 0]; 964 /// 965 /// // Try to read a `NonZeroU32` from `source`. 966 /// let maybe_u32: Result<NonZeroU32, TryReadError<&[u8], NonZeroU32>> 967 /// = NonZeroU32::try_read_from_bytes(&source[..]); 968 /// 969 /// // Map the error's source to its size. 970 /// let maybe_u32: Result<NonZeroU32, TryReadError<usize, NonZeroU32>> = 971 /// maybe_u32.map_err(|err| { 972 /// err.map_src(|src| src.len()) 973 /// }); 974 /// ``` 975 #[inline] 976 pub fn map_src<NewSrc>(self, f: impl FnOnce(Src) -> NewSrc) -> TryReadError<NewSrc, Dst> { 977 match self { 978 Self::Alignment(i) => match i {}, 979 Self::Size(e) => TryReadError::Size(e.map_src(f)), 980 Self::Validity(e) => TryReadError::Validity(e.map_src(f)), 981 } 982 } 983 } 984 985 /// The error type of well-aligned, fallible casts. 986 /// 987 /// This is like [`TryCastError`], but for casts that are always well-aligned. 988 /// It is identical to `TryCastError`, except that its alignment error is 989 /// [`Infallible`]. 990 /// 991 /// As of this writing, none of zerocopy's API produces this error directly. 992 /// However, it is useful since it permits users to infallibly discard alignment 993 /// errors when they can prove statically that alignment errors are impossible. 994 /// 995 /// # Examples 996 /// 997 /// ``` 998 /// use core::convert::Infallible; 999 /// use zerocopy::*; 1000 /// # use zerocopy_derive::*; 1001 /// 1002 /// #[derive(TryFromBytes, KnownLayout, Unaligned, Immutable)] 1003 /// #[repr(C, packed)] 1004 /// struct Bools { 1005 /// one: bool, 1006 /// two: bool, 1007 /// many: [bool], 1008 /// } 1009 /// 1010 /// impl Bools { 1011 /// fn parse(bytes: &[u8]) -> Result<&Bools, AlignedTryCastError<&[u8], Bools>> { 1012 /// // Since `Bools: Unaligned`, we can infallibly discard 1013 /// // the alignment error. 1014 /// Bools::try_ref_from_bytes(bytes).map_err(Into::into) 1015 /// } 1016 /// } 1017 /// ``` 1018 #[allow(type_alias_bounds)] 1019 pub type AlignedTryCastError<Src, Dst: ?Sized + TryFromBytes> = 1020 ConvertError<Infallible, SizeError<Src, Dst>, ValidityError<Src, Dst>>; 1021 1022 /// The error type of a failed allocation. 1023 /// 1024 /// This type is intended to be deprecated in favor of the standard library's 1025 /// [`AllocError`] type once it is stabilized. When that happens, this type will 1026 /// be replaced by a type alias to the standard library type. We do not intend 1027 /// to treat this as a breaking change; users who wish to avoid breakage should 1028 /// avoid writing code which assumes that this is *not* such an alias. For 1029 /// example, implementing the same trait for both types will result in an impl 1030 /// conflict once this type is an alias. 1031 /// 1032 /// [`AllocError`]: https://doc.rust-lang.org/alloc/alloc/struct.AllocError.html 1033 #[derive(Copy, Clone, PartialEq, Eq, Debug)] 1034 pub struct AllocError; 1035 1036 #[cfg(test)] 1037 mod tests { 1038 use core::convert::Infallible; 1039 1040 use super::*; 1041 1042 #[test] 1043 fn test_send_sync() { 1044 // Test that all error types are `Send + Sync` even if `Dst: !Send + 1045 // !Sync`. 1046 1047 #[allow(dead_code)] 1048 fn is_send_sync<T: Send + Sync>(_t: T) {} 1049 1050 #[allow(dead_code)] 1051 fn alignment_err_is_send_sync<Src: Send + Sync, Dst>(err: AlignmentError<Src, Dst>) { 1052 is_send_sync(err) 1053 } 1054 1055 #[allow(dead_code)] 1056 fn size_err_is_send_sync<Src: Send + Sync, Dst>(err: SizeError<Src, Dst>) { 1057 is_send_sync(err) 1058 } 1059 1060 #[allow(dead_code)] 1061 fn validity_err_is_send_sync<Src: Send + Sync, Dst: TryFromBytes>( 1062 err: ValidityError<Src, Dst>, 1063 ) { 1064 is_send_sync(err) 1065 } 1066 1067 #[allow(dead_code)] 1068 fn convert_error_is_send_sync<Src: Send + Sync, Dst: TryFromBytes>( 1069 err: ConvertError< 1070 AlignmentError<Src, Dst>, 1071 SizeError<Src, Dst>, 1072 ValidityError<Src, Dst>, 1073 >, 1074 ) { 1075 is_send_sync(err) 1076 } 1077 } 1078 1079 #[test] 1080 fn test_eq_partial_eq_clone() { 1081 // Test that all error types implement `Eq`, `PartialEq` 1082 // and `Clone` if src does 1083 // even if `Dst: !Eq`, `!PartialEq`, `!Clone`. 1084 1085 #[allow(dead_code)] 1086 fn is_eq_partial_eq_clone<T: Eq + PartialEq + Clone>(_t: T) {} 1087 1088 #[allow(dead_code)] 1089 fn alignment_err_is_eq_partial_eq_clone<Src: Eq + PartialEq + Clone, Dst>( 1090 err: AlignmentError<Src, Dst>, 1091 ) { 1092 is_eq_partial_eq_clone(err) 1093 } 1094 1095 #[allow(dead_code)] 1096 fn size_err_is_eq_partial_eq_clone<Src: Eq + PartialEq + Clone, Dst>( 1097 err: SizeError<Src, Dst>, 1098 ) { 1099 is_eq_partial_eq_clone(err) 1100 } 1101 1102 #[allow(dead_code)] 1103 fn validity_err_is_eq_partial_eq_clone<Src: Eq + PartialEq + Clone, Dst: TryFromBytes>( 1104 err: ValidityError<Src, Dst>, 1105 ) { 1106 is_eq_partial_eq_clone(err) 1107 } 1108 1109 #[allow(dead_code)] 1110 fn convert_error_is_eq_partial_eq_clone<Src: Eq + PartialEq + Clone, Dst: TryFromBytes>( 1111 err: ConvertError< 1112 AlignmentError<Src, Dst>, 1113 SizeError<Src, Dst>, 1114 ValidityError<Src, Dst>, 1115 >, 1116 ) { 1117 is_eq_partial_eq_clone(err) 1118 } 1119 } 1120 1121 #[test] 1122 fn alignment_display() { 1123 #[repr(C, align(128))] 1124 struct Aligned { 1125 bytes: [u8; 128], 1126 } 1127 1128 impl_known_layout!(elain::Align::<8>); 1129 1130 let aligned = Aligned { bytes: [0; 128] }; 1131 1132 let bytes = &aligned.bytes[1..]; 1133 let addr = crate::util::AsAddress::addr(bytes); 1134 assert_eq!( 1135 AlignmentError::<_, elain::Align::<8>>::new_checked(bytes).to_string(), 1136 format!("The conversion failed because the address of the source is not a multiple of the alignment of the destination type.\n\ 1137 \nSource type: &[u8]\ 1138 \nSource address: 0x{:x} (a multiple of 1)\ 1139 \nDestination type: elain::Align<8>\ 1140 \nDestination alignment: 8", addr) 1141 ); 1142 1143 let bytes = &aligned.bytes[2..]; 1144 let addr = crate::util::AsAddress::addr(bytes); 1145 assert_eq!( 1146 AlignmentError::<_, elain::Align::<8>>::new_checked(bytes).to_string(), 1147 format!("The conversion failed because the address of the source is not a multiple of the alignment of the destination type.\n\ 1148 \nSource type: &[u8]\ 1149 \nSource address: 0x{:x} (a multiple of 2)\ 1150 \nDestination type: elain::Align<8>\ 1151 \nDestination alignment: 8", addr) 1152 ); 1153 1154 let bytes = &aligned.bytes[3..]; 1155 let addr = crate::util::AsAddress::addr(bytes); 1156 assert_eq!( 1157 AlignmentError::<_, elain::Align::<8>>::new_checked(bytes).to_string(), 1158 format!("The conversion failed because the address of the source is not a multiple of the alignment of the destination type.\n\ 1159 \nSource type: &[u8]\ 1160 \nSource address: 0x{:x} (a multiple of 1)\ 1161 \nDestination type: elain::Align<8>\ 1162 \nDestination alignment: 8", addr) 1163 ); 1164 1165 let bytes = &aligned.bytes[4..]; 1166 let addr = crate::util::AsAddress::addr(bytes); 1167 assert_eq!( 1168 AlignmentError::<_, elain::Align::<8>>::new_checked(bytes).to_string(), 1169 format!("The conversion failed because the address of the source is not a multiple of the alignment of the destination type.\n\ 1170 \nSource type: &[u8]\ 1171 \nSource address: 0x{:x} (a multiple of 4)\ 1172 \nDestination type: elain::Align<8>\ 1173 \nDestination alignment: 8", addr) 1174 ); 1175 } 1176 1177 #[test] 1178 fn size_display() { 1179 assert_eq!( 1180 SizeError::<_, [u8]>::new(&[0u8; 2][..]).to_string(), 1181 "The conversion failed because the source was incorrectly sized to complete the conversion into the destination type.\n\ 1182 \nSource type: &[u8]\ 1183 \nSource size: 2 bytes\ 1184 \nDestination type: [u8]" 1185 ); 1186 1187 assert_eq!( 1188 SizeError::<_, [u8; 2]>::new(&[0u8; 1][..]).to_string(), 1189 "The conversion failed because the source was incorrectly sized to complete the conversion into the destination type.\n\ 1190 \nSource type: &[u8]\ 1191 \nSource size: 1 byte\ 1192 \nDestination size: 2 bytes\ 1193 \nDestination type: [u8; 2]" 1194 ); 1195 } 1196 1197 #[test] 1198 fn validity_display() { 1199 assert_eq!( 1200 ValidityError::<_, bool>::new(&[2u8; 1][..]).to_string(), 1201 "The conversion failed because the source bytes are not a valid value of the destination type.\n\ 1202 \n\ 1203 Destination type: bool" 1204 ); 1205 } 1206 1207 #[test] 1208 fn test_convert_error_debug() { 1209 let err: ConvertError< 1210 AlignmentError<&[u8], u16>, 1211 SizeError<&[u8], u16>, 1212 ValidityError<&[u8], bool>, 1213 > = ConvertError::Alignment(AlignmentError::new_checked(&[0u8])); 1214 assert_eq!(format!("{:?}", err), "Alignment(AlignmentError)"); 1215 1216 let err: ConvertError< 1217 AlignmentError<&[u8], u16>, 1218 SizeError<&[u8], u16>, 1219 ValidityError<&[u8], bool>, 1220 > = ConvertError::Size(SizeError::new(&[0u8])); 1221 assert_eq!(format!("{:?}", err), "Size(SizeError)"); 1222 1223 let err: ConvertError< 1224 AlignmentError<&[u8], u16>, 1225 SizeError<&[u8], u16>, 1226 ValidityError<&[u8], bool>, 1227 > = ConvertError::Validity(ValidityError::new(&[0u8])); 1228 assert_eq!(format!("{:?}", err), "Validity(ValidityError)"); 1229 } 1230 1231 #[test] 1232 fn test_convert_error_from_unaligned() { 1233 // u8 is Unaligned 1234 let err: ConvertError< 1235 AlignmentError<&[u8], u8>, 1236 SizeError<&[u8], u8>, 1237 ValidityError<&[u8], bool>, 1238 > = ConvertError::Size(SizeError::new(&[0u8])); 1239 let converted: ConvertError<Infallible, SizeError<&[u8], u8>, ValidityError<&[u8], bool>> = 1240 ConvertError::from(err); 1241 match converted { 1242 ConvertError::Size(_) => {} 1243 _ => panic!("Expected Size error"), 1244 } 1245 } 1246 1247 #[test] 1248 fn test_alignment_error_display_debug() { 1249 let err: AlignmentError<&[u8], u16> = AlignmentError::new_checked(&[0u8]); 1250 assert!(format!("{:?}", err).contains("AlignmentError")); 1251 assert!(format!("{}", err).contains("address of the source is not a multiple")); 1252 } 1253 1254 #[test] 1255 fn test_size_error_display_debug() { 1256 let err: SizeError<&[u8], u16> = SizeError::new(&[0u8]); 1257 assert!(format!("{:?}", err).contains("SizeError")); 1258 assert!(format!("{}", err).contains("source was incorrectly sized")); 1259 } 1260 1261 #[test] 1262 fn test_validity_error_display_debug() { 1263 let err: ValidityError<&[u8], bool> = ValidityError::new(&[0u8]); 1264 assert!(format!("{:?}", err).contains("ValidityError")); 1265 assert!(format!("{}", err).contains("source bytes are not a valid value")); 1266 } 1267 1268 #[test] 1269 fn test_convert_error_display_debug_more() { 1270 let err: ConvertError< 1271 AlignmentError<&[u8], u16>, 1272 SizeError<&[u8], u16>, 1273 ValidityError<&[u8], bool>, 1274 > = ConvertError::Alignment(AlignmentError::new_checked(&[0u8])); 1275 assert!(format!("{}", err).contains("address of the source is not a multiple")); 1276 1277 let err: ConvertError< 1278 AlignmentError<&[u8], u16>, 1279 SizeError<&[u8], u16>, 1280 ValidityError<&[u8], bool>, 1281 > = ConvertError::Size(SizeError::new(&[0u8])); 1282 assert!(format!("{}", err).contains("source was incorrectly sized")); 1283 1284 let err: ConvertError< 1285 AlignmentError<&[u8], u16>, 1286 SizeError<&[u8], u16>, 1287 ValidityError<&[u8], bool>, 1288 > = ConvertError::Validity(ValidityError::new(&[0u8])); 1289 assert!(format!("{}", err).contains("source bytes are not a valid value")); 1290 } 1291 1292 #[test] 1293 fn test_alignment_error_methods() { 1294 let err: AlignmentError<&[u8], u16> = AlignmentError::new_checked(&[0u8]); 1295 1296 // into_src 1297 let src = err.clone().into_src(); 1298 assert_eq!(src, &[0u8]); 1299 1300 // into 1301 let converted: ConvertError< 1302 AlignmentError<&[u8], u16>, 1303 SizeError<&[u8], u16>, 1304 ValidityError<&[u8], bool>, 1305 > = err.clone().into(); 1306 match converted { 1307 ConvertError::Alignment(_) => {} 1308 _ => panic!("Expected Alignment error"), 1309 } 1310 1311 // clone 1312 let cloned = err.clone(); 1313 assert_eq!(err, cloned); 1314 1315 // eq 1316 assert_eq!(err, cloned); 1317 let err2: AlignmentError<&[u8], u16> = AlignmentError::new_checked(&[1u8]); 1318 assert_ne!(err, err2); 1319 } 1320 1321 #[test] 1322 fn test_convert_error_from_unaligned_variants() { 1323 // u8 is Unaligned 1324 let err: ConvertError< 1325 AlignmentError<&[u8], u8>, 1326 SizeError<&[u8], u8>, 1327 ValidityError<&[u8], bool>, 1328 > = ConvertError::Validity(ValidityError::new(&[0u8])); 1329 let converted: ConvertError<Infallible, SizeError<&[u8], u8>, ValidityError<&[u8], bool>> = 1330 ConvertError::from(err); 1331 match converted { 1332 ConvertError::Validity(_) => {} 1333 _ => panic!("Expected Validity error"), 1334 } 1335 1336 let err: ConvertError< 1337 AlignmentError<&[u8], u8>, 1338 SizeError<&[u8], u8>, 1339 ValidityError<&[u8], bool>, 1340 > = ConvertError::Size(SizeError::new(&[0u8])); 1341 let converted: ConvertError<Infallible, SizeError<&[u8], u8>, ValidityError<&[u8], bool>> = 1342 ConvertError::from(err); 1343 match converted { 1344 ConvertError::Size(_) => {} 1345 _ => panic!("Expected Size error"), 1346 } 1347 } 1348 } 1349