use alloc::vec::Vec;
use core::ptr::NonNull;
use crate::{
attachment::RawAttachment,
handlers::ContextHandler,
report::{
raw::{RawReport, RawReportMut, RawReportRef},
vtable::ReportVtable,
},
util::Erased,
};
#[repr(C)]
pub(crate) struct ReportData<C: 'static> {
vtable: &'static ReportVtable,
children: Vec<RawReport>,
attachments: Vec<RawAttachment>,
context: C,
}
impl<C: 'static> ReportData<C> {
#[inline]
pub(super) fn new<H: ContextHandler<C>>(
context: C,
children: Vec<RawReport>,
attachments: Vec<RawAttachment>,
) -> Self {
Self {
vtable: ReportVtable::new::<C, H>(),
children,
attachments,
context,
}
}
}
impl RawReport {
pub unsafe fn into_parts<C: 'static>(self) -> (C, Vec<RawReport>, Vec<RawAttachment>) {
let ptr: NonNull<ReportData<Erased>> = self.into_non_null();
let ptr: NonNull<ReportData<C>> = ptr.cast::<ReportData<C>>();
let ptr: *const ReportData<C> = ptr.as_ptr();
let arc = unsafe { triomphe::Arc::<ReportData<C>>::from_raw(ptr) };
match triomphe::Arc::try_unique(arc) {
Ok(unique) => {
let data = triomphe::UniqueArc::into_inner(unique);
(data.context, data.children, data.attachments)
}
Err(_) => {
unreachable!("Caller did not fulfill the guarantee that pointer is unique")
}
}
}
}
impl<'a> RawReportRef<'a> {
#[inline]
pub(super) fn vtable(self) -> &'static ReportVtable {
let ptr = self.as_ptr();
let vtable_ptr: *const &'static ReportVtable = unsafe {
&raw const (*ptr).vtable
};
unsafe { *vtable_ptr }
}
#[inline]
pub fn children(self) -> &'a Vec<RawReport> {
let ptr: *const ReportData<Erased> = self.as_ptr();
let children_ptr: *const Vec<RawReport> = unsafe {
&raw const (*ptr).children
};
unsafe { &*children_ptr }
}
#[inline]
pub fn attachments(self) -> &'a Vec<RawAttachment> {
let ptr = self.as_ptr();
let attachments_ptr: *const Vec<RawAttachment> = unsafe {
&raw const (*ptr).attachments
};
unsafe { &*attachments_ptr }
}
#[inline]
pub unsafe fn context_downcast_unchecked<C: 'static>(self) -> &'a C {
let this = unsafe { self.cast_inner::<C>() };
&this.context
}
}
impl<'a> RawReportMut<'a> {
#[inline]
pub unsafe fn into_children_mut(self) -> &'a mut Vec<RawReport> {
let ptr = self.into_mut_ptr();
let children_ptr: *mut Vec<RawReport> = unsafe {
&raw mut (*ptr).children
};
unsafe { &mut *children_ptr }
}
#[inline]
pub unsafe fn into_attachments_mut(self) -> &'a mut Vec<RawAttachment> {
let ptr = self.into_mut_ptr();
let attachments_ptr: *mut Vec<RawAttachment> = unsafe {
&raw mut (*ptr).attachments
};
unsafe { &mut *attachments_ptr }
}
#[inline]
pub unsafe fn into_context_downcast_unchecked<C: 'static>(self) -> &'a mut C {
let this = unsafe { self.cast_inner::<C>() };
&mut this.context
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_report_data_field_offsets() {
use core::mem::{offset_of, size_of};
fn check<T: 'static>() {
assert_eq!(offset_of!(ReportData<T>, vtable), 0);
assert_eq!(
offset_of!(ReportData<T>, children),
size_of::<&'static ReportVtable>()
);
assert_eq!(
offset_of!(ReportData<T>, attachments),
size_of::<&'static ReportVtable>() + size_of::<Vec<RawAttachment>>()
);
assert!(
offset_of!(ReportData<T>, context)
>= size_of::<&'static ReportVtable>()
+ size_of::<Vec<RawAttachment>>()
+ size_of::<Vec<RawReport>>()
);
}
#[repr(align(32))]
struct LargeAlignment {
_value: u8,
}
check::<u8>();
check::<i32>();
check::<[u64; 4]>();
check::<i32>();
check::<LargeAlignment>();
}
}