1use bytes::Bytes;
10use serde::{Deserialize, Serialize};
11use std::collections::BTreeMap;
12
13use crate::abi::{EntityId, InstanceId, Principal, RouteId, Tick, TypeCode};
14use crate::state::config::InstanceConfig;
15use crate::state::ledger::ResourceLedger;
16use crate::state::scheduler::Scheduler;
17
18#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
20pub struct EntityMeta {
21 pub owner: Principal,
23 pub created: Tick,
25}
26
27#[derive(Debug, Default, Clone, Serialize, Deserialize)]
28pub(crate) struct IdCounters {
29 pub next_entity: u64,
30 pub next_scheduled: u64,
31 pub next_source_seq: u64,
32}
33
34pub(crate) struct Instance {
35 #[cfg_attr(not(test), allow(dead_code))]
39 id: InstanceId,
40 config: InstanceConfig,
41 entities: BTreeMap<EntityId, EntityMeta>,
42 components: BTreeMap<(EntityId, TypeCode), Bytes>,
44 scheduler: Scheduler,
45 id_counters: IdCounters,
46 ledger: ResourceLedger,
47 inflight_refs: BTreeMap<RouteId, u32>,
49 wall_remainder: u128,
51 local_tick: u64,
53}
54
55impl Instance {
56 pub(crate) fn new(id: InstanceId, config: InstanceConfig) -> Self {
57 Self {
58 id,
59 config,
60 entities: BTreeMap::new(),
61 components: BTreeMap::new(),
62 scheduler: Scheduler::new(),
63 id_counters: IdCounters::default(),
64 ledger: ResourceLedger::new(),
65 inflight_refs: BTreeMap::new(),
66 wall_remainder: 0,
67 local_tick: 0,
68 }
69 }
70
71 #[inline]
77 pub(crate) fn id(&self) -> InstanceId {
78 self.id
79 }
80 #[inline]
81 pub(crate) fn config(&self) -> &InstanceConfig {
82 &self.config
83 }
84 #[inline]
85 pub(crate) fn entities_len(&self) -> usize {
86 self.entities.len()
87 }
88 #[inline]
89 pub(crate) fn components_len(&self) -> usize {
90 self.components.len()
91 }
92 #[inline]
93 pub(crate) fn local_tick(&self) -> u64 {
94 self.local_tick
95 }
96 #[cfg_attr(not(test), allow(dead_code))]
97 #[inline]
98 pub(crate) fn wall_remainder(&self) -> u128 {
99 self.wall_remainder
100 }
101 #[inline]
102 pub(crate) fn ledger(&self) -> &ResourceLedger {
103 &self.ledger
104 }
105 #[cfg_attr(not(test), allow(dead_code))]
106 #[inline]
107 pub(crate) fn scheduler(&self) -> &Scheduler {
108 &self.scheduler
109 }
110 #[cfg_attr(not(test), allow(dead_code))]
111 #[inline]
112 pub(crate) fn id_counters(&self) -> &IdCounters {
113 &self.id_counters
114 }
115 #[cfg_attr(not(test), allow(dead_code))]
116 #[inline]
117 pub(crate) fn inflight_refs_len(&self) -> usize {
118 self.inflight_refs.len()
119 }
120 #[cfg_attr(not(test), allow(dead_code))]
121 #[inline]
122 pub(crate) fn inflight_refs_for(&self, route: RouteId) -> u32 {
123 *self.inflight_refs.get(&route).unwrap_or(&0)
124 }
125
126 pub(crate) fn entity_meta(&self, entity: EntityId) -> Option<&EntityMeta> {
128 self.entities.get(&entity)
129 }
130
131 pub(crate) fn component(&self, entity: EntityId, type_code: TypeCode) -> Option<&Bytes> {
134 self.components.get(&(entity, type_code))
135 }
136
137 pub(crate) fn entities_iter(&self) -> impl Iterator<Item = (EntityId, &EntityMeta)> + '_ {
141 self.entities.iter().map(|(id, meta)| (*id, meta))
142 }
143
144 pub(crate) fn components_by_type_iter(
148 &self,
149 type_code: TypeCode,
150 ) -> impl Iterator<Item = (EntityId, &Bytes)> + '_ {
151 self.components
152 .iter()
153 .filter_map(move |((eid, tc), bytes)| {
154 if *tc == type_code {
155 Some((*eid, bytes))
156 } else {
157 None
158 }
159 })
160 }
161
162 pub(crate) fn insert_entity(&mut self, id: EntityId, meta: EntityMeta) {
171 self.entities.insert(id, meta);
172 }
173
174 pub(crate) fn remove_entity(&mut self, id: EntityId) -> Option<EntityMeta> {
175 self.entities.remove(&id)
176 }
177
178 pub(crate) fn insert_component(&mut self, key: (EntityId, TypeCode), bytes: Bytes) {
179 self.components.insert(key, bytes);
180 }
181
182 pub(crate) fn remove_component(&mut self, key: (EntityId, TypeCode)) -> Option<Bytes> {
183 self.components.remove(&key)
184 }
185
186 pub(crate) fn scheduler_mut(&mut self) -> &mut Scheduler {
187 &mut self.scheduler
188 }
189
190 pub(crate) fn ledger_mut(&mut self) -> &mut ResourceLedger {
191 &mut self.ledger
192 }
193
194 pub(crate) fn id_counters_mut(&mut self) -> &mut IdCounters {
195 &mut self.id_counters
196 }
197
198 pub(crate) fn inflight_refs_mut(&mut self) -> &mut BTreeMap<RouteId, u32> {
199 &mut self.inflight_refs
200 }
201
202 pub(crate) fn advance_wall_remainder(&mut self, delta: u128) {
203 self.wall_remainder = self.wall_remainder.saturating_add(delta);
204 }
205
206 pub(crate) fn advance_local_tick(&mut self, delta: u64) {
207 self.local_tick = self.local_tick.saturating_add(delta);
208 }
209
210 pub(crate) fn id_counters_snapshot(&self) -> IdCounters {
213 self.id_counters.clone()
214 }
215
216 pub(crate) fn to_snapshot(&self) -> InstanceSnapshot {
219 InstanceSnapshot {
220 id: self.id,
221 config: self.config.clone(),
222 entities: self.entities.clone(),
223 components: self.components.clone(),
224 scheduler: self.scheduler.clone(),
225 id_counters: self.id_counters.clone(),
226 ledger: self.ledger.clone(),
227 inflight_refs: self.inflight_refs.clone(),
228 wall_remainder: self.wall_remainder,
229 local_tick: self.local_tick,
230 }
231 }
232
233 pub(crate) fn from_snapshot(snap: InstanceSnapshot) -> Self {
235 Self {
236 id: snap.id,
237 config: snap.config,
238 entities: snap.entities,
239 components: snap.components,
240 scheduler: snap.scheduler,
241 id_counters: snap.id_counters,
242 ledger: snap.ledger,
243 inflight_refs: snap.inflight_refs,
244 wall_remainder: snap.wall_remainder,
245 local_tick: snap.local_tick,
246 }
247 }
248}
249
250#[derive(Debug, Clone, Serialize, Deserialize)]
255pub struct InstanceSnapshot {
256 pub(crate) id: InstanceId,
257 pub(crate) config: InstanceConfig,
258 pub(crate) entities: BTreeMap<EntityId, EntityMeta>,
259 pub(crate) components: BTreeMap<(EntityId, TypeCode), Bytes>,
260 pub(crate) scheduler: Scheduler,
261 pub(crate) id_counters: IdCounters,
262 pub(crate) ledger: ResourceLedger,
263 pub(crate) inflight_refs: BTreeMap<RouteId, u32>,
264 pub(crate) wall_remainder: u128,
265 pub(crate) local_tick: u64,
266}
267
268#[cfg(test)]
269mod tests {
270 use super::*;
271 use crate::abi::CapabilityMask;
272 use crate::state::quota::QuotaReductionPolicy;
273
274 fn id(n: u64) -> InstanceId {
275 InstanceId::new(n).unwrap()
276 }
277
278 fn cfg() -> InstanceConfig {
279 InstanceConfig {
280 default_caps: CapabilityMask::default(),
281 max_entities: 100,
282 max_scheduled: 1000,
283 memory_budget_bytes: 1 << 20,
284 parent: None,
285 quota_reduction: QuotaReductionPolicy::default(),
286 }
287 }
288
289 #[test]
290 fn instance_new_initial_state() {
291 let inst = Instance::new(id(1), cfg());
292 assert_eq!(inst.id(), id(1));
293 assert_eq!(inst.entities_len(), 0);
294 assert_eq!(inst.components_len(), 0);
295 assert_eq!(inst.local_tick(), 0);
296 assert_eq!(inst.wall_remainder(), 0);
297 assert_eq!(inst.ledger().total_entities(), 0);
298 assert!(inst.scheduler().is_empty());
299 assert_eq!(inst.inflight_refs_len(), 0);
300 }
301
302 #[test]
303 fn instance_id_counters_default_zero() {
304 let inst = Instance::new(id(1), cfg());
305 let c = inst.id_counters();
306 assert_eq!(c.next_entity, 0);
307 assert_eq!(c.next_scheduled, 0);
308 assert_eq!(c.next_source_seq, 0);
309 }
310
311 #[test]
312 fn instance_config_carried_through() {
313 let mut config = cfg();
314 config.max_entities = 7;
315 config.parent = id(99).into(); let inst = Instance::new(id(2), config);
317 assert_eq!(inst.config().max_entities, 7);
318 assert_eq!(inst.config().parent, Some(id(99)));
319 }
320
321 #[test]
322 fn multiple_instances_independent() {
323 let inst1 = Instance::new(id(1), cfg());
324 let inst2 = Instance::new(id(2), cfg());
325 assert_ne!(inst1.id(), inst2.id());
326 assert!(inst1.scheduler().is_empty());
327 assert!(inst2.scheduler().is_empty());
328 assert_eq!(inst1.ledger().total_bytes(), 0);
329 assert_eq!(inst2.ledger().total_bytes(), 0);
330 }
331
332 #[test]
333 fn entity_meta_clone_preserves_fields() {
334 let m1 = EntityMeta {
335 owner: Principal::System,
336 created: Tick(5),
337 };
338 let m2 = m1.clone();
339 assert!(matches!(m2.owner, Principal::System));
340 assert_eq!(m2.created, Tick(5));
341 }
342
343 #[test]
344 fn id_counters_default_clone_independent() {
345 let c1 = IdCounters::default();
346 let mut c2 = c1.clone();
347 c2.next_entity = 10;
348 assert_eq!(c1.next_entity, 0);
349 assert_eq!(c2.next_entity, 10);
350 }
351
352 }