ExperimentalStableMemory

Byte-level access to (virtual) stable memory.

WARNING: As its name suggests, this library is experimental, subject to change and may be replaced by safer alternatives in later versions of Motoko. Use at your own risk and discretion.

This is a lightweight abstraction over IC stable memory and supports persisting raw binary data across Motoko upgrades. Use of this module is fully compatible with Motoko’s use of stable variables, whose persistence mechanism also uses (real) IC stable memory internally, but does not interfere with this API.

Memory is allocated, using 'grow(pages)`, sequentially and on demand, in units of 64KiB pages, starting with 0 allocated pages. New pages are zero initialized.

Each load operation loads from byte address offset in little-endian format using the natural bit-width of the type in question. The operation traps if attempting to read beyond the current stable memory size.

Each store operation stores to byte address offset in little-endian format using the natural bit-width of the type in question. The operation traps if attempting to write beyond the current stable memory size.

Text values can be handled by using Text.decodeUtf8 and Text.encodeUtf8, in conjunction with loadBlob and storeBlob.

The current page allocation and page contents is preserved across upgrades.

NB: The IC’s actual stable memory size (ic0.stable_size) may exceed the page size reported by Motoko function size(). This is to accommodate Motoko’s stable variables.

size

let size : () -> (pages : Nat32)

Current size of the stable memory, in pages. Each page is 64KiB (65536 bytes). Initially 0. Preserved across upgrades, together with contents of allocated StableMemory.

grow

let grow : (new_pages : Nat32) -> (oldpages : Nat32)

Grow current size of stable memory by pagecount pages. Each page is 64KiB (65536 bytes). Returns previous size when able to grow. Returns 0xFFFF if remaining pages insufficient. Every new page is zero-initialized, containing byte 0 at every offset.

loadNat32

let loadNat32 : (offset : Nat32) -> Nat32

storeNat32

let storeNat32 : (offset : Nat32, value : Nat32) -> ()

loadNat8

let loadNat8 : (offset : Nat32) -> Nat8

storeNat8

let storeNat8 : (offset : Nat32, value : Nat8) -> ()

loadNat16

let loadNat16 : (offset : Nat32) -> Nat16

storeNat16

let storeNat16 : (offset : Nat32, value : Nat16) -> ()

loadNat64

let loadNat64 : (offset : Nat32) -> Nat64

storeNat64

let storeNat64 : (offset : Nat32, value : Nat64) -> ()

loadInt32

let loadInt32 : (offset : Nat32) -> Int32

storeInt32

let storeInt32 : (offset : Nat32, value : Int32) -> ()

loadInt8

let loadInt8 : (offset : Nat32) -> Int8

storeInt8

let storeInt8 : (offset : Nat32, value : Int8) -> ()

loadInt16

let loadInt16 : (offset : Nat32) -> Int16

storeInt16

let storeInt16 : (offset : Nat32, value : Int16) -> ()

loadInt64

let loadInt64 : (offset : Nat32) -> Int64

storeInt64

let storeInt64 : (offset : Nat32, value : Int64) -> ()

loadFloat

let loadFloat : (offset : Nat32) -> Float

storeFloat

let storeFloat : (offset : Nat32, value : Float) -> ()

loadBlob

let loadBlob : (offset : Nat32, size : Nat) -> Blob

Load size bytes starting from offset as a Blob. Traps on out-of-bounds access.

storeBlob

let storeBlob : (offset : Nat32, value : Blob) -> ()

Write bytes of blob beginning at offset. Traps on out-of-bounds access.