Skip to main content

canic_memory/api/
mod.rs

1use crate::{
2    cdk::structures::{
3        DefaultMemoryImpl,
4        memory::{MemoryId, VirtualMemory},
5    },
6    manager::MEMORY_MANAGER,
7    registry::{MemoryRegistry, MemoryRegistryError},
8    runtime::{MemoryRuntimeApi, registry::MemoryRegistryInitSummary},
9};
10
11///
12/// MemoryApi
13///
14
15pub struct MemoryApi;
16
17impl MemoryApi {
18    /// Bootstrap eager TLS, eager-init hooks, and the caller's initial reserved range.
19    pub fn bootstrap_registry(
20        crate_name: &'static str,
21        start: u8,
22        end: u8,
23    ) -> Result<MemoryRegistryInitSummary, MemoryRegistryError> {
24        MemoryRuntimeApi::bootstrap_registry(crate_name, start, end)
25    }
26
27    /// Register one stable-memory ID and return its opened virtual memory handle.
28    ///
29    /// Call `bootstrap_registry(...)` first so the caller's owned range is reserved.
30    pub fn register_memory(
31        id: u8,
32        crate_name: &str,
33        label: &str,
34    ) -> Result<VirtualMemory<DefaultMemoryImpl>, MemoryRegistryError> {
35        if let Some(entry) = MemoryRegistry::get(id)
36            && entry.crate_name == crate_name
37            && entry.label == label
38        {
39            return Ok(open_memory(id));
40        }
41
42        MemoryRegistry::register(id, crate_name, label)?;
43        Ok(open_memory(id))
44    }
45}
46
47// Open a registered virtual memory slot through the shared manager.
48fn open_memory(id: u8) -> VirtualMemory<DefaultMemoryImpl> {
49    MEMORY_MANAGER.with_borrow_mut(|mgr| mgr.get(MemoryId::new(id)))
50}
51
52///
53/// TESTS
54///
55
56#[cfg(test)]
57mod tests {
58    use super::*;
59    use crate::registry::{MemoryRegistryError, reset_for_tests};
60
61    #[test]
62    fn register_memory_returns_opened_memory_for_reserved_slot() {
63        reset_for_tests();
64        let _ = MemoryApi::bootstrap_registry("crate_a", 1, 3).expect("bootstrap registry");
65
66        let _memory = MemoryApi::register_memory(2, "crate_a", "slot").expect("register memory");
67    }
68
69    #[test]
70    fn register_memory_is_idempotent_for_same_entry() {
71        reset_for_tests();
72        let _ = MemoryApi::bootstrap_registry("crate_a", 1, 3).expect("bootstrap registry");
73        let _ = MemoryApi::register_memory(2, "crate_a", "slot").expect("first register succeeds");
74
75        let _ = MemoryApi::register_memory(2, "crate_a", "slot").expect("second register succeeds");
76    }
77
78    #[test]
79    fn register_memory_rejects_unreserved_id() {
80        reset_for_tests();
81
82        let Err(err) = MemoryApi::register_memory(9, "crate_a", "slot") else {
83            panic!("unreserved slot must fail")
84        };
85        assert!(matches!(err, MemoryRegistryError::NoReservedRange { .. }));
86    }
87
88    #[test]
89    fn register_memory_preserves_duplicate_id_error_for_conflicts() {
90        reset_for_tests();
91        let _ = MemoryApi::bootstrap_registry("crate_a", 1, 3).expect("bootstrap registry");
92        MemoryApi::register_memory(2, "crate_a", "slot").expect("first register succeeds");
93
94        let Err(err) = MemoryApi::register_memory(2, "crate_a", "other") else {
95            panic!("conflicting duplicate register must fail")
96        };
97        assert!(matches!(err, MemoryRegistryError::DuplicateId(2)));
98    }
99}