1mod admission;
2#[cfg(test)]
3mod admission_tests;
4mod adoption;
5#[cfg(test)]
6mod adoption_tests;
7mod allocations;
8mod backing;
9mod bootstrap;
10mod config;
11mod construction;
12mod custody;
13mod diagnostics;
14mod error;
15#[cfg(any(test, feature = "test-support"))]
16pub mod fixture_diagnostics;
17#[cfg(test)]
18mod fixtures;
19mod layout;
20mod policy;
21
22#[cfg(all(test, not(target_arch = "wasm32")))]
23mod allocation_tests;
24#[cfg(test)]
25mod custody_tests;
26#[cfg(test)]
27mod growth_tests;
28#[cfg(test)]
29#[expect(
30 unsafe_code,
31 reason = "exercise raw reads with valid uninitialized destinations"
32)]
33mod read_tests;
34#[cfg(test)]
35mod request_tests;
36#[cfg(test)]
37mod tests;
38
39pub use admission::{BootstrapAdmission, BootstrapAdmissionError};
40pub use adoption::RuntimeAdoptionError;
41
42pub use allocations::{
43 MemoryAllocation, MemoryAllocationSummary, MemoryAllocations, MemoryBindingSummary,
44};
45pub use backing::RuntimeMemory;
46pub use config::MemoryManagerConfig;
47pub use custody::{BackingIdentity, MemoryBacking};
48pub use error::{
49 MemoryResolutionError, RuntimeBootstrapError, RuntimeConstructionError, RuntimeDiagnosticError,
50 RuntimeGrowError, RuntimeOpenError,
51};
52pub use layout::MemoryManagerLayoutError;
53pub use policy::GenericAllocationPolicy;
54
55use crate::{
56 LedgerCommitStore, PolicyIdentity, StableCellLedgerError, requests::SealedDeclarationSnapshot,
57 slot::MEMORY_MANAGER_LEDGER_ID, stable_cell::decode_stable_cell_commit_store_from_memory,
58};
59use ic_platform::stable_structures::{
60 Memory,
61 memory_manager::{MemoryId, MemoryManager},
62};
63
64use std::rc::Rc;
65
66enum RuntimeLifecycle {
67 Unbootstrapped,
68 Bootstrapped(CommittedRuntime),
69}
70
71struct CommittedRuntime {
72 allocations: Vec<crate::AllocationDeclaration>,
73 generation: u64,
74 binding: RuntimeBootstrapBinding,
75}
76
77struct RuntimeBootstrapBinding {
78 source: SealedDeclarationSnapshot,
79 policy_identity: PolicyIdentity,
80 pool: crate::MemoryAllocationPool,
81}
82
83pub struct MemoryRuntime<M: Memory> {
110 memory_manager: MemoryManager<Rc<M>>,
111 growth: Rc<backing::GrowthState<M>>,
114 lifecycle: RuntimeLifecycle,
115}
116
117impl<M: Memory> MemoryRuntime<M> {
118 #[must_use]
120 pub const fn is_bootstrapped(&self) -> bool {
121 matches!(self.lifecycle, RuntimeLifecycle::Bootstrapped(_))
122 }
123
124 const fn committed_state(&self) -> Option<&CommittedRuntime> {
125 match &self.lifecycle {
126 RuntimeLifecycle::Unbootstrapped => None,
127 RuntimeLifecycle::Bootstrapped(committed) => Some(committed),
128 }
129 }
130
131 fn committed(&self) -> Result<&CommittedRuntime, RuntimeOpenError> {
132 self.committed_state()
133 .ok_or(RuntimeOpenError::NotBootstrapped)
134 }
135
136 pub fn committed_allocations(
139 &self,
140 ) -> Result<&[crate::AllocationDeclaration], RuntimeOpenError> {
141 Ok(&self.committed()?.allocations)
142 }
143
144 pub const fn committed_generation(&self) -> Result<u64, RuntimeOpenError> {
146 match self.committed_state() {
147 Some(committed) => Ok(committed.generation),
148 None => Err(RuntimeOpenError::NotBootstrapped),
149 }
150 }
151
152 pub const fn allocation_pool(&self) -> Result<&crate::MemoryAllocationPool, RuntimeOpenError> {
155 match self.committed_state() {
156 Some(committed) => Ok(&committed.binding.pool),
157 None => Err(RuntimeOpenError::NotBootstrapped),
158 }
159 }
160
161 pub fn open_memory(&self, stable_key: &str) -> Result<RuntimeMemory<M>, RuntimeOpenError> {
168 self.memory(self.memory_id(stable_key)?)
169 }
170
171 pub fn memory_id(&self, stable_key: &str) -> Result<u8, RuntimeOpenError> {
174 crate::key::validate(stable_key)?;
175 if crate::is_ic_memory_stable_key(stable_key) {
176 return Err(RuntimeOpenError::ReservedStableKey {
177 stable_key: stable_key.to_string(),
178 });
179 }
180 let declaration =
181 crate::declaration::declaration_for_key(self.committed_allocations()?, stable_key)
182 .ok_or_else(|| RuntimeOpenError::StableKeyNotCommitted(stable_key.to_string()))?;
183 Ok(declaration.slot().id())
184 }
185
186 fn memory(&self, id: u8) -> Result<RuntimeMemory<M>, RuntimeOpenError> {
187 let (word, mask) = self.growth.lease_bit(id);
188 if word.get() & mask != 0 {
189 return Err(RuntimeOpenError::AlreadyOpen { id });
190 }
191 self.growth.verify()?;
192 let memory = self.memory_manager.get(MemoryId::new(id));
193 word.set(word.get() | mask);
194 Ok(RuntimeMemory {
195 memory,
196 growth: Rc::clone(&self.growth),
197 id,
198 })
199 }
200
201 fn memory_size_pages(&self, id: u8) -> u64 {
203 self.memory_manager.get(MemoryId::new(id)).size()
204 }
205
206 fn ledger_record_from_memory(&self) -> Result<LedgerCommitStore, StableCellLedgerError> {
207 let memory = self
210 .memory_manager
211 .get(MemoryId::new(MEMORY_MANAGER_LEDGER_ID));
212 decode_stable_cell_commit_store_from_memory(&memory)
213 }
214}