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