| Crates.io | canic-memory |
| lib.rs | canic-memory |
| version | 0.9.10 |
| created_at | 2025-12-06 10:22:54.393593+00 |
| updated_at | 2026-01-23 13:33:07.364187+00 |
| description | Canic — a canister orchestration and management toolkit for the Internet Computer |
| homepage | https://github.com/dragginzgame/canic |
| repository | https://github.com/dragginzgame/canic |
| max_upload_size | |
| id | 1969954 |
| size | 66,244 |
Shared stable-memory helpers you can drop into any IC canister, even if you are not using the rest of Canic. It keeps you honest about which IDs you use and makes TLS-backed stable structures initialize in a predictable order.
What you get:
ic_memory! + registry).canic crate (only canic-utils and canic-cdk).Sample boot logs when everything is wired correctly:
17:27:24.796 [...] [Init] 🔧 --------------------- 'canic v0.6.x -----------------------
17:27:24.796 [...] [Init] 🏁 init: root (Prime)
17:27:24.796 [...] [Memory] 💾 memory.range: canic-core [5-30] (15/26 slots used)
17:27:24.796 [...] [Wasm] 📄 registry.insert: app (1013.10 KB)
...
17:27:26.879 [...] [CanisterLifecycle] ⚡ create_canister: nssc3-p7777-77777-aaawa-cai (5.000 TC)
17:27:27.549 [...] [Init] 🏁 init: app
17:27:27.549 [...] [Memory] 💾 memory.range: canic-core [5-30] (15/26 slots used)
manager — thread-local MemoryManager<DefaultMemoryImpl> used by all helpers.registry — range reservation + ID registry with pending queues for macro-driven registration.runtime — eager TLS initialization and registry startup helpers.macros — ic_memory!, ic_memory_range!, eager_static!, eager_init!.Add the crate to your Cargo.toml:
canic-memory = { workspace = true }
// Reserve IDs 10–19 for this crate (usually in a module's init or ctor).
canic_memory::ic_memory_range!(10, 19);
// Declare a stable-memory slot at ID 10 and wrap it in a stable BTreeMap.
use canic_memory::ic_memory;
use canic_memory::cdk::structures::{BTreeMap, DefaultMemoryImpl, memory::VirtualMemory};
use std::cell::RefCell;
thread_local! {
static USERS: RefCell<BTreeMap<u64, u64, VirtualMemory<DefaultMemoryImpl>>> =
RefCell::new(BTreeMap::init(ic_memory!(Users, 10)));
}
Call the runtime registry initializer once during init/post-upgrade to validate ranges and apply any pending registrations queued by macros. Repeated calls are allowed when the initial range is identical; conflicts return a MemoryRegistryError.
use canic_memory::runtime::registry::MemoryRegistryRuntime;
fn init_memory() {
// Optionally reserve an initial range for this crate before flushing queues.
// Pass `None` if you reserve exclusively via `ic_memory_range!` calls.
MemoryRegistryRuntime::init(Some((env!("CARGO_PKG_NAME"), 10, 19))).unwrap();
}
init_memory will:
Why bother? thread_local! values are lazy. If a stable BTreeMap (or similar) spins up the first time an endpoint is called, you get:
eager_static! and eager_init! make TLS setup a deliberate part of your init flow: run init_eager_tls() → run eager_init! blocks → flush the registry. After that, every endpoint starts with the same, prebuilt memory layout.
use canic_memory::{eager_init, eager_static, runtime::init_eager_tls};
use canic_memory::runtime::registry::MemoryRegistryRuntime;
use std::cell::RefCell;
eager_static! {
static CACHE: RefCell<u32> = const { RefCell::new(0) };
}
eager_init!({
// any one-time setup before entrypoints (optional)
});
fn init() {
// force eager TLS initialization first
init_eager_tls();
// then flush memory registrations
MemoryRegistryRuntime::init(None).unwrap();
}
The registry surfaces MemoryRegistryError for:
Handle these at init time so your canister fails fast on invalid memory layout.
registry::reset_for_tests() clears the registry and pending queues to keep unit tests isolated. Example:
#[test]
fn reserves_and_registers() {
canic_memory::registry::reset_for_tests();
canic_memory::runtime::registry::MemoryRegistryRuntime::init(Some(("my_crate", 1, 2))).unwrap();
canic_memory::registry::MemoryRegistry::register(1, "my_crate", "Slot").unwrap();
}
For diagnostics, the registry can provide:
MemoryRegistry::export_range_entries()MemoryRegistry::export_ids_by_range()These helpers are intended for debugging and tests, not as a stable API contract.
CARGO_PKG_NAME) when validating ranges.ic_memory_range! calls before init_memory.canic-memory plus canic-utils and canic-cdk; the rest of the stack is optional.