sqlite_wasm_rs/vfs/
utils.rs

1//! Some tools for implementing VFS
2
3use crate::libsqlite3::*;
4
5use fragile::Fragile;
6use js_sys::{Date, Math, Number, Uint8Array};
7use parking_lot::Mutex;
8use std::{
9    ffi::{CStr, CString},
10    ops::{Deref, DerefMut},
11};
12use wasm_array_cp::ArrayBufferCopy;
13
14/// The header of the SQLite file is used to determine whether the imported file is legal.
15pub const SQLITE3_HEADER: &str = "SQLite format 3";
16
17/// A [`FragileComfirmed<T>`] wraps a non sendable `T` to be safely send to other threads.
18///
19/// Once the value has been wrapped it can be sent to other threads but access
20/// to the value on those threads will fail.
21pub struct FragileComfirmed<T> {
22    fragile: Fragile<T>,
23}
24
25unsafe impl<T> Send for FragileComfirmed<T> {}
26unsafe impl<T> Sync for FragileComfirmed<T> {}
27
28impl<T> FragileComfirmed<T> {
29    pub fn new(t: T) -> Self {
30        FragileComfirmed {
31            fragile: Fragile::new(t),
32        }
33    }
34}
35
36impl<T> Deref for FragileComfirmed<T> {
37    type Target = T;
38    fn deref(&self) -> &Self::Target {
39        self.fragile.get()
40    }
41}
42
43impl<T> DerefMut for FragileComfirmed<T> {
44    fn deref_mut(&mut self) -> &mut Self::Target {
45        self.fragile.get_mut()
46    }
47}
48
49/// get random name if zFileName is null and other cases
50pub fn get_random_name() -> String {
51    let random = Number::from(Math::random()).to_string(36).unwrap();
52    random.slice(2, random.length()).as_string().unwrap()
53}
54
55/// Copy `Uint8Array` and return new `Vec<u8>`
56pub fn copy_to_vec(src: &Uint8Array) -> Vec<u8> {
57    ArrayBufferCopy::to_vec(src)
58}
59
60/// Copy `Uint8Array` to `slice`
61pub fn copy_to_slice(src: &Uint8Array, dst: &mut [u8]) {
62    ArrayBufferCopy::copy_to(src, dst);
63}
64
65/// Copy `slice` and return new `Uint8Array`
66pub fn copy_to_uint8_array(src: &[u8]) -> Uint8Array {
67    ArrayBufferCopy::from_slice(src)
68}
69
70/// Copy `slice` to `Unit8Array`
71pub fn copy_to_uint8_array_subarray(src: &[u8], dst: &Uint8Array) {
72    ArrayBufferCopy::copy_from(dst, src)
73}
74
75/// Return error code if expr is true.
76///
77/// The default error code is SQLITE_ERROR.
78#[macro_export]
79macro_rules! bail {
80    ($ex:expr) => {
81        bail!($ex, SQLITE_ERROR);
82    };
83    ($ex:expr, $code: expr) => {
84        if $ex {
85            return $code;
86        }
87    };
88}
89
90/// Unpack Option<T>.
91///
92/// If it is None, return an error code.
93///
94/// The default error code is SQLITE_ERROR.
95#[macro_export]
96macro_rules! check_option {
97    ($ex:expr) => {
98        check_option!($ex, SQLITE_ERROR)
99    };
100    ($ex:expr, $code: expr) => {
101        if let Some(v) = $ex {
102            v
103        } else {
104            return $code;
105        }
106    };
107}
108
109/// Unpack Ok<T>.
110///
111/// If it is Err, return an error code.
112///
113/// The default err code is SQLITE_ERROR.
114#[macro_export]
115macro_rules! check_result {
116    ($ex:expr) => {
117        check_result!($ex, SQLITE_ERROR)
118    };
119    ($ex:expr, $code: expr) => {
120        if let Ok(v) = $ex {
121            v
122        } else {
123            return $code;
124        }
125    };
126}
127
128/// Mark unused parameter
129#[macro_export]
130macro_rules! unused {
131    ($ex:expr) => {
132        let _ = $ex;
133    };
134}
135
136/// The actual pFile type in Vfs.
137///
138/// `szOsFile` must be set to the size of `SQLiteVfsFile`.
139#[repr(C)]
140pub struct SQLiteVfsFile {
141    /// The first field must be of type sqlite_file.
142    /// In C layout, the pointer to SQLiteVfsFile is the pointer to io_methods.
143    pub io_methods: sqlite3_file,
144    /// The vfs where the file is located, usually used to manage files.
145    pub vfs: *mut sqlite3_vfs,
146    /// Flags used to open the database.
147    pub flags: i32,
148    /// The pointer to the file name.
149    /// If it is a leaked static pointer, you need to drop it manually when xClose it.
150    pub name_ptr: *const u8,
151    /// Length of the file name, on wasm32 platform, usize is u32.
152    pub name_length: usize,
153}
154
155impl SQLiteVfsFile {
156    /// Convert a `sqlite3_file` pointer to a `SQLiteVfsFile` pointer.
157    ///
158    /// # Safety
159    ///
160    /// You must ensure that the pointer passed in is `SQLiteVfsFile`
161    pub unsafe fn from_file(file: *mut sqlite3_file) -> &'static SQLiteVfsFile {
162        &*file.cast::<Self>()
163    }
164
165    /// Get the file name.
166    ///
167    /// # Safety
168    ///
169    /// When xClose, you can free the memory by `drop(Box::from_raw(ptr));`.
170    ///
171    /// Do not use again after free.
172    pub unsafe fn name(&self) -> &'static mut str {
173        // emm, `from_raw_parts_mut` is unstable
174        std::str::from_utf8_unchecked_mut(std::slice::from_raw_parts_mut(
175            self.name_ptr.cast_mut(),
176            self.name_length,
177        ))
178    }
179
180    /// Convert a `&'static SQLiteVfsFile` pointer to `*mut sqlite3_file` pointer.
181    pub fn sqlite3_file(&'static self) -> *mut sqlite3_file {
182        self as *const SQLiteVfsFile as *mut sqlite3_file
183    }
184}
185
186/// Possible errors when registering Vfs
187#[derive(thiserror::Error, Debug)]
188pub enum RegisterVfsError {
189    #[error("An error occurred converting the given vfs name to a CStr")]
190    ToCStr,
191    #[error("An error occurred while registering vfs with sqlite")]
192    RegisterVfs,
193}
194
195/// Register vfs general method
196pub fn register_vfs<IO: SQLiteIoMethods, V: SQLiteVfs<IO>>(
197    vfs_name: &str,
198    app_data: IO::AppData,
199    default_vfs: bool,
200) -> Result<*mut sqlite3_vfs, RegisterVfsError> {
201    let name = CString::new(vfs_name).map_err(|_| RegisterVfsError::ToCStr)?;
202    let name_ptr = name.into_raw();
203    let app_data = VfsAppData::new(app_data).leak();
204
205    let vfs = Box::leak(Box::new(V::vfs(name_ptr, app_data.cast())));
206    let ret = unsafe { sqlite3_vfs_register(vfs, i32::from(default_vfs)) };
207
208    if ret != SQLITE_OK {
209        unsafe {
210            drop(Box::from_raw(vfs));
211            drop(CString::from_raw(name_ptr));
212            drop(VfsAppData::from_raw(app_data));
213        }
214        return Err(RegisterVfsError::RegisterVfs);
215    }
216
217    Ok(vfs as *mut sqlite3_vfs)
218}
219
220/// Generic function for reading by page (block)
221pub fn page_read<T, G: Fn(usize) -> Option<T>, R: Fn(T, &mut [u8], (usize, usize))>(
222    buf: &mut [u8],
223    page_size: usize,
224    file_size: usize,
225    offset: usize,
226    get_page: G,
227    read_fn: R,
228) -> i32 {
229    if page_size == 0 || file_size == 0 {
230        buf.fill(0);
231        return SQLITE_IOERR_SHORT_READ;
232    }
233
234    let mut bytes_read = 0;
235    let mut p_data_offset = 0;
236    let p_data_length = buf.len();
237    let i_offset = offset;
238
239    while p_data_offset < p_data_length {
240        let file_offset = i_offset + p_data_offset;
241        let page_idx = file_offset / page_size;
242        let page_offset = file_offset % page_size;
243        let page_addr = page_idx * page_size;
244
245        let Some(page) = get_page(page_addr) else {
246            break;
247        };
248
249        let page_length = (page_size - page_offset).min(p_data_length - p_data_offset);
250        read_fn(
251            page,
252            &mut buf[p_data_offset..p_data_offset + page_length],
253            (page_offset, page_offset + page_length),
254        );
255
256        p_data_offset += page_length;
257        bytes_read += page_length;
258    }
259
260    if bytes_read < p_data_length {
261        buf[bytes_read..].fill(0);
262        return SQLITE_IOERR_SHORT_READ;
263    }
264
265    SQLITE_OK
266}
267
268/// Chunks storage in memory, used for temporary file
269pub struct MemChunksFile {
270    chunks: Vec<Vec<u8>>,
271    chunk_size: Option<usize>,
272    file_size: usize,
273}
274
275impl Default for MemChunksFile {
276    fn default() -> Self {
277        Self::new(512)
278    }
279}
280
281impl MemChunksFile {
282    /// New with chunk size
283    pub fn new(chunk_size: usize) -> Self {
284        assert!(chunk_size != 0, "chunk size can't be zero");
285        MemChunksFile {
286            chunks: Vec::new(),
287            chunk_size: Some(chunk_size),
288            file_size: 0,
289        }
290    }
291
292    /// The chunk size is determined when writing for the first time.
293    ///
294    /// This is often used for the main DB file implementation.
295    pub fn waiting_for_write() -> Self {
296        MemChunksFile {
297            chunks: Vec::new(),
298            chunk_size: None,
299            file_size: 0,
300        }
301    }
302}
303
304impl VfsFile for MemChunksFile {
305    fn read(&self, buf: &mut [u8], offset: usize) -> VfsResult<i32> {
306        let Some(chunk_size) = self.chunk_size else {
307            buf.fill(0);
308            return Ok(SQLITE_IOERR_SHORT_READ);
309        };
310
311        if self.file_size <= offset {
312            buf.fill(0);
313            return Ok(SQLITE_IOERR_SHORT_READ);
314        }
315
316        if chunk_size == buf.len() && offset % chunk_size == 0 {
317            buf.copy_from_slice(&self.chunks[offset / chunk_size]);
318            Ok(SQLITE_OK)
319        } else {
320            let mut size = buf.len();
321            let chunk_idx = offset / chunk_size;
322            let mut remaining_idx = offset % chunk_size;
323            let mut offset = 0;
324
325            for chunk in &self.chunks[chunk_idx..] {
326                let n = std::cmp::min(chunk_size.min(self.file_size) - remaining_idx, size);
327                buf[offset..offset + n].copy_from_slice(&chunk[remaining_idx..remaining_idx + n]);
328                offset += n;
329                size -= n;
330                remaining_idx = 0;
331                if size == 0 {
332                    break;
333                }
334            }
335
336            if offset < buf.len() {
337                buf[offset..].fill(0);
338                Ok(SQLITE_IOERR_SHORT_READ)
339            } else {
340                Ok(SQLITE_OK)
341            }
342        }
343    }
344
345    fn write(&mut self, buf: &[u8], offset: usize) -> VfsResult<()> {
346        if buf.is_empty() {
347            return Ok(());
348        }
349
350        let chunk_size = if let Some(chunk_size) = self.chunk_size {
351            chunk_size
352        } else {
353            let size = buf.len();
354            self.chunk_size = Some(size);
355            size
356        };
357
358        let new_length = self.file_size.max(offset + buf.len());
359
360        if chunk_size == buf.len() && offset % chunk_size == 0 {
361            for _ in self.chunks.len()..offset / chunk_size {
362                self.chunks.push(vec![0; chunk_size]);
363            }
364            if let Some(buffer) = self.chunks.get_mut(offset / chunk_size) {
365                buffer.copy_from_slice(buf);
366            } else {
367                self.chunks.push(buf.to_vec());
368            }
369        } else {
370            let mut size = buf.len();
371            let chunk_start_idx = offset / chunk_size;
372            let chunk_end_idx = (offset + size - 1) / chunk_size;
373            let chunks_length = self.chunks.len();
374
375            for _ in chunks_length..=chunk_end_idx {
376                self.chunks.push(vec![0; chunk_size]);
377            }
378
379            let mut remaining_idx = offset % chunk_size;
380            let mut offset = 0;
381
382            for idx in chunk_start_idx..=chunk_end_idx {
383                let n = std::cmp::min(chunk_size - remaining_idx, size);
384                self.chunks[idx][remaining_idx..remaining_idx + n]
385                    .copy_from_slice(&buf[offset..offset + n]);
386                offset += n;
387                size -= n;
388                remaining_idx = 0;
389                if size == 0 {
390                    break;
391                }
392            }
393        }
394
395        self.file_size = new_length;
396
397        Ok(())
398    }
399
400    fn truncate(&mut self, size: usize) -> VfsResult<()> {
401        if let Some(chunk_size) = self.chunk_size {
402            if size == 0 {
403                std::mem::take(&mut self.chunks);
404            } else {
405                let idx = ((size - 1) / chunk_size) + 1;
406                self.chunks.drain(idx..);
407            }
408        } else if size != 0 {
409            assert_eq!(self.file_size, 0);
410            return Err(VfsError::new(SQLITE_IOERR, "Failed to truncate".into()));
411        }
412        self.file_size = size;
413        Ok(())
414    }
415
416    fn flush(&mut self) -> VfsResult<()> {
417        Ok(())
418    }
419
420    fn size(&self) -> VfsResult<usize> {
421        Ok(self.file_size)
422    }
423}
424
425/// Linear storage in memory, used for temporary DB
426#[derive(Default)]
427pub struct MemLinearFile(Vec<u8>);
428
429impl VfsFile for MemLinearFile {
430    fn read(&self, buf: &mut [u8], offset: usize) -> VfsResult<i32> {
431        let size = buf.len();
432        let end = size + offset;
433        if self.0.len() <= offset {
434            buf.fill(0);
435            return Ok(SQLITE_IOERR_SHORT_READ);
436        }
437
438        let read_end = end.min(self.0.len());
439        let read_size = read_end - offset;
440        buf[..read_size].copy_from_slice(&self.0[offset..read_end]);
441
442        if read_size < size {
443            buf[read_size..].fill(0);
444            return Ok(SQLITE_IOERR_SHORT_READ);
445        }
446        Ok(SQLITE_OK)
447    }
448
449    fn write(&mut self, buf: &[u8], offset: usize) -> VfsResult<()> {
450        let end = buf.len() + offset;
451        if end > self.0.len() {
452            self.0.resize(end, 0);
453        }
454        self.0[offset..end].copy_from_slice(buf);
455        Ok(())
456    }
457
458    fn truncate(&mut self, size: usize) -> VfsResult<()> {
459        self.0.truncate(size);
460        Ok(())
461    }
462
463    fn flush(&mut self) -> VfsResult<()> {
464        Ok(())
465    }
466
467    fn size(&self) -> VfsResult<usize> {
468        Ok(self.0.len())
469    }
470}
471
472/// Used to log and retrieve Vfs errors
473#[derive(Debug)]
474pub struct VfsError {
475    code: i32,
476    message: String,
477}
478
479impl VfsError {
480    pub fn new(code: i32, message: String) -> Self {
481        VfsError { code, message }
482    }
483}
484
485/// Wrapper for `Result`
486pub type VfsResult<T> = Result<T, VfsError>;
487
488/// Wrapper for `pAppData`
489pub struct VfsAppData<T> {
490    data: T,
491    last_err: Mutex<Option<(i32, String)>>,
492}
493
494impl<T> VfsAppData<T> {
495    pub fn new(t: T) -> Self {
496        VfsAppData {
497            data: t,
498            last_err: Mutex::new(None),
499        }
500    }
501
502    /// Leak, then pAppData can be set to VFS
503    pub fn leak(self) -> *mut Self {
504        Box::into_raw(Box::new(self))
505    }
506
507    /// # Safety
508    ///
509    /// You have to make sure the pointer is correct
510    pub unsafe fn from_raw(t: *mut Self) -> VfsAppData<T> {
511        *Box::from_raw(t)
512    }
513
514    /// Pop vfs last errcode and errmsg
515    pub fn pop_err(&self) -> Option<(i32, String)> {
516        self.last_err.lock().take()
517    }
518
519    /// Store errcode and errmsg
520    pub fn store_err(&self, err: VfsError) -> i32 {
521        let VfsError { code, message } = err;
522        self.last_err.lock().replace((code, message));
523        code
524    }
525}
526
527/// Deref only, returns immutable reference, avoids race conditions
528impl<T> Deref for VfsAppData<T> {
529    type Target = T;
530
531    fn deref(&self) -> &Self::Target {
532        &self.data
533    }
534}
535
536/// Some basic capabilities of file
537pub trait VfsFile {
538    /// Abstraction of `xRead`, returns `SQLITE_OK` or `SQLITE_IOERR_SHORT_READ`
539    fn read(&self, buf: &mut [u8], offset: usize) -> VfsResult<i32>;
540    /// Abstraction of `xWrite`
541    fn write(&mut self, buf: &[u8], offset: usize) -> VfsResult<()>;
542    /// Abstraction of `xTruncate`
543    fn truncate(&mut self, size: usize) -> VfsResult<()>;
544    /// Abstraction of `xSync`
545    fn flush(&mut self) -> VfsResult<()>;
546    /// Abstraction of `xFileSize`
547    fn size(&self) -> VfsResult<usize>;
548}
549
550/// Make changes to files
551pub trait VfsStore<File, AppData> {
552    /// Convert pAppData to the type we need
553    ///
554    /// # Safety
555    ///
556    /// As long as it is set through the abstract VFS interface, it is safe
557    unsafe fn app_data(vfs: *mut sqlite3_vfs) -> &'static VfsAppData<AppData> {
558        &*(*vfs).pAppData.cast()
559    }
560    /// Get file path, use for `xOpen`
561    fn name2path(vfs: *mut sqlite3_vfs, file: &str) -> VfsResult<String> {
562        unused!(vfs);
563        Ok(file.into())
564    }
565    /// Adding files to the Store, use for `xOpen` and `xAccess`
566    fn add_file(vfs: *mut sqlite3_vfs, file: &str, flags: i32) -> VfsResult<()>;
567    /// Checks if the specified file exists in the Store, use for `xOpen` and `xAccess`
568    fn contains_file(vfs: *mut sqlite3_vfs, file: &str) -> VfsResult<bool>;
569    /// Delete the specified file in the Store, use for `xClose` and `xDelete`
570    fn delete_file(vfs: *mut sqlite3_vfs, file: &str) -> VfsResult<()>;
571    /// Read the file contents, use for `xRead`, `xFileSize`
572    fn with_file<F: Fn(&File) -> i32>(vfs_file: &SQLiteVfsFile, f: F) -> VfsResult<i32>;
573    /// Write the file contents, use for `xWrite`, `xTruncate` and `xSync`
574    fn with_file_mut<F: Fn(&mut File) -> i32>(vfs_file: &SQLiteVfsFile, f: F) -> VfsResult<i32>;
575}
576
577/// Abstraction of SQLite vfs
578#[allow(clippy::missing_safety_doc)]
579pub trait SQLiteVfs<IO: SQLiteIoMethods> {
580    const VERSION: ::std::os::raw::c_int;
581    const MAX_PATH_SIZE: ::std::os::raw::c_int = 1024;
582
583    fn vfs(
584        vfs_name: *const ::std::os::raw::c_char,
585        app_data: *mut VfsAppData<IO::AppData>,
586    ) -> sqlite3_vfs {
587        sqlite3_vfs {
588            iVersion: Self::VERSION,
589            szOsFile: std::mem::size_of::<SQLiteVfsFile>() as i32,
590            mxPathname: Self::MAX_PATH_SIZE,
591            pNext: std::ptr::null_mut(),
592            zName: vfs_name,
593            pAppData: app_data.cast(),
594            xOpen: Some(Self::xOpen),
595            xDelete: Some(Self::xDelete),
596            xAccess: Some(Self::xAccess),
597            xFullPathname: Some(Self::xFullPathname),
598            xDlOpen: None,
599            xDlError: None,
600            xDlSym: None,
601            xDlClose: None,
602            xRandomness: Some(x_methods_shim::xRandomness),
603            xSleep: Some(x_methods_shim::xSleep),
604            xCurrentTime: Some(x_methods_shim::xCurrentTime),
605            xGetLastError: Some(Self::xGetLastError),
606            xCurrentTimeInt64: Some(x_methods_shim::xCurrentTimeInt64),
607            xSetSystemCall: None,
608            xGetSystemCall: None,
609            xNextSystemCall: None,
610        }
611    }
612
613    unsafe extern "C" fn xOpen(
614        pVfs: *mut sqlite3_vfs,
615        zName: sqlite3_filename,
616        pFile: *mut sqlite3_file,
617        flags: ::std::os::raw::c_int,
618        pOutFlags: *mut ::std::os::raw::c_int,
619    ) -> ::std::os::raw::c_int {
620        let app_data = IO::Store::app_data(pVfs);
621
622        let name = if zName.is_null() {
623            get_random_name()
624        } else {
625            check_result!(CStr::from_ptr(zName).to_str()).into()
626        };
627
628        let name = match IO::Store::name2path(pVfs, &name) {
629            Ok(name) => name,
630            Err(err) => return app_data.store_err(err),
631        };
632
633        let exist = match IO::Store::contains_file(pVfs, &name) {
634            Ok(exist) => exist,
635            Err(err) => return app_data.store_err(err),
636        };
637
638        if !exist {
639            if flags & SQLITE_OPEN_CREATE == 0 {
640                return app_data.store_err(VfsError::new(
641                    SQLITE_CANTOPEN,
642                    format!("file not found: {name}"),
643                ));
644            }
645            if let Err(err) = IO::Store::add_file(pVfs, &name, flags) {
646                return app_data.store_err(err);
647            }
648        }
649
650        let leak = name.leak();
651        let vfs_file = pFile.cast::<SQLiteVfsFile>();
652        (*vfs_file).vfs = pVfs;
653        (*vfs_file).flags = flags;
654        (*vfs_file).name_ptr = leak.as_ptr();
655        (*vfs_file).name_length = leak.len();
656
657        (*pFile).pMethods = &IO::METHODS;
658
659        if !pOutFlags.is_null() {
660            *pOutFlags = flags;
661        }
662
663        SQLITE_OK
664    }
665
666    unsafe extern "C" fn xDelete(
667        pVfs: *mut sqlite3_vfs,
668        zName: *const ::std::os::raw::c_char,
669        syncDir: ::std::os::raw::c_int,
670    ) -> ::std::os::raw::c_int {
671        unused!(syncDir);
672
673        let app_data = IO::Store::app_data(pVfs);
674        bail!(zName.is_null(), SQLITE_IOERR_DELETE);
675        let s = check_result!(CStr::from_ptr(zName).to_str());
676        if let Err(err) = IO::Store::delete_file(pVfs, s) {
677            app_data.store_err(err)
678        } else {
679            SQLITE_OK
680        }
681    }
682
683    unsafe extern "C" fn xAccess(
684        pVfs: *mut sqlite3_vfs,
685        zName: *const ::std::os::raw::c_char,
686        flags: ::std::os::raw::c_int,
687        pResOut: *mut ::std::os::raw::c_int,
688    ) -> ::std::os::raw::c_int {
689        unused!(flags);
690
691        *pResOut = if zName.is_null() {
692            0
693        } else {
694            let app_data = IO::Store::app_data(pVfs);
695            let file = check_result!(CStr::from_ptr(zName).to_str());
696            let exist = match IO::Store::contains_file(pVfs, file) {
697                Ok(exist) => exist,
698                Err(err) => return app_data.store_err(err),
699            };
700            i32::from(exist)
701        };
702
703        SQLITE_OK
704    }
705
706    unsafe extern "C" fn xFullPathname(
707        pVfs: *mut sqlite3_vfs,
708        zName: *const ::std::os::raw::c_char,
709        nOut: ::std::os::raw::c_int,
710        zOut: *mut ::std::os::raw::c_char,
711    ) -> ::std::os::raw::c_int {
712        unused!(pVfs);
713        bail!(zName.is_null() || zOut.is_null(), SQLITE_CANTOPEN);
714        let len = CStr::from_ptr(zName).to_bytes_with_nul().len();
715        bail!(len > nOut as usize, SQLITE_CANTOPEN);
716        zName.copy_to(zOut, len);
717        SQLITE_OK
718    }
719
720    unsafe extern "C" fn xGetLastError(
721        pVfs: *mut sqlite3_vfs,
722        nOut: ::std::os::raw::c_int,
723        zOut: *mut ::std::os::raw::c_char,
724    ) -> ::std::os::raw::c_int {
725        let app_data = IO::Store::app_data(pVfs);
726        let Some((code, msg)) = app_data.pop_err() else {
727            return SQLITE_OK;
728        };
729        if !zOut.is_null() {
730            let nOut = nOut as usize;
731            let count = msg.len().min(nOut);
732            msg.as_ptr().copy_to(zOut.cast(), count);
733            let zero = match nOut.cmp(&msg.len()) {
734                std::cmp::Ordering::Less | std::cmp::Ordering::Equal => nOut,
735                std::cmp::Ordering::Greater => msg.len() + 1,
736            };
737            if zero > 0 {
738                std::ptr::write(zOut.add(zero - 1), 0);
739            }
740        }
741        code
742    }
743}
744
745/// Abstraction of SQLite vfs's io methods
746#[allow(clippy::missing_safety_doc)]
747pub trait SQLiteIoMethods {
748    type File: VfsFile;
749    type AppData: 'static;
750    type Store: VfsStore<Self::File, Self::AppData>;
751
752    const VERSION: ::std::os::raw::c_int;
753
754    const METHODS: sqlite3_io_methods = sqlite3_io_methods {
755        iVersion: Self::VERSION,
756        xClose: Some(Self::xClose),
757        xRead: Some(Self::xRead),
758        xWrite: Some(Self::xWrite),
759        xTruncate: Some(Self::xTruncate),
760        xSync: Some(Self::xSync),
761        xFileSize: Some(Self::xFileSize),
762        xLock: Some(Self::xLock),
763        xUnlock: Some(Self::xUnlock),
764        xCheckReservedLock: Some(Self::xCheckReservedLock),
765        xFileControl: Some(Self::xFileControl),
766        xSectorSize: Some(Self::xSectorSize),
767        xDeviceCharacteristics: Some(Self::xDeviceCharacteristics),
768        xShmMap: None,
769        xShmLock: None,
770        xShmBarrier: None,
771        xShmUnmap: None,
772        xFetch: None,
773        xUnfetch: None,
774    };
775
776    unsafe extern "C" fn xClose(pFile: *mut sqlite3_file) -> ::std::os::raw::c_int {
777        let vfs_file = SQLiteVfsFile::from_file(pFile);
778        let app_data = Self::Store::app_data(vfs_file.vfs);
779
780        if vfs_file.flags & SQLITE_OPEN_DELETEONCLOSE != 0 {
781            if let Err(err) = Self::Store::delete_file(vfs_file.vfs, vfs_file.name()) {
782                return app_data.store_err(err);
783            }
784        }
785
786        drop(Box::from_raw(vfs_file.name()));
787
788        SQLITE_OK
789    }
790
791    unsafe extern "C" fn xRead(
792        pFile: *mut sqlite3_file,
793        zBuf: *mut ::std::os::raw::c_void,
794        iAmt: ::std::os::raw::c_int,
795        iOfst: sqlite3_int64,
796    ) -> ::std::os::raw::c_int {
797        let vfs_file = SQLiteVfsFile::from_file(pFile);
798        let app_data = Self::Store::app_data(vfs_file.vfs);
799
800        let f = |file: &Self::File| {
801            let size = iAmt as usize;
802            let offset = iOfst as usize;
803            let slice = std::slice::from_raw_parts_mut(zBuf.cast::<u8>(), size);
804            match file.read(slice, offset) {
805                Ok(code) => code,
806                Err(err) => app_data.store_err(err),
807            }
808        };
809
810        match Self::Store::with_file(vfs_file, f) {
811            Ok(code) => code,
812            Err(err) => app_data.store_err(err),
813        }
814    }
815
816    unsafe extern "C" fn xWrite(
817        pFile: *mut sqlite3_file,
818        zBuf: *const ::std::os::raw::c_void,
819        iAmt: ::std::os::raw::c_int,
820        iOfst: sqlite3_int64,
821    ) -> ::std::os::raw::c_int {
822        let vfs_file = SQLiteVfsFile::from_file(pFile);
823        let app_data = Self::Store::app_data(vfs_file.vfs);
824
825        let f = |file: &mut Self::File| {
826            let (offset, size) = (iOfst as usize, iAmt as usize);
827            let slice = std::slice::from_raw_parts(zBuf.cast::<u8>(), size);
828            if let Err(err) = file.write(slice, offset) {
829                app_data.store_err(err)
830            } else {
831                SQLITE_OK
832            }
833        };
834
835        match Self::Store::with_file_mut(vfs_file, f) {
836            Ok(code) => code,
837            Err(err) => app_data.store_err(err),
838        }
839    }
840
841    unsafe extern "C" fn xTruncate(
842        pFile: *mut sqlite3_file,
843        size: sqlite3_int64,
844    ) -> ::std::os::raw::c_int {
845        let vfs_file = SQLiteVfsFile::from_file(pFile);
846        let app_data = Self::Store::app_data(vfs_file.vfs);
847
848        let f = |file: &mut Self::File| {
849            if let Err(err) = file.truncate(size as usize) {
850                app_data.store_err(err)
851            } else {
852                SQLITE_OK
853            }
854        };
855
856        match Self::Store::with_file_mut(vfs_file, f) {
857            Ok(code) => code,
858            Err(err) => app_data.store_err(err),
859        }
860    }
861
862    unsafe extern "C" fn xSync(
863        pFile: *mut sqlite3_file,
864        flags: ::std::os::raw::c_int,
865    ) -> ::std::os::raw::c_int {
866        unused!(flags);
867
868        let vfs_file = SQLiteVfsFile::from_file(pFile);
869        let app_data = Self::Store::app_data(vfs_file.vfs);
870
871        let f = |file: &mut Self::File| {
872            if let Err(err) = file.flush() {
873                app_data.store_err(err)
874            } else {
875                SQLITE_OK
876            }
877        };
878
879        match Self::Store::with_file_mut(vfs_file, f) {
880            Ok(code) => code,
881            Err(err) => app_data.store_err(err),
882        }
883    }
884
885    unsafe extern "C" fn xFileSize(
886        pFile: *mut sqlite3_file,
887        pSize: *mut sqlite3_int64,
888    ) -> ::std::os::raw::c_int {
889        let vfs_file = SQLiteVfsFile::from_file(pFile);
890        let app_data = Self::Store::app_data(vfs_file.vfs);
891
892        let f = |file: &Self::File| match file.size() {
893            Ok(size) => {
894                *pSize = size as sqlite3_int64;
895                SQLITE_OK
896            }
897            Err(err) => app_data.store_err(err),
898        };
899
900        match Self::Store::with_file(vfs_file, f) {
901            Ok(code) => code,
902            Err(err) => app_data.store_err(err),
903        }
904    }
905
906    unsafe extern "C" fn xLock(
907        pFile: *mut sqlite3_file,
908        eLock: ::std::os::raw::c_int,
909    ) -> ::std::os::raw::c_int {
910        unused!((pFile, eLock));
911        SQLITE_OK
912    }
913
914    unsafe extern "C" fn xUnlock(
915        pFile: *mut sqlite3_file,
916        eLock: ::std::os::raw::c_int,
917    ) -> ::std::os::raw::c_int {
918        unused!((pFile, eLock));
919        SQLITE_OK
920    }
921
922    unsafe extern "C" fn xCheckReservedLock(
923        pFile: *mut sqlite3_file,
924        pResOut: *mut ::std::os::raw::c_int,
925    ) -> ::std::os::raw::c_int {
926        unused!(pFile);
927        *pResOut = 0;
928        SQLITE_OK
929    }
930
931    unsafe extern "C" fn xFileControl(
932        pFile: *mut sqlite3_file,
933        op: ::std::os::raw::c_int,
934        pArg: *mut ::std::os::raw::c_void,
935    ) -> ::std::os::raw::c_int {
936        unused!((pFile, op, pArg));
937        SQLITE_NOTFOUND
938    }
939
940    unsafe extern "C" fn xSectorSize(pFile: *mut sqlite3_file) -> ::std::os::raw::c_int {
941        unused!(pFile);
942        512
943    }
944
945    unsafe extern "C" fn xDeviceCharacteristics(pFile: *mut sqlite3_file) -> ::std::os::raw::c_int {
946        unused!(pFile);
947        0
948    }
949}
950
951/// Some x methods simulated using JS
952#[allow(clippy::missing_safety_doc)]
953pub mod x_methods_shim {
954    use super::*;
955
956    /// thread::sleep is available when atomics is enabled
957    #[cfg(target_feature = "atomics")]
958    pub unsafe extern "C" fn xSleep(
959        _pVfs: *mut sqlite3_vfs,
960        microseconds: ::std::os::raw::c_int,
961    ) -> ::std::os::raw::c_int {
962        use std::{thread, time::Duration};
963        thread::sleep(Duration::from_micros(microseconds as u64));
964        SQLITE_OK
965    }
966
967    #[cfg(not(target_feature = "atomics"))]
968    pub unsafe extern "C" fn xSleep(
969        _pVfs: *mut sqlite3_vfs,
970        _microseconds: ::std::os::raw::c_int,
971    ) -> ::std::os::raw::c_int {
972        SQLITE_OK
973    }
974
975    /// https://github.com/sqlite/sqlite/blob/fb9e8e48fd70b463fb7ba6d99e00f2be54df749e/ext/wasm/api/sqlite3-vfs-opfs.c-pp.js#L951
976    pub unsafe extern "C" fn xRandomness(
977        _pVfs: *mut sqlite3_vfs,
978        nByte: ::std::os::raw::c_int,
979        zOut: *mut ::std::os::raw::c_char,
980    ) -> ::std::os::raw::c_int {
981        for i in 0..nByte as usize {
982            *zOut.add(i) = (Math::random() * 255000.0) as _;
983        }
984        nByte
985    }
986
987    /// https://github.com/sqlite/sqlite/blob/fb9e8e48fd70b463fb7ba6d99e00f2be54df749e/ext/wasm/api/sqlite3-vfs-opfs.c-pp.js#L870
988    pub unsafe extern "C" fn xCurrentTime(
989        _pVfs: *mut sqlite3_vfs,
990        pTimeOut: *mut f64,
991    ) -> ::std::os::raw::c_int {
992        *pTimeOut = 2440587.5 + (Date::new_0().get_time() / 86400000.0);
993        SQLITE_OK
994    }
995
996    /// https://github.com/sqlite/sqlite/blob/fb9e8e48fd70b463fb7ba6d99e00f2be54df749e/ext/wasm/api/sqlite3-vfs-opfs.c-pp.js#L877
997    pub unsafe extern "C" fn xCurrentTimeInt64(
998        _pVfs: *mut sqlite3_vfs,
999        pOut: *mut sqlite3_int64,
1000    ) -> ::std::os::raw::c_int {
1001        *pOut = ((2440587.5 * 86400000.0) + Date::new_0().get_time()) as sqlite3_int64;
1002        SQLITE_OK
1003    }
1004}
1005
1006#[derive(thiserror::Error, Debug)]
1007pub enum ImportDbError {
1008    #[error("Byte array size is invalid for an SQLite db.")]
1009    InvalidDbSize,
1010    #[error("Input does not contain an SQLite database header.")]
1011    InvalidHeader,
1012    #[error("Page size must be a power of two between 512 and 65536 inclusive")]
1013    InvalidPageSize,
1014}
1015
1016/// Simple verification when importing db, and return page size;
1017pub fn check_import_db(bytes: &[u8]) -> Result<usize, ImportDbError> {
1018    let length = bytes.len();
1019
1020    if length < 512 || length % 512 != 0 {
1021        return Err(ImportDbError::InvalidDbSize);
1022    }
1023
1024    if SQLITE3_HEADER
1025        .as_bytes()
1026        .iter()
1027        .zip(bytes)
1028        .any(|(x, y)| x != y)
1029    {
1030        return Err(ImportDbError::InvalidHeader);
1031    }
1032
1033    // The database page size in bytes.
1034    // Must be a power of two between 512 and 32768 inclusive, or the value 1 representing a page size of 65536.
1035    let page_size = u16::from_be_bytes([bytes[16], bytes[17]]);
1036    let page_size = if page_size == 1 {
1037        65536
1038    } else {
1039        usize::from(page_size)
1040    };
1041
1042    Ok(page_size)
1043}
1044
1045/// Check db and page size, page size must be a power of two between 512 and 65536 inclusive, db size must be a multiple of page size.
1046pub fn check_db_and_page_size(db_size: usize, page_size: usize) -> Result<(), ImportDbError> {
1047    if !(page_size.is_power_of_two() && (512..=65536).contains(&page_size)) {
1048        return Err(ImportDbError::InvalidPageSize);
1049    }
1050    if db_size % page_size != 0 {
1051        return Err(ImportDbError::InvalidDbSize);
1052    }
1053    Ok(())
1054}
1055
1056#[cfg(test)]
1057mod tests {
1058    use crate::{
1059        vfs::utils::{copy_to_slice, copy_to_uint8_array_subarray},
1060        SQLITE_IOERR_SHORT_READ,
1061    };
1062
1063    use super::{copy_to_uint8_array, copy_to_vec, MemChunksFile, VfsFile};
1064    use js_sys::Uint8Array;
1065    use wasm_bindgen_test::wasm_bindgen_test;
1066
1067    #[wasm_bindgen_test]
1068    fn test_js_utils() {
1069        let buf1 = vec![1, 2, 3, 4];
1070        let uint8 = copy_to_uint8_array(&buf1);
1071        let buf2 = copy_to_vec(&uint8);
1072        assert_eq!(buf1, buf2);
1073
1074        let mut buf3 = vec![0u8; 2];
1075        copy_to_slice(&uint8.subarray(0, 2), &mut buf3);
1076        assert_eq!(buf3, vec![1, 2]);
1077
1078        let buf4 = Uint8Array::new_with_length(3);
1079        copy_to_uint8_array_subarray(&buf3, &buf4.subarray(1, 3));
1080        assert!(buf4.get_index(0) == 0);
1081        assert!(buf4.get_index(1) == 1);
1082        assert!(buf4.get_index(2) == 2);
1083    }
1084
1085    #[wasm_bindgen_test]
1086    fn test_chunks_file() {
1087        let mut file = MemChunksFile::new(512);
1088        file.write(&[], 0).unwrap();
1089        assert!(file.size().unwrap() == 0);
1090
1091        let mut buffer = [1; 2];
1092        let ret = file.read(&mut buffer, 0).unwrap();
1093        assert_eq!(ret, SQLITE_IOERR_SHORT_READ);
1094        assert_eq!([0; 2], buffer);
1095
1096        file.write(&[1], 0).unwrap();
1097        assert!(file.size().unwrap() == 1);
1098        let mut buffer = [2; 2];
1099        let ret = file.read(&mut buffer, 0).unwrap();
1100        assert_eq!(ret, SQLITE_IOERR_SHORT_READ);
1101        assert_eq!([1, 0], buffer);
1102
1103        let mut file = MemChunksFile::new(512);
1104        file.write(&[1; 512], 0).unwrap();
1105        assert!(file.size().unwrap() == 512);
1106        assert!(file.chunks.len() == 1);
1107
1108        file.truncate(512).unwrap();
1109        assert!(file.size().unwrap() == 512);
1110        assert!(file.chunks.len() == 1);
1111
1112        file.write(&[41, 42, 43], 511).unwrap();
1113        assert!(file.size().unwrap() == 514);
1114        assert!(file.chunks.len() == 2);
1115
1116        let mut buffer = [0; 3];
1117        let ret = file.read(&mut buffer, 511).unwrap();
1118        assert_eq!(ret, 0);
1119        assert_eq!(buffer, [41, 42, 43]);
1120
1121        file.truncate(513).unwrap();
1122        assert!(file.size().unwrap() == 513);
1123        assert!(file.chunks.len() == 2);
1124
1125        file.write(&[1], 2048).unwrap();
1126        assert!(file.size().unwrap() == 2049);
1127        assert!(file.chunks.len() == 5);
1128
1129        file.truncate(0).unwrap();
1130        assert!(file.size().unwrap() == 0);
1131        assert!(file.chunks.len() == 0);
1132    }
1133}