sqlite_wasm_rs/vfs/
memory.rs1use crate::libsqlite3::*;
28use crate::vfs::utils::{
29 check_import_db, ImportDbError, MemChunksFile, SQLiteIoMethods, SQLiteVfs, SQLiteVfsFile,
30 VfsAppData, VfsError, VfsFile, VfsResult, VfsStore,
31};
32
33use std::cell::RefCell;
34use std::collections::HashMap;
35use std::ffi::CStr;
36
37const VFS_NAME: &CStr = c"memvfs";
38
39type Result<T> = std::result::Result<T, MemVfsError>;
40
41pub(crate) enum MemFile {
42 Main(MemChunksFile),
43 Temp(MemChunksFile),
44}
45
46impl MemFile {
47 fn new(flags: i32) -> Self {
48 if flags & SQLITE_OPEN_MAIN_DB == 0 {
49 Self::Temp(MemChunksFile::default())
50 } else {
51 Self::Main(MemChunksFile::waiting_for_write())
52 }
53 }
54
55 fn file(&self) -> &MemChunksFile {
56 let (MemFile::Main(file) | MemFile::Temp(file)) = self;
57 file
58 }
59
60 fn file_mut(&mut self) -> &mut MemChunksFile {
61 let (MemFile::Main(file) | MemFile::Temp(file)) = self;
62 file
63 }
64}
65
66impl VfsFile for MemFile {
67 fn read(&self, buf: &mut [u8], offset: usize) -> VfsResult<bool> {
68 self.file().read(buf, offset)
69 }
70
71 fn write(&mut self, buf: &[u8], offset: usize) -> VfsResult<()> {
72 self.file_mut().write(buf, offset)
73 }
74
75 fn truncate(&mut self, size: usize) -> VfsResult<()> {
76 self.file_mut().truncate(size)
77 }
78
79 fn flush(&mut self) -> VfsResult<()> {
80 self.file_mut().flush()
81 }
82
83 fn size(&self) -> VfsResult<usize> {
84 self.file().size()
85 }
86}
87
88type MemAppData = RefCell<HashMap<String, MemFile>>;
89
90struct MemStore;
91
92impl VfsStore<MemFile, MemAppData> for MemStore {
93 fn add_file(vfs: *mut sqlite3_vfs, file: &str, flags: i32) -> VfsResult<()> {
94 let app_data = unsafe { Self::app_data(vfs) };
95 app_data
96 .borrow_mut()
97 .insert(file.into(), MemFile::new(flags));
98 Ok(())
99 }
100
101 fn contains_file(vfs: *mut sqlite3_vfs, file: &str) -> VfsResult<bool> {
102 let app_data = unsafe { Self::app_data(vfs) };
103 Ok(app_data.borrow().contains_key(file))
104 }
105
106 fn delete_file(vfs: *mut sqlite3_vfs, file: &str) -> VfsResult<()> {
107 let app_data = unsafe { Self::app_data(vfs) };
108 if app_data.borrow_mut().remove(file).is_none() {
109 return Err(VfsError::new(
110 SQLITE_IOERR_DELETE,
111 format!("{file} not found"),
112 ));
113 }
114 Ok(())
115 }
116
117 fn with_file<F: Fn(&MemFile) -> VfsResult<i32>>(
118 vfs_file: &SQLiteVfsFile,
119 f: F,
120 ) -> VfsResult<i32> {
121 let name = unsafe { vfs_file.name() };
122 let app_data = unsafe { Self::app_data(vfs_file.vfs) };
123 match app_data.borrow().get(name) {
124 Some(file) => f(file),
125 None => Err(VfsError::new(SQLITE_IOERR, format!("{name} not found"))),
126 }
127 }
128
129 fn with_file_mut<F: Fn(&mut MemFile) -> VfsResult<i32>>(
130 vfs_file: &SQLiteVfsFile,
131 f: F,
132 ) -> VfsResult<i32> {
133 let name = unsafe { vfs_file.name() };
134 let app_data = unsafe { Self::app_data(vfs_file.vfs) };
135 match app_data.borrow_mut().get_mut(name) {
136 Some(file) => f(file),
137 None => Err(VfsError::new(SQLITE_IOERR, format!("{name} not found"))),
138 }
139 }
140}
141
142struct MemIoMethods;
143
144impl SQLiteIoMethods for MemIoMethods {
145 type File = MemFile;
146 type AppData = MemAppData;
147 type Store = MemStore;
148
149 const VERSION: ::std::os::raw::c_int = 1;
150}
151
152struct MemVfs;
153
154impl SQLiteVfs<MemIoMethods> for MemVfs {
155 const VERSION: ::std::os::raw::c_int = 1;
156}
157
158#[derive(thiserror::Error, Debug)]
159pub enum MemVfsError {
160 #[error(transparent)]
161 ImportDb(#[from] ImportDbError),
162 #[error("Generic error: {0}")]
163 Generic(String),
164}
165
166pub struct MemVfsUtil(&'static VfsAppData<MemAppData>);
168
169unsafe impl Send for MemVfsUtil {}
173unsafe impl Sync for MemVfsUtil {}
174
175impl Default for MemVfsUtil {
176 fn default() -> Self {
177 MemVfsUtil::new()
178 }
179}
180
181impl MemVfsUtil {
182 pub fn new() -> Self {
184 MemVfsUtil(unsafe { install() })
185 }
186}
187
188impl MemVfsUtil {
189 fn import_db_unchecked_impl(
190 &self,
191 filename: &str,
192 bytes: &[u8],
193 page_size: usize,
194 clear_wal: bool,
195 ) -> Result<()> {
196 if self.exists(filename) {
197 return Err(MemVfsError::Generic(format!(
198 "{filename} file already exists"
199 )));
200 }
201
202 self.0.borrow_mut().insert(filename.into(), {
203 let mut file = MemFile::Main(MemChunksFile::new(page_size));
204 file.write(bytes, 0).unwrap();
205 if clear_wal {
206 file.write(&[1, 1], 18).unwrap();
207 }
208 file
209 });
210
211 Ok(())
212 }
213
214 pub fn import_db(&self, filename: &str, bytes: &[u8]) -> Result<()> {
222 let page_size = check_import_db(bytes)?;
223 self.import_db_unchecked_impl(filename, bytes, page_size, true)
224 }
225
226 pub fn import_db_unchecked(
228 &self,
229 filename: &str,
230 bytes: &[u8],
231 page_size: usize,
232 ) -> Result<()> {
233 self.import_db_unchecked_impl(filename, bytes, page_size, false)
234 }
235
236 pub fn export_db(&self, filename: &str) -> Result<Vec<u8>> {
238 let name2file = self.0.borrow();
239
240 if let Some(file) = name2file.get(filename) {
241 let file_size = file.size().unwrap();
242 let mut ret = vec![0; file_size];
243 file.read(&mut ret, 0).unwrap();
244 Ok(ret)
245 } else {
246 Err(MemVfsError::Generic(
247 "The file to be exported does not exist".into(),
248 ))
249 }
250 }
251
252 pub fn delete_db(&self, filename: &str) {
254 self.0.borrow_mut().remove(filename);
255 }
256
257 pub fn clear_all(&self) {
259 std::mem::take(&mut *self.0.borrow_mut());
260 }
261
262 pub fn exists(&self, filename: &str) -> bool {
264 self.0.borrow().contains_key(filename)
265 }
266
267 pub fn list(&self) -> Vec<String> {
269 self.0.borrow().keys().cloned().collect()
270 }
271
272 pub fn count(&self) -> usize {
274 self.0.borrow().len()
275 }
276}
277
278pub(crate) unsafe fn install() -> &'static VfsAppData<MemAppData> {
279 let vfs = sqlite3_vfs_find(VFS_NAME.as_ptr());
280
281 let vfs = if vfs.is_null() {
282 let vfs = Box::leak(Box::new(MemVfs::vfs(
283 VFS_NAME.as_ptr(),
284 VfsAppData::new(MemAppData::default()).leak(),
285 )));
286 assert_eq!(
287 sqlite3_vfs_register(vfs, 1),
288 SQLITE_OK,
289 "failed to register memvfs"
290 );
291 vfs as *mut sqlite3_vfs
292 } else {
293 vfs
294 };
295
296 MemStore::app_data(vfs)
297}
298
299pub(crate) unsafe fn uninstall() {
300 let vfs = sqlite3_vfs_find(VFS_NAME.as_ptr());
301
302 if !vfs.is_null() {
303 assert_eq!(
304 sqlite3_vfs_unregister(vfs),
305 SQLITE_OK,
306 "failed to unregister memvfs"
307 );
308 drop(VfsAppData::<MemAppData>::from_raw(
309 (*vfs).pAppData as *mut _,
310 ));
311 drop(Box::from_raw(vfs));
312 }
313}
314
315#[cfg(test)]
316mod tests {
317 use crate::{
318 mem_vfs::{MemAppData, MemFile, MemStore},
319 utils::{test_suite::test_vfs_store, VfsAppData},
320 };
321 use wasm_bindgen_test::wasm_bindgen_test;
322
323 #[wasm_bindgen_test]
324 fn test_memory_vfs_store() {
325 test_vfs_store::<MemAppData, MemFile, MemStore>(VfsAppData::new(MemAppData::default()))
326 .unwrap();
327 }
328}