ic_stable_memory/mem/mod.rs
1//! Various functions and data structures to organize and work with raw stable memory.
2//!
3//! This crate provides a notion of "stable memory ownership". This is a set of techniques, that
4//! make data stored in stable memory appear like it gets automatically garbage collected by Rust's borrower.
5//! Each stable memory primitive includes a `stable drop flag` - a special flag, that is used by
6//! Rust to understand, whether it should release stable memory, when it naturally drops the value,
7//! or not.
8//!
9//! Stable drop flag rules are simple:
10//! 1. When you write something to stable memory, set its drop flag to `off` position.
11//! 2. When you read something from stable memory, and you don't have an intention to move it,
12//! set the drop flag to `off` position.
13//! 3. When you read something from stable memory with an intention to move this data somewhere else,
14//! set the drop flag to `on` position.
15//! 4. When you [Drop] the value, if the drop flag is `on` - call [StableType::stable_drop](crate::StableType::stable_drop),
16//! otherwise just [Drop].
17//!
18//! These rules are transparently managed at runtime. For users of this crate it appears like
19//! stable memory can "own" some value. A set of lifetimes applied later, on the data structure layer,
20//! to make it seem like the value is owned by a particular stable data structure.
21//!
22//! If you're thinking of implementing your own data structure using this crate, check [this](https://github.com/seniorjoinu/ic-stable-memory/docs/user-defined-data-structures.md)
23//! document for more info on this topic.
24
25use crate::encoding::{AsFixedSizeBytes, Buffer};
26use crate::primitive::StableType;
27use crate::stable;
28
29pub mod allocator;
30pub mod free_block;
31pub mod s_slice;
32
33/// A pointer to something is stable memory.
34///
35/// Just a handy alias for [u64].
36pub type StablePtr = u64;
37pub(crate) type StablePtrBuf = <u64 as AsFixedSizeBytes>::Buf;
38
39#[inline]
40pub(crate) fn stable_ptr_buf() -> StablePtrBuf {
41 StablePtrBuf::new(<StablePtr as AsFixedSizeBytes>::SIZE)
42}
43
44/// Reads raw bytes from stable memory.
45///
46/// Under the hood simply calls [stable64_read](ic_cdk::api::stable::stable64_read).
47///
48/// # Safety
49/// Make sure you're reading from a valid memory block. All kinds of bad things can happen.
50/// Also, this function does not handle stable memory `ownership` in any way, so you have to make sure
51/// your data won't get stable-dropped manually. See [crate::SBox] for an example of how this can be done.
52#[inline]
53pub unsafe fn read_bytes(ptr: StablePtr, buf: &mut [u8]) {
54 stable::read(ptr, buf);
55}
56
57/// Write raw bytes to stable memory.
58///
59/// Under the hood simply calls [stable64_write](ic_cdk::api::stable::stable64_write).
60///
61/// # Safety
62/// Make sure you're writing to a valid memory block. All kinds of bad things can happen.
63/// Also, this function does not handle stable memory `ownership` in any way, so you have to make sure
64/// your data won't get stable-dropped manually. See [SBox](crate::SBox) for an example of how this can be done.
65#[inline]
66pub unsafe fn write_bytes(ptr: StablePtr, buf: &[u8]) {
67 stable::write(ptr, buf);
68}
69
70fn read_fixed<T: AsFixedSizeBytes>(ptr: StablePtr) -> T {
71 let mut b = T::Buf::new(T::SIZE);
72 stable::read(ptr, b._deref_mut());
73
74 T::from_fixed_size_bytes(b._deref())
75}
76
77/// Reads a [StableType](crate::StableType) value *that won't move* implementing [AsFixedSizeBytes](crate::AsFixedSizeBytes) trait from stable memory.
78///
79/// See also [read_fixed_for_move].
80///
81/// This function creates an intermediate buffer of [AsFixedSizeBytes::SIZE](crate::AsFixedSizeBytes::SIZE) bytes,
82/// reads bytes from stable memory into it, then deserializes this buffer into a value itself and
83/// then sets its stable drop flag to `off` position.
84///
85/// # Safety
86/// Make sure you're reading from a valid memory block. All kinds of bad things can happen.
87/// This function manages stable memory `ownership`, this value *won't* be stable-dropped after it goes
88/// out of scope. Make sure you treat it accordingly.
89#[inline]
90pub unsafe fn read_fixed_for_reference<T: AsFixedSizeBytes + StableType>(ptr: StablePtr) -> T {
91 let mut it = read_fixed::<T>(ptr);
92 it.stable_drop_flag_off();
93
94 it
95}
96
97/// Reads a [StableType](crate::StableType) value *that will move* implementing [AsFixedSizeBytes](crate::AsFixedSizeBytes) trait from stable memory.
98///
99/// See also [read_fixed_for_reference].
100///
101/// This function creates an intermediate buffer of [AsFixedSizeBytes::SIZE](crate::AsFixedSizeBytes::SIZE) bytes,
102/// reads bytes from stable memory into it, then deserializes this buffer into a value itself and
103/// then sets its stable drop flag to `on` position.
104///
105/// # Safety
106/// Make sure you're reading from a valid memory block. All kinds of bad things can happen.
107/// This function manages stable memory `ownership`, this value *will* be stable-dropped after it goes
108/// out of scope. Make sure you treat it accordingly.
109#[inline]
110pub unsafe fn read_fixed_for_move<T: AsFixedSizeBytes + StableType>(ptr: StablePtr) -> T {
111 let mut it = read_fixed::<T>(ptr);
112 it.stable_drop_flag_on();
113
114 it
115}
116
117/// Writes a [StableType](crate::StableType) value implementing [AsFixedSizeBytes](crate::AsFixedSizeBytes) trait to stable memory.
118///
119/// This function creates an intermediate buffer of [AsFixedSizeBytes::SIZE](crate::AsFixedSizeBytes::SIZE) bytes,
120/// serializes the value into that buffer, writes the buffer into stable memory and then sets the stable
121/// drop flag of the value to `off` position.
122///
123/// # Safety
124/// Make sure you're writing to a valid memory block. All kinds of bad things can happen.
125/// This function manages stable memory `ownership`, this value *won't* be stable-dropped after it goes
126/// out of scope. Make sure you treat it accordingly.
127#[inline]
128pub unsafe fn write_fixed<T: AsFixedSizeBytes + StableType>(ptr: StablePtr, it: &mut T) {
129 it.stable_drop_flag_off();
130 stable::write(ptr, it.as_new_fixed_size_bytes()._deref())
131}
132
133/// Wipes out stable memory, making it zero pages again.
134///
135/// Utility function which is only available for targets other than `wasm`. Useful for tests.
136///
137/// # Safety
138/// Make sure to drop all previously created stable structures and reinit the allocator.
139#[cfg(not(target_family = "wasm"))]
140#[inline]
141pub unsafe fn clear() {
142 stable::clear();
143}