Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions docs/src/concepts/memory-trait.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,15 @@ pub trait Memory {
fn size(&self) -> u64;

/// Equivalent to WebAssembly memory.grow.
/// Returns the previous size, or -1 if the grow fails.
fn grow(&self, pages: u64) -> i64;

/// Copies bytes from this memory to the heap (in Wasm, memory 0).
/// Panics or traps if out of bounds.
fn read(&self, offset: u64, dst: &mut [u8]);

/// Writes bytes from the heap (in Wasm, memory 0) to this memory.
/// Panics or traps if out of bounds.
fn write(&self, offset: u64, src: &[u8]);
}
```
Expand All @@ -23,6 +26,13 @@ The `Memory` trait intentionally models a [WebAssembly memory instance](https://
This design choice ensures consistency with the interface of memories available to canisters.
It also provides future compatibility with potential multi-memory support in canisters.

## Panics

⚠️ `read` and `write` **assume the caller will not access memory outside the current size**.

If the range `[offset … offset + len)` exceeds available memory, the call panics (in native tests) or traps (in a Wasm canister).
Callers must store and check data lengths themselves or use higher-level containers such as `StableVec`.

## Available Memory Implementations

The library provides several implementations of the `Memory` trait, each designed for specific use cases:
Expand Down
42 changes: 24 additions & 18 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,34 +45,39 @@ const WASM_PAGE_SIZE: u64 = 65536;
/// The maximum number of stable memory pages a canister can address.
pub const MAX_PAGES: u64 = u64::MAX / WASM_PAGE_SIZE;

/// Abstraction over a WebAssembly-style linear memory (e.g., stable memory).
///
/// Implementations are expected to mirror WebAssembly semantics:
/// out-of-bounds accesses will cause a panic (in native) or trap (in Wasm).
pub trait Memory {
/// Returns the current size of the stable memory in WebAssembly
/// pages. (One WebAssembly page is 64Ki bytes.)
/// Returns the current size of the memory in WebAssembly pages.
///
/// One WebAssembly page is 64 KiB.
fn size(&self) -> u64;

/// Tries to grow the memory by `pages` many pages containing
/// zeroes. If successful, returns the previous size of the
/// memory (in pages). Otherwise, returns -1.
/// Grows the memory by `pages` pages filled with zeroes.
///
/// Returns the previous size in pages on success, or -1 if the
/// memory could not be grown.
fn grow(&self, pages: u64) -> i64;

/// Copies the data referred to by `offset` out of the stable memory
/// and replaces the corresponding bytes in `dst`.
/// Copies `dst.len()` bytes from memory starting at `offset` into `dst`.
///
/// Panics or traps if the read would go out of bounds.
fn read(&self, offset: u64, dst: &mut [u8]);

/// Copies `count` number of bytes of the data starting from `offset` out of the stable memory
/// into the buffer starting at `dst`.
/// Unsafe variant of `read` for advanced use.
///
/// This method is an alternative to `read` which does not require initializing a buffer and may
/// therefore be faster.
/// Copies `count` bytes from memory starting at `offset` into the
/// raw pointer `dst`. Initializes the destination before reading.
///
/// # Safety
///
/// Callers must guarantee that
/// * it is valid to write `count` number of bytes starting from `dst`,
/// * `dst..dst + count` does not overlap with `self`.
/// Caller must ensure:
/// - `dst` points to valid writable memory of at least `count` bytes.
/// - The memory range `dst..dst+count` does not overlap with `self`.
///
/// Implementations must guarantee that before the method returns, `count` number of bytes
/// starting from `dst` will be initialized.
/// Panics or traps if the read would go out of bounds.
#[inline]
unsafe fn read_unsafe(&self, offset: u64, dst: *mut u8, count: usize) {
// Initialize the buffer to make the slice valid.
Expand All @@ -81,8 +86,9 @@ pub trait Memory {
self.read(offset, slice)
}

/// Copies the data referred to by `src` and replaces the
/// corresponding segment starting at offset in the stable memory.
/// Copies `src.len()` bytes from `src` into memory starting at `offset`.
///
/// Panics or traps if the write would go out of bounds.
fn write(&self, offset: u64, src: &[u8]);
}

Expand Down