Skip to main content

ic_sqlite_vfs/stable/
raw_memory.rs

1//! Stable-memory backend used by the local MemoryManager fork.
2//!
3//! This module keeps only the byte-addressed memory trait and the native/IC
4//! implementations required by SQLite's virtual-memory adapter.
5
6use crate::config::STABLE_PAGE_SIZE;
7use std::cell::RefCell;
8use std::ops::Deref;
9use std::rc::Rc;
10
11#[derive(Clone, Copy, Debug, Eq, PartialEq)]
12pub enum MemoryBackendIdentity {
13    Ic0StableMemory,
14    Address(*const ()),
15}
16
17pub trait Memory {
18    fn identity(&self) -> MemoryBackendIdentity {
19        MemoryBackendIdentity::Address(std::ptr::from_ref(self).cast::<()>())
20    }
21
22    fn size(&self) -> u64;
23    fn grow(&self, pages: u64) -> i64;
24    fn read(&self, offset: u64, dst: &mut [u8]);
25    fn write(&self, offset: u64, src: &[u8]);
26
27    unsafe fn read_unsafe(&self, offset: u64, dst: *mut u8, count: usize) {
28        std::ptr::write_bytes(dst, 0, count);
29        let slice = std::slice::from_raw_parts_mut(dst, count);
30        self.read(offset, slice);
31    }
32}
33
34#[cfg(target_arch = "wasm32")]
35pub type DefaultMemoryImpl = Ic0StableMemory;
36
37#[cfg(not(target_arch = "wasm32"))]
38pub type DefaultMemoryImpl = VectorMemory;
39
40#[cfg(target_arch = "wasm32")]
41#[derive(Clone, Copy, Default)]
42pub struct Ic0StableMemory;
43
44#[cfg(target_arch = "wasm32")]
45#[link(wasm_import_module = "ic0")]
46extern "C" {
47    fn stable64_size() -> u64;
48    fn stable64_grow(additional_pages: u64) -> i64;
49    fn stable64_read(dst: u64, offset: u64, size: u64);
50    fn stable64_write(offset: u64, src: u64, size: u64);
51}
52
53#[cfg(target_arch = "wasm32")]
54impl Memory for Ic0StableMemory {
55    fn identity(&self) -> MemoryBackendIdentity {
56        MemoryBackendIdentity::Ic0StableMemory
57    }
58
59    fn size(&self) -> u64 {
60        unsafe { stable64_size() }
61    }
62
63    fn grow(&self, pages: u64) -> i64 {
64        unsafe { stable64_grow(pages) }
65    }
66
67    fn read(&self, offset: u64, dst: &mut [u8]) {
68        unsafe { stable64_read(dst.as_mut_ptr() as u64, offset, dst.len() as u64) }
69    }
70
71    unsafe fn read_unsafe(&self, offset: u64, dst: *mut u8, count: usize) {
72        stable64_read(dst as u64, offset, count as u64);
73    }
74
75    fn write(&self, offset: u64, src: &[u8]) {
76        unsafe { stable64_write(offset, src.as_ptr() as u64, src.len() as u64) }
77    }
78}
79
80#[allow(dead_code)]
81pub type VectorMemory = Rc<RefCell<Vec<u8>>>;
82
83impl Memory for RefCell<Vec<u8>> {
84    fn identity(&self) -> MemoryBackendIdentity {
85        MemoryBackendIdentity::Address(std::ptr::from_ref(self).cast::<()>())
86    }
87
88    fn size(&self) -> u64 {
89        self.borrow().len() as u64 / STABLE_PAGE_SIZE
90    }
91
92    fn grow(&self, pages: u64) -> i64 {
93        let size = self.size();
94        let Some(next_size) = size.checked_add(pages) else {
95            return -1;
96        };
97        let Some(next_bytes) = next_size.checked_mul(STABLE_PAGE_SIZE) else {
98            return -1;
99        };
100        if next_bytes > usize::MAX as u64 {
101            return -1;
102        }
103        self.borrow_mut().resize(next_bytes as usize, 0);
104        size as i64
105    }
106
107    fn read(&self, offset: u64, dst: &mut [u8]) {
108        let end = checked_end(offset, dst.len(), "read");
109        dst.copy_from_slice(&self.borrow()[offset as usize..end as usize]);
110    }
111
112    unsafe fn read_unsafe(&self, offset: u64, dst: *mut u8, count: usize) {
113        let end = checked_end(offset, count, "read");
114        assert!(end as usize <= self.borrow().len(), "read: out of bounds");
115        std::ptr::copy(self.borrow().as_ptr().add(offset as usize), dst, count);
116    }
117
118    fn write(&self, offset: u64, src: &[u8]) {
119        let end = checked_end(offset, src.len(), "write");
120        self.borrow_mut()[offset as usize..end as usize].copy_from_slice(src);
121    }
122}
123
124impl<M: Memory> Memory for Rc<M> {
125    fn identity(&self) -> MemoryBackendIdentity {
126        self.deref().identity()
127    }
128
129    fn size(&self) -> u64 {
130        self.deref().size()
131    }
132
133    fn grow(&self, pages: u64) -> i64 {
134        self.deref().grow(pages)
135    }
136
137    fn read(&self, offset: u64, dst: &mut [u8]) {
138        self.deref().read(offset, dst);
139    }
140
141    unsafe fn read_unsafe(&self, offset: u64, dst: *mut u8, count: usize) {
142        self.deref().read_unsafe(offset, dst, count);
143    }
144
145    fn write(&self, offset: u64, src: &[u8]) {
146        self.deref().write(offset, src);
147    }
148}
149
150fn checked_end(offset: u64, len: usize, operation: &str) -> u64 {
151    let end = offset
152        .checked_add(len as u64)
153        .unwrap_or_else(|| panic!("{operation}: out of bounds"));
154    assert!(end <= usize::MAX as u64, "{operation}: out of bounds");
155    end
156}