1//! SQLite's default VFS in the browser: the files of `fs`, so that a replica, its write-ahead
2//! log and the notebook it caches are kept and written out alike.
3#![allow(unsafe_code)]
4
5use super::{FILES, Node, Shared, normal, with};
6use rsqlite_vfs::{
7 OsCallback, SQLiteIoMethods, SQLiteVfs, SQLiteVfsFile, VfsError, VfsFile, VfsResult, VfsStore,
8 ffi::{SQLITE_CANTOPEN, SQLITE_IOERR, SQLITE_IOERR_DELETE, sqlite3_vfs},
9};
10use std::{path::Path, rc::Rc, sync::Once, time::Duration};
11
12/// Registers the VFS as SQLite's default, once.
13pub(crate) fn install() {
14 static INSTALLED: Once = Once::new();
15 INSTALLED.call_once(|| {
16 rsqlite_vfs::register_vfs::<Methods, Vfs>("snowbound", (), true)
17 .expect("SQLite takes the browser's VFS");
18 });
19}
20
21struct File(Shared);
22
23impl VfsFile for File {
24 fn read(&self, output: &mut [u8], offset: usize) -> VfsResult<bool> {
25 let data = self.0.borrow();
26 let rest = data.bytes.get(offset..).unwrap_or_default();
27 let count = rest.len().min(output.len());
28 output[..count].copy_from_slice(&rest[..count]);
29 output[count..].fill(0);
30 Ok(count == output.len())
31 }
32
33 fn write(&mut self, input: &[u8], offset: usize) -> VfsResult<()> {
34 let mut data = self.0.borrow_mut();
35 let end = offset + input.len();
36 if data.bytes.len() < end {
37 data.bytes.resize(end, 0);
38 }
39 data.bytes[offset..end].copy_from_slice(input);
40 data.wrote(offset..end);
41 Ok(())
42 }
43
44 fn truncate(&mut self, size: usize) -> VfsResult<()> {
45 let mut data = self.0.borrow_mut();
46 data.bytes.truncate(size);
47 data.wrote(size..size);
48 Ok(())
49 }
50
51 fn flush(&mut self) -> VfsResult<()> {
52 self.0.borrow_mut().flush();
53 Ok(())
54 }
55
56 fn size(&self) -> VfsResult<usize> {
57 Ok(self.0.borrow().bytes.len())
58 }
59}
60
61fn error(code: i32, error: std::io::Error) -> VfsError {
62 VfsError::new(code, error.to_string())
63}
64
65struct Store;
66
67impl Store {
68 fn data(file: &SQLiteVfsFile) -> VfsResult<Shared> {
69 // SAFETY: SQLite hands back the file `xOpen` named, whose name lives until `xClose`.
70 let name = unsafe { file.name() };
71 with(|files| files.file(&normal(Path::new(name)))).map_err(|io| error(SQLITE_IOERR, io))
72 }
73}
74
75impl VfsStore<File, ()> for Store {
76 fn add_file(_: *mut sqlite3_vfs, name: &str, _: i32) -> VfsResult<()> {
77 // SQLite's own files beside a replica, such as its write-ahead log, need no folder made.
78 let path = normal(Path::new(name));
79 with(|files| files.create(path))
80 .map(drop)
81 .map_err(|io| error(SQLITE_CANTOPEN, io))
82 }
83
84 fn contains_file(_: *mut sqlite3_vfs, name: &str) -> VfsResult<bool> {
85 let path = normal(Path::new(name));
86 Ok(FILES.with_borrow(|files| matches!(files.nodes.get(&path), Some(Node::File(_)))))
87 }
88
89 fn delete_file(_: *mut sqlite3_vfs, name: &str) -> VfsResult<()> {
90 super::remove_file(name).map_err(|io| error(SQLITE_IOERR_DELETE, io))
91 }
92
93 fn with_file<F: Fn(&File) -> VfsResult<i32>>(file: &SQLiteVfsFile, act: F) -> VfsResult<i32> {
94 act(&File(Self::data(file)?))
95 }
96
97 fn with_file_mut<F: Fn(&mut File) -> VfsResult<i32>>(
98 file: &SQLiteVfsFile,
99 act: F,
100 ) -> VfsResult<i32> {
101 act(&mut File(Rc::clone(&Self::data(file)?)))
102 }
103}
104
105struct Methods;
106
107impl SQLiteIoMethods for Methods {
108 type File = File;
109 type AppData = ();
110 type Store = Store;
111
112 const VERSION: i32 = 1;
113}
114
115struct Vfs;
116
117impl OsCallback for Vfs {
118 /// Nothing else holds a lock to wait for.
119 fn sleep(_: Duration) {}
120
121 fn random(bytes: &mut [u8]) {
122 let _ = getrandom::fill(bytes);
123 }
124
125 fn epoch_timestamp_in_ms() -> i64 {
126 js_sys::Date::now() as i64
127 }
128}
129
130impl SQLiteVfs<Methods> for Vfs {
131 const VERSION: i32 = 1;
132
133 fn sleep(duration: Duration) {
134 <Self as OsCallback>::sleep(duration)
135 }
136
137 fn random(bytes: &mut [u8]) {
138 <Self as OsCallback>::random(bytes)
139 }
140
141 fn epoch_timestamp_in_ms() -> i64 {
142 <Self as OsCallback>::epoch_timestamp_in_ms()
143 }
144}