mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-15 14:18:04 +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
|
/// 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
|
/// layout, so downstream reads yield the declared default again. Functionally
|
||||||
/// a write of the default; the value carries nothing.
|
/// a write of the default; the value carries nothing.
|
||||||
@@ -410,6 +449,27 @@ mod tests {
|
|||||||
assert_eq!(row.size, size_of::<&str>());
|
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]
|
#[test]
|
||||||
fn reregistration_at_the_same_type_is_idempotent() {
|
fn reregistration_at_the_same_type_is_idempotent() {
|
||||||
register::<Opacity>();
|
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()
|
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()) {
|
match deep_field_glue(value.as_any().type_id()) {
|
||||||
Some(glue) => (glue.copy_out)(&*value).unwrap_or(value),
|
Some(glue) => (glue.copy_out)(&*value).unwrap_or(value),
|
||||||
None => 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 element slot a record wire of `T` carries, its erased glue bound at
|
||||||
/// the statically-known type.
|
/// the statically-known type.
|
||||||
pub fn element_write<T: Clone + Send + Sync + dyn_any::StaticTypeSized>() -> ElementWrite
|
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.
|
// SAFETY: the slot images a parked field of this descriptor.
|
||||||
let value = deepen_field_value(unsafe { (field.read_erased)(slot.cast_const()) });
|
let value = deepen_field_value(unsafe { (field.read_erased)(slot.cast_const()) });
|
||||||
match deep_field_glue(value.as_any().type_id()) {
|
let resident = replay_field_value(&*value, promotion.persistent)?;
|
||||||
Some(glue) => match (glue.replay)(&*value, promotion.persistent)? {
|
// SAFETY: the replay produced this field's own value type.
|
||||||
// SAFETY: the replay produced this field's own value type.
|
unsafe { repark(resident.as_deref().unwrap_or(&*value), slot, promotion.persistent) }?;
|
||||||
Some(resident) => unsafe { repark(&*resident, slot, promotion.persistent) }?,
|
|
||||||
None => unsafe { repark(&*value, slot, promotion.persistent) }?,
|
|
||||||
},
|
|
||||||
None => unsafe { repark(&*value, slot, promotion.persistent) }?,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Some(())
|
Some(())
|
||||||
}
|
}
|
||||||
@@ -2203,13 +2210,8 @@ impl OwnedRecord {
|
|||||||
for (index, value) in &self.fields {
|
for (index, value) in &self.fields {
|
||||||
let field = &layout.fields[*index];
|
let field = &layout.fields[*index];
|
||||||
let repark = field.repark.expect("copied fields carry re-park glue");
|
let repark = field.repark.expect("copied fields carry re-park glue");
|
||||||
match deep_field_glue(value.as_any().type_id()) {
|
let resident = replay_field_value(&**value, arena)?;
|
||||||
Some(glue) => match (glue.replay)(&**value, arena)? {
|
unsafe { repark(resident.as_deref().unwrap_or(&**value), dst.add(field.offset), 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) }?,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Some(())
|
Some(())
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user