1use crate::libsqlite3::*;
4
5use fragile::Fragile;
6use js_sys::{Date, Math, Number, Uint8Array, WebAssembly};
7use std::{
8 collections::HashMap,
9 ffi::{CStr, CString},
10 ops::{Deref, DerefMut},
11};
12use wasm_bindgen::{prelude::wasm_bindgen, JsCast};
13
14pub const SQLITE3_HEADER: &str = "SQLite format 3";
16
17#[derive(Hash, PartialEq, Eq)]
21pub struct VfsPtr(pub *mut sqlite3_vfs);
22
23unsafe impl Send for VfsPtr {}
24unsafe impl Sync for VfsPtr {}
25
26#[derive(Hash, PartialEq, Eq)]
30pub struct FilePtr(pub *mut sqlite3_file);
31
32unsafe impl Send for FilePtr {}
33unsafe impl Sync for FilePtr {}
34
35pub struct FragileComfirmed<T> {
40 fragile: Fragile<T>,
41}
42
43unsafe impl<T> Send for FragileComfirmed<T> {}
44unsafe impl<T> Sync for FragileComfirmed<T> {}
45
46impl<T> FragileComfirmed<T> {
47 pub fn new(t: T) -> Self {
48 FragileComfirmed {
49 fragile: Fragile::new(t),
50 }
51 }
52}
53
54impl<T> Deref for FragileComfirmed<T> {
55 type Target = T;
56 fn deref(&self) -> &Self::Target {
57 self.fragile.get()
58 }
59}
60
61impl<T> DerefMut for FragileComfirmed<T> {
62 fn deref_mut(&mut self) -> &mut Self::Target {
63 self.fragile.get_mut()
64 }
65}
66
67pub fn get_random_name() -> String {
69 let random = Number::from(Math::random()).to_string(36).unwrap();
70 random.slice(2, random.length()).as_string().unwrap()
71}
72
73#[wasm_bindgen(module = "/src/vfs/utils.js")]
83extern "C" {
84 type JSUtils;
85
86 #[wasm_bindgen(static_method_of = JSUtils, js_name = toSlice)]
87 fn to_slice(memory: &WebAssembly::Memory, buffer: &Uint8Array, dst: *mut u8, len: usize);
88
89 #[wasm_bindgen(static_method_of = JSUtils, js_name = toUint8Array)]
90 fn to_uint8_array(memory: &WebAssembly::Memory, src: *const u8, len: usize, dst: &Uint8Array);
91}
92
93pub fn copy_to_vec(src: &Uint8Array) -> Vec<u8> {
95 let mut vec = vec![0u8; src.length() as usize];
96 copy_to_slice(src, vec.as_mut_slice());
97 vec
98}
99
100pub fn copy_to_slice(src: &Uint8Array, dst: &mut [u8]) {
102 assert!(
103 src.length() as usize == dst.len(),
104 "Unit8Array and slice have different sizes"
105 );
106
107 let buf = wasm_bindgen::memory();
108 let mem = buf.unchecked_ref::<WebAssembly::Memory>();
109 JSUtils::to_slice(mem, src, dst.as_mut_ptr(), dst.len());
110}
111
112pub fn copy_to_uint8_array(src: &[u8]) -> Uint8Array {
114 let uint8 = Uint8Array::new_with_length(src.len() as u32);
115 copy_to_uint8_array_subarray(src, &uint8);
116 uint8
117}
118
119pub fn copy_to_uint8_array_subarray(src: &[u8], dst: &Uint8Array) {
121 assert!(
122 src.len() == dst.length() as _,
123 "Unit8Array and slice have different sizes"
124 );
125 let buf = wasm_bindgen::memory();
126 let mem = buf.unchecked_ref::<WebAssembly::Memory>();
127 JSUtils::to_uint8_array(mem, src.as_ptr(), src.len(), dst)
128}
129
130#[macro_export]
134macro_rules! bail {
135 ($ex:expr) => {
136 bail!($ex, SQLITE_ERROR);
137 };
138 ($ex:expr, $code: expr) => {
139 if $ex {
140 return $code;
141 }
142 };
143}
144
145#[macro_export]
151macro_rules! check_option {
152 ($ex:expr) => {
153 check_option!($ex, SQLITE_ERROR)
154 };
155 ($ex:expr, $code: expr) => {
156 if let Some(v) = $ex {
157 v
158 } else {
159 return $code;
160 }
161 };
162}
163
164#[macro_export]
170macro_rules! check_result {
171 ($ex:expr) => {
172 check_result!($ex, SQLITE_ERROR)
173 };
174 ($ex:expr, $code: expr) => {
175 if let Ok(v) = $ex {
176 v
177 } else {
178 return $code;
179 }
180 };
181}
182
183#[repr(C)]
187pub struct SQLiteVfsFile {
188 pub io_methods: sqlite3_file,
191 pub vfs: *mut sqlite3_vfs,
193 pub flags: i32,
195 pub name_ptr: *const u8,
198 pub name_length: usize,
200}
201
202impl SQLiteVfsFile {
203 pub unsafe fn from_file(file: *mut sqlite3_file) -> &'static SQLiteVfsFile {
209 &*file.cast::<Self>()
210 }
211
212 pub unsafe fn name(&self) -> &'static mut str {
218 std::str::from_utf8_unchecked_mut(std::slice::from_raw_parts_mut(
220 self.name_ptr.cast_mut(),
221 self.name_length,
222 ))
223 }
224}
225
226#[derive(thiserror::Error, Debug)]
228pub enum VfsError {
229 #[error("An error occurred converting the given vfs name to a CStr")]
230 ToCStr,
231 #[error("An error occurred while registering vfs with sqlite")]
232 RegisterVfs,
233}
234
235pub fn register_vfs(
237 vfs_name: &str,
238 default_vfs: bool,
239 register_fn: fn(*const std::os::raw::c_char) -> sqlite3_vfs,
240) -> Result<*mut sqlite3_vfs, VfsError> {
241 let name = CString::new(vfs_name).map_err(|_| VfsError::ToCStr)?;
242 let name_ptr = name.into_raw();
243 let vfs = Box::leak(Box::new(register_fn(name_ptr)));
244
245 let ret = unsafe { sqlite3_vfs_register(vfs, i32::from(default_vfs)) };
246 if ret != SQLITE_OK {
247 unsafe {
248 drop(Box::from_raw(vfs));
249 drop(CString::from_raw(name_ptr));
250 }
251 return Err(VfsError::RegisterVfs);
252 }
253
254 Ok(vfs as *mut sqlite3_vfs)
255}
256
257pub fn page_read<T, G: Fn(usize) -> Option<T>, R: Fn(T, &mut [u8], (usize, usize))>(
259 buf: &mut [u8],
260 page_size: usize,
261 file_size: usize,
262 offset: usize,
263 get_page: G,
264 read_fn: R,
265) -> i32 {
266 if page_size == 0 || file_size == 0 {
267 buf.fill(0);
268 return SQLITE_IOERR_SHORT_READ;
269 }
270
271 let mut bytes_read = 0;
272 let mut p_data_offset = 0;
273 let p_data_length = buf.len();
274 let i_offset = offset;
275
276 while p_data_offset < p_data_length {
277 let file_offset = i_offset + p_data_offset;
278 let page_idx = file_offset / page_size;
279 let page_offset = file_offset % page_size;
280 let page_addr = page_idx * page_size;
281
282 let Some(page) = get_page(page_addr) else {
283 break;
284 };
285
286 let page_length = (page_size - page_offset).min(p_data_length - p_data_offset);
287 read_fn(
288 page,
289 &mut buf[p_data_offset..p_data_offset + page_length],
290 (page_offset, page_offset + page_length),
291 );
292
293 p_data_offset += page_length;
294 bytes_read += page_length;
295 }
296
297 if bytes_read < p_data_length {
298 buf[bytes_read..].fill(0);
299 return SQLITE_IOERR_SHORT_READ;
300 }
301
302 SQLITE_OK
303}
304
305pub trait VfsStore {
307 fn read(&self, buf: &mut [u8], offset: usize) -> i32;
309 fn write(&mut self, buf: &[u8], offset: usize);
311 fn truncate(&mut self, size: usize);
313 fn size(&self) -> usize;
315}
316
317#[derive(Default)]
319pub struct MemLinearStore(Vec<u8>);
320
321impl VfsStore for MemLinearStore {
322 fn read(&self, buf: &mut [u8], offset: usize) -> i32 {
323 let size = buf.len();
324 let end = size + offset;
325 if self.0.len() <= offset {
326 buf.fill(0);
327 return SQLITE_IOERR_SHORT_READ;
328 }
329
330 let read_end = end.min(self.0.len());
331 let read_size = read_end - offset;
332 buf[..read_size].copy_from_slice(&self.0[offset..read_end]);
333
334 if read_size < size {
335 buf[read_size..].fill(0);
336 return SQLITE_IOERR_SHORT_READ;
337 }
338 SQLITE_OK
339 }
340
341 fn write(&mut self, buf: &[u8], offset: usize) {
342 let end = buf.len() + offset;
343 if end > self.0.len() {
344 self.0.resize(end, 0);
345 }
346 self.0[offset..end].copy_from_slice(buf);
347 }
348
349 fn truncate(&mut self, size: usize) {
350 self.0.truncate(size);
351 }
352
353 fn size(&self) -> usize {
354 self.0.len()
355 }
356}
357
358#[derive(Default)]
362pub struct MemPageStore {
363 pages: HashMap<usize, Vec<u8>>,
364 file_size: usize,
365 page_size: usize,
366}
367
368impl VfsStore for MemPageStore {
369 fn read(&self, buf: &mut [u8], offset: usize) -> i32 {
370 page_read(
371 buf,
372 self.page_size,
373 self.file_size,
374 offset,
375 |addr| self.pages.get(&addr),
376 |page, buf, (start, end)| {
377 buf.copy_from_slice(&page[start..end]);
378 },
379 )
380 }
381
382 fn write(&mut self, buf: &[u8], offset: usize) {
383 let size = buf.len();
384 let end = size + offset;
385
386 for fill in (self.file_size..end).step_by(size) {
387 self.pages.insert(fill, vec![0; size]);
388 }
389 if let Some(buffer) = self.pages.get_mut(&offset) {
390 buffer.copy_from_slice(buf);
391 } else {
392 self.pages.insert(offset, buf.to_vec());
393 }
394
395 self.page_size = size;
396 self.file_size = self.file_size.max(end);
397 }
398
399 fn truncate(&mut self, size: usize) {
400 for offset in size..self.file_size {
401 self.pages.remove(&offset);
402 }
403 self.file_size = size;
404 }
405
406 fn size(&self) -> usize {
407 self.file_size
408 }
409}
410
411pub trait StoreControl<S> {
413 fn add_file(vfs: *mut sqlite3_vfs, file: &str, flags: i32);
415 fn contains_file(vfs: *mut sqlite3_vfs, file: &str) -> bool;
417 fn delete_file(vfs: *mut sqlite3_vfs, file: &str) -> Option<S>;
419 fn with_file<F: Fn(&S) -> i32>(vfs_file: &SQLiteVfsFile, f: F) -> Option<i32>;
421 fn with_file_mut<F: Fn(&mut S) -> i32>(vfs_file: &SQLiteVfsFile, f: F) -> Option<i32>;
423}
424
425#[allow(clippy::missing_safety_doc)]
427pub trait SQLiteVfs<IO: SQLiteIoMethods> {
428 const VERSION: ::std::os::raw::c_int;
429
430 fn vfs(vfs_name: *const ::std::os::raw::c_char) -> sqlite3_vfs {
431 sqlite3_vfs {
432 iVersion: Self::VERSION,
433 szOsFile: std::mem::size_of::<SQLiteVfsFile>() as i32,
434 mxPathname: 1024,
435 pNext: std::ptr::null_mut(),
436 zName: vfs_name,
437 pAppData: std::ptr::null_mut(),
438 xOpen: Some(Self::xOpen),
439 xDelete: Some(Self::xDelete),
440 xAccess: Some(Self::xAccess),
441 xFullPathname: Some(Self::xFullPathname),
442 xDlOpen: None,
443 xDlError: None,
444 xDlSym: None,
445 xDlClose: None,
446 xRandomness: Some(x_methods_shim::xRandomness),
447 xSleep: Some(x_methods_shim::xSleep),
448 xCurrentTime: Some(x_methods_shim::xCurrentTime),
449 xGetLastError: Some(Self::xGetLastError),
450 xCurrentTimeInt64: Some(x_methods_shim::xCurrentTimeInt64),
451 xSetSystemCall: None,
452 xGetSystemCall: None,
453 xNextSystemCall: None,
454 }
455 }
456
457 unsafe extern "C" fn xOpen(
458 pVfs: *mut sqlite3_vfs,
459 zName: sqlite3_filename,
460 pFile: *mut sqlite3_file,
461 flags: ::std::os::raw::c_int,
462 pOutFlags: *mut ::std::os::raw::c_int,
463 ) -> ::std::os::raw::c_int {
464 let name = if zName.is_null() {
465 get_random_name()
466 } else {
467 check_result!(CStr::from_ptr(zName).to_str()).into()
468 };
469
470 if !IO::StoreControl::contains_file(pVfs, &name) {
471 if flags & SQLITE_OPEN_CREATE == 0 {
472 return SQLITE_CANTOPEN;
473 }
474 IO::StoreControl::add_file(pVfs, &name, flags);
475 }
476
477 let leak = name.leak();
478 let vfs_file = pFile.cast::<SQLiteVfsFile>();
479 (*vfs_file).vfs = pVfs;
480 (*vfs_file).flags = flags;
481 (*vfs_file).name_ptr = leak.as_ptr();
482 (*vfs_file).name_length = leak.len();
483
484 (*pFile).pMethods = &IO::METHODS;
485
486 if !pOutFlags.is_null() {
487 *pOutFlags = flags;
488 }
489
490 SQLITE_OK
491 }
492
493 unsafe extern "C" fn xDelete(
494 pVfs: *mut sqlite3_vfs,
495 zName: *const ::std::os::raw::c_char,
496 _syncDir: ::std::os::raw::c_int,
497 ) -> ::std::os::raw::c_int {
498 bail!(zName.is_null(), SQLITE_IOERR_DELETE);
499 let s = check_result!(CStr::from_ptr(zName).to_str());
500 IO::StoreControl::delete_file(pVfs, s);
501 SQLITE_OK
502 }
503
504 unsafe extern "C" fn xAccess(
505 pVfs: *mut sqlite3_vfs,
506 zName: *const ::std::os::raw::c_char,
507 _flags: ::std::os::raw::c_int,
508 pResOut: *mut ::std::os::raw::c_int,
509 ) -> ::std::os::raw::c_int {
510 *pResOut = if zName.is_null() {
511 0
512 } else {
513 let s = check_result!(CStr::from_ptr(zName).to_str());
514 i32::from(IO::StoreControl::contains_file(pVfs, s))
515 };
516
517 SQLITE_OK
518 }
519
520 unsafe extern "C" fn xFullPathname(
521 _pVfs: *mut sqlite3_vfs,
522 zName: *const ::std::os::raw::c_char,
523 nOut: ::std::os::raw::c_int,
524 zOut: *mut ::std::os::raw::c_char,
525 ) -> ::std::os::raw::c_int {
526 bail!(zName.is_null() || zOut.is_null(), SQLITE_CANTOPEN);
527 let len = CStr::from_ptr(zName).count_bytes() + 1;
528 bail!(len > nOut as usize, SQLITE_CANTOPEN);
529 zName.copy_to(zOut, len);
530 SQLITE_OK
531 }
532
533 unsafe extern "C" fn xGetLastError(
534 _pVfs: *mut sqlite3_vfs,
535 _nOut: ::std::os::raw::c_int,
536 _zOut: *mut ::std::os::raw::c_char,
537 ) -> ::std::os::raw::c_int {
538 SQLITE_OK
539 }
540}
541
542#[allow(clippy::missing_safety_doc)]
544pub trait SQLiteIoMethods {
545 type Store: VfsStore;
546 type StoreControl: StoreControl<Self::Store>;
547
548 const VERSION: ::std::os::raw::c_int;
549
550 const METHODS: sqlite3_io_methods = sqlite3_io_methods {
551 iVersion: Self::VERSION,
552 xClose: Some(Self::xClose),
553 xRead: Some(Self::xRead),
554 xWrite: Some(Self::xWrite),
555 xTruncate: Some(Self::xTruncate),
556 xSync: Some(Self::xSync),
557 xFileSize: Some(Self::xFileSize),
558 xLock: Some(Self::xLock),
559 xUnlock: Some(Self::xUnlock),
560 xCheckReservedLock: Some(Self::xCheckReservedLock),
561 xFileControl: Some(Self::xFileControl),
562 xSectorSize: Some(Self::xSectorSize),
563 xDeviceCharacteristics: Some(Self::xDeviceCharacteristics),
564 xShmMap: None,
565 xShmLock: None,
566 xShmBarrier: None,
567 xShmUnmap: None,
568 xFetch: None,
569 xUnfetch: None,
570 };
571
572 unsafe extern "C" fn xClose(pFile: *mut sqlite3_file) -> ::std::os::raw::c_int {
573 let vfs_file = SQLiteVfsFile::from_file(pFile);
574 if vfs_file.flags & SQLITE_OPEN_DELETEONCLOSE != 0 {
575 check_option!(Self::StoreControl::delete_file(
576 vfs_file.vfs,
577 vfs_file.name()
578 ));
579 }
580 drop(unsafe { Box::from_raw(vfs_file.name()) });
581 SQLITE_OK
582 }
583
584 unsafe extern "C" fn xRead(
585 pFile: *mut sqlite3_file,
586 zBuf: *mut ::std::os::raw::c_void,
587 iAmt: ::std::os::raw::c_int,
588 iOfst: sqlite3_int64,
589 ) -> ::std::os::raw::c_int {
590 let vfs_file = SQLiteVfsFile::from_file(pFile);
591 let f = |store: &Self::Store| {
592 let size = iAmt as usize;
593 let offset = iOfst as usize;
594 let slice = std::slice::from_raw_parts_mut(zBuf.cast::<u8>(), size);
595 store.read(slice, offset)
596 };
597 check_option!(Self::StoreControl::with_file(vfs_file, f))
598 }
599
600 unsafe extern "C" fn xWrite(
601 pFile: *mut sqlite3_file,
602 zBuf: *const ::std::os::raw::c_void,
603 iAmt: ::std::os::raw::c_int,
604 iOfst: sqlite3_int64,
605 ) -> ::std::os::raw::c_int {
606 let vfs_file = SQLiteVfsFile::from_file(pFile);
607 let f = |store: &mut Self::Store| {
608 let (offset, size) = (iOfst as usize, iAmt as usize);
609 let slice = std::slice::from_raw_parts(zBuf.cast::<u8>(), size);
610 store.write(slice, offset);
611 SQLITE_OK
612 };
613 check_option!(Self::StoreControl::with_file_mut(vfs_file, f))
614 }
615
616 unsafe extern "C" fn xTruncate(
617 pFile: *mut sqlite3_file,
618 size: sqlite3_int64,
619 ) -> ::std::os::raw::c_int {
620 let vfs_file = SQLiteVfsFile::from_file(pFile);
621 let f = |store: &mut Self::Store| {
622 store.truncate(size as usize);
623 SQLITE_OK
624 };
625 check_option!(Self::StoreControl::with_file_mut(vfs_file, f))
626 }
627
628 unsafe extern "C" fn xSync(
629 _pFile: *mut sqlite3_file,
630 _flags: ::std::os::raw::c_int,
631 ) -> ::std::os::raw::c_int {
632 SQLITE_OK
633 }
634
635 unsafe extern "C" fn xFileSize(
636 pFile: *mut sqlite3_file,
637 pSize: *mut sqlite3_int64,
638 ) -> ::std::os::raw::c_int {
639 let vfs_file = SQLiteVfsFile::from_file(pFile);
640 let f = |store: &Self::Store| {
641 *pSize = store.size() as sqlite3_int64;
642 SQLITE_OK
643 };
644 check_option!(Self::StoreControl::with_file(vfs_file, f))
645 }
646
647 unsafe extern "C" fn xLock(
648 _pFile: *mut sqlite3_file,
649 _eLock: ::std::os::raw::c_int,
650 ) -> ::std::os::raw::c_int {
651 SQLITE_OK
652 }
653
654 unsafe extern "C" fn xUnlock(
655 _pFile: *mut sqlite3_file,
656 _eLock: ::std::os::raw::c_int,
657 ) -> ::std::os::raw::c_int {
658 SQLITE_OK
659 }
660
661 unsafe extern "C" fn xCheckReservedLock(
662 _pFile: *mut sqlite3_file,
663 pResOut: *mut ::std::os::raw::c_int,
664 ) -> ::std::os::raw::c_int {
665 *pResOut = 0;
666 SQLITE_OK
667 }
668
669 unsafe extern "C" fn xFileControl(
670 _pFile: *mut sqlite3_file,
671 _op: ::std::os::raw::c_int,
672 _pArg: *mut ::std::os::raw::c_void,
673 ) -> ::std::os::raw::c_int {
674 SQLITE_NOTFOUND
675 }
676
677 unsafe extern "C" fn xSectorSize(_pFile: *mut sqlite3_file) -> ::std::os::raw::c_int {
678 512
679 }
680
681 unsafe extern "C" fn xDeviceCharacteristics(_arg1: *mut sqlite3_file) -> ::std::os::raw::c_int {
682 0
683 }
684}
685
686#[allow(clippy::missing_safety_doc)]
688pub mod x_methods_shim {
689 use super::*;
690
691 #[cfg(target_feature = "atomics")]
693 pub unsafe extern "C" fn xSleep(
694 _pVfs: *mut sqlite3_vfs,
695 microseconds: ::std::os::raw::c_int,
696 ) -> ::std::os::raw::c_int {
697 use std::{thread, time::Duration};
698
699 thread::sleep(Duration::from_micros(microseconds as u64));
700 SQLITE_OK
701 }
702
703 #[cfg(not(target_feature = "atomics"))]
704 pub unsafe extern "C" fn xSleep(
705 _pVfs: *mut sqlite3_vfs,
706 _microseconds: ::std::os::raw::c_int,
707 ) -> ::std::os::raw::c_int {
708 SQLITE_OK
709 }
710
711 pub unsafe extern "C" fn xRandomness(
713 _pVfs: *mut sqlite3_vfs,
714 nByte: ::std::os::raw::c_int,
715 zOut: *mut ::std::os::raw::c_char,
716 ) -> ::std::os::raw::c_int {
717 for i in 0..nByte {
718 *zOut.offset(i as isize) = (Math::random() * 255000.0) as _;
719 }
720 nByte
721 }
722
723 pub unsafe extern "C" fn xCurrentTime(
725 _pVfs: *mut sqlite3_vfs,
726 pTimeOut: *mut f64,
727 ) -> ::std::os::raw::c_int {
728 *pTimeOut = 2440587.5 + (Date::new_0().get_time() / 86400000.0);
729 SQLITE_OK
730 }
731
732 pub unsafe extern "C" fn xCurrentTimeInt64(
734 _pVfs: *mut sqlite3_vfs,
735 pOut: *mut sqlite3_int64,
736 ) -> ::std::os::raw::c_int {
737 *pOut = ((2440587.5 * 86400000.0) + Date::new_0().get_time()) as sqlite3_int64;
738 SQLITE_OK
739 }
740}
741
742#[cfg(test)]
743mod tests {
744 wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser);
745
746 use crate::vfs::utils::{copy_to_slice, copy_to_uint8_array_subarray};
747
748 use super::{copy_to_uint8_array, copy_to_vec};
749 use js_sys::Uint8Array;
750 use wasm_bindgen_test::wasm_bindgen_test;
751
752 #[wasm_bindgen_test]
753 fn test_js_utils() {
754 let buf1 = vec![1, 2, 3, 4];
755 let uint8 = copy_to_uint8_array(&buf1);
756 let buf2 = copy_to_vec(&uint8);
757 assert_eq!(buf1, buf2);
758
759 let mut buf3 = vec![0u8; 2];
760 copy_to_slice(&uint8.subarray(0, 2), &mut buf3);
761 assert_eq!(buf3, vec![1, 2]);
762
763 let buf4 = Uint8Array::new_with_length(3);
764 copy_to_uint8_array_subarray(&buf3, &buf4.subarray(1, 3));
765 assert!(buf4.get_index(0) == 0);
766 assert!(buf4.get_index(1) == 1);
767 assert!(buf4.get_index(2) == 2);
768 }
769}