1 use crate::error::ptr::{MutPtr, OwnedPtr, SharedPtr};
2 use crate::error::{
3     ConcreteError, DynError, ErrorExt, OomOrDynErrorMut, OomOrDynErrorRef, OutOfMemory,
4 };
5 use core::{any::TypeId, fmt, ptr::NonNull};
6 use std_alloc::boxed::Box;
7 
8 /// A vtable containing the `ErrorExt` methods for some type `T`.
9 ///
10 /// This is used to create thin-pointer equivalents of `Box<dyn ErrorExt>`,
11 /// `&dyn ErrorExt`, and `&mut ErrorExt`, which would all otherwise be two words
12 /// in size.
13 ///
14 /// # Safety
15 ///
16 /// The safety contract for all vtable functions is the same:
17 ///
18 /// * `SharedPtr<'_, DynError>`s must be valid for reading a `ConcreteError<T>`,
19 ///   `MutPtr<'_, DynError>`s must additionally be valid for writing a
20 ///   `ConcreteError<T>`, and `OwnedPtr<DynError>`s must additionally be valid
21 ///   to deallocate with `ConcreteError<T>`'s layout.
22 ///
23 /// * If a `OomOrDynError{Ref,Mut}` return value contains a `{Shared,Mut}Ptr<'_,
24 ///   DynError>`, it must be valid for reading (and, in the case of `MutPtr`,
25 ///   writing) `DynError`s.
26 #[repr(C)]
27 pub(crate) struct Vtable {
28     pub(crate) display: unsafe fn(SharedPtr<'_, DynError>, &mut fmt::Formatter<'_>) -> fmt::Result,
29     pub(crate) debug: unsafe fn(SharedPtr<'_, DynError>, &mut fmt::Formatter<'_>) -> fmt::Result,
30     pub(crate) source: unsafe fn(SharedPtr<'_, DynError>) -> Option<OomOrDynErrorRef<'_>>,
31     pub(crate) source_mut: unsafe fn(MutPtr<'_, DynError>) -> Option<OomOrDynErrorMut<'_>>,
32     pub(crate) is: unsafe fn(SharedPtr<'_, DynError>, TypeId) -> bool,
33     pub(crate) as_dyn_core_error:
34         unsafe fn(SharedPtr<'_, DynError>) -> &(dyn core::error::Error + Send + Sync + 'static),
35     pub(crate) into_boxed_dyn_core_error:
36         unsafe fn(
37             OwnedPtr<DynError>,
38         )
39             -> Result<Box<dyn core::error::Error + Send + Sync + 'static>, OutOfMemory>,
40     pub(crate) drop_and_deallocate: unsafe fn(OwnedPtr<DynError>),
41 
42     /// Additional safety requirement: the `NonNull<u8>` pointer must be valid
43     /// for writing a `T`.
44     ///
45     /// Upon successful return, a `T` will have been written to that memory
46     /// block.
47     pub(crate) downcast: unsafe fn(OwnedPtr<DynError>, TypeId, NonNull<u8>),
48 }
49 
50 impl Vtable {
51     /// Get the `Vtable` of the `E: ErrorExt` type parameter.
52     pub(crate) fn of<E>() -> &'static Self
53     where
54         E: ErrorExt,
55     {
56         &Vtable {
57             display: display::<E>,
58             debug: debug::<E>,
59             source: source::<E>,
60             source_mut: source_mut::<E>,
61             is: is::<E>,
62             as_dyn_core_error: as_dyn_core_error::<E>,
63             into_boxed_dyn_core_error: into_boxed_dyn_core_error::<E>,
64             drop_and_deallocate: drop_and_deallocate::<E>,
65             downcast: downcast::<E>,
66         }
67     }
68 }
69 
70 unsafe fn display<E>(error: SharedPtr<'_, DynError>, f: &mut fmt::Formatter<'_>) -> fmt::Result
71 where
72     E: ErrorExt,
73 {
74     let error = error.cast::<ConcreteError<E>>();
75     // Safety: implied by all vtable functions' safety contract.
76     let error = unsafe { error.as_ref() };
77     fmt::Display::fmt(error.error.ext_as_dyn_core_error(), f)
78 }
79 
80 unsafe fn debug<E>(error: SharedPtr<'_, DynError>, f: &mut fmt::Formatter<'_>) -> fmt::Result
81 where
82     E: ErrorExt,
83 {
84     let error = error.cast::<ConcreteError<E>>();
85     // Safety: implied by all vtable functions' safety contract.
86     let error = unsafe { error.as_ref() };
87     fmt::Debug::fmt(error.error.ext_as_dyn_core_error(), f)
88 }
89 
90 unsafe fn source<E>(error: SharedPtr<'_, DynError>) -> Option<OomOrDynErrorRef<'_>>
91 where
92     E: ErrorExt,
93 {
94     let error = error.cast::<ConcreteError<E>>();
95     // Safety: implied by all vtable functions' safety contract.
96     let error = unsafe { error.as_ref() };
97     error.error.ext_source()
98 }
99 
100 unsafe fn source_mut<E>(error: MutPtr<'_, DynError>) -> Option<OomOrDynErrorMut<'_>>
101 where
102     E: ErrorExt,
103 {
104     let mut error = error.cast::<ConcreteError<E>>();
105     // Safety: implied by all vtable functions' safety contract.
106     let error = unsafe { error.as_mut() };
107     error.error.ext_source_mut()
108 }
109 
110 unsafe fn is<E>(error: SharedPtr<'_, DynError>, type_id: TypeId) -> bool
111 where
112     E: ErrorExt,
113 {
114     let error = error.cast::<ConcreteError<E>>();
115     // Safety: implied by all vtable functions' safety contract.
116     let error = unsafe { error.as_ref() };
117     error.error.ext_is(type_id)
118 }
119 
120 unsafe fn as_dyn_core_error<E>(
121     error: SharedPtr<'_, DynError>,
122 ) -> &(dyn core::error::Error + Send + Sync + 'static)
123 where
124     E: ErrorExt,
125 {
126     let error = error.cast::<ConcreteError<E>>();
127     // Safety: implied by all vtable functions' safety contract.
128     let error = unsafe { error.as_ref() };
129     error.error.ext_as_dyn_core_error()
130 }
131 
132 unsafe fn into_boxed_dyn_core_error<E>(
133     error: OwnedPtr<DynError>,
134 ) -> Result<Box<dyn core::error::Error + Send + Sync + 'static>, OutOfMemory>
135 where
136     E: ErrorExt,
137 {
138     let error = error.cast::<ConcreteError<E>>();
139     // Safety: implied by all vtable functions' safety contract.
140     let error = unsafe { error.into_box() };
141     error.error.ext_into_boxed_dyn_core_error()
142 }
143 
144 unsafe fn drop_and_deallocate<E>(error: OwnedPtr<DynError>)
145 where
146     E: ErrorExt,
147 {
148     let error = error.cast::<ConcreteError<E>>();
149     // Safety: implied by all vtable functions' safety contract.
150     let _ = unsafe { error.into_box() };
151 }
152 
153 unsafe fn downcast<E>(error: OwnedPtr<DynError>, type_id: TypeId, ret_ptr: NonNull<u8>)
154 where
155     E: ErrorExt,
156 {
157     let error = error.cast::<ConcreteError<E>>();
158     // Safety: implied by all vtable functions' safety contract.
159     let mut error = unsafe { error.into_box() };
160 
161     if error.error.ext_is(type_id) {
162         // Safety: Implied by `downcast`'s additional safety safety requirement.
163         unsafe {
164             error.error.ext_move(ret_ptr);
165         }
166     } else {
167         let source = error
168             .error
169             .ext_take_source()
170             .expect("must have a source up the chain if `E` is not our target type");
171         // Safety: implied by downcast's additional safety requirement.
172         unsafe {
173             source.downcast(type_id, ret_ptr);
174         }
175     }
176 }
177