1use std::collections::BTreeMap;
26
27use serde::{Deserialize, Serialize};
28
29use crate::abi::InstanceId;
30use crate::state::instance::InstanceSnapshot;
31
32#[derive(Debug, Clone, Serialize, Deserialize)]
40pub struct KernelSnapshot {
41 pub(crate) instances: BTreeMap<InstanceId, InstanceSnapshot>,
42 pub(crate) next_instance_id: u64,
43}
44
45impl KernelSnapshot {
46 pub fn serialize(&self) -> Result<Vec<u8>, SnapshotError> {
48 postcard::to_allocvec(self).map_err(|e| SnapshotError::SerializeFailed(format!("{}", e)))
49 }
50
51 pub fn deserialize(bytes: &[u8]) -> Result<Self, SnapshotError> {
53 postcard::from_bytes(bytes).map_err(|e| SnapshotError::DeserializeFailed(format!("{}", e)))
54 }
55
56 pub fn instance_count(&self) -> usize {
58 self.instances.len()
59 }
60
61 pub fn instance_ids(&self) -> impl Iterator<Item = InstanceId> + '_ {
63 self.instances.keys().copied()
64 }
65
66 #[doc(hidden)]
68 pub fn __construct(
69 instances: BTreeMap<InstanceId, InstanceSnapshot>,
70 next_instance_id: u64,
71 ) -> Self {
72 Self {
73 instances,
74 next_instance_id,
75 }
76 }
77
78 #[doc(hidden)]
80 pub fn __into_parts(self) -> (BTreeMap<InstanceId, InstanceSnapshot>, u64) {
81 (self.instances, self.next_instance_id)
82 }
83}
84
85#[non_exhaustive]
87#[derive(Debug, Clone, PartialEq, Eq)]
88pub enum SnapshotError {
89 SerializeFailed(String),
91 DeserializeFailed(String),
93}
94
95impl core::fmt::Display for SnapshotError {
96 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
97 match self {
98 Self::SerializeFailed(m) => write!(f, "snapshot serialize failed: {}", m),
99 Self::DeserializeFailed(m) => write!(f, "snapshot deserialize failed: {}", m),
100 }
101 }
102}
103
104impl std::error::Error for SnapshotError {}
105
106#[cfg(test)]
107mod tests {
108 use super::*;
109 use crate::abi::{CapabilityMask, EntityId, Principal, Tick, TypeCode};
110 use crate::state::traits::_sealed::Sealed;
111 use crate::state::{ActionCompute, ActionContext, ActionDeriv, InstanceConfig, Op};
112 use crate::Kernel;
113 use bytes::Bytes;
114 use serde::{Deserialize as De, Serialize as Ser};
115
116 #[derive(Ser, De)]
119 struct SpawnSetAction {
120 id: u64,
121 }
122 impl Sealed for SpawnSetAction {}
123 impl ActionDeriv for SpawnSetAction {
124 const TYPE_CODE: TypeCode = TypeCode(900);
125 const SCHEMA_VERSION: u32 = 1;
126 }
127 impl ActionCompute for SpawnSetAction {
128 fn compute(&self, _ctx: &ActionContext) -> Vec<Op> {
129 let entity = EntityId::new(self.id).unwrap();
130 vec![
131 Op::SpawnEntity {
132 id: entity,
133 owner: Principal::System,
134 },
135 Op::SetComponent {
136 entity,
137 type_code: TypeCode(7),
138 bytes: Bytes::from(vec![0xCDu8; 4]),
139 size: 4,
140 },
141 ]
142 }
143 }
144
145 fn submit(k: &mut Kernel, inst: InstanceId, id: u64) {
146 use crate::state::Action;
147 let bytes = Action::canonical_bytes(&SpawnSetAction { id });
148 k.submit(
149 inst,
150 Principal::System,
151 None,
152 Tick(0),
153 SpawnSetAction::TYPE_CODE,
154 bytes,
155 )
156 .unwrap();
157 }
158
159 fn boot_with_state(actions: &[u64]) -> (Kernel, InstanceId) {
160 let mut k = Kernel::new();
161 k.register_action::<SpawnSetAction>();
162 let inst = k.create_instance(InstanceConfig::default());
163 for id in actions {
164 submit(&mut k, inst, *id);
165 let _ = k.step(Tick(0), CapabilityMask::SYSTEM);
166 }
167 (k, inst)
168 }
169
170 #[test]
171 fn snapshot_empty_kernel_serdes_roundtrip() {
172 let k = Kernel::new();
173 let snap = k.snapshot();
174 assert_eq!(snap.instance_count(), 0);
175 let bytes = snap.serialize().unwrap();
176 let back = KernelSnapshot::deserialize(&bytes).unwrap();
177 assert_eq!(back.instance_count(), 0);
178 }
179
180 #[test]
181 fn snapshot_captures_instance_state() {
182 let (k, inst) = boot_with_state(&[1, 2, 3]);
183 let snap = k.snapshot();
184 assert_eq!(snap.instance_count(), 1);
185 let ids: Vec<InstanceId> = snap.instance_ids().collect();
186 assert_eq!(ids, vec![inst]);
187 }
188
189 #[test]
190 fn snapshot_preserves_entities_and_components() {
191 let (k1, inst) = boot_with_state(&[1, 2]);
192 let snap = k1.snapshot();
193 let bytes = snap.serialize().unwrap();
194 let snap2 = KernelSnapshot::deserialize(&bytes).unwrap();
195 let mut k2 = Kernel::from_snapshot(snap2);
196 let v1 = k1.instance_view(inst).unwrap();
200 let v2 = k2.instance_view(inst).unwrap();
201 assert_eq!(v1.entity_count(), v2.entity_count());
202 assert_eq!(v1.component_count(), v2.component_count());
203 assert_eq!(
204 v1.component(EntityId::new(1).unwrap(), TypeCode(7)),
205 v2.component(EntityId::new(1).unwrap(), TypeCode(7)),
206 );
207 assert_eq!(
208 v1.component(EntityId::new(2).unwrap(), TypeCode(7)),
209 v2.component(EntityId::new(2).unwrap(), TypeCode(7)),
210 );
211 k2.register_action::<SpawnSetAction>();
214 submit(&mut k2, inst, 3);
215 let _ = k2.step(Tick(0), CapabilityMask::SYSTEM);
216 assert_eq!(k2.instance_view(inst).unwrap().entity_count(), 3);
217 }
218
219 #[test]
220 fn snapshot_preserves_id_counters() {
221 let mut k1 = Kernel::new();
224 let _ = k1.create_instance(InstanceConfig::default());
225 let _ = k1.create_instance(InstanceConfig::default());
226 let _ = k1.create_instance(InstanceConfig::default()); let snap = k1.snapshot();
228 let bytes = snap.serialize().unwrap();
229 let snap2 = KernelSnapshot::deserialize(&bytes).unwrap();
230 let mut k2 = Kernel::from_snapshot(snap2);
231 let next1 = k1.create_instance(InstanceConfig::default());
233 let next2 = k2.create_instance(InstanceConfig::default());
234 assert_eq!(next1, next2);
235 assert_eq!(next1.get(), 4);
236 }
237
238 #[test]
239 fn snapshot_preserves_local_tick_and_wall_remainder() {
240 let (k1, inst) = boot_with_state(&[1]);
244 let snap = k1.snapshot();
245 let bytes = snap.serialize().unwrap();
246 let k2 = Kernel::from_snapshot(KernelSnapshot::deserialize(&bytes).unwrap());
247 assert_eq!(
248 k1.instance_view(inst).unwrap().local_tick(),
249 k2.instance_view(inst).unwrap().local_tick(),
250 );
251 }
252
253 #[test]
254 fn snapshot_deserialize_fresh_kernel_no_observers_no_registry() {
255 let (k1, inst) = boot_with_state(&[1]);
259 let snap = k1.snapshot();
260 let mut k2 =
261 Kernel::from_snapshot(KernelSnapshot::deserialize(&snap.serialize().unwrap()).unwrap());
262 assert_eq!(k2.stats().observer_count, 0);
263 k2.submit(
266 inst,
267 Principal::System,
268 None,
269 Tick(0),
270 TypeCode(900),
271 vec![1u8],
272 )
273 .unwrap();
274 let report = k2.step(Tick(0), CapabilityMask::SYSTEM);
275 assert_eq!(report.actions_executed, 1);
276 assert_eq!(report.effects_applied, 0);
277 }
278
279 #[test]
280 fn snapshot_deterministic_same_state_same_bytes() {
281 let (k1, _) = boot_with_state(&[1, 2, 3]);
285 let (k2, _) = boot_with_state(&[1, 2, 3]);
286 let b1 = k1.snapshot().serialize().unwrap();
287 let b2 = k2.snapshot().serialize().unwrap();
288 assert_eq!(
289 b1, b2,
290 "identical state must produce identical snapshot bytes"
291 );
292 }
293}