ic_sqlite_vfs/stable/
raw_memory.rs1use crate::config::STABLE_PAGE_SIZE;
7use std::cell::RefCell;
8use std::ops::Deref;
9use std::rc::Rc;
10
11pub trait Memory {
12 fn size(&self) -> u64;
13 fn grow(&self, pages: u64) -> i64;
14 fn read(&self, offset: u64, dst: &mut [u8]);
15 fn write(&self, offset: u64, src: &[u8]);
16
17 unsafe fn read_unsafe(&self, offset: u64, dst: *mut u8, count: usize) {
18 std::ptr::write_bytes(dst, 0, count);
19 let slice = std::slice::from_raw_parts_mut(dst, count);
20 self.read(offset, slice);
21 }
22}
23
24#[cfg(target_arch = "wasm32")]
25pub type DefaultMemoryImpl = Ic0StableMemory;
26
27#[cfg(not(target_arch = "wasm32"))]
28pub type DefaultMemoryImpl = VectorMemory;
29
30#[cfg(target_arch = "wasm32")]
31#[derive(Clone, Copy, Default)]
32pub struct Ic0StableMemory;
33
34#[cfg(target_arch = "wasm32")]
35#[link(wasm_import_module = "ic0")]
36extern "C" {
37 fn stable64_size() -> u64;
38 fn stable64_grow(additional_pages: u64) -> i64;
39 fn stable64_read(dst: u64, offset: u64, size: u64);
40 fn stable64_write(offset: u64, src: u64, size: u64);
41}
42
43#[cfg(target_arch = "wasm32")]
44impl Memory for Ic0StableMemory {
45 fn size(&self) -> u64 {
46 unsafe { stable64_size() }
47 }
48
49 fn grow(&self, pages: u64) -> i64 {
50 unsafe { stable64_grow(pages) }
51 }
52
53 fn read(&self, offset: u64, dst: &mut [u8]) {
54 unsafe { stable64_read(dst.as_mut_ptr() as u64, offset, dst.len() as u64) }
55 }
56
57 unsafe fn read_unsafe(&self, offset: u64, dst: *mut u8, count: usize) {
58 stable64_read(dst as u64, offset, count as u64);
59 }
60
61 fn write(&self, offset: u64, src: &[u8]) {
62 unsafe { stable64_write(offset, src.as_ptr() as u64, src.len() as u64) }
63 }
64}
65
66pub type VectorMemory = Rc<RefCell<Vec<u8>>>;
67
68impl Memory for RefCell<Vec<u8>> {
69 fn size(&self) -> u64 {
70 self.borrow().len() as u64 / STABLE_PAGE_SIZE
71 }
72
73 fn grow(&self, pages: u64) -> i64 {
74 let size = self.size();
75 let Some(next_size) = size.checked_add(pages) else {
76 return -1;
77 };
78 let Some(next_bytes) = next_size.checked_mul(STABLE_PAGE_SIZE) else {
79 return -1;
80 };
81 if next_bytes > usize::MAX as u64 {
82 return -1;
83 }
84 self.borrow_mut().resize(next_bytes as usize, 0);
85 size as i64
86 }
87
88 fn read(&self, offset: u64, dst: &mut [u8]) {
89 let end = checked_end(offset, dst.len(), "read");
90 dst.copy_from_slice(&self.borrow()[offset as usize..end as usize]);
91 }
92
93 unsafe fn read_unsafe(&self, offset: u64, dst: *mut u8, count: usize) {
94 let end = checked_end(offset, count, "read");
95 assert!(end as usize <= self.borrow().len(), "read: out of bounds");
96 std::ptr::copy(self.borrow().as_ptr().add(offset as usize), dst, count);
97 }
98
99 fn write(&self, offset: u64, src: &[u8]) {
100 let end = checked_end(offset, src.len(), "write");
101 self.borrow_mut()[offset as usize..end as usize].copy_from_slice(src);
102 }
103}
104
105impl<M: Memory> Memory for Rc<M> {
106 fn size(&self) -> u64 {
107 self.deref().size()
108 }
109
110 fn grow(&self, pages: u64) -> i64 {
111 self.deref().grow(pages)
112 }
113
114 fn read(&self, offset: u64, dst: &mut [u8]) {
115 self.deref().read(offset, dst);
116 }
117
118 unsafe fn read_unsafe(&self, offset: u64, dst: *mut u8, count: usize) {
119 self.deref().read_unsafe(offset, dst, count);
120 }
121
122 fn write(&self, offset: u64, src: &[u8]) {
123 self.deref().write(offset, src);
124 }
125}
126
127fn checked_end(offset: u64, len: usize, operation: &str) -> u64 {
128 let end = offset
129 .checked_add(len as u64)
130 .unwrap_or_else(|| panic!("{operation}: out of bounds"));
131 assert!(end <= usize::MAX as u64, "{operation}: out of bounds");
132 end
133}