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, WebAssembly};
7use std::{
8    collections::HashMap,
9    ffi::{CStr, CString},
10    ops::{Deref, DerefMut},
11};
12use wasm_bindgen::{prelude::wasm_bindgen, JsCast};
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/// Wrap the pVfs pointer, which is often used in VFS implementation.
18///
19/// Use vfs pointer as the map key to find the corresponding vfs handle, such as `OpfsSAHPool`.
20#[derive(Hash, PartialEq, Eq)]
21pub struct VfsPtr(pub *mut sqlite3_vfs);
22
23unsafe impl Send for VfsPtr {}
24unsafe impl Sync for VfsPtr {}
25
26/// Wrap the pFile pointer, which is often used in VFS implementation.
27///
28/// Use file pointer as the map key to find the corresponding file handle, such as `MemFile`.
29#[derive(Hash, PartialEq, Eq)]
30pub struct FilePtr(pub *mut sqlite3_file);
31
32unsafe impl Send for FilePtr {}
33unsafe impl Sync for FilePtr {}
34
35/// A [`FragileComfirmed<T>`] wraps a non sendable `T` to be safely send to other threads.
36///
37/// Once the value has been wrapped it can be sent to other threads but access
38/// to the value on those threads will fail.
39pub 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
67/// get random name if zFileName is null and other cases
68pub 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/// Directly using copy_from and copy_to to convert Uint8Array and Vec<u8> is risky.
74/// There is a possibility that the memory will grow and the buffer will be detached during copy.
75/// So here we convert on the js side.
76///
77/// Related issues:
78///
79/// <https://github.com/rustwasm/wasm-bindgen/issues/4395>
80///
81/// <https://github.com/rustwasm/wasm-bindgen/issues/4392>
82#[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
93/// Copy `Uint8Array` and return new `Vec<u8>`
94pub 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
100/// Copy `Uint8Array` to `slice`
101pub 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
112/// Copy `slice` and return new `Uint8Array`
113pub 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
119/// Copy `slice` to `Unit8Array`
120pub 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/// Return error code if expr is true.
131///
132/// The default error code is SQLITE_ERROR.
133#[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/// Unpack Option<T>.
146///
147/// If it is None, return an error code.
148///
149/// The default error code is SQLITE_ERROR.
150#[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/// Unpack Ok<T>.
165///
166/// If it is Err, return an error code.
167///
168/// The default err code is SQLITE_ERROR.
169#[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/// The actual pFile type in Vfs.
184///
185/// `szOsFile` must be set to the size of `SQLiteVfsFile`.
186#[repr(C)]
187pub struct SQLiteVfsFile {
188    /// The first field must be of type sqlite_file.
189    /// In C layout, the pointer to SQLiteVfsFile is the pointer to io_methods.
190    pub io_methods: sqlite3_file,
191    /// The vfs where the file is located, usually used to manage files.
192    pub vfs: *mut sqlite3_vfs,
193    /// Flags used to open the database.
194    pub flags: i32,
195    /// The pointer to the file name.
196    /// If it is a leaked static pointer, you need to drop it manually when xClose it.
197    pub name_ptr: *const u8,
198    /// Length of the file name, on wasm32 platform, usize is u32.
199    pub name_length: usize,
200}
201
202impl SQLiteVfsFile {
203    /// Convert a `sqlite3_file` pointer to a `SQLiteVfsFile` pointer.
204    ///
205    /// # Safety
206    ///
207    /// You must ensure that the pointer passed in is `SQLiteVfsFile`
208    pub unsafe fn from_file(file: *mut sqlite3_file) -> &'static SQLiteVfsFile {
209        &*file.cast::<Self>()
210    }
211
212    /// Get the file name. When xClose, you can release the memory by `drop(Box::from_raw(ptr));`.
213    ///
214    /// # Safety
215    ///
216    /// You must ensure that the pointer passed in is `SQLiteVfsFile`
217    pub unsafe fn name(&self) -> &'static mut str {
218        // emm, `from_raw_parts_mut` is unstable
219        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/// Possible errors when registering Vfs
227#[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
235/// Register vfs general method
236pub 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
257/// Generic function for reading by page (block)
258pub 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
305/// Some basic capabilities of Store
306pub trait VfsStore {
307    /// Abstraction of `xRead`, returns `SQLITE_OK` or `SQLITE_IOERR_SHORT_READ`
308    fn read(&self, buf: &mut [u8], offset: usize) -> i32;
309    /// Abstraction of `xWrite`
310    fn write(&mut self, buf: &[u8], offset: usize);
311    /// Abstraction of `xTruncate`
312    fn truncate(&mut self, size: usize);
313    /// Get file size
314    fn size(&self) -> usize;
315}
316
317/// Linear storage in memory, used for temporary DB
318#[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/// Memory storage structure by page (block)
359///
360/// Used by memory vfs and relaxed-idb vfs
361#[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
411/// Control the Store and make changes to files
412pub trait StoreControl<S> {
413    /// Adding files to the Store, use for `xOpen`
414    fn add_file(vfs: *mut sqlite3_vfs, file: &str, flags: i32);
415    /// Checks if the specified file exists in the Store, use for `xOpen`
416    fn contains_file(vfs: *mut sqlite3_vfs, file: &str) -> bool;
417    /// Delete the specified file in the Store, use for `xClose` and `xDelete`
418    fn delete_file(vfs: *mut sqlite3_vfs, file: &str) -> Option<S>;
419    /// Read the file contents, use for `xRead`, `xFileSize`
420    fn with_file<F: Fn(&S) -> i32>(vfs_file: &SQLiteVfsFile, f: F) -> Option<i32>;
421    /// Write the file contents, use for `xWrite`, `xTruncate`
422    fn with_file_mut<F: Fn(&mut S) -> i32>(vfs_file: &SQLiteVfsFile, f: F) -> Option<i32>;
423}
424
425/// Abstraction of SQLite vfs
426#[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/// Abstraction of SQLite vfs's io methods
543#[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/// Some x methods simulated using JS
687#[allow(clippy::missing_safety_doc)]
688pub mod x_methods_shim {
689    use super::*;
690
691    /// thread::sleep is available when atomics are enabled
692    #[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    /// https://github.com/sqlite/sqlite/blob/fb9e8e48fd70b463fb7ba6d99e00f2be54df749e/ext/wasm/api/sqlite3-vfs-opfs.c-pp.js#L951
712    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    /// https://github.com/sqlite/sqlite/blob/fb9e8e48fd70b463fb7ba6d99e00f2be54df749e/ext/wasm/api/sqlite3-vfs-opfs.c-pp.js#L870
724    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    /// https://github.com/sqlite/sqlite/blob/fb9e8e48fd70b463fb7ba6d99e00f2be54df749e/ext/wasm/api/sqlite3-vfs-opfs.c-pp.js#L877
733    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}