rootcause_internals/attachment/
data.rs

1//! This module encapsulates the fields of the [`AttachmentData`]. Since this is the only place
2//! they are visible, this means that the type of the [`AttachmentVtable`] is guaranteed to always be in sync
3//! with the type of the actual attachment. This follows from the fact that they are in sync
4//! when created and that the API offers no way to change the [`AttachmentVtable`] or attachment type after
5//! creation.
6
7use crate::{
8    attachment::{raw::RawAttachmentRef, vtable::AttachmentVtable},
9    handlers::AttachmentHandler,
10};
11
12/// Type-erased attachment data structure with vtable-based dispatch.
13///
14/// This struct uses `#[repr(C)]` to enable safe field access in type-erased contexts,
15/// allowing access to the vtable field even when the concrete attachment type `A` is unknown.
16#[repr(C)]
17pub(super) struct AttachmentData<A: 'static> {
18    /// The Vtable of this attachment
19    vtable: &'static AttachmentVtable,
20    /// The actual attachment data
21    attachment: A,
22}
23
24impl<A: 'static> AttachmentData<A> {
25    /// Creates a new [`AttachmentData`] with the specified handler and attachment.
26    ///
27    /// This method creates the vtable for type-erased dispatch and pairs it with the attachment data.
28    pub(super) fn new<H: AttachmentHandler<A>>(attachment: A) -> Self {
29        Self {
30            vtable: AttachmentVtable::new::<A, H>(),
31            attachment,
32        }
33    }
34}
35
36impl<'a> RawAttachmentRef<'a> {
37    /// Returns a reference to the [`AttachmentVtable`] of the [`AttachmentData`] instance.
38    pub(super) fn vtable(self) -> &'static AttachmentVtable {
39        let ptr = self.as_ptr();
40        // SAFETY: We don't know the actual inner attachment type, but we do know
41        // that it points to an instance of `AttachmentData<A>` for some specific `A`.
42        // Since `AttachmentData<A>` is `#[repr(C)]`, that means that it's
43        // safe to create pointers to the fields before the actual attachment.
44        //
45        // We need to take care to avoid creating an actual reference to
46        // the `AttachmentData` itself though, as that would still be undefined behavior
47        // since we don't have the right type.
48        let vtable_ptr: *const &'static AttachmentVtable = unsafe { &raw const (*ptr).vtable };
49
50        // SAFETY: Deferencing the pointer and getting out the `&'static AttachmentVtable` is valid
51        // for the same reasons
52        unsafe { *vtable_ptr }
53    }
54
55    /// Accesses the inner attachment of the [`AttachmentData`] instance as a reference to the specified type.
56    ///
57    /// # Safety
58    ///
59    /// The caller must ensure that the type `A` matches the actual attachment type stored in the [`AttachmentData`].
60    pub unsafe fn attachment_downcast_unchecked<A: 'static>(self) -> &'a A {
61        // SAFETY: The inner function requires that `A` matches the type stored, but that is guaranteed by our caller.
62        let this = unsafe { self.cast_inner::<A>() };
63        &this.attachment
64    }
65}
66
67#[cfg(test)]
68mod tests {
69    use super::*;
70
71    #[test]
72    fn test_attachment_field_offsets() {
73        use core::mem::{offset_of, size_of};
74
75        #[repr(align(32))]
76        struct LargeAlignment {
77            _value: u8,
78        }
79
80        assert_eq!(offset_of!(AttachmentData<u8>, vtable), 0);
81        assert_eq!(offset_of!(AttachmentData<u32>, vtable), 0);
82        assert_eq!(offset_of!(AttachmentData<[u64; 4]>, vtable), 0);
83        assert_eq!(offset_of!(AttachmentData<LargeAlignment>, vtable), 0);
84
85        assert!(
86            offset_of!(AttachmentData<u8>, attachment) >= size_of::<&'static AttachmentVtable>()
87        );
88        assert!(
89            offset_of!(AttachmentData<u32>, attachment) >= size_of::<&'static AttachmentVtable>()
90        );
91        assert!(
92            offset_of!(AttachmentData<[u64; 4]>, attachment)
93                >= size_of::<&'static AttachmentVtable>()
94        );
95        assert!(
96            offset_of!(AttachmentData<LargeAlignment>, attachment)
97                >= size_of::<&'static AttachmentVtable>()
98        );
99    }
100}