3 个版本

0.4.2 2023年2月22日
0.4.1 2023年1月29日
0.4.0 2023年1月29日

#41 in #collections

Download history 44/week @ 2024-03-12 83/week @ 2024-03-19 47/week @ 2024-03-26 142/week @ 2024-04-02 31/week @ 2024-04-09 5/week @ 2024-04-16 10/week @ 2024-04-23 18/week @ 2024-04-30 11/week @ 2024-05-07 92/week @ 2024-05-14 10/week @ 2024-05-21 365/week @ 2024-05-28 439/week @ 2024-06-04 99/week @ 2024-06-11 80/week @ 2024-06-18 67/week @ 2024-06-25

每月下载量 770
用于 ic-stable-memory

MIT 许可证

19KB
395

test coverage 88.82%

IC 稳定内存

允许将 canister 的稳定内存用作主内存。

功能

  • 8 稳定数据结构
    • SBox 用于替代 Box
    • SVecSLog 用于替代 Vec
    • SHashMap 用于替代 HashMap
    • SHashSet 用于替代 HashSet
    • SBTreeMap 用于替代 BTreeMap
    • SBTreeSet 用于替代 BTreeSet
    • SCertifiedBTreeMap 用于替代 Dfinity 的 RBTree
    • SCertifiedBTreeSet 作为 SCertifiedBTreeMap<T, ()> 的轻量级包装
  • 强制执行 Rust 的借用规则
    • 数据结构在离开作用域时自动释放
    • 数据结构拥有其内部值,允许通过引用访问
  • API 允许程序对 OutOfMemory 错误进行编程式响应,同时几乎与 std 相同
  • 构建您自己的稳定数据结构的完整工具集

安装

# cargo.toml

[dependencies]
ic-stable-memory = "0.4"

快速示例

让我们构建一个 Todo 应用程序,因为它们非常受欢迎 :)

use candid::{CandidType, Deserialize};
use ic_cdk_macros::{init, post_upgrade, pre_upgrade, query, update};
use ic_stable_memory::collections::SVec;
use ic_stable_memory::derive::{CandidAsDynSizeBytes, StableType};
use ic_stable_memory::{
  retrieve_custom_data, stable_memory_init, stable_memory_post_upgrade,
  stable_memory_pre_upgrade, store_custom_data, SBox,
};
use std::cell::RefCell;

#[derive(CandidType, Deserialize, StableType, CandidAsDynSizeBytes, Debug, Clone)]
struct Task {
  title: String,
  description: String,
}

// If you can implement AsFixedSizeBytes for your data type, 
// you can store it directly, without wrapping in SBox
type State = SVec<SBox<Task>>;

thread_local! {
  static STATE: RefCell<Option<State>> = RefCell::default();
}

#[update]
fn add_task(task: Task) {
  STATE.with(|s| {
    let boxed_task = SBox::new(task).expect("Out of memory");
    s.borrow_mut()
            .as_mut()
            .unwrap()
            .push(boxed_task)
            .expect("Out of memory");
  });
}

#[update]
fn remove_task(idx: u32) {
  STATE.with(|s| {
    s.borrow_mut().as_mut().unwrap().remove(idx as usize);
  });
}

#[update]
fn swap_tasks(idx_1: u32, idx_2: u32) {
  STATE.with(|s| {
    s.borrow_mut()
            .as_mut()
            .unwrap()
            .swap(idx_1 as usize, idx_2 as usize);
  });
}

#[query]
fn get_todo_list() -> Vec<Task> {
  STATE.with(|s| {
    let mut result = Vec::new();

    for task in s.borrow().as_ref().unwrap().iter() {
      result.push(task.clone());
    }

    result
  })
}

#[init]
fn init() {
  stable_memory_init();

  STATE.with(|s| {
    *s.borrow_mut() = Some(SVec::new());
  });
}

#[pre_upgrade]
fn pre_upgrade() {
  let state: State = STATE.with(|s| s.borrow_mut().take().unwrap());
  let boxed_state = SBox::new(state).expect("Out of memory");

  store_custom_data(0, boxed_state);

  stable_memory_pre_upgrade().expect("Out of memory");
}

#[post_upgrade]
fn post_upgrade() {
  stable_memory_post_upgrade();

  let state = retrieve_custom_data::<State>(0).unwrap().into_inner();
  STATE.with(|s| {
    *s.borrow_mut() = Some(state);
  });
}

文档

  1. 快速入门
  2. 完整的 API 文档
  3. 如何迁移运行中的 canister
  4. 如何处理 OutOfMemory 错误
  5. 如何确保数据可升级性
  6. 如何实现编码特性
  7. 如何节省 cycles 并使其更快
  8. 基准测试
  9. 如何构建您自己的稳定数据结构
  10. 内部结构

示例项目

版本控制

ic-stable-memory 遵循语义版本控制指南,并将其推进了一步。当小版本或补丁版本发生变化时,您可以安全地更新这个依赖项。但如果主要版本发生变化,这意味着您的canister将无法与新版本兼容,因此不应更新。这种事件不会经常发生,实际上,这个库有很多改进的空间,而无需破坏性更改,但这可能发生。

贡献

这是一个新兴的软件,因此任何帮助都备受赞赏。您可以通过Github issues自由提出PR、架构建议、错误报告或任何其他反馈。

测试覆盖率检查

  • cargo安装 grcov
  • rustup组件添加 llvm-tools-preview
  • ./coverage.sh --test(没有 --test 将不会重新构建)

依赖项

~1.5MB
~34K SLoC