1use 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#[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#[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#[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#[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
82pub 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
105pub const SQLITE3_HEADER: &str = "SQLite format 3";
107
108pub 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
114pub 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 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 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#[repr(C)]
275pub struct SQLiteVfsFile {
276 pub io_methods: sqlite3_file,
279 pub vfs: *mut sqlite3_vfs,
281 pub flags: i32,
283 pub name_ptr: *const u8,
286 pub name_length: usize,
288}
289
290impl SQLiteVfsFile {
291 pub unsafe fn from_file(file: *mut sqlite3_file) -> &'static SQLiteVfsFile {
297 &*file.cast::<Self>()
298 }
299
300 pub unsafe fn name(&self) -> &'static mut str {
308 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 pub fn sqlite3_file(&'static self) -> *mut sqlite3_file {
317 self as *const SQLiteVfsFile as *mut sqlite3_file
318 }
319}
320
321#[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
330pub fn register_vfs<IO: SQLiteIoMethods, V: SQLiteVfs<IO>>(
332 vfs_name: &str,
333 app_data: IO::AppData,
334 default_vfs: bool,
335) -> Result<*mut sqlite3_vfs, RegisterVfsError> {
336 let name = CString::new(vfs_name).map_err(|_| RegisterVfsError::ToCStr)?;
337 let name_ptr = name.into_raw();
338 let app_data = VfsAppData::new(app_data).leak();
339
340 let vfs = Box::leak(Box::new(V::vfs(name_ptr, app_data.cast())));
341 let ret = unsafe { sqlite3_vfs_register(vfs, i32::from(default_vfs)) };
342
343 if ret != SQLITE_OK {
344 unsafe {
345 drop(Box::from_raw(vfs));
346 drop(CString::from_raw(name_ptr));
347 drop(VfsAppData::from_raw(app_data));
348 }
349 return Err(RegisterVfsError::RegisterVfs);
350 }
351
352 Ok(vfs as *mut sqlite3_vfs)
353}
354
355#[derive(Debug)]
357pub struct VfsError {
358 code: i32,
359 message: String,
360}
361
362impl VfsError {
363 pub fn new(code: i32, message: String) -> Self {
364 VfsError { code, message }
365 }
366}
367
368pub type VfsResult<T> = Result<T, VfsError>;
370
371pub struct VfsAppData<T> {
373 data: T,
374 last_err: RefCell<Option<(i32, String)>>,
375}
376
377impl<T> VfsAppData<T> {
378 pub fn new(t: T) -> Self {
379 VfsAppData {
380 data: t,
381 last_err: RefCell::new(None),
382 }
383 }
384
385 pub fn leak(self) -> *mut Self {
387 Box::into_raw(Box::new(self))
388 }
389
390 pub unsafe fn from_raw(t: *mut Self) -> VfsAppData<T> {
394 *Box::from_raw(t)
395 }
396
397 pub fn pop_err(&self) -> Option<(i32, String)> {
399 self.last_err.borrow_mut().take()
400 }
401
402 pub fn store_err(&self, err: VfsError) -> i32 {
404 let VfsError { code, message } = err;
405 self.last_err.borrow_mut().replace((code, message));
406 code
407 }
408}
409
410impl<T> Deref for VfsAppData<T> {
412 type Target = T;
413
414 fn deref(&self) -> &Self::Target {
415 &self.data
416 }
417}
418
419pub trait VfsFile {
421 fn read(&self, buf: &mut [u8], offset: usize) -> VfsResult<bool>;
423 fn write(&mut self, buf: &[u8], offset: usize) -> VfsResult<()>;
425 fn truncate(&mut self, size: usize) -> VfsResult<()>;
427 fn flush(&mut self) -> VfsResult<()>;
429 fn size(&self) -> VfsResult<usize>;
431}
432
433pub trait VfsStore<File, AppData> {
435 unsafe fn app_data(vfs: *mut sqlite3_vfs) -> &'static VfsAppData<AppData> {
441 &*(*vfs).pAppData.cast()
442 }
443 fn add_file(vfs: *mut sqlite3_vfs, file: &str, flags: i32) -> VfsResult<()>;
445 fn contains_file(vfs: *mut sqlite3_vfs, file: &str) -> VfsResult<bool>;
447 fn delete_file(vfs: *mut sqlite3_vfs, file: &str) -> VfsResult<()>;
449 fn with_file<F: Fn(&File) -> VfsResult<i32>>(vfs_file: &SQLiteVfsFile, f: F) -> VfsResult<i32>;
451 fn with_file_mut<F: Fn(&mut File) -> VfsResult<i32>>(
453 vfs_file: &SQLiteVfsFile,
454 f: F,
455 ) -> VfsResult<i32>;
456}
457
458#[allow(clippy::missing_safety_doc)]
460pub trait SQLiteVfs<IO: SQLiteIoMethods> {
461 const VERSION: ::std::os::raw::c_int;
462 const MAX_PATH_SIZE: ::std::os::raw::c_int = 1024;
463
464 fn vfs(
465 vfs_name: *const ::std::os::raw::c_char,
466 app_data: *mut VfsAppData<IO::AppData>,
467 ) -> sqlite3_vfs {
468 sqlite3_vfs {
469 iVersion: Self::VERSION,
470 szOsFile: std::mem::size_of::<SQLiteVfsFile>() as i32,
471 mxPathname: Self::MAX_PATH_SIZE,
472 pNext: std::ptr::null_mut(),
473 zName: vfs_name,
474 pAppData: app_data.cast(),
475 xOpen: Some(Self::xOpen),
476 xDelete: Some(Self::xDelete),
477 xAccess: Some(Self::xAccess),
478 xFullPathname: Some(Self::xFullPathname),
479 xDlOpen: None,
480 xDlError: None,
481 xDlSym: None,
482 xDlClose: None,
483 xRandomness: Some(x_methods_shim::xRandomness),
484 xSleep: Some(x_methods_shim::xSleep),
485 xCurrentTime: Some(x_methods_shim::xCurrentTime),
486 xGetLastError: Some(Self::xGetLastError),
487 xCurrentTimeInt64: Some(x_methods_shim::xCurrentTimeInt64),
488 xSetSystemCall: None,
489 xGetSystemCall: None,
490 xNextSystemCall: None,
491 }
492 }
493
494 unsafe extern "C" fn xOpen(
495 pVfs: *mut sqlite3_vfs,
496 zName: sqlite3_filename,
497 pFile: *mut sqlite3_file,
498 flags: ::std::os::raw::c_int,
499 pOutFlags: *mut ::std::os::raw::c_int,
500 ) -> ::std::os::raw::c_int {
501 let app_data = IO::Store::app_data(pVfs);
502
503 let name = if zName.is_null() {
504 random_name()
505 } else {
506 check_result!(CStr::from_ptr(zName).to_str()).into()
507 };
508
509 let exist = match IO::Store::contains_file(pVfs, &name) {
510 Ok(exist) => exist,
511 Err(err) => return app_data.store_err(err),
512 };
513
514 if !exist {
515 if flags & SQLITE_OPEN_CREATE == 0 {
516 return app_data.store_err(VfsError::new(
517 SQLITE_CANTOPEN,
518 format!("file not found: {name}"),
519 ));
520 }
521 if let Err(err) = IO::Store::add_file(pVfs, &name, flags) {
522 return app_data.store_err(err);
523 }
524 }
525
526 let leak = name.leak();
527 let vfs_file = pFile.cast::<SQLiteVfsFile>();
528 (*vfs_file).vfs = pVfs;
529 (*vfs_file).flags = flags;
530 (*vfs_file).name_ptr = leak.as_ptr();
531 (*vfs_file).name_length = leak.len();
532
533 (*pFile).pMethods = &IO::METHODS;
534
535 if !pOutFlags.is_null() {
536 *pOutFlags = flags;
537 }
538
539 SQLITE_OK
540 }
541
542 unsafe extern "C" fn xDelete(
543 pVfs: *mut sqlite3_vfs,
544 zName: *const ::std::os::raw::c_char,
545 syncDir: ::std::os::raw::c_int,
546 ) -> ::std::os::raw::c_int {
547 unused!(syncDir);
548
549 let app_data = IO::Store::app_data(pVfs);
550 bail!(zName.is_null(), SQLITE_IOERR_DELETE);
551 let s = check_result!(CStr::from_ptr(zName).to_str());
552 if let Err(err) = IO::Store::delete_file(pVfs, s) {
553 app_data.store_err(err)
554 } else {
555 SQLITE_OK
556 }
557 }
558
559 unsafe extern "C" fn xAccess(
560 pVfs: *mut sqlite3_vfs,
561 zName: *const ::std::os::raw::c_char,
562 flags: ::std::os::raw::c_int,
563 pResOut: *mut ::std::os::raw::c_int,
564 ) -> ::std::os::raw::c_int {
565 unused!(flags);
566
567 *pResOut = if zName.is_null() {
568 0
569 } else {
570 let app_data = IO::Store::app_data(pVfs);
571 let file = check_result!(CStr::from_ptr(zName).to_str());
572 let exist = match IO::Store::contains_file(pVfs, file) {
573 Ok(exist) => exist,
574 Err(err) => return app_data.store_err(err),
575 };
576 i32::from(exist)
577 };
578
579 SQLITE_OK
580 }
581
582 unsafe extern "C" fn xFullPathname(
583 pVfs: *mut sqlite3_vfs,
584 zName: *const ::std::os::raw::c_char,
585 nOut: ::std::os::raw::c_int,
586 zOut: *mut ::std::os::raw::c_char,
587 ) -> ::std::os::raw::c_int {
588 unused!(pVfs);
589 bail!(zName.is_null() || zOut.is_null(), SQLITE_CANTOPEN);
590 let len = CStr::from_ptr(zName).to_bytes_with_nul().len();
591 bail!(len > nOut as usize, SQLITE_CANTOPEN);
592 zName.copy_to(zOut, len);
593 SQLITE_OK
594 }
595
596 unsafe extern "C" fn xGetLastError(
597 pVfs: *mut sqlite3_vfs,
598 nOut: ::std::os::raw::c_int,
599 zOut: *mut ::std::os::raw::c_char,
600 ) -> ::std::os::raw::c_int {
601 let app_data = IO::Store::app_data(pVfs);
602 let Some((code, msg)) = app_data.pop_err() else {
603 return SQLITE_OK;
604 };
605 if !zOut.is_null() {
606 let nOut = nOut as usize;
607 let count = msg.len().min(nOut);
608 msg.as_ptr().copy_to(zOut.cast(), count);
609 let zero = match nOut.cmp(&msg.len()) {
610 std::cmp::Ordering::Less | std::cmp::Ordering::Equal => nOut,
611 std::cmp::Ordering::Greater => msg.len() + 1,
612 };
613 if zero > 0 {
614 std::ptr::write(zOut.add(zero - 1), 0);
615 }
616 }
617 code
618 }
619}
620
621#[allow(clippy::missing_safety_doc)]
623pub trait SQLiteIoMethods {
624 type File: VfsFile;
625 type AppData: 'static;
626 type Store: VfsStore<Self::File, Self::AppData>;
627
628 const VERSION: ::std::os::raw::c_int;
629
630 const METHODS: sqlite3_io_methods = sqlite3_io_methods {
631 iVersion: Self::VERSION,
632 xClose: Some(Self::xClose),
633 xRead: Some(Self::xRead),
634 xWrite: Some(Self::xWrite),
635 xTruncate: Some(Self::xTruncate),
636 xSync: Some(Self::xSync),
637 xFileSize: Some(Self::xFileSize),
638 xLock: Some(Self::xLock),
639 xUnlock: Some(Self::xUnlock),
640 xCheckReservedLock: Some(Self::xCheckReservedLock),
641 xFileControl: Some(Self::xFileControl),
642 xSectorSize: Some(Self::xSectorSize),
643 xDeviceCharacteristics: Some(Self::xDeviceCharacteristics),
644 xShmMap: None,
645 xShmLock: None,
646 xShmBarrier: None,
647 xShmUnmap: None,
648 xFetch: None,
649 xUnfetch: None,
650 };
651
652 unsafe extern "C" fn xClose(pFile: *mut sqlite3_file) -> ::std::os::raw::c_int {
653 let vfs_file = SQLiteVfsFile::from_file(pFile);
654 let app_data = Self::Store::app_data(vfs_file.vfs);
655
656 if vfs_file.flags & SQLITE_OPEN_DELETEONCLOSE != 0 {
657 if let Err(err) = Self::Store::delete_file(vfs_file.vfs, vfs_file.name()) {
658 return app_data.store_err(err);
659 }
660 }
661
662 drop(Box::from_raw(vfs_file.name()));
663
664 SQLITE_OK
665 }
666
667 unsafe extern "C" fn xRead(
668 pFile: *mut sqlite3_file,
669 zBuf: *mut ::std::os::raw::c_void,
670 iAmt: ::std::os::raw::c_int,
671 iOfst: sqlite3_int64,
672 ) -> ::std::os::raw::c_int {
673 let vfs_file = SQLiteVfsFile::from_file(pFile);
674 let app_data = Self::Store::app_data(vfs_file.vfs);
675
676 let f = |file: &Self::File| {
677 let size = iAmt as usize;
678 let offset = iOfst as usize;
679 let slice = std::slice::from_raw_parts_mut(zBuf.cast::<u8>(), size);
680 let code = if file.read(slice, offset)? {
681 SQLITE_OK
682 } else {
683 SQLITE_IOERR_SHORT_READ
684 };
685 Ok(code)
686 };
687
688 match Self::Store::with_file(vfs_file, f) {
689 Ok(code) => code,
690 Err(err) => app_data.store_err(err),
691 }
692 }
693
694 unsafe extern "C" fn xWrite(
695 pFile: *mut sqlite3_file,
696 zBuf: *const ::std::os::raw::c_void,
697 iAmt: ::std::os::raw::c_int,
698 iOfst: sqlite3_int64,
699 ) -> ::std::os::raw::c_int {
700 let vfs_file = SQLiteVfsFile::from_file(pFile);
701 let app_data = Self::Store::app_data(vfs_file.vfs);
702
703 let f = |file: &mut Self::File| {
704 let (offset, size) = (iOfst as usize, iAmt as usize);
705 let slice = std::slice::from_raw_parts(zBuf.cast::<u8>(), size);
706 file.write(slice, offset)?;
707 Ok(SQLITE_OK)
708 };
709
710 match Self::Store::with_file_mut(vfs_file, f) {
711 Ok(code) => code,
712 Err(err) => app_data.store_err(err),
713 }
714 }
715
716 unsafe extern "C" fn xTruncate(
717 pFile: *mut sqlite3_file,
718 size: sqlite3_int64,
719 ) -> ::std::os::raw::c_int {
720 let vfs_file = SQLiteVfsFile::from_file(pFile);
721 let app_data = Self::Store::app_data(vfs_file.vfs);
722
723 let f = |file: &mut Self::File| {
724 file.truncate(size as usize)?;
725 Ok(SQLITE_OK)
726 };
727
728 match Self::Store::with_file_mut(vfs_file, f) {
729 Ok(code) => code,
730 Err(err) => app_data.store_err(err),
731 }
732 }
733
734 unsafe extern "C" fn xSync(
735 pFile: *mut sqlite3_file,
736 flags: ::std::os::raw::c_int,
737 ) -> ::std::os::raw::c_int {
738 unused!(flags);
739
740 let vfs_file = SQLiteVfsFile::from_file(pFile);
741 let app_data = Self::Store::app_data(vfs_file.vfs);
742
743 let f = |file: &mut Self::File| {
744 file.flush()?;
745 Ok(SQLITE_OK)
746 };
747
748 match Self::Store::with_file_mut(vfs_file, f) {
749 Ok(code) => code,
750 Err(err) => app_data.store_err(err),
751 }
752 }
753
754 unsafe extern "C" fn xFileSize(
755 pFile: *mut sqlite3_file,
756 pSize: *mut sqlite3_int64,
757 ) -> ::std::os::raw::c_int {
758 let vfs_file = SQLiteVfsFile::from_file(pFile);
759 let app_data = Self::Store::app_data(vfs_file.vfs);
760
761 let f = |file: &Self::File| {
762 file.size().map(|size| {
763 *pSize = size as sqlite3_int64;
764 })?;
765 Ok(SQLITE_OK)
766 };
767
768 match Self::Store::with_file(vfs_file, f) {
769 Ok(code) => code,
770 Err(err) => app_data.store_err(err),
771 }
772 }
773
774 unsafe extern "C" fn xLock(
775 pFile: *mut sqlite3_file,
776 eLock: ::std::os::raw::c_int,
777 ) -> ::std::os::raw::c_int {
778 unused!((pFile, eLock));
779 SQLITE_OK
780 }
781
782 unsafe extern "C" fn xUnlock(
783 pFile: *mut sqlite3_file,
784 eLock: ::std::os::raw::c_int,
785 ) -> ::std::os::raw::c_int {
786 unused!((pFile, eLock));
787 SQLITE_OK
788 }
789
790 unsafe extern "C" fn xCheckReservedLock(
791 pFile: *mut sqlite3_file,
792 pResOut: *mut ::std::os::raw::c_int,
793 ) -> ::std::os::raw::c_int {
794 unused!(pFile);
795 *pResOut = 0;
796 SQLITE_OK
797 }
798
799 unsafe extern "C" fn xFileControl(
800 pFile: *mut sqlite3_file,
801 op: ::std::os::raw::c_int,
802 pArg: *mut ::std::os::raw::c_void,
803 ) -> ::std::os::raw::c_int {
804 unused!((pFile, op, pArg));
805 SQLITE_NOTFOUND
806 }
807
808 unsafe extern "C" fn xSectorSize(pFile: *mut sqlite3_file) -> ::std::os::raw::c_int {
809 unused!(pFile);
810 512
811 }
812
813 unsafe extern "C" fn xDeviceCharacteristics(pFile: *mut sqlite3_file) -> ::std::os::raw::c_int {
814 unused!(pFile);
815 0
816 }
817}
818
819#[allow(clippy::missing_safety_doc)]
821pub mod x_methods_shim {
822 use super::*;
823
824 #[cfg(target_feature = "atomics")]
826 pub unsafe extern "C" fn xSleep(
827 _pVfs: *mut sqlite3_vfs,
828 microseconds: ::std::os::raw::c_int,
829 ) -> ::std::os::raw::c_int {
830 use std::{thread, time::Duration};
831 thread::sleep(Duration::from_micros(microseconds as u64));
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: ::std::os::raw::c_int,
839 ) -> ::std::os::raw::c_int {
840 SQLITE_OK
841 }
842
843 pub unsafe extern "C" fn xRandomness(
845 _pVfs: *mut sqlite3_vfs,
846 nByte: ::std::os::raw::c_int,
847 zOut: *mut ::std::os::raw::c_char,
848 ) -> ::std::os::raw::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 ) -> ::std::os::raw::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 ) -> ::std::os::raw::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#[cfg(test)]
925pub mod test_suite {
926 use super::{
927 sqlite3_file, sqlite3_vfs, SQLiteVfsFile, VfsAppData, VfsError, VfsFile, VfsResult,
928 VfsStore, SQLITE_IOERR, SQLITE_OK, SQLITE_OPEN_CREATE, SQLITE_OPEN_MAIN_DB,
929 SQLITE_OPEN_READWRITE,
930 };
931
932 fn test_vfs_file<File: VfsFile>(file: &mut File) -> VfsResult<i32> {
933 let base_offset = 1024 * 1024;
934
935 let mut write_buffer = vec![42; 64 * 1024];
936 let mut read_buffer = vec![42; base_offset + write_buffer.len()];
937 let hw = "hello world!";
938 write_buffer[0..hw.len()].copy_from_slice(hw.as_bytes());
939
940 file.write(&write_buffer, 0)?;
941 assert!(!file.read(&mut read_buffer, 0)?);
942 if &read_buffer[0..hw.len()] != hw.as_bytes()
943 || read_buffer[hw.len()..write_buffer.len()]
944 .iter()
945 .any(|&x| x != 42)
946 || read_buffer[write_buffer.len()..].iter().any(|&x| x != 0)
947 {
948 return Err(VfsError::new(SQLITE_IOERR, "incorrect buffer data".into()))?;
949 }
950 if file.size()? != write_buffer.len() {
951 return Err(VfsError::new(
952 SQLITE_IOERR,
953 "incorrect buffer length".into(),
954 ))?;
955 }
956
957 file.write(&write_buffer, base_offset)?;
958 if file.size()? != base_offset + write_buffer.len() {
959 return Err(VfsError::new(
960 SQLITE_IOERR,
961 "incorrect buffer length".into(),
962 ))?;
963 }
964 assert!(file.read(&mut read_buffer, 0)?);
965 if &read_buffer[0..hw.len()] != hw.as_bytes()
966 || read_buffer[hw.len()..write_buffer.len()]
967 .iter()
968 .any(|&x| x != 42)
969 || read_buffer[write_buffer.len()..base_offset]
970 .iter()
971 .all(|&x| x != 0)
972 || &read_buffer[base_offset..base_offset + hw.len()] != hw.as_bytes()
973 || read_buffer[base_offset + hw.len()..]
974 .iter()
975 .any(|&x| x != 42)
976 {
977 return Err(VfsError::new(SQLITE_IOERR, "incorrect buffer data".into()))?;
978 }
979
980 Ok(SQLITE_OK)
981 }
982
983 pub fn test_vfs_store<AppData, File: VfsFile, Store: VfsStore<File, AppData>>(
984 vfs_data: VfsAppData<AppData>,
985 ) -> VfsResult<()> {
986 let layout = std::alloc::Layout::new::<sqlite3_vfs>();
987 let vfs = unsafe {
988 let vfs = std::alloc::alloc(layout) as *mut sqlite3_vfs;
989 (*vfs).pAppData = vfs_data.leak().cast();
990 vfs
991 };
992
993 let test_file = |filename: &str, flags: i32| {
994 if Store::contains_file(vfs, filename)? {
995 return Err(VfsError::new(SQLITE_IOERR, "found file before test".into()))?;
996 }
997
998 let vfs_file = SQLiteVfsFile {
999 io_methods: sqlite3_file {
1000 pMethods: std::ptr::null(),
1001 },
1002 vfs,
1003 flags,
1004 name_ptr: filename.as_ptr(),
1005 name_length: filename.len(),
1006 };
1007
1008 Store::add_file(vfs, filename, flags)?;
1009
1010 if !Store::contains_file(vfs, filename)? {
1011 return Err(VfsError::new(
1012 SQLITE_IOERR,
1013 "not found file after create".into(),
1014 ))?;
1015 }
1016
1017 Store::with_file_mut(&vfs_file, |file| test_vfs_file(file))?;
1018
1019 Store::delete_file(vfs, filename)?;
1020
1021 if Store::contains_file(vfs, filename)? {
1022 return Err(VfsError::new(
1023 SQLITE_IOERR,
1024 "found file after delete".into(),
1025 ))?;
1026 }
1027
1028 Ok(())
1029 };
1030
1031 test_file(
1032 "___test_vfs_store#1___",
1033 SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_MAIN_DB,
1034 )?;
1035
1036 test_file(
1037 "___test_vfs_store#2___",
1038 SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE,
1039 )?;
1040
1041 unsafe {
1042 drop(VfsAppData::<AppData>::from_raw((*vfs).pAppData as *mut _));
1043 std::alloc::dealloc(vfs.cast(), layout);
1044 }
1045
1046 Ok(())
1047 }
1048}
1049
1050#[cfg(test)]
1051mod tests {
1052 use super::{MemChunksFile, VfsFile};
1053 use wasm_bindgen_test::wasm_bindgen_test;
1054
1055 #[wasm_bindgen_test]
1056 fn test_chunks_file() {
1057 let mut file = MemChunksFile::new(512);
1058 file.write(&[], 0).unwrap();
1059 assert!(file.size().unwrap() == 0);
1060
1061 let mut buffer = [1; 2];
1062 let ret = file.read(&mut buffer, 0).unwrap();
1063 assert_eq!(ret, false);
1064 assert_eq!([0; 2], buffer);
1065
1066 file.write(&[1], 0).unwrap();
1067 assert!(file.size().unwrap() == 1);
1068 let mut buffer = [2; 2];
1069 let ret = file.read(&mut buffer, 0).unwrap();
1070 assert_eq!(ret, false);
1071 assert_eq!([1, 0], buffer);
1072
1073 let mut file = MemChunksFile::new(512);
1074 file.write(&[1; 512], 0).unwrap();
1075 assert!(file.size().unwrap() == 512);
1076 assert!(file.chunks.len() == 1);
1077
1078 file.truncate(512).unwrap();
1079 assert!(file.size().unwrap() == 512);
1080 assert!(file.chunks.len() == 1);
1081
1082 file.write(&[41, 42, 43], 511).unwrap();
1083 assert!(file.size().unwrap() == 514);
1084 assert!(file.chunks.len() == 2);
1085
1086 let mut buffer = [0; 3];
1087 let ret = file.read(&mut buffer, 511).unwrap();
1088 assert_eq!(ret, true);
1089 assert_eq!(buffer, [41, 42, 43]);
1090
1091 file.truncate(513).unwrap();
1092 assert!(file.size().unwrap() == 513);
1093 assert!(file.chunks.len() == 2);
1094
1095 file.write(&[1], 2048).unwrap();
1096 assert!(file.size().unwrap() == 2049);
1097 assert!(file.chunks.len() == 5);
1098
1099 file.truncate(0).unwrap();
1100 assert!(file.size().unwrap() == 0);
1101 assert!(file.chunks.len() == 0);
1102 }
1103}