rootcause_internals/report/data.rs
1//! This module encapsulates the fields of the [`ReportData`]. Since this is the
2//! only place they are visible, this means that the type of the
3//! [`ReportVtable`] is guaranteed to always be in sync with the type of the
4//! actual context. This follows from the fact that they are in sync
5//! when created and that the API offers no way to change the [`ReportVtable`]
6//! or context type after creation.
7
8use alloc::vec::Vec;
9use core::ptr::NonNull;
10
11use crate::{
12 attachment::RawAttachment,
13 handlers::ContextHandler,
14 report::{
15 raw::{RawReport, RawReportMut, RawReportRef},
16 vtable::ReportVtable,
17 },
18 util::Erased,
19};
20
21/// Type-erased report data structure with vtable-based dispatch.
22///
23/// This struct uses `#[repr(C)]` to enable safe field access in type-erased
24/// contexts, allowing access to the vtable and other fields even when the
25/// concrete context type `C` is unknown.
26#[repr(C)]
27pub(super) struct ReportData<C: 'static> {
28 /// Reference to the vtable of this report
29 vtable: &'static ReportVtable,
30 /// The children of this report
31 children: Vec<RawReport>,
32 /// The attachments of this report
33 attachments: Vec<RawAttachment>,
34 /// The context data of this report
35 context: C,
36}
37
38impl<C: 'static> ReportData<C> {
39 /// Creates a new [`ReportData`] with the specified handler, context,
40 /// children and attachments.
41 ///
42 /// This method creates the vtable for type-erased dispatch and pairs it
43 /// with the report data.
44 #[inline]
45 pub(super) fn new<H: ContextHandler<C>>(
46 context: C,
47 children: Vec<RawReport>,
48 attachments: Vec<RawAttachment>,
49 ) -> Self {
50 Self {
51 vtable: ReportVtable::new::<C, H>(),
52 children,
53 attachments,
54 context,
55 }
56 }
57}
58
59impl RawReport {
60 /// # Safety
61 ///
62 /// - The caller must ensure that the type `C` matches the actual context
63 /// type stored in the [`ReportData`].
64 /// - The caller must ensure that this is the only existing reference
65 /// pointing to the inner [`ReportData`].
66 pub unsafe fn into_parts<C: 'static>(self) -> (C, Vec<RawReport>, Vec<RawAttachment>) {
67 let ptr: NonNull<ReportData<Erased>> = self.into_non_null();
68 let ptr: NonNull<ReportData<C>> = ptr.cast::<ReportData<C>>();
69 let ptr: *const ReportData<C> = ptr.as_ptr();
70
71 // SAFETY: The requirements to this
72 // - The given pointer must be a valid pointer to `T` that came from
73 // [`Arc::into_raw`].
74 // - After `from_raw`, the pointer must not be accessed.
75 //
76 // Both of these are guaranteed by our caller
77 let arc: triomphe::Arc<ReportData<C>> = unsafe { triomphe::Arc::from_raw(ptr) };
78
79 match triomphe::Arc::try_unique(arc) {
80 Ok(unique) => {
81 let data = triomphe::UniqueArc::into_inner(unique);
82 (data.context, data.children, data.attachments)
83 }
84 Err(_) => {
85 if cfg!(debug_assertions) {
86 unreachable!("Caller did not fulfill the guarantee that pointer is unique")
87 } else {
88 // SAFETY: This unsafe block *will* cause Undefined Behavior. However our caller
89 // guarantees that the pointer must be unique. This match
90 // statement can only be reached when our caller has broken
91 // that requirement, so it is valid to cause Undefined Behavior in this case.
92 unsafe { core::hint::unreachable_unchecked() }
93 }
94 }
95 }
96 }
97}
98
99impl<'a> RawReportRef<'a> {
100 /// Returns a reference to the [`ReportVtable`] of the [`ReportData`]
101 /// instance.
102 #[inline]
103 pub(super) fn vtable(self) -> &'static ReportVtable {
104 let ptr = self.as_ptr();
105 // SAFETY: We don't know the actual inner context type, but we do know
106 // that it points to an instance of `ReportData<C>` for some specific `C`.
107 // Since `ReportData<C>` is `#[repr(C)]`, that means we can access
108 // the fields before the actual context.
109 //
110 // We need to take care to avoid creating an actual reference to
111 // the `ReportData` itself though, as that would still be undefined behavior
112 // since we don't have the right type.
113 let vtable_ptr: *const &'static ReportVtable = unsafe { &raw const (*ptr).vtable };
114
115 // SAFETY: Deferencing the pointer and getting out the `&'static ReportVtable`
116 // is valid for the same reasons
117 unsafe { *vtable_ptr }
118 }
119
120 /// Returns the child reports of this report.
121 #[inline]
122 pub fn children(self) -> &'a Vec<RawReport> {
123 let ptr = self.as_ptr();
124 // SAFETY: We don't know the actual inner context type, but we do know
125 // that it points to an instance of `ReportData<C>` for some specific `C`.
126 // Since `ReportData<C>` is `#[repr(C)]`, that means we can access
127 // the fields before the actual context.
128 //
129 // We need to take care to avoid creating an actual reference to
130 // the `ReportData` itself though, as that would still be undefined behavior
131 // since we don't have the right type.
132 let children_ptr: *const Vec<RawReport> = unsafe { &raw const (*ptr).children };
133
134 // SAFETY: We turn the `*const` pointer into a `&'a` reference. This is valid
135 // because the existence of the `RawReportMut<'a>` already implied that
136 // we had readable access to the report for the 'a lifetime.
137 unsafe { &*children_ptr }
138 }
139
140 /// Returns the attachments of this report.
141 #[inline]
142 pub fn attachments(self) -> &'a Vec<RawAttachment> {
143 let ptr = self.as_ptr();
144 // SAFETY: We don't know the actual inner context type, but we do know
145 // that it points to an instance of `ReportData<C>` for some specific `C`.
146 // Since `ReportData<C>` is `#[repr(C)]`, that means we can access
147 // the fields before the actual context.
148 //
149 // We need to take care to avoid creating an actual reference to
150 // the `ReportData` itself though, as that would still be undefined behavior
151 // since we don't have the right type.
152 let attachments_ptr: *const Vec<RawAttachment> = unsafe { &raw const (*ptr).attachments };
153
154 // SAFETY: We turn the `*const` pointer into a `&'a` reference. This is valid
155 // because the existence of the `RawReportMut<'a>` already implied that
156 // we had readable access to the report for the 'a lifetime.
157 unsafe { &*attachments_ptr }
158 }
159
160 /// Accesses the inner context of the [`ReportData`] instance as a reference
161 /// to the specified type.
162 ///
163 /// # Safety
164 ///
165 /// The caller must ensure that the type `C` matches the actual context type
166 /// stored in the [`ReportData`].
167 #[inline]
168 pub unsafe fn context_downcast_unchecked<C: 'static>(self) -> &'a C {
169 // SAFETY: The inner function requires that `C` matches the type stored, but
170 // that is guaranteed by our caller.
171 let this = unsafe { self.cast_inner::<C>() };
172 &this.context
173 }
174}
175
176impl<'a> RawReportMut<'a> {
177 /// Gets a mutable reference to the child reports.
178 #[inline]
179 pub fn into_children_mut(self) -> &'a mut Vec<RawReport> {
180 let ptr = self.into_mut_ptr();
181
182 // SAFETY: We don't know the actual inner context type, but we do know
183 // that it points to an instance of `ReportData<C>` for some specific `C`.
184 // Since `ReportData<C>` is `#[repr(C)]`, that means we can access
185 // the fields before the actual context.
186 //
187 // We need to take care to avoid creating an actual reference to
188 // the `ReportData` itself though, as that would still be undefined behavior
189 // since we don't have the right type.
190 let children_ptr: *mut Vec<RawReport> = unsafe { &raw mut (*ptr).children };
191
192 // SAFETY: We turn the `*mut` pointer into a `&'a mut` reference. This is valid
193 // because the existence of the `RawReportMut<'a>` already implied that
194 // nobody else has mutable access to the report for the 'a lifetime.
195 unsafe { &mut *children_ptr }
196 }
197
198 /// Gets a mutable reference to the attachments.
199 #[inline]
200 pub fn into_attachments_mut(self) -> &'a mut Vec<RawAttachment> {
201 let ptr = self.into_mut_ptr();
202
203 // SAFETY: We don't know the actual inner context type, but we do know
204 // that it points to an instance of `ReportData<C>` for some specific `C`.
205 // Since `ReportData<C>` is `#[repr(C)]`, that means we can access
206 // the fields before the actual context.
207 //
208 // We need to take care to avoid creating an actual reference to
209 // the `ReportData` itself though, as that would still be undefined behavior
210 // since we don't have the right type.
211 let attachments_ptr: *mut Vec<RawAttachment> = unsafe { &raw mut (*ptr).attachments };
212
213 // SAFETY: We turn the `*mut` pointer into a `&'a mut` reference. This is valid
214 // because the existence of the `RawReportMut<'a>` already implied that
215 // nobody else has mutable access to the report for the 'a lifetime.
216 unsafe { &mut *attachments_ptr }
217 }
218
219 /// Accesses the inner context of the [`ReportData`] instance as a mutable
220 /// reference to the specified type.
221 ///
222 /// # Safety
223 ///
224 /// The caller must ensure that the type `C` matches the actual context type
225 /// stored in the [`ReportData`].
226 #[inline]
227 pub unsafe fn into_context_downcast_unchecked<C: 'static>(self) -> &'a mut C {
228 // SAFETY: The inner function requires that `C` matches the type stored, but
229 // that is guaranteed by our caller.
230 let this = unsafe { self.cast_inner::<C>() };
231 &mut this.context
232 }
233}
234
235#[cfg(test)]
236mod tests {
237 use super::*;
238
239 #[test]
240 fn test_report_data_field_offsets() {
241 // Test that fields are accessible in the expected order for type-erased access
242 use core::mem::{offset_of, size_of};
243
244 fn check<T>() {
245 // Verify field order: vtable, children, attachments, context
246 assert_eq!(offset_of!(ReportData<T>, vtable), 0);
247 assert_eq!(
248 offset_of!(ReportData<T>, children),
249 size_of::<&'static ReportVtable>()
250 );
251 assert_eq!(
252 offset_of!(ReportData<T>, attachments),
253 size_of::<&'static ReportVtable>() + size_of::<Vec<RawAttachment>>()
254 );
255 assert!(
256 offset_of!(ReportData<T>, context)
257 >= size_of::<&'static ReportVtable>()
258 + size_of::<Vec<RawAttachment>>()
259 + size_of::<Vec<RawReport>>()
260 );
261 }
262
263 #[repr(align(32))]
264 struct LargeAlignment {
265 _value: u8,
266 }
267
268 check::<u8>();
269 check::<i32>();
270 check::<[u64; 4]>();
271 check::<i32>();
272 check::<LargeAlignment>();
273 }
274}