Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 26 additions & 1 deletion rootcause-internals/src/attachment/data.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,10 @@
//! reference to the full struct.

use crate::{
attachment::{raw::RawAttachmentRef, vtable::AttachmentVtable},
attachment::{
raw::{RawAttachmentMut, RawAttachmentRef},
vtable::AttachmentVtable,
},
handlers::AttachmentHandler,
};

Expand Down Expand Up @@ -112,6 +115,28 @@ impl<'a> RawAttachmentRef<'a> {
}
}

impl<'a> RawAttachmentMut<'a> {
/// Accesses the inner attachment of the [`AttachmentData`] instance as a
/// reference to the specified type.
///
/// # Safety
///
/// The caller must ensure:
///
/// 1. The type `A` matches the actual attachment type stored in the
/// [`AttachmentData`].
#[inline]
pub unsafe fn into_attachment_downcast_unchecked<A: 'static>(self) -> &'a mut A {
// SAFETY:
// 1. Guaranteed by the caller
let this = unsafe {
// @add-unsafe-context: AttachmentData
self.cast_inner::<A>()
};
&mut this.attachment
}
}

#[cfg(test)]
mod tests {
use super::*;
Expand Down
2 changes: 1 addition & 1 deletion rootcause-internals/src/attachment/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,4 @@ pub(crate) mod data;
pub(crate) mod raw;
pub(crate) mod vtable;

pub use self::raw::{RawAttachment, RawAttachmentRef};
pub use self::raw::{RawAttachment, RawAttachmentMut, RawAttachmentRef};
125 changes: 123 additions & 2 deletions rootcause-internals/src/attachment/raw.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
//! attachments.

use alloc::boxed::Box;
use core::{any::TypeId, ptr::NonNull};
use core::{any::TypeId, marker::PhantomData, ptr::NonNull};

use crate::{
attachment::data::AttachmentData,
Expand Down Expand Up @@ -89,6 +89,17 @@ impl RawAttachment {
#[inline]
pub fn as_ref(&self) -> RawAttachmentRef<'_> {
RawAttachmentRef {
// Safety???
ptr: self.ptr,
_marker: core::marker::PhantomData,
}
}

/// Returns a mutable reference to the [`AttachmentData`] instance.
#[inline]
pub fn as_mut(&mut self) -> RawAttachmentMut<'_> {
RawAttachmentMut {
// Safety???
ptr: self.ptr,
_marker: core::marker::PhantomData,
}
Expand Down Expand Up @@ -190,7 +201,7 @@ impl<'a> RawAttachmentRef<'a> {
self.vtable().type_name()
}

/// Returns the [`TypeId`] of the attachment.
/// Returns the [`TypeId`] of the attachment handler.
#[inline]
pub fn attachment_handler_type_id(self) -> TypeId {
self.vtable().handler_type_id()
Expand Down Expand Up @@ -254,6 +265,94 @@ impl<'a> RawAttachmentRef<'a> {
}
}

/// A mutable lifetime-bound pointer to an [`AttachmentData`] that is guaranteed to
/// be the sole mutable(?) pointer to an initialized instance of an [`AttachmentData<A>`] for some
/// specific `A`, though we do not know which actual `A` it is.
///
/// We cannot use a [`&'a mut AttachmentData<A>`] directly, because that would
/// require us to know the actual type of the attachment, which we do not.
///
/// [`&'a mut AttachmentData<A>`]: AttachmentData
#[repr(transparent)]
pub struct RawAttachmentMut<'a> {
/// Pointer to the inner attachment data
///
/// # Safety
///
/// The following safety invariants are guaranteed to be upheld as long as
/// this struct exists:
///
/// 1. The pointer must have been created from a `Box<AttachmentData<A>>`
/// for some `A` using `Box::into_raw`.
/// 2. The pointer will point to the same `AttachmentData<A>` for the entire
/// lifetime of this object.
/// 3. This pointer represents exclusive mutable access to the `AttachmentData`.
ptr: NonNull<AttachmentData<Erased>>,

/// Marker to tell the compiler that we should
/// behave the same as a `&'a mut AttachmentData<Erased>`
_marker: core::marker::PhantomData<&'a mut AttachmentData<Erased>>,
}

impl<'a> RawAttachmentMut<'a> {
/// Casts the [`RawAttachmentMut`] to an [`AttachmentData<A>`] mutable reference.
///
/// # Safety
///
/// The caller must ensure:
///
/// 1. The type `A` matches the actual attachment type stored in the
/// [`AttachmentData`].
#[inline]
pub(super) unsafe fn cast_inner<A>(self) -> &'a mut AttachmentData<A> {
// Debug assertion to catch type mismatches in case of bugs
debug_assert_eq!(self.as_ref().vtable().type_id(), TypeId::of::<A>());

let mut this = self.ptr.cast::<AttachmentData<A>>();
// SAFETY: Converting the NonNull pointer to a mutable reference is sound because:
// - The pointer is non-null, properly aligned, and dereferenceable (guaranteed
// by RawAttachmentMut's type invariants)
// - The pointee is properly initialized (RawAttachmentMut's doc comment
// guarantees it is the exclusive pointer to an initialized AttachmentData<A> for some A)
// - The type `A` matches the actual attachment type (guaranteed by caller)
// - Shared access is NOT allowed
// - The reference lifetime 'a is valid (tied to RawAttachmentMut<'a>'s
// lifetime)
unsafe { this.as_mut() }
}

/// Reborrows the mutable reference to the [`AttachmentData`] with a shorter
/// lifetime.
#[inline]
pub fn reborrow<'b>(&'b mut self) -> RawAttachmentMut<'b> {
RawAttachmentMut {
ptr: self.ptr,
_marker: core::marker::PhantomData,
}
}

/// Returns a reference to the [`AttachmentData`] instance.
#[inline]
pub fn as_ref<'b: 'a>(&'b self) -> RawAttachmentRef<'b> {
RawAttachmentRef {
// Safety???
ptr: self.ptr,
_marker: PhantomData,
}
}

/// Consumes the mutable reference and returns an immutable one with the
/// same lifetime.
#[inline]
pub fn into_ref(self) -> RawAttachmentRef<'a> {
RawAttachmentRef {
// Safety???
ptr: self.ptr,
_marker: PhantomData,
}
}
}

#[cfg(test)]
mod tests {
use alloc::string::String;
Expand Down Expand Up @@ -326,6 +425,27 @@ mod tests {
core::mem::size_of::<Option<Option<RawAttachmentRef<'_>>>>(),
core::mem::size_of::<Option<usize>>()
);

assert_eq!(
core::mem::size_of::<RawAttachmentMut<'_>>(),
core::mem::size_of::<usize>()
);
assert_eq!(
core::mem::size_of::<Option<RawAttachmentMut<'_>>>(),
core::mem::size_of::<usize>()
);
assert_eq!(
core::mem::size_of::<Result<(), RawAttachmentMut<'_>>>(),
core::mem::size_of::<usize>()
);
assert_eq!(
core::mem::size_of::<Result<String, RawAttachmentMut<'_>>>(),
core::mem::size_of::<String>()
);
assert_eq!(
core::mem::size_of::<Option<Option<RawAttachmentMut<'_>>>>(),
core::mem::size_of::<Option<usize>>()
);
}

#[test]
Expand Down Expand Up @@ -419,5 +539,6 @@ mod tests {
fn test_send_sync() {
static_assertions::assert_not_impl_any!(RawAttachment: Send, Sync);
static_assertions::assert_not_impl_any!(RawAttachmentRef<'_>: Send, Sync);
static_assertions::assert_not_impl_any!(RawAttachmentMut<'_>: Send, Sync);
}
}
4 changes: 2 additions & 2 deletions rootcause-internals/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
//!
//! - **[`attachment`]**: Type-erased attachment storage
//! - [`RawAttachment`]: Owned attachment with [`Box`]-based allocation
//! - [`RawAttachmentRef`]: Borrowed reference to an attachment
//! - [`RawAttachmentRef`]/[`RawAttachmentMut`]: Borrowed reference to an attachment (shared/mutable)
//! - [`AttachmentData`]: `#[repr(C)]` wrapper enabling field access on erased
//! types
//! - [`AttachmentVtable`]: Function pointers for type-erased dispatch
Expand Down Expand Up @@ -86,5 +86,5 @@ pub mod handlers;
mod report;
mod util;

pub use attachment::{RawAttachment, RawAttachmentRef};
pub use attachment::{RawAttachment, RawAttachmentMut, RawAttachmentRef};
pub use report::{RawReport, RawReportMut, RawReportRef};
4 changes: 4 additions & 0 deletions rootcause-internals/src/report/raw.rs
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,7 @@ impl RawReport {
#[inline]
pub unsafe fn as_mut(&mut self) -> RawReportMut<'_> {
RawReportMut {
// Safety???
ptr: self.ptr,
_marker: core::marker::PhantomData,
}
Expand Down Expand Up @@ -412,6 +413,7 @@ impl<'a> RawReportMut<'a> {
#[inline]
pub fn reborrow<'b>(&'b mut self) -> RawReportMut<'b> {
RawReportMut {
// Safety???
ptr: self.ptr,
_marker: core::marker::PhantomData,
}
Expand All @@ -421,6 +423,7 @@ impl<'a> RawReportMut<'a> {
#[inline]
pub fn as_ref(&self) -> RawReportRef<'_> {
RawReportRef {
// Safety???
ptr: self.ptr,
_marker: core::marker::PhantomData,
}
Expand All @@ -431,6 +434,7 @@ impl<'a> RawReportMut<'a> {
#[inline]
pub fn into_ref(self) -> RawReportRef<'a> {
RawReportRef {
// Safety???
ptr: self.ptr,
_marker: core::marker::PhantomData,
}
Expand Down
10 changes: 2 additions & 8 deletions src/report/ref_.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,7 @@ use rootcause_internals::handlers::{ContextFormattingStyle, FormattingFunction};
use crate::{
Report, ReportIter,
markers::{Cloneable, Dynamic, Local, Mutable, SendSync, Uncloneable},
preformatted::{self, PreformattedAttachment, PreformattedContext},
report_attachment::ReportAttachment,
preformatted::{self, PreformattedContext},
report_attachments::ReportAttachments,
report_collection::ReportCollection,
util::format_helper,
Expand Down Expand Up @@ -588,12 +587,7 @@ impl<'a, C: ?Sized, O, T> ReportRef<'a, C, O, T> {
.collect(),
self.attachments()
.iter()
.map(|attachment| {
ReportAttachment::new_custom::<preformatted::PreformattedHandler>(
PreformattedAttachment::new_from_attachment(attachment),
)
.into_dynamic()
})
.map(|attachment| attachment.preformat().into_dynamic())
.collect(),
)
}
Expand Down
3 changes: 2 additions & 1 deletion src/report_attachment/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,8 @@
//! [`Display`]: crate::handlers::Display
//! [`Debug`]: crate::handlers::Debug

mod mut_;
mod owned;
mod ref_;

pub use self::{owned::ReportAttachment, ref_::ReportAttachmentRef};
pub use self::{mut_::ReportAttachmentMut, owned::ReportAttachment, ref_::ReportAttachmentRef};
Loading
Loading