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