execution_reservation_materialization.bend

bendvy-ecs-core@0.1.0.3 · 151 lines · 34 definitions

1import Base
2import bendvy-ecs-core@0.1.0.3/execution_resource_policy.bend as Policy
3import bendvy-ecs-core@0.1.0.3/execution_reservation_bundle.bend as Bundle
4import bendvy-ecs-core@0.1.0.3/execution_composer_layout.bend as Layout
5import bendvy-ecs-core@0.1.0.3/execution_command_permission.bend as Permission
6import bendvy-ecs-core@0.1.0.3/execution_query_identity.bend as Index
7import bendvy-ecs-core@0.1.0.3/execution_command_removed.bend as Removed
8import bendvy-ecs-core@0.1.0.3/owner_product.bend as Product
9import bendvy-ecs-core@0.1.0.3/chunk_column.bend as Column
10import bendvy-ecs-core@0.1.0.3/change_tick.bend as Tick
11import bendvy-ecs-core@0.1.0.3/world.bend as W
12import bendvy-ecs-core@0.1.0.3/lifecycle.bend as L
13import bendvy-ecs-core@0.1.0.3/chunk_shape.bend as Part
14import bendvy-ecs-core@0.1.0.3/execution_lifecycle_notices.bend as Notice
15import bendvy-ecs-core@0.1.0.3/clock_state.bend as Clock
16import bendvy-ecs-core@0.1.0.3/deferred_command.bend as Deferred
17import bendvy-ecs-core@0.1.0.3/entity_allocator.bend as A
18
19type Remainder<-S:Data,-C:Type,-R:Type,-E:Data> is Type:
20  Remainder{namespace:U32,resource:R,events:List<&2,E>,pending:List<Deferred.Command<W.World<S,C,R,E>>>,registrations:List<&2,W.RegistrationMeta>,nextSystemId:U32,clock:Clock.State,lifecycle:Notice.Journal<S>}
21
22# Preflight inspects actual presence without a callback or schema refresher.
23# No incoming payload is initialized until the full tree has been checked.
24def reason(ok:Bool,key:String) -> Maybe<&2,Bundle.Refusal>:
25  match ok:
26    case True{}:None{}
27    case False{}:Some{Bundle.OccupiedFamily{key}}
28def first(left:Maybe<&2,Bundle.Refusal>,right:Maybe<&2,Bundle.Refusal>) -> Maybe<&2,Bundle.Refusal>:
29  match left:
30    case None{}:right
31    case Some{error}:Some{error}
32def leaf_checked(~S:Data,-P:Type,-V:Data,+key:String,-origin:Layout.Layout,bundle:Maybe<&1,P>,result:Column.Column<S,Layout.Viewed<P,V>> & Maybe<&2,Bool>) -> Layout.ColumnSlot<S,Layout.Viewed<P,V>,key,origin> & (Maybe<&1,P> & Maybe<&2,Bundle.Refusal>):
33  match result:
34    case (column,None{}):(Layout.ColumnSlot{column},(bundle,Some{Bundle.MalformedColumn{key}}))
35    case (column,Some{vacant}):(Layout.ColumnSlot{column},(bundle,reason(vacant,key)))
36def vacancy_within(~S:Data,-P:Type,-V:Data,+key:String,-origin:Layout.Layout,index:U32,bundle:Maybe<&1,P>,column:Column.Column<S,Layout.Viewed<P,V>>,within:Bool) -> Layout.ColumnSlot<S,Layout.Viewed<P,V>,key,origin> & (Maybe<&1,P> & Maybe<&2,Bundle.Refusal>):
37  match within:
38    case False{}:(Layout.ColumnSlot{column},(bundle,None{}))
39    case True{}:leaf_checked(~S,P,V,key,origin,bundle,Column.vacancy_generic(S,Layout.Viewed<P,V>,column,index))
40def shape_allowed(~S:Data,-P:Type,-V:Data,+key:String,-origin:Layout.Layout,+index:U32,capacity:U32,bundle:Maybe<&1,P>,column:Column.Column<S,Layout.Viewed<P,V>>,valid:Bool) -> Layout.ColumnSlot<S,Layout.Viewed<P,V>,key,origin> & (Maybe<&1,P> & Maybe<&2,Bundle.Refusal>):
41  match valid:
42    case False{}:(Layout.ColumnSlot{column},(bundle,Some{Bundle.MalformedColumn{key}}))
43    case True{}:vacancy_within(~S,P,V,key,origin,index,bundle,column,U32.is_lt(index,capacity))
44def shape_checked(~S:Data,-P:Type,-V:Data,+key:String,-origin:Layout.Layout,index:U32,bundle:Maybe<&1,P>,result:Column.Column<S,Layout.Viewed<P,V>> & Part.Inspection) -> Layout.ColumnSlot<S,Layout.Viewed<P,V>,key,origin> & (Maybe<&1,P> & Maybe<&2,Bundle.Refusal>):
45  match result:
46    case (column,Part.Inspection{_,+capacity,valid}):shape_allowed(~S,P,V,key,origin,index,capacity,bundle,column,Bool.and(valid,U32.is_le(capacity,Column.maximum_capacity())))
47def check_leaf(~S:Data,-P:Type,-V:Data,+key:String,-origin:Layout.Layout,index:U32,slot:Layout.ColumnSlot<S,Layout.Viewed<P,V>,key,origin>,bundle:Maybe<&1,P>) -> Layout.ColumnSlot<S,Layout.Viewed<P,V>,key,origin> & (Maybe<&1,P> & Maybe<&2,Bundle.Refusal>):
48  match slot bundle:
49    case slot None{}:(slot,(None{},None{}))
50    case Layout.ColumnSlot{column} Some{owner}:shape_checked(~S,P,V,key,origin,index,Some{owner},Part.inspect(S,Layout.Viewed<P,V>,column))
51def check_right(~S:Data,-left:Layout.Layout,-right:Layout.Layout,leftOwner:Layout.columns(~S,left),leftBundle:Bundle.Payload(left),leftReason:Maybe<&2,Bundle.Refusal>,result:Layout.columns(~S,right) & (Bundle.Payload(right) & Maybe<&2,Bundle.Refusal>)) -> Layout.columns(~S,Layout.LayoutPair{left,right}) & (Bundle.Payload(Layout.LayoutPair{left,right}) & Maybe<&2,Bundle.Refusal>):
52  match result:
53    case (rightOwner,(rightBundle,rightReason)):(Product.Owner{leftOwner,rightOwner},(Product.Owner{leftBundle,rightBundle},first(leftReason,rightReason)))
54def check_left(~S:Data,-left:Layout.Layout,-right:Layout.Layout,checkRight:U32 -> Layout.columns(~S,right) -> Bundle.Payload(right) -> Layout.columns(~S,right) & (Bundle.Payload(right) & Maybe<&2,Bundle.Refusal>),index:U32,rightOwner:Layout.columns(~S,right),rightBundle:Bundle.Payload(right),result:Layout.columns(~S,left) & (Bundle.Payload(left) & Maybe<&2,Bundle.Refusal>)) -> Layout.columns(~S,Layout.LayoutPair{left,right}) & (Bundle.Payload(Layout.LayoutPair{left,right}) & Maybe<&2,Bundle.Refusal>):
55  match result:
56    case (leftOwner,(leftBundle,leftReason)):check_right(~S,left,right,leftOwner,leftBundle,leftReason,checkRight(index,rightOwner,rightBundle))
57def check_pair(~S:Data,-left:Layout.Layout,-right:Layout.Layout,checkLeft:U32 -> Layout.columns(~S,left) -> Bundle.Payload(left) -> Layout.columns(~S,left) & (Bundle.Payload(left) & Maybe<&2,Bundle.Refusal>),checkRight:U32 -> Layout.columns(~S,right) -> Bundle.Payload(right) -> Layout.columns(~S,right) & (Bundle.Payload(right) & Maybe<&2,Bundle.Refusal>),+index:U32,store:Layout.columns(~S,Layout.LayoutPair{left,right}),bundle:Bundle.Payload(Layout.LayoutPair{left,right})) -> Layout.columns(~S,Layout.LayoutPair{left,right}) & (Bundle.Payload(Layout.LayoutPair{left,right}) & Maybe<&2,Bundle.Refusal>):
58  match store bundle:
59    case Product.Owner{lo,ro} Product.Owner{lb,rb}:check_left(~S,left,right,checkRight,index,ro,rb,checkLeft(index,lo,lb))
60def check(~S:Data,layout:Layout.Layout) -> U32 -> Layout.columns(~S,layout) -> Bundle.Payload(layout) -> Layout.columns(~S,layout) & (Bundle.Payload(layout) & Maybe<&2,Bundle.Refusal>):
61  match layout:
62    case Layout.EmptyLayout{}:index => store => bundle => (store,(bundle,None{}))
63    case Layout.LayoutLeaf{P,V,refresh,key,accessPolicy}:check_leaf(~S,P,V,key,Layout.LayoutLeaf{P,V,refresh,key,accessPolicy})
64    case Layout.ClassifiedLayoutLeaf{P,V,refresh,key,policy,accessPolicy}:check_leaf(~S,P,V,key,Layout.ClassifiedLayoutLeaf{P,V,refresh,key,policy,accessPolicy})
65    case Layout.LayoutPair{left,right}:index => store => bundle => check_pair(~S,left,right,check(~S,left),check(~S,right),index,store,bundle)
66
67# Internal storage primitive, called only after whole-tree preflight and ensure.
68# Like canonical Column.swap_leaf, it preserves the displaced affine owner and
69# updates both lifecycle stamps using the captured publication tick.
70def written_stamp(~S:Data,-P:Type,values:Array<Maybe<&1,P>>,previous:Maybe<&1,P>,+index:U32,tick:Tick.Tick,wasPresent:Bool,result:Array<L.Stamp> & L.Stamp) -> Column.Column<S,P> & Maybe<&1,P>:
71  match result:
72    case (stamps,stamp):(Column.Leaf{values,Array.set(L.Stamp,stamps,index,L.after_write(True{},wasPresent,stamp,tick))},previous)
73def written_value(~S:Data,-P:Type,stamps:Array<L.Stamp>,+index:U32,tick:Tick.Tick,result:Array<Maybe<&1,P>> & Maybe<&1,P>) -> Column.Column<S,P> & Maybe<&1,P>:
74  match result:
75    case (values,None{}):written_stamp(~S,P,values,None{},index,tick,False{},Array.get(L.Stamp,stamps,index))
76    case (values,Some{previous}):written_stamp(~S,P,values,Some{previous},index,tick,True{},Array.get(L.Stamp,stamps,index))
77def replace_left(~S:Data,-P:Type,half:U32,right:Column.Column<S,P>,result:Column.Column<S,P> & Maybe<&1,P>) -> Column.Column<S,P> & Maybe<&1,P>:
78  match result:
79    case (left,previous):(Column.Branch{half,left,right},previous)
80def replace_right(~S:Data,-P:Type,half:U32,left:Column.Column<S,P>,result:Column.Column<S,P> & Maybe<&1,P>) -> Column.Column<S,P> & Maybe<&1,P>:
81  match result:
82    case (right,previous):(Column.Branch{half,left,right},previous)
83def branch_results(~S:Data,-P:Type,half:U32,resultLeft:Column.Column<S,P> & Maybe<&1,P>,resultRight:Column.Column<S,P> & Maybe<&1,P>,first:Bool) -> Column.Column<S,P> & Maybe<&1,P>:
84  match resultLeft resultRight first:
85    case (left,previous), (right,_), True{}:(Column.Branch{half,left,right},previous)
86    case (left,_), (right,previous), False{}:(Column.Branch{half,left,right},previous)
87def write_branch(~S:Data,-P:Type,writeLeft:Maybe<&1,P> -> Column.Column<S,P> & Maybe<&1,P>,writeRight:Maybe<&1,P> -> Column.Column<S,P> & Maybe<&1,P>,half:U32,incoming:P,first:Bool) -> Column.Column<S,P> & Maybe<&1,P>:
88  match first:
89    case True{}:branch_results(~S,P,half,writeLeft(Some{incoming}),writeRight(None{}),True{})
90    case False{}:branch_results(~S,P,half,writeLeft(None{}),writeRight(Some{incoming}),False{})
91def write_column(~S:Data,-P:Type,column:Column.Column<S,P>,+index:U32,+tick:Tick.Tick,incoming:Maybe<&1,P>) -> Column.Column<S,P> & Maybe<&1,P>:
92  match column incoming:
93    case column None{}:(column,None{})
94    case Column.Leaf{values,stamps} Some{incoming}:written_value(~S,P,stamps,index,tick,Array.swap(Maybe<&1,P>,values,index,Some{incoming}))
95    case Column.Branch{+half,left,right} Some{incoming}:write_branch(~S,P,write_column(~S,P,left,index,tick),write_column(~S,P,right,U32.sub(index,half),tick),half,incoming,U32.is_lt(index,half))
96def installed_leaf(~S:Data,-P:Type,-V:Data,-key:String,-origin:Layout.Layout,result:Column.Column<S,Layout.Viewed<P,V>> & Maybe<&1,Layout.Viewed<P,V>>) -> Layout.ColumnSlot<S,Layout.Viewed<P,V>,key,origin> & Maybe<&1,P>:
97  match result:
98    case (column,previous):(Layout.ColumnSlot{column},Removed.raw(P,V,previous))
99def install_leaf(~S:Data,-P:Type,-V:Data,refresh:P -> P & V,-key:String,-origin:Layout.Layout,+index:U32,tick:Tick.Tick,slot:Layout.ColumnSlot<S,Layout.Viewed<P,V>,key,origin>,bundle:Maybe<&1,P>) -> Layout.ColumnSlot<S,Layout.Viewed<P,V>,key,origin> & Maybe<&1,P>:
100  match slot bundle:
101    case slot None{}:(slot,None{})
102    case Layout.ColumnSlot{column} Some{incoming}:installed_leaf(~S,P,V,key,origin,write_column(~S,Layout.Viewed<P,V>,Column.ensure_generic(S,Layout.Viewed<P,V>,column,U32.add(index,1)),index,tick,Some{Layout.initialized(P,V,refresh(incoming))}))
103def installed_right(~S:Data,-left:Layout.Layout,-right:Layout.Layout,leftOwner:Layout.columns(~S,left),leftPrevious:Bundle.Payload(left),result:Layout.columns(~S,right) & Bundle.Payload(right)) -> Layout.columns(~S,Layout.LayoutPair{left,right}) & Bundle.Payload(Layout.LayoutPair{left,right}):
104  match result:
105    case (rightOwner,rightPrevious):(Product.Owner{leftOwner,rightOwner},Product.Owner{leftPrevious,rightPrevious})
106def installed_left(~S:Data,-left:Layout.Layout,-right:Layout.Layout,installRight:U32 -> Tick.Tick -> Layout.columns(~S,right) -> Bundle.Payload(right) -> Layout.columns(~S,right) & Bundle.Payload(right),index:U32,tick:Tick.Tick,rightOwner:Layout.columns(~S,right),rightBundle:Bundle.Payload(right),result:Layout.columns(~S,left) & Bundle.Payload(left)) -> Layout.columns(~S,Layout.LayoutPair{left,right}) & Bundle.Payload(Layout.LayoutPair{left,right}):
107  match result:
108    case (leftOwner,leftPrevious):installed_right(~S,left,right,leftOwner,leftPrevious,installRight(index,tick,rightOwner,rightBundle))
109def install_pair(~S:Data,-left:Layout.Layout,-right:Layout.Layout,installLeft:U32 -> Tick.Tick -> Layout.columns(~S,left) -> Bundle.Payload(left) -> Layout.columns(~S,left) & Bundle.Payload(left),installRight:U32 -> Tick.Tick -> Layout.columns(~S,right) -> Bundle.Payload(right) -> Layout.columns(~S,right) & Bundle.Payload(right),+index:U32,+tick:Tick.Tick,store:Layout.columns(~S,Layout.LayoutPair{left,right}),bundle:Bundle.Payload(Layout.LayoutPair{left,right})) -> Layout.columns(~S,Layout.LayoutPair{left,right}) & Bundle.Payload(Layout.LayoutPair{left,right}):
110  match store bundle:
111    case Product.Owner{lo,ro} Product.Owner{lb,rb}:installed_left(~S,left,right,installRight,index,tick,ro,rb,installLeft(index,tick,lo,lb))
112def install(~S:Data,layout:Layout.Layout) -> U32 -> Tick.Tick -> Layout.columns(~S,layout) -> Bundle.Payload(layout) -> Layout.columns(~S,layout) & Bundle.Payload(layout):
113  match layout:
114    case Layout.EmptyLayout{}:index => tick => store => bundle => (store,Unit{})
115    case Layout.LayoutLeaf{P,V,refresh,key,accessPolicy}:index => tick => slot => bundle => install_leaf(~S,P,V,refresh,key,Layout.LayoutLeaf{P,V,refresh,key,accessPolicy},index,tick,slot,bundle)
116    case Layout.ClassifiedLayoutLeaf{P,V,refresh,key,policy,accessPolicy}:index => tick => slot => bundle => install_leaf(~S,P,V,refresh,key,Layout.ClassifiedLayoutLeaf{P,V,refresh,key,policy,accessPolicy},index,tick,slot,bundle)
117    case Layout.LayoutPair{left,right}:index => tick => store => bundle => install_pair(~S,left,right,install(~S,left),install(~S,right),index,tick,store,bundle)
118
119def assembled(~S:Data,~R:Type,~E:Data,~layout:Layout.Layout,allocator:A.Allocator,rest:Remainder<S,Layout.columns(~S,layout),R,E>,store:Layout.columns(~S,layout)) -> W.World<S,Layout.columns(~S,layout),R,E>:
120  match rest:
121    case Remainder{namespace,resource,events,pending,registrations,nextSystemId,clock,lifecycle}:W.World{namespace,allocator,store,resource,events,pending,registrations,nextSystemId,clock,lifecycle}
122def installed(~S:Data,~R:Type,~E:Data,~layout:Layout.Layout,allocator:A.Allocator,rest:Remainder<S,Layout.columns(~S,layout),R,E>,+handle:W.Handle<S>,result:Layout.columns(~S,layout) & Bundle.Payload(layout)) -> Bundle.Publication<S,Layout.columns(~S,layout),R,E,layout>:
123  match result:
124    case (store,previous):Bundle.Materialized{assembled(~S,~R,~E,~layout,A.write_phase(allocator,W.handle_key(~S,handle),A.Live{}),rest,store),handle,previous}
125def checked_store(~S:Data,~R:Type,~E:Data,~layout:Layout.Layout,installTree:U32 -> Tick.Tick -> Layout.columns(~S,layout) -> Bundle.Payload(layout) -> Layout.columns(~S,layout) & Bundle.Payload(layout),allocator:A.Allocator,rest:Remainder<S,Layout.columns(~S,layout),R,E>,handle:W.Handle<S>,index:U32,tick:Tick.Tick,result:Layout.columns(~S,layout) & (Bundle.Payload(layout) & Maybe<&2,Bundle.Refusal>)) -> Bundle.Publication<S,Layout.columns(~S,layout),R,E,layout>:
126  match result:
127    case (store,(bundle,Some{reason})):Bundle.RefusedPublication{assembled(~S,~R,~E,~layout,allocator,rest,store),handle,bundle,reason}
128    case (store,(bundle,None{})):installed(~S,~R,~E,~layout,allocator,rest,handle,installTree(index,tick,store,bundle))
129def preflight(~S:Data,~R:Type,~E:Data,~layout:Layout.Layout,world:W.World<S,Layout.columns(~S,layout),R,E>,handle:W.Handle<S>,bundle:Bundle.Payload(layout),+index:U32,tick:Tick.Tick) -> Bundle.Publication<S,Layout.columns(~S,layout),R,E,layout>:
130  match world:
131    case W.World{namespace,allocator,store,resource,events,pending,registrations,nextSystemId,clock,lifecycle}:checked_store(~S,~R,~E,~layout,install(~S,layout),allocator,Remainder{namespace,resource,events,pending,registrations,nextSystemId,clock,lifecycle},handle,index,tick,check(~S,layout,index,store,bundle))
132def physical(~S:Data,~R:Type,~E:Data,~layout:Layout.Layout,world:W.World<S,Layout.columns(~S,layout),R,E>,handle:W.Handle<S>,bundle:Bundle.Payload(layout),tick:Tick.Tick,offset:Maybe<&2,U32>) -> Bundle.Publication<S,Layout.columns(~S,layout),R,E,layout>:
133  match offset:
134    case None{}:Bundle.RefusedPublication{world,handle,bundle,Bundle.PhysicalIndex{}}
135    case Some{index}:preflight(~S,~R,~E,layout,world,handle,bundle,index,tick)
136def unique(~S:Data,~R:Type,~E:Data,~layout:Layout.Layout,world:W.World<S,Layout.columns(~S,layout),R,E>,+handle:W.Handle<S>,bundle:Bundle.Payload(layout),tick:Tick.Tick,valid:Bool) -> Bundle.Publication<S,Layout.columns(~S,layout),R,E,layout>:
137  match valid:
138    case False{}:Bundle.RefusedPublication{world,handle,bundle,Bundle.DuplicateFamily{}}
139    case True{}:physical(~S,~R,~E,layout,world,handle,bundle,tick,Index.physical_index(W.handle_index(~S,handle)))
140def permitted(~S:Data,~R:Type,~E:Data,~layout:Layout.Layout,world:W.World<S,Layout.columns(~S,layout),R,E>,handle:W.Handle<S>,bundle:Bundle.Payload(layout),tick:Tick.Tick,permission:Permission.Permission) -> Bundle.Publication<S,Layout.columns(~S,layout),R,E,layout>:
141  match permission:
142    case Permission.FamilyOnly{}:Bundle.RefusedPublication{world,handle,bundle,Bundle.PermissionDenied{}}
143    case Permission.EntityStructural{}:unique(~S,~R,~E,layout,world,handle,bundle,tick,Layout.unique(layout))
144def pending_checked(~S:Data,~R:Type,~E:Data,~layout:Layout.Layout,permission:Permission.Permission,handle:W.Handle<S>,bundle:Bundle.Payload(layout),tick:Tick.Tick,result:W.World<S,Layout.columns(~S,layout),R,E> & Bool) -> Bundle.Publication<S,Layout.columns(~S,layout),R,E,layout>:
145  match result:
146    case (world,False{}):Bundle.RefusedPublication{world,handle,bundle,Bundle.InvalidPending{W.MissingEntity{}}}
147    case (world,True{}):permitted(~S,~R,~E,layout,world,handle,bundle,tick,permission)
148# Existing command publication calls this only at its barrier, with captured tick.
149# Pending namespace/generation/phase validation precedes even bundle preflight.
150def apply_at(~S:Data,~R:Type,~E:Data,~layout:Layout.Layout,permission:Permission.Permission,world:W.World<S,Layout.columns(~S,layout),R,E>,+handle:W.Handle<S>,bundle:Bundle.Payload(layout),tick:Tick.Tick) -> Bundle.Publication<S,Layout.columns(~S,layout),R,E,layout>:
151  pending_checked(~S,~R,~E,layout,permission,handle,bundle,tick,W.pending_valid(~S,~(Layout.columns(~S,layout)),~R,~E,world,handle))