{"id":13524620,"url":"https://github.com/dfinity/stable-structures","last_synced_at":"2025-04-05T12:02:06.900Z","repository":{"id":59693200,"uuid":"389440236","full_name":"dfinity/stable-structures","owner":"dfinity","description":"A collection of data structures for fearless canister upgrades.","archived":false,"fork":false,"pushed_at":"2024-04-22T11:01:42.000Z","size":1628,"stargazers_count":76,"open_issues_count":11,"forks_count":23,"subscribers_count":30,"default_branch":"main","last_synced_at":"2024-04-22T12:02:03.450Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/dfinity.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"Contributing.md","funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":".github/CODEOWNERS","security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2021-07-25T21:01:08.000Z","updated_at":"2024-06-04T16:44:13.653Z","dependencies_parsed_at":"2024-03-26T11:49:34.070Z","dependency_job_id":"6e66ebd3-fc75-453d-ba94-b3c4f6e6c1b8","html_url":"https://github.com/dfinity/stable-structures","commit_stats":{"total_commits":74,"total_committers":9,"mean_commits":8.222222222222221,"dds":0.527027027027027,"last_synced_commit":"d4718730d1f521e35818d846febdd3ba742ce7e7"},"previous_names":[],"tags_count":22,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dfinity%2Fstable-structures","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dfinity%2Fstable-structures/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dfinity%2Fstable-structures/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dfinity%2Fstable-structures/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/dfinity","download_url":"https://codeload.github.com/dfinity/stable-structures/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247332556,"owners_count":20921853,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-08-01T06:01:11.786Z","updated_at":"2025-04-05T12:02:06.880Z","avatar_url":"https://github.com/dfinity.png","language":"Rust","readme":"\u003cp\u003e\n  \u003ca href=\"https://crates.io/crates/ic-stable-structures\"\u003e\u003cimg alt=\"Crate Info\" src=\"https://img.shields.io/crates/v/ic-stable-structures.svg\"/\u003e\u003c/a\u003e\n  \u003ca href=\"https://github.com/dfinity/stable-structures/blob/master/LICENSE\"\u003e\u003cimg alt=\"Apache-2.0\" src=\"https://img.shields.io/github/license/dfinity/stable-structures\"/\u003e\u003c/a\u003e\n  \u003ca href=\"https://docs.rs/ic-stable-structures\"\u003e\u003cimg alt=\"API Docs\" src=\"https://img.shields.io/badge/docs.rs-ic--stable--structures-blue\"/\u003e\u003c/a\u003e\n  \u003ca href=\"https://forum.dfinity.org/\"\u003e\u003cimg alt=\"Chat on the Forum\" src=\"https://img.shields.io/badge/help-post%20on%20forum.dfinity.org-blue\"\u003e\u003c/a\u003e\n\u003c/p\u003e\n\n# Stable Structures\n\nA collection of scalable data structures for the [Internet Computer](https://internetcomputer.org) that persist across upgrades.\n\nStable structures are designed to use stable memory as the backing store, allowing them to grow to gigabytes in size without the need for `pre_upgrade`/`post_upgrade` hooks.\n\n## Background\n\nThe conventional approach to canister state persistence is to serialize the entire state to stable memory in the `pre_upgrade` hook and decode it back in the `post_upgrade` hook.\nThis approach is easy to implement and works well for relatively small datasets.\nUnfortunately, it does not scale well and can render a canister non-upgradable.\n\nThis library aims to simplify managing data structures directly in stable memory.\nFor more information about the philosophy behind the library, see [Roman's tutorial on stable structures](https://mmapped.blog/posts/14-stable-structures.html).\n\n## Available Data Structures\n\n- [BTreeMap]: A Key-Value store\n- [Vec]: A growable array\n- [Log]: An append-only list of variable-size entries\n- [Cell]: A serializable value\n- [MinHeap]: A priority queue.\n\n## Tutorials\n\n[Schema Upgrades](./docs/schema-upgrades.md)\n\n## How it Works\n\nStable structures are able to work directly in stable memory because each data structure manages\nits own memory.\nWhen initializing a stable structure, a memory is provided that the data structure can use to store its data.\n\nHere's a basic example:\n\n```rust\nuse ic_stable_structures::{BTreeMap, DefaultMemoryImpl};\nlet mut map: BTreeMap\u003cu64, u64, _\u003e = BTreeMap::init(DefaultMemoryImpl::default());\n\nmap.insert(1, 2);\nassert_eq!(map.get(\u00261), Some(2));\n```\n\nMemories are abstracted with the [Memory] trait, and stable structures can work with any storage\nbackend that implements this trait.\nThis includes stable memory, a vector ([VectorMemory]), or even a flat file ([FileMemory]).\n\nThe example above initializes a [BTreeMap] with a [DefaultMemoryImpl], which maps to stable memory when used in a canister and to a [VectorMemory] otherwise.\n\n\nNote that **stable structures cannot share memories.**\nEach memory must belong to only one stable structure.\nFor example, this fails when run in a canister:\n\n\n```no_run\nuse ic_stable_structures::{BTreeMap, DefaultMemoryImpl};\nlet mut map_1: BTreeMap\u003cu64, u64, _\u003e = BTreeMap::init(DefaultMemoryImpl::default());\nlet mut map_2: BTreeMap\u003cu64, u64, _\u003e = BTreeMap::init(DefaultMemoryImpl::default());\n\nmap_1.insert(1, 2);\nmap_2.insert(1, 3);\nassert_eq!(map_1.get(\u00261), Some(2)); // This assertion fails.\n```\n\nIt fails because both `map_1` and `map_2` are using the same stable memory under the hood, and so changes in `map_1` end up changing or corrupting `map_2`.\n\nTo address this issue, we make use of the [MemoryManager](memory_manager::MemoryManager), which takes a single memory and creates up to 255 virtual memories for our disposal.\nHere's the above failing example, but fixed by using the [MemoryManager](memory_manager::MemoryManager):\n\n```rust\nuse ic_stable_structures::{\n   memory_manager::{MemoryId, MemoryManager},\n   BTreeMap, DefaultMemoryImpl,\n};\nlet mem_mgr = MemoryManager::init(DefaultMemoryImpl::default());\nlet mut map_1: BTreeMap\u003cu64, u64, _\u003e = BTreeMap::init(mem_mgr.get(MemoryId::new(0)));\nlet mut map_2: BTreeMap\u003cu64, u64, _\u003e = BTreeMap::init(mem_mgr.get(MemoryId::new(1)));\n\nmap_1.insert(1, 2);\nmap_2.insert(1, 3);\nassert_eq!(map_1.get(\u00261), Some(2)); // Succeeds, as expected.\n```\n\n## Example Canister\n\nHere's a fully working canister example that ties everything together.\n\nDependencies:\n\n```toml\n[dependencies]\nic-cdk = \"0.6.8\"\nic-cdk-macros = \"0.6.8\"\nic-stable-structures = \"0.5.6\"\n```\n\nCode:\n\n```rust\nuse ic_stable_structures::memory_manager::{MemoryId, MemoryManager, VirtualMemory};\nuse ic_stable_structures::{DefaultMemoryImpl, StableBTreeMap};\nuse std::cell::RefCell;\n\ntype Memory = VirtualMemory\u003cDefaultMemoryImpl\u003e;\n\nthread_local! {\n    // The memory manager is used for simulating multiple memories. Given a `MemoryId` it can\n    // return a memory that can be used by stable structures.\n    static MEMORY_MANAGER: RefCell\u003cMemoryManager\u003cDefaultMemoryImpl\u003e\u003e =\n        RefCell::new(MemoryManager::init(DefaultMemoryImpl::default()));\n\n    // Initialize a `StableBTreeMap` with `MemoryId(0)`.\n    static MAP: RefCell\u003cStableBTreeMap\u003cu128, u128, Memory\u003e\u003e = RefCell::new(\n        StableBTreeMap::init(\n            MEMORY_MANAGER.with(|m| m.borrow().get(MemoryId::new(0))),\n        )\n    );\n}\n\n// Retrieves the value associated with the given key if it exists.\n#[ic_cdk_macros::query]\nfn get(key: u128) -\u003e Option\u003cu128\u003e {\n    MAP.with(|p| p.borrow().get(\u0026key))\n}\n\n// Inserts an entry into the map and returns the previous value of the key if it exists.\n#[ic_cdk_macros::update]\nfn insert(key: u128, value: u128) -\u003e Option\u003cu128\u003e {\n    MAP.with(|p| p.borrow_mut().insert(key, value))\n}\n```\n\n### More Examples\n\n- [Basic Example](https://github.com/dfinity/stable-structures/tree/main/examples/src/basic_example) (the one above)\n- [Quickstart Example](https://github.com/dfinity/stable-structures/tree/main/examples/src/quick_start): Ideal as a template when developing a new canister\n- [Custom Types Example](https://github.com/dfinity/stable-structures/tree/main/examples/src/custom_types_example): Showcases storing your own custom types\n\n## Combined Persistence\n\nIf your project exclusively relies on stable structures, the memory can expand in size without the requirement of `pre_upgrade`/`post_upgrade` hooks.\n\nHowever, it's important to note that if you also intend to perform serialization/deserialization of the heap data, utilizing the memory manager becomes necessary. To effectively combine both approaches, refer to the [Quickstart Example](https://github.com/dfinity/stable-structures/tree/main/examples/src/quick_start) for guidance.\n\n## Fuzzing\n\nStable structures requires strong guarantees to work reliably and scale over millions of operations. To that extent, we use fuzzing to emulate such operations on the available data structures.\n\nTo run a fuzzer locally, \n```sh\nrustup toolchain install nightly\ncargo install cargo-fuzz\n\n# To list available fuzzer targets\ncargo +nightly fuzz list\n\n# To run a target \ncargo +nightly fuzz run \u003cTARGET_NAME\u003e\n```","funding_links":[],"categories":["Storage and Databases"],"sub_categories":["Candid implementations"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdfinity%2Fstable-structures","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdfinity%2Fstable-structures","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdfinity%2Fstable-structures/lists"}