Skip to main content

ic_storage/
memory.rs

1//! Storage-owned backing interface and native/IC implementations.
2use crate::WASM_PAGE_SIZE_BYTES as PAGE;
3use std::{cell::RefCell, rc::Rc};
4
5///
6/// Memory
7///
8/// Page-addressed backing for Storage. Growth returns the previous page count,
9/// or -1 without changing memory. Successful growth adds zero-filled pages.
10/// Reads and writes must reject the complete out-of-bounds range before IO.
11/// Implementing this trait alone does not grant allocation authority.
12///
13pub trait Memory {
14    /// Current extent in 65,536-byte pages.
15    fn size(&self) -> u64;
16    /// Add zero-filled pages, or return -1 on refusal.
17    fn grow(&self, pages: u64) -> i64;
18    /// Read an admitted byte range.
19    fn read(&self, offset: u64, bytes: &mut [u8]);
20    /// Write an admitted byte range. Native writes need not be transactional.
21    fn write(&self, offset: u64, bytes: &[u8]);
22}
23
24impl<M: Memory + ?Sized> Memory for Rc<M> {
25    fn size(&self) -> u64 {
26        self.as_ref().size()
27    }
28    fn grow(&self, pages: u64) -> i64 {
29        self.as_ref().grow(pages)
30    }
31    fn read(&self, offset: u64, bytes: &mut [u8]) {
32        self.as_ref().read(offset, bytes);
33    }
34    fn write(&self, offset: u64, bytes: &[u8]) {
35        self.as_ref().write(offset, bytes);
36    }
37}
38impl<M: Memory + ?Sized> Memory for &M {
39    fn size(&self) -> u64 {
40        (**self).size()
41    }
42    fn grow(&self, pages: u64) -> i64 {
43        (**self).grow(pages)
44    }
45    fn read(&self, offset: u64, bytes: &mut [u8]) {
46        (**self).read(offset, bytes);
47    }
48    fn write(&self, offset: u64, bytes: &[u8]) {
49        (**self).write(offset, bytes);
50    }
51}
52
53/// Shared native backing; clones identify the same physical bytes.
54pub type VectorMemory = Rc<RefCell<Vec<u8>>>;
55impl Memory for RefCell<Vec<u8>> {
56    fn size(&self) -> u64 {
57        self.borrow().len() as u64 / PAGE
58    }
59    fn grow(&self, pages: u64) -> i64 {
60        let previous = self.size();
61        let Some(length) = previous
62            .checked_add(pages)
63            .and_then(|value| value.checked_mul(PAGE))
64            .and_then(|value| usize::try_from(value).ok())
65        else {
66            return -1;
67        };
68        let mut bytes = self.borrow_mut();
69        let additional = length - bytes.len();
70        if bytes.try_reserve(additional).is_err() {
71            return -1;
72        }
73        bytes.resize(length, 0);
74        i64::try_from(previous).expect("native page count")
75    }
76    fn read(&self, offset: u64, bytes: &mut [u8]) {
77        let start = usize::try_from(offset).expect("native offset");
78        let end = start
79            .checked_add(bytes.len())
80            .expect("native range overflow");
81        bytes.copy_from_slice(&self.borrow()[start..end]);
82    }
83    fn write(&self, offset: u64, bytes: &[u8]) {
84        let start = usize::try_from(offset).expect("native offset");
85        let end = start
86            .checked_add(bytes.len())
87            .expect("native range overflow");
88        self.borrow_mut()[start..end].copy_from_slice(bytes);
89    }
90}
91
92/// Native default backing for fixture and host integration.
93#[cfg(not(target_arch = "wasm32"))]
94pub type DefaultMemoryImpl = VectorMemory;
95
96///
97/// DefaultMemoryImpl
98///
99/// Canister stable memory through IC Platform's owned system-call boundary.
100///
101#[cfg(target_arch = "wasm32")]
102#[derive(Clone, Copy, Default)]
103pub struct DefaultMemoryImpl {
104    _private: (),
105}
106#[cfg(target_arch = "wasm32")]
107impl Memory for DefaultMemoryImpl {
108    fn size(&self) -> u64 {
109        ic_platform_runtime::ic0::stable64_size()
110    }
111    fn grow(&self, pages: u64) -> i64 {
112        let previous = ic_platform_runtime::ic0::stable64_grow(pages);
113        if previous == u64::MAX {
114            -1
115        } else {
116            i64::try_from(previous).expect("IC page count")
117        }
118    }
119    fn read(&self, offset: u64, bytes: &mut [u8]) {
120        ic_platform_runtime::ic0::stable64_read(bytes, offset);
121    }
122    fn write(&self, offset: u64, bytes: &[u8]) {
123        ic_platform_runtime::ic0::stable64_write(bytes, offset);
124    }
125}