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}