diff --git a/docs/book.toml b/docs/book.toml new file mode 100644 index 00000000..6d59056e --- /dev/null +++ b/docs/book.toml @@ -0,0 +1,20 @@ +[book] +title = "Stable Structures" +description = "Documentation for the Stable Structures library" +authors = ["DFINITY Foundation"] +language = "en" +multilingual = false + +[build] +build-dir = "book" +create-missing = false + +[output.html] +git-repository-url = "https://github.com/dfinity/stable-structures" +additional-css = ["./mdbook-admonish.css"] + +[preprocessor] + +[preprocessor.admonish] +command = "mdbook-admonish" +assets_version = "3.0.3" # do not edit: managed by `mdbook-admonish install` diff --git a/docs/src/SUMMARY.md b/docs/src/SUMMARY.md index b5e0bd95..ecc463c0 100644 --- a/docs/src/SUMMARY.md +++ b/docs/src/SUMMARY.md @@ -1,7 +1,11 @@ # Summary -- [Introduction](./introduction.md) - - [Design Principles](./design-principles.md) - - [Available Data Structures](./available-data-structures.md) +- [Introduction](./introduction/introduction.md) + - [Design Principles](./introduction/design-principles.md) + - [Available Data Structures](./introduction/available-data-structures.md) + +- [Concepts](./concepts/concepts.md) + - [The Memory Trait](./concepts/memory-trait.md) + - [Memory Manager](./concepts/memory-manager.md) - [Schema Upgrades](./schema-upgrades.md) diff --git a/docs/src/concepts/concepts.md b/docs/src/concepts/concepts.md new file mode 100644 index 00000000..0d1a36c1 --- /dev/null +++ b/docs/src/concepts/concepts.md @@ -0,0 +1,3 @@ +# Concepts + +This section covers fundamental concepts for understanding how stable structures work and how they can be used effectively and safely. diff --git a/docs/src/concepts/memory-manager.md b/docs/src/concepts/memory-manager.md new file mode 100644 index 00000000..026b8e14 --- /dev/null +++ b/docs/src/concepts/memory-manager.md @@ -0,0 +1,37 @@ +# Memory Manager + +As mentioned in the previous section, each stable structure requires its own dedicated `Memory` instance. +This is an intentional design decision that limits [the blast radius](./design-principles.md) of potential bugs, ensuring that issues only affect the specific stable structure and its associated memory, not other stable structures. + +## Overview + +The Memory Manager enables the creation of up to 255 virtual memories from a single underlying memory instance. +When used with stable memory, this allows you to maintain up to 255 separate stable structures, each with its own isolated memory space. + +## Usage Example + +The following example demonstrates how to use the Memory Manager to create multiple stable structures: + +```rust +use ic_stable_structures::{ + memory_manager::{MemoryId, MemoryManager}, + BTreeMap, DefaultMemoryImpl, +}; + +// Initialize a MemoryManager with DefaultMemoryImpl as the underlying memory +let mem_mgr = MemoryManager::init(DefaultMemoryImpl::default()); + +// Create two separate BTreeMaps, each with its own virtual memory +let mut map_1: BTreeMap = BTreeMap::init(mem_mgr.get(MemoryId::new(0))); +let mut map_2: BTreeMap = BTreeMap::init(mem_mgr.get(MemoryId::new(1))); + +// Demonstrate independent operation of the two maps +map_1.insert(1, 2); +map_2.insert(1, 3); +assert_eq!(map_1.get(&1), Some(2)); // Succeeds as expected +``` + +```admonish warning "" +Virtual memories from the `MemoryManager` cannot be shared between stable structures. +Each memory instance should be assigned to exactly one stable structure. +``` diff --git a/docs/src/concepts/memory-trait.md b/docs/src/concepts/memory-trait.md new file mode 100644 index 00000000..78f494f1 --- /dev/null +++ b/docs/src/concepts/memory-trait.md @@ -0,0 +1,74 @@ +# The Memory Trait + +Stable structures are responsible for managing their own memory. +To provide maximum flexibility, the library introduces the `Memory` trait: + +```rust +pub trait Memory { + /// Equivalent to WebAssembly memory.size. + fn size(&self) -> u64; + + /// Equivalent to WebAssembly memory.grow. + fn grow(&self, pages: u64) -> i64; + + /// Copies bytes from this memory to the heap (in Wasm, memory 0). + fn read(&self, offset: u64, dst: &mut [u8]); + + /// Writes bytes from the heap (in Wasm, memory 0) to this memory. + fn write(&self, offset: u64, src: &[u8]); +} +``` + +The `Memory` trait intentionally models a [WebAssembly memory instance](https://webassembly.github.io/multi-memory/core/exec/runtime.html#memory-instances). +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. + +## Available Memory Implementations + +The library provides several implementations of the `Memory` trait, each designed for specific use cases: + +- `Ic0StableMemory`: Stores data in the Internet Computer's stable memory +- `VectorMemory`: An in-memory implementation backed by a Rust `Vec` +- `DefaultMemoryImpl`: A smart implementation that automatically selects the appropriate memory backend: + - Uses `Ic0StableMemory` when running in an Internet Computer canister (wasm32 target) + - Falls back to `VectorMemory` in other environments (like tests or non-IC contexts) + +Additional implementations such as `FileMemory` and `RestrictedMemory` exist but are less commonly used. + +```admonish note "" +In most cases, you should use `DefaultMemoryImpl` as your memory implementation. +``` + +### Usage Example + +Here's how to initialize a stable `BTreeMap` using `DefaultMemoryImpl`: + +```rust +use ic_stable_structures::{BTreeMap, DefaultMemoryImpl}; +let mut map: BTreeMap = BTreeMap::init(DefaultMemoryImpl::default()); +``` + +```admonish warning "" +**Important**: Stable structures cannot share memories. +Each memory must be dedicated to a single stable structure. +``` + +While the above example works correctly, it demonstrates a potential issue: the `BTreeMap` will use the entire stable memory. +This becomes problematic when trying to use multiple stable structures. +For example, the following code will fail in a canister: + +```rust +use ic_stable_structures::{BTreeMap, DefaultMemoryImpl}; +let mut map_1: BTreeMap = BTreeMap::init(DefaultMemoryImpl::default()); +let mut map_2: BTreeMap = BTreeMap::init(DefaultMemoryImpl::default()); + +map_1.insert(1, 2); +map_2.insert(1, 3); +assert_eq!(map_1.get(&1), Some(2)); // This assertion fails. +``` + +The code fails because both `map_1` and `map_2` are using the same stable memory. +This causes changes in one map to affect or corrupt the other. + +To solve this problem, the library provides the [MemoryManager](./memory-manager.md), which creates up to 255 virtual memories from a single memory instance. +We'll explore this solution in the next section. diff --git a/docs/src/available-data-structures.md b/docs/src/introduction/available-data-structures.md similarity index 100% rename from docs/src/available-data-structures.md rename to docs/src/introduction/available-data-structures.md diff --git a/docs/src/design-principles.md b/docs/src/introduction/design-principles.md similarity index 100% rename from docs/src/design-principles.md rename to docs/src/introduction/design-principles.md diff --git a/docs/src/introduction.md b/docs/src/introduction/introduction.md similarity index 100% rename from docs/src/introduction.md rename to docs/src/introduction/introduction.md