mirror of
https://github.com/typst/typst
synced 2025-08-19 09:28:33 +08:00
Compare commits
2 Commits
1fd9a10f35
...
19559466e5
Author | SHA1 | Date | |
---|---|---|---|
|
19559466e5 | ||
|
d12cad6e46 |
@ -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
|
||||
/// casted to a pointer to its first field.
|
||||
/// cast 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
|
||||
/// pointer to `Inner<E>` and pointers to `Header`. See the documentation
|
||||
/// pointers 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 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.
|
||||
/// 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.
|
||||
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 layouted output.
|
||||
/// The element's location which identifies it in the laid-out 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 {
|
||||
/// Create raw content wrapped an element, with all metadata set to its
|
||||
/// defaults (including a detached span).
|
||||
/// Creates raw content wrapping an element, with all metadata set to
|
||||
/// default (including a detached span).
|
||||
pub(super) fn new<E: NativeElement>(data: E) -> Self {
|
||||
Self::create(
|
||||
data,
|
||||
@ -103,7 +103,7 @@ impl RawContent {
|
||||
)
|
||||
}
|
||||
|
||||
/// Creates raw content and allocates.
|
||||
/// Creates and allocates raw content.
|
||||
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 elements
|
||||
/// Casts into a trait object for a given trait if the packed element
|
||||
/// 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 a repr(transparent) `Content`, we can also
|
||||
// use a `*const Content` pointer.
|
||||
// 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.
|
||||
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 elements
|
||||
/// implements said trait.
|
||||
/// Casts into a mutable trait object for a given trait if the packed
|
||||
/// element 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 a repr(transparent) `Content`, we can also
|
||||
// use a `*const Content` pointer.
|
||||
// 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.
|
||||
//
|
||||
// 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()) })
|
||||
|
@ -17,27 +17,19 @@
|
||||
//! 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`
|
||||
//! function as it needs to return new, fully populated raw content). Which one
|
||||
//! it is, is documented for each.
|
||||
//! 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
|
||||
|
||||
@ -97,7 +89,7 @@ pub struct ContentVtable<T: 'static = RawContent> {
|
||||
|
||||
/// Subvtables for all fields of the element.
|
||||
pub(super) fields: &'static [FieldVtable<T>],
|
||||
/// Determine's the ID for a field name. This is a separate function instead
|
||||
/// Determines 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>,
|
||||
@ -226,7 +218,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.
|
||||
@ -265,13 +257,13 @@ impl<T> ContentHandle<T> {
|
||||
}
|
||||
|
||||
impl ContentHandle<&RawContent> {
|
||||
/// See [`ContentVtable::has`].
|
||||
/// See [`ContentVtable::debug`].
|
||||
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::has`].
|
||||
/// See [`ContentVtable::repr`].
|
||||
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)) }
|
||||
@ -330,8 +322,8 @@ pub struct FieldVtable<T: 'static = RawContent> {
|
||||
pub(super) synthesized: bool,
|
||||
/// Reflects what types the field's parameter accepts.
|
||||
pub(super) input: fn() -> CastInfo,
|
||||
/// The default value of the field, if any. This would e.g. be `None` for
|
||||
/// a required parameter.
|
||||
/// Produces 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
|
||||
@ -341,7 +333,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, they style chain, or a mix (if it's a
|
||||
/// element, the 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.
|
||||
|
Loading…
x
Reference in New Issue
Block a user