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