sqlite_wasm_rs/vfs/
utils.rs

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