canic_memory/macros/memory.rs
1/// Declare a stable-memory slot backed by the Canic memory registry.
2///
3/// The macro registers a declaration descriptor for lifecycle bootstrap
4/// validation during process startup. Evaluating the macro expression returns the
5/// [`VirtualMemory`](crate::cdk::structures::memory::VirtualMemory)
6/// handle only after bootstrap has validated the sealed declaration snapshot.
7/// Memory IDs are automatically namespaced per crate via `CARGO_PKG_NAME`.
8#[macro_export]
9macro_rules! ic_memory {
10 ($label:path, $id:expr) => {{
11 const _: () = {
12 #[ $crate::__reexports::ctor::ctor(unsafe, anonymous, crate_path = $crate::__reexports::ctor) ]
13 fn __canic_declare_memory_slot() {
14 $crate::registry::defer_register($id, env!("CARGO_PKG_NAME"), stringify!($label))
15 .expect("memory id declaration validation failed");
16 }
17 };
18
19 $crate::runtime::assert_memory_bootstrap_ready(stringify!($label), $id);
20
21 // Force the compiler to resolve the type. This causes a compile-time error
22 // if `$label` does not exist or is not a valid local type.
23 let _type_check: Option<$label> = None;
24
25 // Return the stable memory handle for further wrapping after bootstrap.
26 $crate::manager::MEMORY_MANAGER
27 .with_borrow_mut(|mgr| mgr.get($crate::cdk::structures::memory::MemoryId::new($id)))
28 }};
29}
30
31/// Declare a stable-memory slot with an explicit ABI-stable key.
32///
33/// Use this for every Canic-managed memory. The key, not the crate name or Rust
34/// type path, is the durable stable-memory identity.
35#[macro_export]
36macro_rules! ic_memory_key {
37 ($stable_key:literal, $label:path, $id:expr) => {{
38 const _: () = {
39 #[ $crate::__reexports::ctor::ctor(unsafe, anonymous, crate_path = $crate::__reexports::ctor) ]
40 fn __canic_declare_memory_slot() {
41 $crate::registry::defer_register_with_key(
42 $id,
43 env!("CARGO_PKG_NAME"),
44 stringify!($label),
45 $stable_key,
46 )
47 .expect("memory id declaration validation failed");
48 }
49 };
50
51 $crate::runtime::assert_memory_bootstrap_ready(stringify!($label), $id);
52
53 // Force the compiler to resolve the type. This causes a compile-time error
54 // if `$label` does not exist or is not a valid local type.
55 let _type_check: Option<$label> = None;
56
57 $crate::manager::MEMORY_MANAGER
58 .with_borrow_mut(|mgr| mgr.get($crate::cdk::structures::memory::MemoryId::new($id)))
59 }};
60}
61
62/// Reserve a contiguous block of stable-memory IDs for the current crate.
63///
64/// Stores the range request for validation during memory bootstrap. The
65/// reservation shares the crate namespace used by [`macro@ic_memory`].
66#[macro_export]
67macro_rules! ic_memory_range {
68 ($start:expr, $end:expr) => {{
69 // Enqueue this range reservation. The actual check/insert happens in
70 // `init_eager_tls()`. This guarantees the reservation is made
71 // before any memory IDs from this range are registered.
72 $crate::registry::defer_reserve_range(env!("CARGO_PKG_NAME"), $start, $end)
73 .expect("memory range reservation validation failed");
74 $crate::runtime::registry::MemoryRegistryRuntime::commit_pending_if_initialized()
75 .expect("late memory range registration commit failed");
76 }};
77}