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