1use crate::libsqlite3::*;
4
5use fragile::Fragile;
6use js_sys::{Date, Math, Number, Uint8Array, WebAssembly};
7use parking_lot::Mutex;
8use std::{
9 ffi::{CStr, CString},
10 ops::{Deref, DerefMut},
11};
12use wasm_bindgen::{prelude::wasm_bindgen, JsCast};
13
14pub const SQLITE3_HEADER: &str = "SQLite format 3";
16
17pub 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
49pub 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#[wasm_bindgen(module = "/src/vfs/utils.js")]
65extern "C" {
66 type JSUtils;
67
68 #[wasm_bindgen(static_method_of = JSUtils, js_name = toSlice)]
69 fn to_slice(memory: &WebAssembly::Memory, buffer: &Uint8Array, dst: *mut u8, len: usize);
70
71 #[wasm_bindgen(static_method_of = JSUtils, js_name = toUint8Array)]
72 fn to_uint8_array(memory: &WebAssembly::Memory, src: *const u8, len: usize, dst: &Uint8Array);
73}
74
75pub fn copy_to_vec(src: &Uint8Array) -> Vec<u8> {
77 let mut vec = vec![0u8; src.length() as usize];
78 copy_to_slice(src, vec.as_mut_slice());
79 vec
80}
81
82pub fn copy_to_slice(src: &Uint8Array, dst: &mut [u8]) {
84 assert!(
85 src.length() as usize == dst.len(),
86 "Unit8Array and slice have different sizes"
87 );
88
89 let buf = wasm_bindgen::memory();
90 let mem = buf.unchecked_ref::<WebAssembly::Memory>();
91 JSUtils::to_slice(mem, src, dst.as_mut_ptr(), dst.len());
92}
93
94pub fn copy_to_uint8_array(src: &[u8]) -> Uint8Array {
96 let uint8 = Uint8Array::new_with_length(src.len() as u32);
97 copy_to_uint8_array_subarray(src, &uint8);
98 uint8
99}
100
101pub fn copy_to_uint8_array_subarray(src: &[u8], dst: &Uint8Array) {
103 assert!(
104 src.len() == dst.length() as _,
105 "Unit8Array and slice have different sizes"
106 );
107 let buf = wasm_bindgen::memory();
108 let mem = buf.unchecked_ref::<WebAssembly::Memory>();
109 JSUtils::to_uint8_array(mem, src.as_ptr(), src.len(), dst)
110}
111
112#[macro_export]
116macro_rules! bail {
117 ($ex:expr) => {
118 bail!($ex, SQLITE_ERROR);
119 };
120 ($ex:expr, $code: expr) => {
121 if $ex {
122 return $code;
123 }
124 };
125}
126
127#[macro_export]
133macro_rules! check_option {
134 ($ex:expr) => {
135 check_option!($ex, SQLITE_ERROR)
136 };
137 ($ex:expr, $code: expr) => {
138 if let Some(v) = $ex {
139 v
140 } else {
141 return $code;
142 }
143 };
144}
145
146#[macro_export]
152macro_rules! check_result {
153 ($ex:expr) => {
154 check_result!($ex, SQLITE_ERROR)
155 };
156 ($ex:expr, $code: expr) => {
157 if let Ok(v) = $ex {
158 v
159 } else {
160 return $code;
161 }
162 };
163}
164
165#[macro_export]
167macro_rules! unused {
168 ($ex:expr) => {
169 let _ = $ex;
170 };
171}
172
173#[repr(C)]
177pub struct SQLiteVfsFile {
178 pub io_methods: sqlite3_file,
181 pub vfs: *mut sqlite3_vfs,
183 pub flags: i32,
185 pub name_ptr: *const u8,
188 pub name_length: usize,
190}
191
192impl SQLiteVfsFile {
193 pub unsafe fn from_file(file: *mut sqlite3_file) -> &'static SQLiteVfsFile {
199 &*file.cast::<Self>()
200 }
201
202 pub unsafe fn name(&self) -> &'static mut str {
210 std::str::from_utf8_unchecked_mut(std::slice::from_raw_parts_mut(
212 self.name_ptr.cast_mut(),
213 self.name_length,
214 ))
215 }
216
217 pub fn sqlite3_file(&'static self) -> *mut sqlite3_file {
219 self as *const SQLiteVfsFile as *mut sqlite3_file
220 }
221}
222
223#[derive(thiserror::Error, Debug)]
225pub enum RegisterVfsError {
226 #[error("An error occurred converting the given vfs name to a CStr")]
227 ToCStr,
228 #[error("An error occurred while registering vfs with sqlite")]
229 RegisterVfs,
230}
231
232pub fn register_vfs<IO: SQLiteIoMethods, V: SQLiteVfs<IO>>(
234 vfs_name: &str,
235 app_data: IO::AppData,
236 default_vfs: bool,
237) -> Result<*mut sqlite3_vfs, RegisterVfsError> {
238 let name = CString::new(vfs_name).map_err(|_| RegisterVfsError::ToCStr)?;
239 let name_ptr = name.into_raw();
240 let app_data = VfsAppData::new(app_data).leak();
241
242 let vfs = Box::leak(Box::new(V::vfs(name_ptr, app_data.cast())));
243 let ret = unsafe { sqlite3_vfs_register(vfs, i32::from(default_vfs)) };
244
245 if ret != SQLITE_OK {
246 unsafe {
247 drop(Box::from_raw(vfs));
248 drop(CString::from_raw(name_ptr));
249 drop(VfsAppData::from_raw(app_data));
250 }
251 return Err(RegisterVfsError::RegisterVfs);
252 }
253
254 Ok(vfs as *mut sqlite3_vfs)
255}
256
257pub fn page_read<T, G: Fn(usize) -> Option<T>, R: Fn(T, &mut [u8], (usize, usize))>(
259 buf: &mut [u8],
260 page_size: usize,
261 file_size: usize,
262 offset: usize,
263 get_page: G,
264 read_fn: R,
265) -> i32 {
266 if page_size == 0 || file_size == 0 {
267 buf.fill(0);
268 return SQLITE_IOERR_SHORT_READ;
269 }
270
271 let mut bytes_read = 0;
272 let mut p_data_offset = 0;
273 let p_data_length = buf.len();
274 let i_offset = offset;
275
276 while p_data_offset < p_data_length {
277 let file_offset = i_offset + p_data_offset;
278 let page_idx = file_offset / page_size;
279 let page_offset = file_offset % page_size;
280 let page_addr = page_idx * page_size;
281
282 let Some(page) = get_page(page_addr) else {
283 break;
284 };
285
286 let page_length = (page_size - page_offset).min(p_data_length - p_data_offset);
287 read_fn(
288 page,
289 &mut buf[p_data_offset..p_data_offset + page_length],
290 (page_offset, page_offset + page_length),
291 );
292
293 p_data_offset += page_length;
294 bytes_read += page_length;
295 }
296
297 if bytes_read < p_data_length {
298 buf[bytes_read..].fill(0);
299 return SQLITE_IOERR_SHORT_READ;
300 }
301
302 SQLITE_OK
303}
304
305#[derive(Default)]
307pub struct MemLinearFile(Vec<u8>);
308
309impl VfsFile for MemLinearFile {
310 fn read(&self, buf: &mut [u8], offset: usize) -> VfsResult<i32> {
311 let size = buf.len();
312 let end = size + offset;
313 if self.0.len() <= offset {
314 buf.fill(0);
315 return Ok(SQLITE_IOERR_SHORT_READ);
316 }
317
318 let read_end = end.min(self.0.len());
319 let read_size = read_end - offset;
320 buf[..read_size].copy_from_slice(&self.0[offset..read_end]);
321
322 if read_size < size {
323 buf[read_size..].fill(0);
324 return Ok(SQLITE_IOERR_SHORT_READ);
325 }
326 Ok(SQLITE_OK)
327 }
328
329 fn write(&mut self, buf: &[u8], offset: usize) -> VfsResult<()> {
330 let end = buf.len() + offset;
331 if end > self.0.len() {
332 self.0.resize(end, 0);
333 }
334 self.0[offset..end].copy_from_slice(buf);
335 Ok(())
336 }
337
338 fn truncate(&mut self, size: usize) -> VfsResult<()> {
339 self.0.truncate(size);
340 Ok(())
341 }
342
343 fn flush(&mut self) -> VfsResult<()> {
344 Ok(())
345 }
346
347 fn size(&self) -> VfsResult<usize> {
348 Ok(self.0.len())
349 }
350}
351
352pub struct VfsError {
354 code: i32,
355 message: String,
356}
357
358impl VfsError {
359 pub fn new(code: i32, message: String) -> Self {
360 VfsError { code, message }
361 }
362}
363
364pub type VfsResult<T> = Result<T, VfsError>;
366
367pub struct VfsAppData<T> {
369 data: T,
370 last_err: Mutex<Option<(i32, String)>>,
371}
372
373impl<T> VfsAppData<T> {
374 pub fn new(t: T) -> Self {
375 VfsAppData {
376 data: t,
377 last_err: Mutex::new(None),
378 }
379 }
380
381 pub fn leak(self) -> *mut Self {
383 Box::into_raw(Box::new(self))
384 }
385
386 pub unsafe fn from_raw(t: *mut Self) -> VfsAppData<T> {
390 *Box::from_raw(t)
391 }
392
393 pub fn pop_err(&self) -> Option<(i32, String)> {
395 self.last_err.lock().take()
396 }
397
398 pub fn store_err(&self, err: VfsError) -> i32 {
400 let VfsError { code, message } = err;
401 self.last_err.lock().replace((code, message));
402 code
403 }
404}
405
406impl<T> Deref for VfsAppData<T> {
408 type Target = T;
409
410 fn deref(&self) -> &Self::Target {
411 &self.data
412 }
413}
414
415pub trait VfsFile {
417 fn read(&self, buf: &mut [u8], offset: usize) -> VfsResult<i32>;
419 fn write(&mut self, buf: &[u8], offset: usize) -> VfsResult<()>;
421 fn truncate(&mut self, size: usize) -> VfsResult<()>;
423 fn flush(&mut self) -> VfsResult<()>;
425 fn size(&self) -> VfsResult<usize>;
427}
428
429pub trait VfsStore<File, AppData> {
431 unsafe fn app_data(vfs: *mut sqlite3_vfs) -> &'static VfsAppData<AppData> {
437 &*(*vfs).pAppData.cast()
438 }
439 fn name2path(vfs: *mut sqlite3_vfs, file: &str) -> VfsResult<String> {
441 unused!(vfs);
442 Ok(file.into())
443 }
444 fn add_file(vfs: *mut sqlite3_vfs, file: &str, flags: i32) -> VfsResult<()>;
446 fn contains_file(vfs: *mut sqlite3_vfs, file: &str) -> VfsResult<bool>;
448 fn delete_file(vfs: *mut sqlite3_vfs, file: &str) -> VfsResult<()>;
450 fn with_file<F: Fn(&File) -> i32>(vfs_file: &SQLiteVfsFile, f: F) -> VfsResult<i32>;
452 fn with_file_mut<F: Fn(&mut File) -> i32>(vfs_file: &SQLiteVfsFile, f: F) -> VfsResult<i32>;
454}
455
456#[allow(clippy::missing_safety_doc)]
458pub trait SQLiteVfs<IO: SQLiteIoMethods> {
459 const VERSION: ::std::os::raw::c_int;
460 const MAX_PATH_SIZE: ::std::os::raw::c_int = 1024;
461
462 fn vfs(
463 vfs_name: *const ::std::os::raw::c_char,
464 app_data: *mut VfsAppData<IO::AppData>,
465 ) -> sqlite3_vfs {
466 sqlite3_vfs {
467 iVersion: Self::VERSION,
468 szOsFile: std::mem::size_of::<SQLiteVfsFile>() as i32,
469 mxPathname: Self::MAX_PATH_SIZE,
470 pNext: std::ptr::null_mut(),
471 zName: vfs_name,
472 pAppData: app_data.cast(),
473 xOpen: Some(Self::xOpen),
474 xDelete: Some(Self::xDelete),
475 xAccess: Some(Self::xAccess),
476 xFullPathname: Some(Self::xFullPathname),
477 xDlOpen: None,
478 xDlError: None,
479 xDlSym: None,
480 xDlClose: None,
481 xRandomness: Some(x_methods_shim::xRandomness),
482 xSleep: Some(x_methods_shim::xSleep),
483 xCurrentTime: Some(x_methods_shim::xCurrentTime),
484 xGetLastError: Some(Self::xGetLastError),
485 xCurrentTimeInt64: Some(x_methods_shim::xCurrentTimeInt64),
486 xSetSystemCall: None,
487 xGetSystemCall: None,
488 xNextSystemCall: None,
489 }
490 }
491
492 unsafe extern "C" fn xOpen(
493 pVfs: *mut sqlite3_vfs,
494 zName: sqlite3_filename,
495 pFile: *mut sqlite3_file,
496 flags: ::std::os::raw::c_int,
497 pOutFlags: *mut ::std::os::raw::c_int,
498 ) -> ::std::os::raw::c_int {
499 let app_data = IO::Store::app_data(pVfs);
500
501 let name = if zName.is_null() {
502 get_random_name()
503 } else {
504 check_result!(CStr::from_ptr(zName).to_str()).into()
505 };
506
507 let name = match IO::Store::name2path(pVfs, &name) {
508 Ok(name) => name,
509 Err(err) => return app_data.store_err(err),
510 };
511
512 let exist = match IO::Store::contains_file(pVfs, &name) {
513 Ok(exist) => exist,
514 Err(err) => return app_data.store_err(err),
515 };
516
517 if !exist {
518 if flags & SQLITE_OPEN_CREATE == 0 {
519 return app_data.store_err(VfsError::new(
520 SQLITE_CANTOPEN,
521 format!("file not found: {name}"),
522 ));
523 }
524 if let Err(err) = IO::Store::add_file(pVfs, &name, flags) {
525 return app_data.store_err(err);
526 }
527 }
528
529 let leak = name.leak();
530 let vfs_file = pFile.cast::<SQLiteVfsFile>();
531 (*vfs_file).vfs = pVfs;
532 (*vfs_file).flags = flags;
533 (*vfs_file).name_ptr = leak.as_ptr();
534 (*vfs_file).name_length = leak.len();
535
536 (*pFile).pMethods = &IO::METHODS;
537
538 if !pOutFlags.is_null() {
539 *pOutFlags = flags;
540 }
541
542 SQLITE_OK
543 }
544
545 unsafe extern "C" fn xDelete(
546 pVfs: *mut sqlite3_vfs,
547 zName: *const ::std::os::raw::c_char,
548 syncDir: ::std::os::raw::c_int,
549 ) -> ::std::os::raw::c_int {
550 unused!(syncDir);
551
552 let app_data = IO::Store::app_data(pVfs);
553 bail!(zName.is_null(), SQLITE_IOERR_DELETE);
554 let s = check_result!(CStr::from_ptr(zName).to_str());
555 if let Err(err) = IO::Store::delete_file(pVfs, s) {
556 app_data.store_err(err)
557 } else {
558 SQLITE_OK
559 }
560 }
561
562 unsafe extern "C" fn xAccess(
563 pVfs: *mut sqlite3_vfs,
564 zName: *const ::std::os::raw::c_char,
565 flags: ::std::os::raw::c_int,
566 pResOut: *mut ::std::os::raw::c_int,
567 ) -> ::std::os::raw::c_int {
568 unused!(flags);
569
570 *pResOut = if zName.is_null() {
571 0
572 } else {
573 let app_data = IO::Store::app_data(pVfs);
574 let file = check_result!(CStr::from_ptr(zName).to_str());
575 let exist = match IO::Store::contains_file(pVfs, file) {
576 Ok(exist) => exist,
577 Err(err) => return app_data.store_err(err),
578 };
579 i32::from(exist)
580 };
581
582 SQLITE_OK
583 }
584
585 unsafe extern "C" fn xFullPathname(
586 pVfs: *mut sqlite3_vfs,
587 zName: *const ::std::os::raw::c_char,
588 nOut: ::std::os::raw::c_int,
589 zOut: *mut ::std::os::raw::c_char,
590 ) -> ::std::os::raw::c_int {
591 unused!(pVfs);
592 bail!(zName.is_null() || zOut.is_null(), SQLITE_CANTOPEN);
593 let len = CStr::from_ptr(zName).count_bytes() + 1;
594 bail!(len > nOut as usize, SQLITE_CANTOPEN);
595 zName.copy_to(zOut, len);
596 SQLITE_OK
597 }
598
599 unsafe extern "C" fn xGetLastError(
600 pVfs: *mut sqlite3_vfs,
601 nOut: ::std::os::raw::c_int,
602 zOut: *mut ::std::os::raw::c_char,
603 ) -> ::std::os::raw::c_int {
604 let app_data = IO::Store::app_data(pVfs);
605 let Some((code, msg)) = app_data.pop_err() else {
606 return SQLITE_OK;
607 };
608 if !zOut.is_null() {
609 let nOut = nOut as usize;
610 let count = msg.len().min(nOut);
611 msg.as_ptr().copy_to(zOut.cast(), count);
612 let zero = match nOut.cmp(&msg.len()) {
613 std::cmp::Ordering::Less | std::cmp::Ordering::Equal => nOut,
614 std::cmp::Ordering::Greater => msg.len() + 1,
615 };
616 if zero > 0 {
617 std::ptr::write(zOut.add(zero - 1), 0);
618 }
619 }
620 code
621 }
622}
623
624#[allow(clippy::missing_safety_doc)]
626pub trait SQLiteIoMethods {
627 type File: VfsFile;
628 type AppData: 'static;
629 type Store: VfsStore<Self::File, Self::AppData>;
630
631 const VERSION: ::std::os::raw::c_int;
632
633 const METHODS: sqlite3_io_methods = sqlite3_io_methods {
634 iVersion: Self::VERSION,
635 xClose: Some(Self::xClose),
636 xRead: Some(Self::xRead),
637 xWrite: Some(Self::xWrite),
638 xTruncate: Some(Self::xTruncate),
639 xSync: Some(Self::xSync),
640 xFileSize: Some(Self::xFileSize),
641 xLock: Some(Self::xLock),
642 xUnlock: Some(Self::xUnlock),
643 xCheckReservedLock: Some(Self::xCheckReservedLock),
644 xFileControl: Some(Self::xFileControl),
645 xSectorSize: Some(Self::xSectorSize),
646 xDeviceCharacteristics: Some(Self::xDeviceCharacteristics),
647 xShmMap: None,
648 xShmLock: None,
649 xShmBarrier: None,
650 xShmUnmap: None,
651 xFetch: None,
652 xUnfetch: None,
653 };
654
655 unsafe extern "C" fn xClose(pFile: *mut sqlite3_file) -> ::std::os::raw::c_int {
656 let vfs_file = SQLiteVfsFile::from_file(pFile);
657 let app_data = Self::Store::app_data(vfs_file.vfs);
658
659 if vfs_file.flags & SQLITE_OPEN_DELETEONCLOSE != 0 {
660 if let Err(err) = Self::Store::delete_file(vfs_file.vfs, vfs_file.name()) {
661 return app_data.store_err(err);
662 }
663 }
664
665 drop(Box::from_raw(vfs_file.name()));
666
667 SQLITE_OK
668 }
669
670 unsafe extern "C" fn xRead(
671 pFile: *mut sqlite3_file,
672 zBuf: *mut ::std::os::raw::c_void,
673 iAmt: ::std::os::raw::c_int,
674 iOfst: sqlite3_int64,
675 ) -> ::std::os::raw::c_int {
676 let vfs_file = SQLiteVfsFile::from_file(pFile);
677 let app_data = Self::Store::app_data(vfs_file.vfs);
678
679 let f = |file: &Self::File| {
680 let size = iAmt as usize;
681 let offset = iOfst as usize;
682 let slice = std::slice::from_raw_parts_mut(zBuf.cast::<u8>(), size);
683 match file.read(slice, offset) {
684 Ok(code) => code,
685 Err(err) => app_data.store_err(err),
686 }
687 };
688
689 match Self::Store::with_file(vfs_file, f) {
690 Ok(code) => code,
691 Err(err) => app_data.store_err(err),
692 }
693 }
694
695 unsafe extern "C" fn xWrite(
696 pFile: *mut sqlite3_file,
697 zBuf: *const ::std::os::raw::c_void,
698 iAmt: ::std::os::raw::c_int,
699 iOfst: sqlite3_int64,
700 ) -> ::std::os::raw::c_int {
701 let vfs_file = SQLiteVfsFile::from_file(pFile);
702 let app_data = Self::Store::app_data(vfs_file.vfs);
703
704 let f = |file: &mut Self::File| {
705 let (offset, size) = (iOfst as usize, iAmt as usize);
706 let slice = std::slice::from_raw_parts(zBuf.cast::<u8>(), size);
707 if let Err(err) = file.write(slice, offset) {
708 app_data.store_err(err)
709 } else {
710 SQLITE_OK
711 }
712 };
713
714 match Self::Store::with_file_mut(vfs_file, f) {
715 Ok(code) => code,
716 Err(err) => app_data.store_err(err),
717 }
718 }
719
720 unsafe extern "C" fn xTruncate(
721 pFile: *mut sqlite3_file,
722 size: sqlite3_int64,
723 ) -> ::std::os::raw::c_int {
724 let vfs_file = SQLiteVfsFile::from_file(pFile);
725 let app_data = Self::Store::app_data(vfs_file.vfs);
726
727 let f = |file: &mut Self::File| {
728 if let Err(err) = file.truncate(size as usize) {
729 app_data.store_err(err)
730 } else {
731 SQLITE_OK
732 }
733 };
734
735 match Self::Store::with_file_mut(vfs_file, f) {
736 Ok(code) => code,
737 Err(err) => app_data.store_err(err),
738 }
739 }
740
741 unsafe extern "C" fn xSync(
742 pFile: *mut sqlite3_file,
743 flags: ::std::os::raw::c_int,
744 ) -> ::std::os::raw::c_int {
745 unused!(flags);
746
747 let vfs_file = SQLiteVfsFile::from_file(pFile);
748 let app_data = Self::Store::app_data(vfs_file.vfs);
749
750 let f = |file: &mut Self::File| {
751 if let Err(err) = file.flush() {
752 app_data.store_err(err)
753 } else {
754 SQLITE_OK
755 }
756 };
757
758 match Self::Store::with_file_mut(vfs_file, f) {
759 Ok(code) => code,
760 Err(err) => app_data.store_err(err),
761 }
762 }
763
764 unsafe extern "C" fn xFileSize(
765 pFile: *mut sqlite3_file,
766 pSize: *mut sqlite3_int64,
767 ) -> ::std::os::raw::c_int {
768 let vfs_file = SQLiteVfsFile::from_file(pFile);
769 let app_data = Self::Store::app_data(vfs_file.vfs);
770
771 let f = |file: &Self::File| match file.size() {
772 Ok(size) => {
773 *pSize = size as sqlite3_int64;
774 SQLITE_OK
775 }
776 Err(err) => app_data.store_err(err),
777 };
778
779 match Self::Store::with_file(vfs_file, f) {
780 Ok(code) => code,
781 Err(err) => app_data.store_err(err),
782 }
783 }
784
785 unsafe extern "C" fn xLock(
786 pFile: *mut sqlite3_file,
787 eLock: ::std::os::raw::c_int,
788 ) -> ::std::os::raw::c_int {
789 unused!((pFile, eLock));
790 SQLITE_OK
791 }
792
793 unsafe extern "C" fn xUnlock(
794 pFile: *mut sqlite3_file,
795 eLock: ::std::os::raw::c_int,
796 ) -> ::std::os::raw::c_int {
797 unused!((pFile, eLock));
798 SQLITE_OK
799 }
800
801 unsafe extern "C" fn xCheckReservedLock(
802 pFile: *mut sqlite3_file,
803 pResOut: *mut ::std::os::raw::c_int,
804 ) -> ::std::os::raw::c_int {
805 unused!(pFile);
806 *pResOut = 0;
807 SQLITE_OK
808 }
809
810 unsafe extern "C" fn xFileControl(
811 pFile: *mut sqlite3_file,
812 op: ::std::os::raw::c_int,
813 pArg: *mut ::std::os::raw::c_void,
814 ) -> ::std::os::raw::c_int {
815 unused!((pFile, op, pArg));
816 SQLITE_NOTFOUND
817 }
818
819 unsafe extern "C" fn xSectorSize(pFile: *mut sqlite3_file) -> ::std::os::raw::c_int {
820 unused!(pFile);
821 512
822 }
823
824 unsafe extern "C" fn xDeviceCharacteristics(pFile: *mut sqlite3_file) -> ::std::os::raw::c_int {
825 unused!(pFile);
826 0
827 }
828}
829
830#[allow(clippy::missing_safety_doc)]
832pub mod x_methods_shim {
833 use super::*;
834
835 #[cfg(target_feature = "atomics")]
837 pub unsafe extern "C" fn xSleep(
838 _pVfs: *mut sqlite3_vfs,
839 microseconds: ::std::os::raw::c_int,
840 ) -> ::std::os::raw::c_int {
841 use std::{thread, time::Duration};
842 thread::sleep(Duration::from_micros(microseconds as u64));
843 SQLITE_OK
844 }
845
846 #[cfg(not(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 SQLITE_OK
852 }
853
854 pub unsafe extern "C" fn xRandomness(
856 _pVfs: *mut sqlite3_vfs,
857 nByte: ::std::os::raw::c_int,
858 zOut: *mut ::std::os::raw::c_char,
859 ) -> ::std::os::raw::c_int {
860 for i in 0..nByte as usize {
861 *zOut.add(i) = (Math::random() * 255000.0) as _;
862 }
863 nByte
864 }
865
866 pub unsafe extern "C" fn xCurrentTime(
868 _pVfs: *mut sqlite3_vfs,
869 pTimeOut: *mut f64,
870 ) -> ::std::os::raw::c_int {
871 *pTimeOut = 2440587.5 + (Date::new_0().get_time() / 86400000.0);
872 SQLITE_OK
873 }
874
875 pub unsafe extern "C" fn xCurrentTimeInt64(
877 _pVfs: *mut sqlite3_vfs,
878 pOut: *mut sqlite3_int64,
879 ) -> ::std::os::raw::c_int {
880 *pOut = ((2440587.5 * 86400000.0) + Date::new_0().get_time()) as sqlite3_int64;
881 SQLITE_OK
882 }
883}
884
885#[derive(thiserror::Error, Debug)]
886pub enum ImportDbError {
887 #[error("Byte array size is invalid for an SQLite db.")]
888 InvalidDbSize,
889 #[error("Input does not contain an SQLite database header.")]
890 InvalidHeader,
891 #[error("Page size must be a power of two between 512 and 65536 inclusive")]
892 InvalidPageSize,
893}
894
895pub fn check_import_db(bytes: &[u8]) -> Result<usize, ImportDbError> {
897 let length = bytes.len();
898
899 if length < 512 && length % 512 != 0 {
900 return Err(ImportDbError::InvalidDbSize);
901 }
902
903 if SQLITE3_HEADER
904 .as_bytes()
905 .iter()
906 .zip(bytes)
907 .any(|(x, y)| x != y)
908 {
909 return Err(ImportDbError::InvalidHeader);
910 }
911
912 let page_size = u16::from_be_bytes([bytes[16], bytes[17]]);
915 let page_size = if page_size == 1 {
916 65536
917 } else {
918 usize::from(page_size)
919 };
920
921 Ok(page_size)
922}
923
924pub fn check_db_and_page_size(db_size: usize, page_size: usize) -> Result<(), ImportDbError> {
926 if !(page_size.is_power_of_two() && (512..=65536).contains(&page_size)) {
927 return Err(ImportDbError::InvalidPageSize);
928 }
929 if db_size % page_size != 0 {
930 return Err(ImportDbError::InvalidDbSize);
931 }
932 Ok(())
933}
934
935#[cfg(test)]
936mod tests {
937 use crate::vfs::utils::{copy_to_slice, copy_to_uint8_array_subarray};
938
939 use super::{copy_to_uint8_array, copy_to_vec};
940 use js_sys::Uint8Array;
941 use wasm_bindgen_test::wasm_bindgen_test;
942
943 #[wasm_bindgen_test]
944 fn test_js_utils() {
945 let buf1 = vec![1, 2, 3, 4];
946 let uint8 = copy_to_uint8_array(&buf1);
947 let buf2 = copy_to_vec(&uint8);
948 assert_eq!(buf1, buf2);
949
950 let mut buf3 = vec![0u8; 2];
951 copy_to_slice(&uint8.subarray(0, 2), &mut buf3);
952 assert_eq!(buf3, vec![1, 2]);
953
954 let buf4 = Uint8Array::new_with_length(3);
955 copy_to_uint8_array_subarray(&buf3, &buf4.subarray(1, 3));
956 assert!(buf4.get_index(0) == 0);
957 assert!(buf4.get_index(1) == 1);
958 assert!(buf4.get_index(2) == 2);
959 }
960}