mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-15 22:28:10 +08:00
Add the owned attribute crossing over the deep field glue
This commit is contained in:
@@ -79,6 +79,45 @@ impl<'e, A: Attribute> std::fmt::Debug for Attr<'e, A> {
|
||||
}
|
||||
}
|
||||
|
||||
/// An attribute write that outlives the evaluation producing it: an async
|
||||
/// source's slot persists across generations, so a reference value cannot
|
||||
/// cross as itself. The value crosses deep-copied through the field glue and
|
||||
/// parks into the serving arena at every lift, which is why the copy is paid
|
||||
/// once per invocation rather than once per evaluation.
|
||||
pub struct OwnedAttr<A: Attribute>(Box<dyn AnyAttributeValue>, PhantomData<fn() -> A>);
|
||||
|
||||
impl<A: Attribute> OwnedAttr<A> {
|
||||
/// Deep-copies `value` out of the evaluation that produced it.
|
||||
pub fn new(value: A::Value<'_>) -> Self {
|
||||
// SAFETY: the read addresses a live local of the marker's value type.
|
||||
let erased = unsafe { A::read_erased((&raw const value).cast()) };
|
||||
OwnedAttr(crate::record::deepen_field_value(erased), PhantomData)
|
||||
}
|
||||
|
||||
/// Parks the copy into `arena` for one evaluation; `None` reports arena
|
||||
/// exhaustion.
|
||||
pub fn park<'e>(&self, arena: &'e crate::arena::Arena) -> Option<A::Value<'e>> {
|
||||
let resident = crate::record::replay_field_value(&*self.0, arena)?;
|
||||
let mut value = A::default();
|
||||
// SAFETY: the slot is a live field of the marker's value type, and the
|
||||
// stored value is the copy `new` took at that same type.
|
||||
unsafe { write_stored::<A>(resident.as_deref().unwrap_or(&*self.0), (&raw mut value).cast(), arena) }?;
|
||||
Some(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: Attribute> Clone for OwnedAttr<A> {
|
||||
fn clone(&self) -> Self {
|
||||
OwnedAttr(self.0.clone(), PhantomData)
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: Attribute> std::fmt::Debug for OwnedAttr<A> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_tuple(A::NAME).field(&self.0.display_string()).finish()
|
||||
}
|
||||
}
|
||||
|
||||
/// A deletion of `A` in a node's return tuple: the name leaves the output
|
||||
/// layout, so downstream reads yield the declared default again. Functionally
|
||||
/// a write of the default; the value carries nothing.
|
||||
@@ -410,6 +449,27 @@ mod tests {
|
||||
assert_eq!(row.size, size_of::<&str>());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn an_owned_reference_crossing_parks_into_the_serving_arena() {
|
||||
let owned = OwnedAttr::<Name>::new("crossing");
|
||||
let arena = crate::arena::Arena::new(1024).unwrap();
|
||||
assert_eq!(owned.park(&arena).unwrap(), "crossing");
|
||||
assert_eq!(owned.clone().park(&arena).unwrap(), "crossing", "the crossing parks again on every evaluation");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn an_owned_plain_crossing_rides_its_bytes() {
|
||||
let arena = crate::arena::Arena::new(64).unwrap();
|
||||
assert_eq!(OwnedAttr::<Opacity>::new(0.25).park(&arena).unwrap(), 0.25);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn an_exhausted_arena_refuses_an_owned_reference_crossing() {
|
||||
let owned = OwnedAttr::<Name>::new("too long for this arena");
|
||||
let arena = crate::arena::Arena::new(8).unwrap();
|
||||
assert!(owned.park(&arena).is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn reregistration_at_the_same_type_is_idempotent() {
|
||||
register::<Opacity>();
|
||||
|
||||
@@ -1426,13 +1426,25 @@ fn deep_field_glue(type_id: std::any::TypeId) -> Option<DeepFieldGlue> {
|
||||
DEEP_FIELD_VALUES.lock().unwrap().get(&type_id).copied()
|
||||
}
|
||||
|
||||
fn deepen_field_value(value: Box<dyn crate::list::AnyAttributeValue>) -> Box<dyn crate::list::AnyAttributeValue> {
|
||||
/// The copy-out half over an erased field value: the owned form a value takes
|
||||
/// when it crosses out of the evaluation whose arena its content borrows. A
|
||||
/// value with no registered glue already owns everything and passes through.
|
||||
pub fn deepen_field_value(value: Box<dyn crate::list::AnyAttributeValue>) -> Box<dyn crate::list::AnyAttributeValue> {
|
||||
match deep_field_glue(value.as_any().type_id()) {
|
||||
Some(glue) => (glue.copy_out)(&*value).unwrap_or(value),
|
||||
None => value,
|
||||
}
|
||||
}
|
||||
|
||||
/// The replay half over an erased field value: `Some(None)` where the value
|
||||
/// already owns its content, `None` on arena exhaustion.
|
||||
pub fn replay_field_value(value: &dyn crate::list::AnyAttributeValue, arena: &crate::arena::Arena) -> Option<Option<Box<dyn crate::list::AnyAttributeValue>>> {
|
||||
match deep_field_glue(value.as_any().type_id()) {
|
||||
Some(glue) => (glue.replay)(value, arena),
|
||||
None => Some(None),
|
||||
}
|
||||
}
|
||||
|
||||
/// The element slot a record wire of `T` carries, its erased glue bound at
|
||||
/// the statically-known type.
|
||||
pub fn element_write<T: Clone + Send + Sync + dyn_any::StaticTypeSized>() -> ElementWrite
|
||||
@@ -1918,14 +1930,9 @@ unsafe fn promote_record(layout: &Layout, dst: *mut u8, promotion: &Promotion<'_
|
||||
}
|
||||
// SAFETY: the slot images a parked field of this descriptor.
|
||||
let value = deepen_field_value(unsafe { (field.read_erased)(slot.cast_const()) });
|
||||
match deep_field_glue(value.as_any().type_id()) {
|
||||
Some(glue) => match (glue.replay)(&*value, promotion.persistent)? {
|
||||
// SAFETY: the replay produced this field's own value type.
|
||||
Some(resident) => unsafe { repark(&*resident, slot, promotion.persistent) }?,
|
||||
None => unsafe { repark(&*value, slot, promotion.persistent) }?,
|
||||
},
|
||||
None => unsafe { repark(&*value, slot, promotion.persistent) }?,
|
||||
}
|
||||
let resident = replay_field_value(&*value, promotion.persistent)?;
|
||||
// SAFETY: the replay produced this field's own value type.
|
||||
unsafe { repark(resident.as_deref().unwrap_or(&*value), slot, promotion.persistent) }?;
|
||||
}
|
||||
Some(())
|
||||
}
|
||||
@@ -2203,13 +2210,8 @@ impl OwnedRecord {
|
||||
for (index, value) in &self.fields {
|
||||
let field = &layout.fields[*index];
|
||||
let repark = field.repark.expect("copied fields carry re-park glue");
|
||||
match deep_field_glue(value.as_any().type_id()) {
|
||||
Some(glue) => match (glue.replay)(&**value, arena)? {
|
||||
Some(resident) => unsafe { repark(&*resident, dst.add(field.offset), arena) }?,
|
||||
None => unsafe { repark(&**value, dst.add(field.offset), arena) }?,
|
||||
},
|
||||
None => unsafe { repark(&**value, dst.add(field.offset), arena) }?,
|
||||
}
|
||||
let resident = replay_field_value(&**value, arena)?;
|
||||
unsafe { repark(resident.as_deref().unwrap_or(&**value), dst.add(field.offset), arena) }?;
|
||||
}
|
||||
Some(())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user