mod admission;
#[cfg(test)]
mod admission_tests;
mod adoption;
#[cfg(test)]
mod adoption_tests;
mod allocations;
mod backing;
mod bootstrap;
mod config;
mod construction;
mod custody;
mod diagnostics;
mod error;
#[cfg(any(test, feature = "test-support"))]
pub mod fixture_diagnostics;
#[cfg(test)]
mod fixtures;
mod layout;
mod policy;
#[cfg(all(test, not(target_arch = "wasm32")))]
mod allocation_tests;
#[cfg(test)]
mod custody_tests;
#[cfg(test)]
mod growth_tests;
#[cfg(test)]
#[expect(
unsafe_code,
reason = "exercise raw reads with valid uninitialized destinations"
)]
mod read_tests;
#[cfg(test)]
mod request_tests;
#[cfg(test)]
mod tests;
pub use admission::{BootstrapAdmission, BootstrapAdmissionError};
pub use adoption::RuntimeAdoptionError;
pub use allocations::{
MemoryAllocation, MemoryAllocationSummary, MemoryAllocations, MemoryBindingSummary,
};
pub use backing::RuntimeMemory;
pub use config::MemoryManagerConfig;
pub use custody::{BackingIdentity, MemoryBacking};
pub use error::{
MemoryResolutionError, RuntimeBootstrapError, RuntimeConstructionError, RuntimeDiagnosticError,
RuntimeGrowError, RuntimeOpenError,
};
pub use layout::MemoryManagerLayoutError;
pub use policy::GenericAllocationPolicy;
use crate::{
LedgerCommitStore, PolicyIdentity, StableCellLedgerError, requests::SealedDeclarationSnapshot,
slot::MEMORY_MANAGER_LEDGER_ID, stable_cell::decode_stable_cell_commit_store_from_memory,
};
use ic_platform::stable_structures::{
Memory,
memory_manager::{MemoryId, MemoryManager},
};
use std::rc::Rc;
enum RuntimeLifecycle {
Unbootstrapped,
Bootstrapped(CommittedRuntime),
}
struct CommittedRuntime {
allocations: Vec<crate::AllocationDeclaration>,
generation: u64,
binding: RuntimeBootstrapBinding,
}
struct RuntimeBootstrapBinding {
source: SealedDeclarationSnapshot,
policy_identity: PolicyIdentity,
pool: crate::MemoryAllocationPool,
}
pub struct MemoryRuntime<M: Memory> {
memory_manager: MemoryManager<Rc<M>>,
growth: Rc<backing::GrowthState<M>>,
lifecycle: RuntimeLifecycle,
}
impl<M: Memory> MemoryRuntime<M> {
#[must_use]
pub const fn is_bootstrapped(&self) -> bool {
matches!(self.lifecycle, RuntimeLifecycle::Bootstrapped(_))
}
const fn committed_state(&self) -> Option<&CommittedRuntime> {
match &self.lifecycle {
RuntimeLifecycle::Unbootstrapped => None,
RuntimeLifecycle::Bootstrapped(committed) => Some(committed),
}
}
fn committed(&self) -> Result<&CommittedRuntime, RuntimeOpenError> {
self.committed_state()
.ok_or(RuntimeOpenError::NotBootstrapped)
}
pub fn committed_allocations(
&self,
) -> Result<&[crate::AllocationDeclaration], RuntimeOpenError> {
Ok(&self.committed()?.allocations)
}
pub const fn committed_generation(&self) -> Result<u64, RuntimeOpenError> {
match self.committed_state() {
Some(committed) => Ok(committed.generation),
None => Err(RuntimeOpenError::NotBootstrapped),
}
}
pub const fn allocation_pool(&self) -> Result<&crate::MemoryAllocationPool, RuntimeOpenError> {
match self.committed_state() {
Some(committed) => Ok(&committed.binding.pool),
None => Err(RuntimeOpenError::NotBootstrapped),
}
}
pub fn open_memory(&self, stable_key: &str) -> Result<RuntimeMemory<M>, RuntimeOpenError> {
self.memory(self.memory_id(stable_key)?)
}
pub fn memory_id(&self, stable_key: &str) -> Result<u8, RuntimeOpenError> {
crate::key::validate(stable_key)?;
if crate::is_ic_memory_stable_key(stable_key) {
return Err(RuntimeOpenError::ReservedStableKey {
stable_key: stable_key.to_string(),
});
}
let declaration =
crate::declaration::declaration_for_key(self.committed_allocations()?, stable_key)
.ok_or_else(|| RuntimeOpenError::StableKeyNotCommitted(stable_key.to_string()))?;
Ok(declaration.slot().id())
}
fn memory(&self, id: u8) -> Result<RuntimeMemory<M>, RuntimeOpenError> {
let (word, mask) = self.growth.lease_bit(id);
if word.get() & mask != 0 {
return Err(RuntimeOpenError::AlreadyOpen { id });
}
self.growth.verify()?;
let memory = self.memory_manager.get(MemoryId::new(id));
word.set(word.get() | mask);
Ok(RuntimeMemory {
memory,
growth: Rc::clone(&self.growth),
id,
})
}
fn memory_size_pages(&self, id: u8) -> u64 {
self.memory_manager.get(MemoryId::new(id)).size()
}
fn ledger_record_from_memory(&self) -> Result<LedgerCommitStore, StableCellLedgerError> {
let memory = self
.memory_manager
.get(MemoryId::new(MEMORY_MANAGER_LEDGER_ID));
decode_stable_cell_commit_store_from_memory(&memory)
}
}