Compare commits

..

2 Commits

Author SHA1 Message Date
Laurenz
1fd9a10f35 Miri test 2025-07-07 13:55:52 +02:00
Laurenz
f2971ceffc New Content implementation 2025-07-07 13:55:52 +02:00
2 changed files with 48 additions and 40 deletions

View File

@ -47,11 +47,11 @@ pub struct RawContent {
/// The allocated part of an element's representation.
///
/// This is `repr(C)` to ensure that a pointer to the whole structure may be
/// cast to a pointer to its first field.
/// casted to a pointer to its first field.
#[repr(C)]
struct Inner<E> {
/// It is crucial that this is the first field because we cast between
/// pointers to `Inner<E>` and pointers to `Header`. See the documentation
/// pointer to `Inner<E>` and pointers to `Header`. See the documentation
/// of `RawContent::ptr` for more details.
header: Header,
/// The element struct. E.g. `E = HeadingElem`.
@ -60,12 +60,12 @@ struct Inner<E> {
/// The header that is shared by all elements.
struct Header {
/// The element's reference count. This works just like for `Arc`.
/// Unfortunately, we have to reimplement reference counting because we
/// have a custom fat pointer and `Arc` wouldn't know how to drop its
/// contents. Something with `ManuallyDrop<Arc<_>>` might also work, but at
/// that point we're not gaining much and with the way it's implemented now
/// we can also skip the unnecessary weak reference count.
/// The element's reference count. This work just like for `Arc`.
/// Unfortunately, we implement a custom fat pointer and `Arc` wouldn't know
/// how to drop its contents. Something with `ManuallyDrop<Arc<_>>` might
/// also work, but at that point we're not gaining much and with the way
/// it's implemented now we can also skip the unnecessary weak reference
/// count.
refs: AtomicUsize,
/// Metadata for the element.
meta: Meta,
@ -78,7 +78,7 @@ struct Header {
pub(super) struct Meta {
/// An optional label attached to the element.
pub label: Option<Label>,
/// The element's location which identifies it in the laid-out output.
/// The element's location which identifies it in the layouted output.
pub location: Option<Location>,
/// Manages the element during realization.
/// - If bit 0 is set, the element is prepared.
@ -88,8 +88,8 @@ pub(super) struct Meta {
}
impl RawContent {
/// Creates raw content wrapping an element, with all metadata set to
/// default (including a detached span).
/// Create raw content wrapped an element, with all metadata set to its
/// defaults (including a detached span).
pub(super) fn new<E: NativeElement>(data: E) -> Self {
Self::create(
data,
@ -103,7 +103,7 @@ impl RawContent {
)
}
/// Creates and allocates raw content.
/// Creates raw content and allocates.
fn create<E: NativeElement>(data: E, meta: Meta, hash: HashLock, span: Span) -> Self {
let raw = Box::into_raw(Box::<Inner<E>>::new(Inner {
header: Header { refs: AtomicUsize::new(1), meta, hash },
@ -177,7 +177,7 @@ impl RawContent {
debug_assert!(self.is::<E>());
// Safety:
// - The caller guarantees that the content is of type `E`.
// - The caller guarantees that the content is of type `E`
// - `self.ptr` is a valid pointer to an `Inner<E>` (see
// `RawContent::ptr`).
unsafe { &self.ptr.cast::<Inner<E>>().as_ref().data }
@ -199,7 +199,7 @@ impl RawContent {
self.header_mut().hash.reset();
// Safety:
// - The caller guarantees that the content is of type `E`.
// - The caller guarantees that the content is of type `E`
// - `self.ptr` is a valid pointer to an `Inner<E>` (see
// `RawContent::ptr`).
// - We have unique access to the backing allocation (due to header_mut).
@ -245,36 +245,36 @@ impl RawContent {
&mut self.header_mut().meta
}
/// Casts into a trait object for a given trait if the packed element
/// Casts into a trait object for a given trait if the packed elements
/// implements said trait.
pub(super) fn with<C>(&self) -> Option<&C>
where
C: ?Sized + 'static,
{
// Safety: The vtable comes from the `Capable` implementation which
// guarantees to return a matching vtable for `Packed<T>` and `C`. Since
// any `Packed<T>` is repr(transparent) with `Content` and `RawContent`,
// we can also use a `*const RawContent` pointer.
// guarantees to return a matching vtable for `Packed<T>` and `C`.
// Since any `Packed<T>` is a repr(transparent) `Content`, we can also
// use a `*const Content` pointer.
let vtable = (self.elem.vtable().capability)(TypeId::of::<C>())?;
let data = self as *const Self as *const ();
Some(unsafe { &*fat::from_raw_parts(data, vtable.as_ptr()) })
}
/// Casts into a mutable trait object for a given trait if the packed
/// element implements said trait.
/// Casts into a mutable trait object for a given trait if the packed elements
/// implements said trait.
pub(super) fn with_mut<C>(&mut self) -> Option<&mut C>
where
C: ?Sized + 'static,
{
// Safety: The vtable comes from the `Capable` implementation which
// guarantees to return a matching vtable for `Packed<T>` and `C`. Since
// any `Packed<T>` is repr(transparent) with `Content` and `RawContent`,
// we can also use a `*const Content` pointer.
// guarantees to return a matching vtable for `Packed<T>` and `C`.
// Since any `Packed<T>` is a repr(transparent) `Content`, we can also
// use a `*const Content` pointer.
//
// The resulting trait object contains an `&mut Packed<T>`. We do _not_
// need to ensure that we hold the only reference to the `Arc` here
// because `Packed<T>`'s DerefMut impl will take care of that if mutable
// access is required.
// because `Packed<T>`'s DerefMut impl will take care of that if
// mutable access is required.
let vtable = (self.elem.vtable().capability)(TypeId::of::<C>())?;
let data = self as *mut Self as *mut ();
Some(unsafe { &mut *fat::from_raw_parts_mut(data, vtable.as_ptr()) })

View File

@ -17,19 +17,27 @@
//! Note that all vtable methods receive elements of type `Packed<E>`, but some
//! only perform actions on the `E` itself, with the shared part kept outside of
//! the vtable (e.g. `hash`), while some perform the full action (e.g. `clone`
//! as it needs to return new, fully populated raw content). Which one it is, is
//! documented for each.
//! function as it needs to return new, fully populated raw content). Which one
//! it is, is documented for each.
//!
//! # Safety
//! All function pointers that operate on a specific element type are marked as
//! unsafe. In combination with `repr(C)`, this grants us the ability to safely
//! transmute a `ContentVtable<Packed<E>>` into a `ContentVtable<RawContent>`
//! (or just short `ContentVtable`). Callers of functions marked as unsafe have
//! to guarantee that the `ContentVtable` was transmuted from the same `E` as
//! the RawContent was constructed from.
//!
//! This module contains a lot of `unsafe` keywords, but almost all of it is the
//! same and quite straightfoward. All function pointers that operate on a
//! specific element type are marked as unsafe. In combination with `repr(C)`,
//! this grants us the ability to safely transmute a `ContentVtable<Packed<E>>`
//! into a `ContentVtable<RawContent>` (or just short `ContentVtable`). Callers
//! of functions marked as unsafe have to guarantee that the `ContentVtable` was
//! transmuted from the same `E` as the RawContent was constructed from. The
//! `Handle` struct provides a safe access layer, moving the guarantee that the
//! vtable is matching into a single spot.
//! into a `ContentVtable<RawContent>` (or just short `ContentVtable`).
//!
//! Callers of functions marked as unsafe have to guarantee that the
//! `ContentVtable` was transmuted from the same `E` as the RawContent was
//! constructed from. The `Handle` struct provides a safe access layer, moving
//! the guarantee that the vtable is matching into a single spot.
//!
//! [vtable]: https://en.wikipedia.org/wiki/Virtual_method_table
@ -89,7 +97,7 @@ pub struct ContentVtable<T: 'static = RawContent> {
/// Subvtables for all fields of the element.
pub(super) fields: &'static [FieldVtable<T>],
/// Determines the ID for a field name. This is a separate function instead
/// Determine's the ID for a field name. This is a separate function instead
/// of searching through `fields` so that Rust can generate optimized code
/// for the string matching.
pub(super) field_id: fn(name: &str) -> Option<u8>,
@ -98,7 +106,7 @@ pub struct ContentVtable<T: 'static = RawContent> {
pub(super) construct: fn(&mut Engine, &mut Args) -> SourceResult<Content>,
/// The set rule of the element.
pub(super) set: fn(&mut Engine, &mut Args) -> SourceResult<Styles>,
/// The element's local name in a specific lang-region pairing.
/// The element's local name in a specific lang-region pairing.
pub(super) local_name: Option<fn(Lang, Option<Region>) -> &'static str>,
/// Produces the associated [`Scope`] of the element.
pub(super) scope: fn() -> Scope,
@ -218,7 +226,7 @@ impl<E: NativeElement> ContentVtable<Packed<E>> {
/// Type-erases the data.
pub const fn erase(self) -> ContentVtable {
// Safety:
// - `ContentVtable` is `repr(C)`.
// - `ContentVtable` is `repr(C)`
// - `ContentVtable` does not hold any `E`-specific data except for
// function pointers.
// - All functions pointers have the same memory layout.
@ -257,13 +265,13 @@ impl<T> ContentHandle<T> {
}
impl ContentHandle<&RawContent> {
/// See [`ContentVtable::debug`].
/// See [`ContentVtable::has`].
pub fn debug(&self, f: &mut Formatter) -> fmt::Result {
// Safety: `Handle` has the invariant that the vtable is matching.
unsafe { (self.1.debug)(self.0, f) }
}
/// See [`ContentVtable::repr`].
/// See [`ContentVtable::has`].
pub fn repr(&self) -> Option<EcoString> {
// Safety: `Handle` has the invariant that the vtable is matching.
unsafe { self.1.repr.map(|f| f(self.0)) }
@ -322,8 +330,8 @@ pub struct FieldVtable<T: 'static = RawContent> {
pub(super) synthesized: bool,
/// Reflects what types the field's parameter accepts.
pub(super) input: fn() -> CastInfo,
/// Produces the default value of the field, if any. This would e.g. be
/// `None` for a required parameter.
/// The default value of the field, if any. This would e.g. be `None` for
/// a required parameter.
pub(super) default: Option<fn() -> Value>,
/// Whether the field is set on the given element. Always true for required
@ -333,7 +341,7 @@ pub struct FieldVtable<T: 'static = RawContent> {
/// value](crate::foundations::IntoValue).
pub(super) get: unsafe fn(elem: &T) -> Option<Value>,
/// Retrieves the field given styles. The resulting value may come from the
/// element, the style chain, or a mix (if it's a
/// element, they style chain, or a mix (if it's a
/// [`Fold`](crate::foundations::Fold) field).
pub(super) get_with_styles: unsafe fn(elem: &T, StyleChain) -> Option<Value>,
/// Retrieves the field just from the styles.