entity_allocator.bend
bendvy-ecs-core@0.1.0.3 · 167 lines · 45 definitions
1import Base
2import entity_key.bend as K
3
4type Phase is Data:
5 Consumed{}
6 Pending{}
7 Live{}
8 DeadRetained{}
9 Free{}
10type Slot is Data:
11 Slot{generation:U32,phase:Phase}
12type Allocator is Type:
13 Allocator{next:U32,high:U32,slots:Array<Slot>,capacity:U32,depth:Nat,free:List<&2,U32>,order:List<&2,K.Key>}
14type Reservation is Data:
15 Reserved{key:K.Key}
16 Exhausted{}
17
18def empty() -> Allocator:
19 Allocator{1,0,Array.new(Slot,0n,Slot{0,Consumed{}}),1,0n,[],[]}
20
21def live(phase:Phase) -> Bool:
22 match phase:
23 case Live{}:True{}
24 case _:False{}
25def pending(phase:Phase) -> Bool:
26 match phase:
27 case Pending{}:True{}
28 case _:False{}
29def metadata(phase:Phase) -> Bool:
30 match phase:
31 case Free{}:False{}
32 case _:True{}
33def phase_valid(mode:U32,phase:Phase) -> Bool:
34 match mode:
35 case 0:metadata(phase)
36 case 1:live(phase)
37 case _:pending(phase)
38def slot_valid(+key:K.Key,mode:U32,slot:Slot) -> Bool:
39 match slot:
40 case Slot{generation,phase}:Bool.and(U32.is_eq(K.generation(key),generation),phase_valid(mode,phase))
41def checked_slot(+key:K.Key,mode:U32,+next:U32,+high:U32,+capacity:U32,+depth:Nat,+free:List<&2,U32>,+order:List<&2,K.Key>,observed:Array<Slot> & Slot) -> Allocator & Bool:
42 match observed:
43 case (slots,slot):(Allocator{next,high,slots,capacity,depth,free,order},slot_valid(key,mode,slot))
44def checked_bounds(allowed:Bool,allocator:Allocator,+key:K.Key,mode:U32) -> Allocator & Bool:
45 match allowed allocator:
46 case False{} allocator:(allocator,False{})
47 case True{} Allocator{+next,+high,slots,+capacity,+depth,+free,+order}:checked_slot(key,mode,next,high,capacity,depth,free,order,Array.get(Slot,slots,K.index(key)))
48def checked(allocator:Allocator,+key:K.Key,mode:U32) -> Allocator & Bool:
49 match allocator:
50 case Allocator{+next,high,slots,+capacity,depth,free,order}:checked_bounds(Bool.and(U32.is_lt(0,K.index(key)),Bool.and(U32.is_lt(K.index(key),next),U32.is_lt(K.index(key),capacity))),Allocator{next,high,slots,capacity,depth,free,order},key,mode)
51
52def keep(same:Bool,head:K.Key,rest:List<&2,K.Key>) -> List<&2,K.Key>:
53 match same:
54 case True{}:rest
55 case False{}:head <> rest
56
57def without(keys:List<&2,K.Key>,+key:K.Key) -> List<&2,K.Key>:
58 match keys:
59 case []:[]
60 case +head <> rest:keep(K.equal(head,key),head,without(rest,key))
61
62def phase_slot(key:K.Key,phase:Phase) -> Slot:
63 Slot{K.generation(key),phase}
64def write_phase(allocator:Allocator,+key:K.Key,phase:Phase) -> Allocator:
65 match allocator:
66 case Allocator{next,high,slots,capacity,depth,free,order}:Allocator{next,high,Array.set(Slot,slots,K.index(key),phase_slot(key,phase)),capacity,depth,free,order}
67def set_checked(phase:Phase,+key:K.Key,observed:Allocator & Bool) -> Allocator & Bool:
68 match observed:
69 case (allocator,False{}):(allocator,False{})
70 case (allocator,True{}):(write_phase(allocator,key,phase),True{})
71def set(allocator:Allocator,+key:K.Key,phase:Phase) -> Allocator & Bool:
72 set_checked(phase,key,checked(allocator,key,0))
73def abandon_checked(+key:K.Key,observed:Allocator & Bool) -> Allocator:
74 match observed:
75 case (allocator,False{}):allocator
76 case (Allocator{next,high,slots,capacity,depth,free,order},True{}):Allocator{next,high,Array.set(Slot,slots,K.index(key),phase_slot(key,Consumed{})),capacity,depth,free,without(order,key)}
77def abandon(allocator:Allocator,+key:K.Key) -> Allocator:
78 abandon_checked(key,checked(allocator,key,2))
79
80def release_checked(+key:K.Key,observed:Allocator & Bool) -> Allocator:
81 match observed:
82 case (allocator,False{}):allocator
83 case (Allocator{next,high,slots,capacity,depth,free,order},True{}):Allocator{next,high,Array.set(Slot,slots,K.index(key),Slot{(K.generation(key) + 1 : U32),Free{}}),capacity,depth,K.index(key) <> free,without(order,key)}
84def release(allocator:Allocator,+key:K.Key) -> Allocator:
85 release_checked(key,checked(allocator,key,0))
86
87def reserve_recycled(+index:U32,+next:U32,+high:U32,+capacity:U32,+depth:Nat,+free:List<&2,U32>,+order:List<&2,K.Key>,observed:Array<Slot> & Slot) -> Allocator & Reservation:
88 match observed:
89 case (slots,Slot{+generation,_}):(Allocator{next,high,Array.set(Slot,slots,index,Slot{generation,Consumed{}}),capacity,depth,free,List.append(&2,K.Key,order,[K.EntityKey{index,generation}])},Reserved{K.EntityKey{index,generation}})
90def reserve_fresh(available:Bool,grow:Bool,allocator:Allocator) -> Allocator & Reservation:
91 match available grow allocator:
92 case False{} _ allocator:(allocator,Exhausted{})
93 case True{} False{} Allocator{+next,_,slots,+capacity,+depth,free,order}:(Allocator{(next + 1 : U32),next,Array.set(Slot,slots,next,Slot{0,Consumed{}}),capacity,depth,free,List.append(&2,K.Key,order,[K.EntityKey{next,0}])},Reserved{K.EntityKey{next,0}})
94 case True{} True{} Allocator{+next,_,slots,+capacity,+depth,free,order}:(Allocator{(next + 1 : U32),next,Array.set(Slot,ANode{slots,Array.new(Slot,depth,Slot{0,Consumed{}})},next,Slot{0,Consumed{}}),(capacity * 2 : U32),1n+depth,free,List.append(&2,K.Key,order,[K.EntityKey{next,0}])},Reserved{K.EntityKey{next,0}})
95def reserve(allocator:Allocator) -> Allocator & Reservation:
96 match allocator:
97 case Allocator{+next,high,slots,+capacity,depth,[],order}:reserve_fresh(U32.is_lt(next,131073),Bool.not(U32.is_lt(next,capacity)),Allocator{next,high,slots,capacity,depth,[],order})
98 case Allocator{+next,+high,slots,+capacity,+depth,+index <> free,+order}:reserve_recycled(index,next,high,capacity,depth,free,order,Array.get(Slot,slots,index))
99
100def visible_join(+key:K.Key,next:Allocator -> List<&2,K.Key> -> Allocator & List<&2,K.Key>,reversed:List<&2,K.Key>,observed:Allocator & Bool) -> Allocator & List<&2,K.Key>:
101 match observed:
102 case (allocator,present):next(allocator,keep(Bool.not(present),key,reversed))
103def visible_state_join(+key:K.Key,reversed:List<&2,K.Key>,observed:Allocator & Bool) -> Allocator & List<&2,K.Key>:
104 match observed:
105 case (allocator,present):(allocator,keep(Bool.not(present),key,reversed))
106def visible_state_loop(keys:List<&2,K.Key>,state:Allocator & List<&2,K.Key>) -> Allocator & List<&2,K.Key>:
107 match keys state:
108 case [], (allocator,reversed):(allocator,List.reverse(&2,K.Key,reversed))
109 case +key <> rest, (allocator,reversed):visible_state_loop(rest,visible_state_join(key,reversed,checked(allocator,key,1)))
110def visible_loop(keys:List<&2,K.Key>,allocator:Allocator,reversed:List<&2,K.Key>) -> Allocator & List<&2,K.Key>:
111 visible_state_loop(keys,(allocator,reversed))
112def visible(allocator:Allocator) -> Allocator & List<&2,K.Key>:
113 match allocator:
114 case Allocator{next,high,slots,capacity,depth,free,+order}:visible_loop(order,Allocator{next,high,slots,capacity,depth,free,order},[])
115
116
117def abandon_pending_kept(+key:K.Key,observed:Allocator & List<&2,K.Key>) -> Allocator & List<&2,K.Key>:
118 match observed:
119 case (allocator,keys):(allocator,key <> keys)
120def abandon_pending_checked(+key:K.Key,next:Allocator -> Allocator & List<&2,K.Key>,observed:Allocator & Bool) -> Allocator & List<&2,K.Key>:
121 match observed:
122 case (allocator,True{}):next(write_phase(allocator,key,Consumed{}))
123 case (allocator,False{}):abandon_pending_kept(key,next(allocator))
124def abandon_pending_loop(keys:List<&2,K.Key>,allocator:Allocator) -> Allocator & List<&2,K.Key>:
125 match keys:
126 case []:(allocator,[])
127 case +key <> rest:abandon_pending_checked(key,next => abandon_pending_loop(rest,next),checked(allocator,key,2))
128def abandon_pending_join(observed:Allocator & List<&2,K.Key>) -> Allocator:
129 match observed:
130 case (Allocator{next,high,slots,capacity,depth,free,_},order):Allocator{next,high,slots,capacity,depth,free,order}
131def abandon_pending(allocator:Allocator) -> Allocator:
132 match allocator:
133 case Allocator{+next,+high,slots,+capacity,+depth,+free,+order}:abandon_pending_join(abandon_pending_loop(order,Allocator{next,high,slots,capacity,depth,free,order}))
134
135
136def floor_finish(+floor:U32,allocator:Allocator) -> Allocator:
137 match allocator:
138 case Allocator{next,high,slots,capacity,depth,free,order}:Allocator{U32.max(next,floor),U32.max(high,U32.sub(floor,1)),slots,capacity,depth,free,order}
139def floor_step(grow:Bool,+floor:U32,next:Allocator -> Allocator,allocator:Allocator) -> Allocator:
140 match grow allocator:
141 case False{} allocator:floor_finish(floor,allocator)
142 case True{} Allocator{n,high,slots,+capacity,+depth,free,order}:next(Allocator{n,high,ANode{slots,Array.new(Slot,depth,Slot{0,Consumed{}})},(capacity * 2 : U32),1n+depth,free,order})
143def floor_grow(fuel:Nat,+floor:U32,allocator:Allocator) -> Allocator:
144 match fuel allocator:
145 case 0n allocator:floor_finish(floor,allocator)
146 case 1n+rest Allocator{+next,+high,slots,+capacity,+depth,+free,+order}:floor_step(U32.is_lt(capacity,floor),floor,owner => floor_grow(rest,floor,owner),Allocator{next,high,slots,capacity,depth,free,order})
147def floor_checked(allowed:Bool,+floor:U32,allocator:Allocator) -> Allocator & Bool:
148 match allowed:
149 case False{}:(allocator,False{})
150 case True{}:(floor_grow(18n,floor,allocator),True{})
151def advance_floor(allocator:Allocator,+floor:U32) -> Allocator & Bool:
152 floor_checked(Bool.and(U32.is_lt(0,floor),U32.is_le(floor,131073)),floor,allocator)
153
154def count_keys(keys:List<&2,K.Key>) -> Nat:
155 match keys:
156 case []:0n
157 case _ <> rest:1n+count_keys(rest)
158def count_free(keys:List<&2,U32>) -> Nat:
159 match keys:
160 case []:0n
161 case _ <> rest:1n+count_free(rest)
162def restore_room_join(+floor:U32,needed:Nat,observed:Allocator & List<&2,K.Key>) -> Allocator & Bool:
163 match observed:
164 case (Allocator{+next,+high,slots,+capacity,+depth,+free,+order},live):
165 (Allocator{next,high,slots,capacity,depth,free,order},Bool.and(Bool.and(U32.is_lt(0,floor),U32.is_le(floor,131073)),Nat.is_le(needed,Nat.add(Nat.add(count_keys(live),count_free(free)),U32.to_nat(U32.sub(131073,U32.max(next,floor)))))))
166def restore_room(allocator:Allocator,floor:U32,needed:Nat) -> Allocator & Bool:
167 restore_room_join(floor,needed,visible(allocator))