Skip to main content

ic_memory/
lib.rs

1#![forbid(unsafe_code)]
2#![deny(rustdoc::broken_intra_doc_links)]
3#![doc = include_str!("../README.md")]
4
5//! Stable-memory allocation-governance primitives for Internet Computer
6//! canister upgrades.
7//!
8//! `ic-memory` prevents stable-memory slot drift.
9//!
10//! Once a stable key is committed to a physical allocation slot, future binaries
11//! must either reopen that same stable key on that same slot or declare a new
12//! stable key.
13//!
14//! The crate records and validates durable ownership in both directions: an
15//! active stable key cannot move to a different physical slot, and an active
16//! physical slot cannot be reused by a different stable key.
17//!
18//! The intended integration flow is:
19//!
20//! 1. Recover the persisted allocation ledger.
21//! 2. Declare the stable stores expected by the current binary.
22//! 3. Validate those declarations against ledger history and any framework
23//!    policy.
24//! 4. Commit the next generation.
25//! 5. Only then open stable-memory handles through a validated allocation
26//!    session.
27//!
28//! This crate owns allocation invariants, not framework policy. Namespace
29//! rules, range ownership, controller authorization, endpoint lifecycle, schema
30//! migrations, and application validation belong to the framework or
31//! application.
32//!
33//! Use these primitives before opening stable-memory handles. Integrations
34//! should recover the historical ledger, declare the stores expected by the
35//! current binary, validate declarations against history and policy, commit a
36//! new generation, and only then publish a validated allocation session that can
37//! open slots through a storage substrate.
38//!
39//! [`AllocationBootstrap`] is the golden path for whichever layer owns a given
40//! ledger store. Canic may own bootstrap for a framework canister and compose
41//! IcyDB/application declarations through its registry; IcyDB may own bootstrap
42//! directly for generated database stores; or a standalone application canister
43//! may own bootstrap itself. Exactly one owner should bootstrap one ledger
44//! store. Multiple layers in the same canister must either compose declarations
45//! into that owner or use distinct ledger stores and allocation domains.
46//!
47//! `ic-stable-structures` `MemoryManager` IDs are the first-class supported
48//! physical slot substrate. The crate still keeps narrow internal abstractions
49//! for storage adapters and diagnostics, but the native IC path is
50//! `MemoryManager` ID 0 -> `ic-stable-structures::Cell<StableCellLedgerRecord,
51//! _>` -> [`LedgerCommitStore`] -> committed [`AllocationLedger`] payloads.
52//!
53//! `ic-memory` is not a replacement for `ic-stable-structures` collections and
54//! does not wrap typed stores such as `StableBTreeMap`.
55
56pub mod bootstrap;
57pub mod declaration;
58pub mod diagnostics;
59pub mod key;
60pub mod ledger;
61pub mod physical;
62pub mod policy;
63pub mod registry;
64pub mod runtime;
65pub mod schema;
66pub mod session;
67pub mod slot;
68pub mod stable_cell;
69pub mod substrate;
70pub mod validation;
71
72pub use ic_stable_structures as stable_structures;
73
74pub use bootstrap::{
75    AllocationBootstrap, BootstrapCommit, BootstrapError, BootstrapReservationError,
76    BootstrapRetirementError,
77};
78pub use declaration::{
79    AllocationDeclaration, DeclarationCollector, DeclarationSnapshot, DeclarationSnapshotError,
80};
81pub use diagnostics::{DiagnosticExport, DiagnosticGeneration, DiagnosticRecord};
82pub use key::{StableKey, StableKeyError};
83pub use ledger::{
84    AllocationHistory, AllocationLedger, AllocationRecord, AllocationReservationError,
85    AllocationRetirement, AllocationRetirementError, AllocationStageError, AllocationState,
86    CURRENT_LEDGER_SCHEMA_VERSION, CURRENT_PHYSICAL_FORMAT_ID, CborLedgerCodec, GenerationRecord,
87    LedgerCodec, LedgerCommitError, LedgerCommitStore, LedgerCompatibility,
88    LedgerCompatibilityError, LedgerIntegrityError, SchemaMetadataRecord,
89};
90pub use physical::{
91    AuthoritativeSlot, CommitRecoveryError, CommitSlotDiagnostic, CommitSlotIndex,
92    CommitStoreDiagnostic, CommittedGenerationBytes, DualCommitStore, DualProtectedCommitStore,
93    ProtectedGenerationSlot, select_authoritative_slot,
94};
95pub use policy::AllocationPolicy;
96pub use registry::{
97    StaticMemoryDeclaration, StaticMemoryDeclarationError, StaticMemoryRangeDeclaration,
98    collect_static_memory_declarations, register_static_memory_declaration,
99    register_static_memory_manager_declaration,
100    register_static_memory_manager_declaration_with_schema, register_static_memory_manager_range,
101    register_static_memory_range_declaration, static_memory_declaration_snapshot,
102    static_memory_declarations, static_memory_range_authority, static_memory_range_declarations,
103};
104pub use runtime::{
105    RuntimeBootstrapError, RuntimeOpenError, RuntimePolicyError, bootstrap_default_memory_manager,
106    bootstrap_default_memory_manager_with_policy,
107};
108pub use schema::{SchemaMetadata, SchemaMetadataError};
109pub use session::{AllocationSession, AllocationSessionError, ValidatedAllocations};
110pub use slot::{
111    AllocationSlot, AllocationSlotDescriptor, IC_MEMORY_AUTHORITY_OWNER,
112    IC_MEMORY_AUTHORITY_PURPOSE, IC_MEMORY_LEDGER_LABEL, IC_MEMORY_LEDGER_STABLE_KEY,
113    IC_MEMORY_STABLE_KEY_PREFIX, MEMORY_MANAGER_DESCRIPTOR_VERSION,
114    MEMORY_MANAGER_GOVERNANCE_MAX_ID, MEMORY_MANAGER_INVALID_ID, MEMORY_MANAGER_LEDGER_ID,
115    MEMORY_MANAGER_MAX_ID, MEMORY_MANAGER_MIN_ID, MEMORY_MANAGER_SUBSTRATE,
116    MemoryManagerAuthorityRecord, MemoryManagerIdRange, MemoryManagerRangeAuthority,
117    MemoryManagerRangeAuthorityError, MemoryManagerRangeError, MemoryManagerRangeMode,
118    MemoryManagerSlotError, is_ic_memory_stable_key, memory_manager_governance_range,
119    validate_memory_manager_id,
120};
121pub use stable_cell::{
122    STABLE_CELL_HEADER_SIZE, STABLE_CELL_LAYOUT_VERSION, STABLE_CELL_MAGIC,
123    STABLE_CELL_VALUE_OFFSET, StableCellLedgerRecord, StableCellPayloadError,
124    decode_stable_cell_ledger_record, decode_stable_cell_payload,
125};
126pub use substrate::{LedgerAnchor, StorageSubstrate};
127pub use validation::{AllocationValidationError, validate_allocations};
128
129#[doc(hidden)]
130pub mod __reexports {
131    pub use ctor;
132}
133
134/// Register a `MemoryManager` allocation declaration during static initialization.
135///
136/// This macro only registers declaration metadata. It does not open stable
137/// memory. The bootstrap owner still has to collect/seal declarations, validate
138/// them against the ledger, commit the generation, and then open memory handles.
139#[macro_export]
140macro_rules! ic_memory_declaration {
141    (key = $stable_key:literal, ty = $label:path, id = $id:expr $(,)?) => {
142        const _: () = {
143            #[allow(dead_code)]
144            type IcMemoryTypeCheck = $label;
145
146            #[ $crate::__reexports::ctor::ctor(unsafe, anonymous, crate_path = $crate::__reexports::ctor) ]
147            fn __ic_memory_register_static_declaration() {
148                $crate::register_static_memory_manager_declaration(
149                    $id,
150                    env!("CARGO_PKG_NAME"),
151                    stringify!($label),
152                    $stable_key,
153                )
154                .expect("ic-memory static memory declaration failed");
155            }
156        };
157    };
158    (key = $stable_key:literal, label = $label:literal, id = $id:expr $(,)?) => {
159        const _: () = {
160            #[ $crate::__reexports::ctor::ctor(unsafe, anonymous, crate_path = $crate::__reexports::ctor) ]
161            fn __ic_memory_register_static_declaration() {
162                $crate::register_static_memory_manager_declaration(
163                    $id,
164                    env!("CARGO_PKG_NAME"),
165                    $label,
166                    $stable_key,
167                )
168                .expect("ic-memory static memory declaration failed");
169            }
170        };
171    };
172}
173
174/// Declare a `MemoryManager` allocation range during static initialization.
175#[macro_export]
176macro_rules! ic_memory_range {
177    (start = $start:expr, end = $end:expr $(,)?) => {
178        $crate::ic_memory_range!(
179            start = $start,
180            end = $end,
181            mode = Reserved,
182        );
183    };
184    (start = $start:expr, end = $end:expr, mode = $mode:ident $(,)?) => {
185        const _: () = {
186            #[ $crate::__reexports::ctor::ctor(unsafe, anonymous, crate_path = $crate::__reexports::ctor) ]
187            fn __ic_memory_register_static_range() {
188                $crate::register_static_memory_manager_range(
189                    $start,
190                    $end,
191                    env!("CARGO_PKG_NAME"),
192                    $crate::MemoryManagerRangeMode::$mode,
193                    None,
194                )
195                .expect("ic-memory static memory range declaration failed");
196            }
197        };
198    };
199}
200
201/// Declare and open a validated `MemoryManager` slot by stable key.
202///
203/// The macro registers declaration metadata during static initialization and
204/// returns the validated default-runtime memory handle at expression use time.
205#[macro_export]
206macro_rules! ic_memory_key {
207    ($stable_key:literal, $label:path, $id:expr $(,)?) => {{
208        $crate::ic_memory_declaration!(key = $stable_key, ty = $label, id = $id,);
209        $crate::runtime::open_default_memory_manager_memory($stable_key, $id)
210            .expect("ic-memory stable memory opened before runtime bootstrap")
211    }};
212    (key = $stable_key:literal, ty = $label:path, id = $id:expr $(,)?) => {{ $crate::ic_memory_key!($stable_key, $label, $id) }};
213    (key = $stable_key:literal, label = $label:literal, id = $id:expr $(,)?) => {{
214        $crate::ic_memory_declaration!(key = $stable_key, label = $label, id = $id,);
215        $crate::runtime::open_default_memory_manager_memory($stable_key, $id)
216            .expect("ic-memory stable memory opened before runtime bootstrap")
217    }};
218}
219
220/// Register one pre-bootstrap hook.
221#[macro_export]
222macro_rules! eager_init {
223    ($body:block) => {
224        const _: () = {
225            fn __ic_memory_registered_eager_init_body() {
226                $body
227            }
228
229            #[ $crate::__reexports::ctor::ctor(unsafe, anonymous, crate_path = $crate::__reexports::ctor) ]
230            fn __ic_memory_register_eager_init() {
231                $crate::runtime::defer_eager_init(__ic_memory_registered_eager_init_body);
232            }
233        };
234    };
235}