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 vtable: &'static AttachmentVtable,
19 attachment: A,
20}
21
22impl<A: 'static> AttachmentData<A> {
23 /// Creates a new [`AttachmentData`] with the specified handler and attachment.
24 ///
25 /// This method creates the vtable for type-erased dispatch and pairs it with the attachment data.
26 pub(super) fn new<H: AttachmentHandler<A>>(attachment: A) -> Self {
27 Self {
28 vtable: AttachmentVtable::new::<A, H>(),
29 attachment,
30 }
31 }
32}
33
34impl<'a> RawAttachmentRef<'a> {
35 /// Returns a reference to the [`AttachmentVtable`] of the [`AttachmentData`] instance.
36 pub(super) fn vtable(self) -> &'static AttachmentVtable {
37 let ptr = self.as_ptr();
38 // SAFETY: We don't know the actual inner attachment type, but we do know
39 // that it points to an instance of `AttachmentData<A>` for some specific `A`.
40 // Since `AttachmentData<A>` is `#[repr(C)]`, that means we can access
41 // the fields before the actual attachment.
42 //
43 // We need to take care to avoid creating an actual reference to
44 // the `AttachmentData` itself though, as that would still be undefined behavior
45 // since we don't have the right type.
46 unsafe {
47 let vtable_ptr = &raw const (*ptr).vtable;
48 *vtable_ptr
49 }
50 }
51
52 /// Accesses the inner attachment of the [`AttachmentData`] instance as a reference to the specified type.
53 ///
54 /// # Safety
55 ///
56 /// The caller must ensure that the type `A` matches the actual attachment type stored in the [`AttachmentData`].
57 pub unsafe fn attachment_downcast_unchecked<A: 'static>(self) -> &'a A {
58 // SAFETY: The inner function requires that `A` matches the type stored, but that is guaranteed by our caller.
59 let this = unsafe { self.cast_inner::<A>() };
60 &this.attachment
61 }
62}
63
64#[cfg(test)]
65mod tests {
66 use super::*;
67
68 #[test]
69 fn test_attachment_field_offsets() {
70 use core::mem::{offset_of, size_of};
71
72 #[repr(align(32))]
73 struct LargeAlignment {
74 _value: u8,
75 }
76
77 assert_eq!(offset_of!(AttachmentData<u8>, vtable), 0);
78 assert_eq!(offset_of!(AttachmentData<u32>, vtable), 0);
79 assert_eq!(offset_of!(AttachmentData<[u64; 4]>, vtable), 0);
80 assert_eq!(offset_of!(AttachmentData<LargeAlignment>, vtable), 0);
81
82 assert!(
83 offset_of!(AttachmentData<u8>, attachment) >= size_of::<&'static AttachmentVtable>()
84 );
85 assert!(
86 offset_of!(AttachmentData<u32>, attachment) >= size_of::<&'static AttachmentVtable>()
87 );
88 assert!(
89 offset_of!(AttachmentData<[u64; 4]>, attachment)
90 >= size_of::<&'static AttachmentVtable>()
91 );
92 assert!(
93 offset_of!(AttachmentData<LargeAlignment>, attachment)
94 >= size_of::<&'static AttachmentVtable>()
95 );
96 }
97}