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