17个不稳定版本 (3个重大变更)

0.4.4 2023年4月6日
0.4.3 2023年3月28日
0.4.1 2023年2月22日
0.4.0-rc12022年10月24日
0.1.0 2022年7月11日

#482 in 魔法豆

Download history 37/week @ 2024-03-11 84/week @ 2024-03-18 7/week @ 2024-03-25 127/week @ 2024-04-01 41/week @ 2024-04-08 1/week @ 2024-04-15 8/week @ 2024-04-22 16/week @ 2024-04-29 2/week @ 2024-05-06 86/week @ 2024-05-13 9/week @ 2024-05-20 279/week @ 2024-05-27 481/week @ 2024-06-03 119/week @ 2024-06-10 88/week @ 2024-06-17 69/week @ 2024-06-24

每月下载量:760

MIT 许可证

460KB
11K SLoC

test coverage 88.82%

IC稳定内存

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

功能

  • 8 稳定数据结构
    • SBox 替代 Box
    • SVecSLog 替代 Vec
    • SHashMap 替代 HashMap
    • SHashSet 替代 HashSet
    • SBTreeMap 替代 BTreeMap
    • SBTreeSet 替代 BTreeSet
    • SCertifiedBTreeMap 替代Dfinity的 RBTree
  • 强制执行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遵循语义版本控制指南,并将其推进了一步。当小版本或补丁版本发生变化时,您可以安全地更新此依赖项。但如果主要版本发生变化,这意味着您的罐将无法与新版本一起工作,您不应更新。这种情况不会经常发生,实际上,这个库有很多改进空间,无需破坏性变更,但这种情况可能发生。

贡献

这是一个新兴的软件,因此任何帮助都将受到高度重视。请随时通过Github问题提出PR、架构建议、错误报告或其他反馈。

测试覆盖率检查

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

依赖项

~4–13MB
~133K SLoC