rootcause_internals/report/
data.rs

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