rootcause_internals/attachment/
data.rs

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