use crate::error::ptr::{MutPtr, OwnedPtr, SharedPtr}; use crate::error::{ ConcreteError, DynError, ErrorExt, OomOrDynErrorMut, OomOrDynErrorRef, OutOfMemory, }; use core::{any::TypeId, fmt, ptr::NonNull}; use std_alloc::boxed::Box; /// A vtable containing the `ErrorExt` methods for some type `T`. /// /// This is used to create thin-pointer equivalents of `Box`, /// `&dyn ErrorExt`, and `&mut ErrorExt`, which would all otherwise be two words /// in size. /// /// # Safety /// /// The safety contract for all vtable functions is the same: /// /// * `SharedPtr<'_, DynError>`s must be valid for reading a `ConcreteError`, /// `MutPtr<'_, DynError>`s must additionally be valid for writing a /// `ConcreteError`, and `OwnedPtr`s must additionally be valid /// to deallocate with `ConcreteError`'s layout. /// /// * If a `OomOrDynError{Ref,Mut}` return value contains a `{Shared,Mut}Ptr<'_, /// DynError>`, it must be valid for reading (and, in the case of `MutPtr`, /// writing) `DynError`s. #[repr(C)] pub(crate) struct Vtable { pub(crate) display: unsafe fn(SharedPtr<'_, DynError>, &mut fmt::Formatter<'_>) -> fmt::Result, pub(crate) debug: unsafe fn(SharedPtr<'_, DynError>, &mut fmt::Formatter<'_>) -> fmt::Result, pub(crate) source: unsafe fn(SharedPtr<'_, DynError>) -> Option>, pub(crate) source_mut: unsafe fn(MutPtr<'_, DynError>) -> Option>, pub(crate) is: unsafe fn(SharedPtr<'_, DynError>, TypeId) -> bool, pub(crate) as_dyn_core_error: unsafe fn(SharedPtr<'_, DynError>) -> &(dyn core::error::Error + Send + Sync + 'static), pub(crate) into_boxed_dyn_core_error: unsafe fn( OwnedPtr, ) -> Result, OutOfMemory>, pub(crate) drop_and_deallocate: unsafe fn(OwnedPtr), /// Additional safety requirement: the `NonNull` pointer must be valid /// for writing a `T`. /// /// Upon successful return, a `T` will have been written to that memory /// block. pub(crate) downcast: unsafe fn(OwnedPtr, TypeId, NonNull), } impl Vtable { /// Get the `Vtable` of the `E: ErrorExt` type parameter. pub(crate) fn of() -> &'static Self where E: ErrorExt, { &Vtable { display: display::, debug: debug::, source: source::, source_mut: source_mut::, is: is::, as_dyn_core_error: as_dyn_core_error::, into_boxed_dyn_core_error: into_boxed_dyn_core_error::, drop_and_deallocate: drop_and_deallocate::, downcast: downcast::, } } } unsafe fn display(error: SharedPtr<'_, DynError>, f: &mut fmt::Formatter<'_>) -> fmt::Result where E: ErrorExt, { let error = error.cast::>(); // Safety: implied by all vtable functions' safety contract. let error = unsafe { error.as_ref() }; fmt::Display::fmt(error.error.ext_as_dyn_core_error(), f) } unsafe fn debug(error: SharedPtr<'_, DynError>, f: &mut fmt::Formatter<'_>) -> fmt::Result where E: ErrorExt, { let error = error.cast::>(); // Safety: implied by all vtable functions' safety contract. let error = unsafe { error.as_ref() }; fmt::Debug::fmt(error.error.ext_as_dyn_core_error(), f) } unsafe fn source(error: SharedPtr<'_, DynError>) -> Option> where E: ErrorExt, { let error = error.cast::>(); // Safety: implied by all vtable functions' safety contract. let error = unsafe { error.as_ref() }; error.error.ext_source() } unsafe fn source_mut(error: MutPtr<'_, DynError>) -> Option> where E: ErrorExt, { let mut error = error.cast::>(); // Safety: implied by all vtable functions' safety contract. let error = unsafe { error.as_mut() }; error.error.ext_source_mut() } unsafe fn is(error: SharedPtr<'_, DynError>, type_id: TypeId) -> bool where E: ErrorExt, { let error = error.cast::>(); // Safety: implied by all vtable functions' safety contract. let error = unsafe { error.as_ref() }; error.error.ext_is(type_id) } unsafe fn as_dyn_core_error( error: SharedPtr<'_, DynError>, ) -> &(dyn core::error::Error + Send + Sync + 'static) where E: ErrorExt, { let error = error.cast::>(); // Safety: implied by all vtable functions' safety contract. let error = unsafe { error.as_ref() }; error.error.ext_as_dyn_core_error() } unsafe fn into_boxed_dyn_core_error( error: OwnedPtr, ) -> Result, OutOfMemory> where E: ErrorExt, { let error = error.cast::>(); // Safety: implied by all vtable functions' safety contract. let error = unsafe { error.into_box() }; error.error.ext_into_boxed_dyn_core_error() } unsafe fn drop_and_deallocate(error: OwnedPtr) where E: ErrorExt, { let error = error.cast::>(); // Safety: implied by all vtable functions' safety contract. let _ = unsafe { error.into_box() }; } unsafe fn downcast(error: OwnedPtr, type_id: TypeId, ret_ptr: NonNull) where E: ErrorExt, { let error = error.cast::>(); // Safety: implied by all vtable functions' safety contract. let mut error = unsafe { error.into_box() }; if error.error.ext_is(type_id) { // Safety: Implied by `downcast`'s additional safety safety requirement. unsafe { error.error.ext_move(ret_ptr); } } else { let source = error .error .ext_take_source() .expect("must have a source up the chain if `E` is not our target type"); // Safety: implied by downcast's additional safety requirement. unsafe { source.downcast(type_id, ret_ptr); } } }