1use ic_memory::{
8 AllocationSession, AllocationSessionError, AllocationSlotDescriptor, MemoryManagerSlotError,
9 StableKey, StorageSubstrate,
10 stable_structures::{
11 DefaultMemoryImpl,
12 memory_manager::{MemoryId, VirtualMemory},
13 },
14};
15
16pub(crate) mod ledger;
17mod manager;
18mod policy;
19pub mod registry;
20pub mod runtime;
21
22pub use crate::{eager_init, eager_static, ic_memory_key};
23
24#[doc(hidden)]
29#[must_use]
30pub fn open_validated_memory(
31 stable_key: &str,
32 label: &str,
33 id: u8,
34) -> VirtualMemory<DefaultMemoryImpl> {
35 runtime::assert_memory_bootstrap_ready(label, id);
36 try_open_validated_memory(stable_key, id).unwrap_or_else(|err| {
37 panic!(
38 "stable memory slot '{label}' (id {id}, key '{stable_key}') failed validated open: {err}"
39 );
40 })
41}
42
43fn try_open_validated_memory(
44 stable_key: &str,
45 id: u8,
46) -> Result<VirtualMemory<DefaultMemoryImpl>, registry::MemoryRegistryError> {
47 let key = StableKey::parse(stable_key).map_err(|err| {
48 registry::MemoryRegistryError::InvalidDeclaration {
49 stable_key: err.stable_key,
50 reason: err.reason,
51 }
52 })?;
53 let validated = runtime::registry::MemoryRegistryRuntime::validated_allocations()?;
54 let slot = validated
55 .slot_for(&key)
56 .ok_or(registry::MemoryRegistryError::RegistrationAfterBootstrap { registrations: 1 })?;
57 let slot_id = memory_manager_id_from_slot(slot)?;
58 if slot_id != id {
59 return Err(registry::MemoryRegistryError::RegistrationAfterBootstrap { registrations: 1 });
60 }
61
62 let session = AllocationSession::new(MemoryManagerSubstrate, validated);
63 session
64 .open(&key)
65 .map_err(memory_registry_error_from_session_error)
66}
67
68#[derive(Clone, Copy, Debug, Eq, PartialEq)]
69struct MemoryManagerSubstrate;
70
71impl StorageSubstrate for MemoryManagerSubstrate {
72 type Slot = u8;
73 type LedgerMemory = VirtualMemory<DefaultMemoryImpl>;
74 type MemoryHandle = VirtualMemory<DefaultMemoryImpl>;
75 type Error = registry::MemoryRegistryError;
76
77 fn open_ledger(&self) -> Result<Self::LedgerMemory, Self::Error> {
78 Ok(open_memory(ledger::MEMORY_LAYOUT_LEDGER_ID))
79 }
80
81 fn open_slot(
82 &self,
83 slot: &AllocationSlotDescriptor,
84 ) -> Result<Self::MemoryHandle, Self::Error> {
85 let id = memory_manager_id_from_slot(slot)?;
86 Ok(open_memory(id))
87 }
88
89 fn describe_slot(&self, slot: &Self::Slot) -> AllocationSlotDescriptor {
90 AllocationSlotDescriptor::memory_manager(*slot)
91 .expect("MemoryManagerSubstrate only describes usable MemoryManager IDs")
92 }
93}
94
95fn open_memory(id: u8) -> VirtualMemory<DefaultMemoryImpl> {
96 manager::MEMORY_MANAGER.with_borrow_mut(|mgr| mgr.get(MemoryId::new(id)))
97}
98
99fn memory_registry_error_from_session_error(
100 err: AllocationSessionError<registry::MemoryRegistryError>,
101) -> registry::MemoryRegistryError {
102 match err {
103 AllocationSessionError::UnknownStableKey(_) => {
104 registry::MemoryRegistryError::RegistrationAfterBootstrap { registrations: 1 }
105 }
106 AllocationSessionError::Substrate(err) => err,
107 }
108}
109
110fn memory_manager_id_from_slot(
111 slot: &AllocationSlotDescriptor,
112) -> Result<u8, registry::MemoryRegistryError> {
113 slot.memory_manager_id()
114 .map_err(memory_registry_error_from_slot_error)
115}
116
117fn memory_registry_error_from_slot_error(
118 err: MemoryManagerSlotError,
119) -> registry::MemoryRegistryError {
120 match err {
121 MemoryManagerSlotError::InvalidMemoryManagerId { id } => {
122 registry::MemoryRegistryError::InvalidDeclaration {
123 stable_key: "<slot>".to_string(),
124 reason: if id == ic_memory::MEMORY_MANAGER_INVALID_ID {
125 "MemoryManager ID 255 is not usable"
126 } else {
127 "MemoryManager ID is not usable"
128 },
129 }
130 }
131 MemoryManagerSlotError::UnsupportedSlot
132 | MemoryManagerSlotError::UnsupportedSubstrate { .. }
133 | MemoryManagerSlotError::UnsupportedDescriptorVersion { .. } => {
134 registry::MemoryRegistryError::LedgerCorrupt {
135 reason: "unsupported MemoryManager allocation slot descriptor",
136 }
137 }
138 }
139}
140
141#[cfg(test)]
146mod tests {
147 use super::*;
148 use crate::memory::registry::{defer_register_with_key, reset_for_tests};
149
150 #[test]
151 fn validated_open_requires_declared_stable_key() {
152 reset_for_tests();
153 defer_register_with_key(101, "crate_a", "slot", "app.crate_a.slot.v1")
154 .expect("defer register");
155 runtime::registry::MemoryRegistryRuntime::init().expect("bootstrap registry");
156
157 let _memory =
158 try_open_validated_memory("app.crate_a.slot.v1", 101).expect("open by stable key");
159 }
160
161 #[test]
162 fn validated_open_rejects_key_id_mismatch() {
163 reset_for_tests();
164 defer_register_with_key(101, "crate_a", "slot", "app.crate_a.slot.v1")
165 .expect("defer register");
166 runtime::registry::MemoryRegistryRuntime::init().expect("bootstrap registry");
167
168 let Err(err) = try_open_validated_memory("app.crate_a.slot.v1", 102) else {
169 panic!("wrong id must not open declared key");
170 };
171 assert!(matches!(
172 err,
173 registry::MemoryRegistryError::RegistrationAfterBootstrap { .. }
174 ));
175 }
176}