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 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
337pub 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#[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
375pub type VfsResult<T> = Result<T, VfsError>;
377
378pub 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 pub fn leak(self) -> *mut Self {
394 Box::into_raw(Box::new(self))
395 }
396
397 pub unsafe fn from_raw(t: *mut Self) -> VfsAppData<T> {
401 *Box::from_raw(t)
402 }
403
404 pub fn pop_err(&self) -> Option<(i32, String)> {
406 self.last_err.borrow_mut().take()
407 }
408
409 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
417impl<T> Deref for VfsAppData<T> {
419 type Target = T;
420
421 fn deref(&self) -> &Self::Target {
422 &self.data
423 }
424}
425
426pub trait VfsFile {
428 fn read(&self, buf: &mut [u8], offset: usize) -> VfsResult<bool>;
430 fn write(&mut self, buf: &[u8], offset: usize) -> VfsResult<()>;
432 fn truncate(&mut self, size: usize) -> VfsResult<()>;
434 fn flush(&mut self) -> VfsResult<()>;
436 fn size(&self) -> VfsResult<usize>;
438}
439
440pub trait VfsStore<File, AppData> {
442 unsafe fn app_data(vfs: *mut sqlite3_vfs) -> &'static VfsAppData<AppData> {
448 &*(*vfs).pAppData.cast()
449 }
450 fn add_file(vfs: *mut sqlite3_vfs, file: &str, flags: i32) -> VfsResult<()>;
452 fn contains_file(vfs: *mut sqlite3_vfs, file: &str) -> VfsResult<bool>;
454 fn delete_file(vfs: *mut sqlite3_vfs, file: &str) -> VfsResult<()>;
456 fn with_file<F: Fn(&File) -> VfsResult<i32>>(vfs_file: &SQLiteVfsFile, f: F) -> VfsResult<i32>;
458 fn with_file_mut<F: Fn(&mut File) -> VfsResult<i32>>(
460 vfs_file: &SQLiteVfsFile,
461 f: F,
462 ) -> VfsResult<i32>;
463}
464
465#[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 let app_data = IO::Store::app_data(pVfs);
509
510 let name = if zName.is_null() {
511 random_name()
512 } else {
513 check_result!(CStr::from_ptr(zName).to_str()).into()
514 };
515
516 let exist = match IO::Store::contains_file(pVfs, &name) {
517 Ok(exist) => exist,
518 Err(err) => return app_data.store_err(err),
519 };
520
521 if !exist {
522 if flags & SQLITE_OPEN_CREATE == 0 {
523 return app_data.store_err(VfsError::new(
524 SQLITE_CANTOPEN,
525 format!("file not found: {name}"),
526 ));
527 }
528 if let Err(err) = IO::Store::add_file(pVfs, &name, flags) {
529 return app_data.store_err(err);
530 }
531 }
532
533 let leak = name.leak();
534 let vfs_file = pFile.cast::<SQLiteVfsFile>();
535 (*vfs_file).vfs = pVfs;
536 (*vfs_file).flags = flags;
537 (*vfs_file).name_ptr = leak.as_ptr();
538 (*vfs_file).name_length = leak.len();
539
540 (*pFile).pMethods = &IO::METHODS;
541
542 if !pOutFlags.is_null() {
543 *pOutFlags = flags;
544 }
545
546 SQLITE_OK
547 }
548
549 unsafe extern "C" fn xDelete(
550 pVfs: *mut sqlite3_vfs,
551 zName: *const ::std::os::raw::c_char,
552 syncDir: ::std::os::raw::c_int,
553 ) -> ::std::os::raw::c_int {
554 unused!(syncDir);
555
556 let app_data = IO::Store::app_data(pVfs);
557 bail!(zName.is_null(), SQLITE_IOERR_DELETE);
558 let s = check_result!(CStr::from_ptr(zName).to_str());
559 if let Err(err) = IO::Store::delete_file(pVfs, s) {
560 app_data.store_err(err)
561 } else {
562 SQLITE_OK
563 }
564 }
565
566 unsafe extern "C" fn xAccess(
567 pVfs: *mut sqlite3_vfs,
568 zName: *const ::std::os::raw::c_char,
569 flags: ::std::os::raw::c_int,
570 pResOut: *mut ::std::os::raw::c_int,
571 ) -> ::std::os::raw::c_int {
572 unused!(flags);
573
574 *pResOut = if zName.is_null() {
575 0
576 } else {
577 let app_data = IO::Store::app_data(pVfs);
578 let file = check_result!(CStr::from_ptr(zName).to_str());
579 let exist = match IO::Store::contains_file(pVfs, file) {
580 Ok(exist) => exist,
581 Err(err) => return app_data.store_err(err),
582 };
583 i32::from(exist)
584 };
585
586 SQLITE_OK
587 }
588
589 unsafe extern "C" fn xFullPathname(
590 pVfs: *mut sqlite3_vfs,
591 zName: *const ::std::os::raw::c_char,
592 nOut: ::std::os::raw::c_int,
593 zOut: *mut ::std::os::raw::c_char,
594 ) -> ::std::os::raw::c_int {
595 unused!(pVfs);
596 bail!(zName.is_null() || zOut.is_null(), SQLITE_CANTOPEN);
597 let len = CStr::from_ptr(zName).to_bytes_with_nul().len();
598 bail!(len > nOut as usize, SQLITE_CANTOPEN);
599 zName.copy_to(zOut, len);
600 SQLITE_OK
601 }
602
603 unsafe extern "C" fn xGetLastError(
604 pVfs: *mut sqlite3_vfs,
605 nOut: ::std::os::raw::c_int,
606 zOut: *mut ::std::os::raw::c_char,
607 ) -> ::std::os::raw::c_int {
608 let app_data = IO::Store::app_data(pVfs);
609 let Some((code, msg)) = app_data.pop_err() else {
610 return SQLITE_OK;
611 };
612 if !zOut.is_null() {
613 let nOut = nOut as usize;
614 let count = msg.len().min(nOut);
615 msg.as_ptr().copy_to(zOut.cast(), count);
616 let zero = match nOut.cmp(&msg.len()) {
617 std::cmp::Ordering::Less | std::cmp::Ordering::Equal => nOut,
618 std::cmp::Ordering::Greater => msg.len() + 1,
619 };
620 if zero > 0 {
621 std::ptr::write(zOut.add(zero - 1), 0);
622 }
623 }
624 code
625 }
626}
627
628#[allow(clippy::missing_safety_doc)]
630pub trait SQLiteIoMethods {
631 type File: VfsFile;
632 type AppData: 'static;
633 type Store: VfsStore<Self::File, Self::AppData>;
634
635 const VERSION: ::std::os::raw::c_int;
636
637 const METHODS: sqlite3_io_methods = sqlite3_io_methods {
638 iVersion: Self::VERSION,
639 xClose: Some(Self::xClose),
640 xRead: Some(Self::xRead),
641 xWrite: Some(Self::xWrite),
642 xTruncate: Some(Self::xTruncate),
643 xSync: Some(Self::xSync),
644 xFileSize: Some(Self::xFileSize),
645 xLock: Some(Self::xLock),
646 xUnlock: Some(Self::xUnlock),
647 xCheckReservedLock: Some(Self::xCheckReservedLock),
648 xFileControl: Some(Self::xFileControl),
649 xSectorSize: Some(Self::xSectorSize),
650 xDeviceCharacteristics: Some(Self::xDeviceCharacteristics),
651 xShmMap: None,
652 xShmLock: None,
653 xShmBarrier: None,
654 xShmUnmap: None,
655 xFetch: None,
656 xUnfetch: None,
657 };
658
659 unsafe extern "C" fn xClose(pFile: *mut sqlite3_file) -> ::std::os::raw::c_int {
660 let vfs_file = SQLiteVfsFile::from_file(pFile);
661 let app_data = Self::Store::app_data(vfs_file.vfs);
662
663 if vfs_file.flags & SQLITE_OPEN_DELETEONCLOSE != 0 {
664 if let Err(err) = Self::Store::delete_file(vfs_file.vfs, vfs_file.name()) {
665 return app_data.store_err(err);
666 }
667 }
668
669 drop(Box::from_raw(vfs_file.name()));
670
671 SQLITE_OK
672 }
673
674 unsafe extern "C" fn xRead(
675 pFile: *mut sqlite3_file,
676 zBuf: *mut ::std::os::raw::c_void,
677 iAmt: ::std::os::raw::c_int,
678 iOfst: sqlite3_int64,
679 ) -> ::std::os::raw::c_int {
680 let vfs_file = SQLiteVfsFile::from_file(pFile);
681 let app_data = Self::Store::app_data(vfs_file.vfs);
682
683 let f = |file: &Self::File| {
684 let size = iAmt as usize;
685 let offset = iOfst as usize;
686 let slice = std::slice::from_raw_parts_mut(zBuf.cast::<u8>(), size);
687 let code = if file.read(slice, offset)? {
688 SQLITE_OK
689 } else {
690 SQLITE_IOERR_SHORT_READ
691 };
692 Ok(code)
693 };
694
695 match Self::Store::with_file(vfs_file, f) {
696 Ok(code) => code,
697 Err(err) => app_data.store_err(err),
698 }
699 }
700
701 unsafe extern "C" fn xWrite(
702 pFile: *mut sqlite3_file,
703 zBuf: *const ::std::os::raw::c_void,
704 iAmt: ::std::os::raw::c_int,
705 iOfst: sqlite3_int64,
706 ) -> ::std::os::raw::c_int {
707 let vfs_file = SQLiteVfsFile::from_file(pFile);
708 let app_data = Self::Store::app_data(vfs_file.vfs);
709
710 let f = |file: &mut Self::File| {
711 let (offset, size) = (iOfst as usize, iAmt as usize);
712 let slice = std::slice::from_raw_parts(zBuf.cast::<u8>(), size);
713 file.write(slice, offset)?;
714 Ok(SQLITE_OK)
715 };
716
717 match Self::Store::with_file_mut(vfs_file, f) {
718 Ok(code) => code,
719 Err(err) => app_data.store_err(err),
720 }
721 }
722
723 unsafe extern "C" fn xTruncate(
724 pFile: *mut sqlite3_file,
725 size: sqlite3_int64,
726 ) -> ::std::os::raw::c_int {
727 let vfs_file = SQLiteVfsFile::from_file(pFile);
728 let app_data = Self::Store::app_data(vfs_file.vfs);
729
730 let f = |file: &mut Self::File| {
731 file.truncate(size as usize)?;
732 Ok(SQLITE_OK)
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 file.flush()?;
752 Ok(SQLITE_OK)
753 };
754
755 match Self::Store::with_file_mut(vfs_file, f) {
756 Ok(code) => code,
757 Err(err) => app_data.store_err(err),
758 }
759 }
760
761 unsafe extern "C" fn xFileSize(
762 pFile: *mut sqlite3_file,
763 pSize: *mut sqlite3_int64,
764 ) -> ::std::os::raw::c_int {
765 let vfs_file = SQLiteVfsFile::from_file(pFile);
766 let app_data = Self::Store::app_data(vfs_file.vfs);
767
768 let f = |file: &Self::File| {
769 file.size().map(|size| {
770 *pSize = size as sqlite3_int64;
771 })?;
772 Ok(SQLITE_OK)
773 };
774
775 match Self::Store::with_file(vfs_file, f) {
776 Ok(code) => code,
777 Err(err) => app_data.store_err(err),
778 }
779 }
780
781 unsafe extern "C" fn xLock(
782 pFile: *mut sqlite3_file,
783 eLock: ::std::os::raw::c_int,
784 ) -> ::std::os::raw::c_int {
785 unused!((pFile, eLock));
786 SQLITE_OK
787 }
788
789 unsafe extern "C" fn xUnlock(
790 pFile: *mut sqlite3_file,
791 eLock: ::std::os::raw::c_int,
792 ) -> ::std::os::raw::c_int {
793 unused!((pFile, eLock));
794 SQLITE_OK
795 }
796
797 unsafe extern "C" fn xCheckReservedLock(
798 pFile: *mut sqlite3_file,
799 pResOut: *mut ::std::os::raw::c_int,
800 ) -> ::std::os::raw::c_int {
801 unused!(pFile);
802 *pResOut = 0;
803 SQLITE_OK
804 }
805
806 unsafe extern "C" fn xFileControl(
807 pFile: *mut sqlite3_file,
808 op: ::std::os::raw::c_int,
809 pArg: *mut ::std::os::raw::c_void,
810 ) -> ::std::os::raw::c_int {
811 unused!((pFile, op, pArg));
812 SQLITE_NOTFOUND
813 }
814
815 unsafe extern "C" fn xSectorSize(pFile: *mut sqlite3_file) -> ::std::os::raw::c_int {
816 unused!(pFile);
817 512
818 }
819
820 unsafe extern "C" fn xDeviceCharacteristics(pFile: *mut sqlite3_file) -> ::std::os::raw::c_int {
821 unused!(pFile);
822 0
823 }
824}
825
826#[allow(clippy::missing_safety_doc)]
828pub mod x_methods_shim {
829 use super::*;
830
831 #[cfg(target_feature = "atomics")]
833 pub unsafe extern "C" fn xSleep(
834 _pVfs: *mut sqlite3_vfs,
835 microseconds: ::std::os::raw::c_int,
836 ) -> ::std::os::raw::c_int {
837 use std::{thread, time::Duration};
838 thread::sleep(Duration::from_micros(microseconds as u64));
839 SQLITE_OK
840 }
841
842 #[cfg(not(target_feature = "atomics"))]
843 pub unsafe extern "C" fn xSleep(
844 _pVfs: *mut sqlite3_vfs,
845 _microseconds: ::std::os::raw::c_int,
846 ) -> ::std::os::raw::c_int {
847 SQLITE_OK
848 }
849
850 pub unsafe extern "C" fn xRandomness(
852 _pVfs: *mut sqlite3_vfs,
853 nByte: ::std::os::raw::c_int,
854 zOut: *mut ::std::os::raw::c_char,
855 ) -> ::std::os::raw::c_int {
856 for i in 0..nByte as usize {
857 *zOut.add(i) = (Math::random() * 255000.0) as _;
858 }
859 nByte
860 }
861
862 pub unsafe extern "C" fn xCurrentTime(
864 _pVfs: *mut sqlite3_vfs,
865 pTimeOut: *mut f64,
866 ) -> ::std::os::raw::c_int {
867 *pTimeOut = 2440587.5 + (Date::new_0().get_time() / 86400000.0);
868 SQLITE_OK
869 }
870
871 pub unsafe extern "C" fn xCurrentTimeInt64(
873 _pVfs: *mut sqlite3_vfs,
874 pOut: *mut sqlite3_int64,
875 ) -> ::std::os::raw::c_int {
876 *pOut = ((2440587.5 * 86400000.0) + Date::new_0().get_time()) as sqlite3_int64;
877 SQLITE_OK
878 }
879}
880
881#[derive(thiserror::Error, Debug)]
882pub enum ImportDbError {
883 #[error("Byte array size is invalid for an SQLite db.")]
884 InvalidDbSize,
885 #[error("Input does not contain an SQLite database header.")]
886 InvalidHeader,
887 #[error("Page size must be a power of two between 512 and 65536 inclusive")]
888 InvalidPageSize,
889}
890
891pub fn check_import_db(bytes: &[u8]) -> Result<usize, ImportDbError> {
893 let length = bytes.len();
894
895 if length < 512 || length % 512 != 0 {
896 return Err(ImportDbError::InvalidDbSize);
897 }
898
899 if SQLITE3_HEADER
900 .as_bytes()
901 .iter()
902 .zip(bytes)
903 .any(|(x, y)| x != y)
904 {
905 return Err(ImportDbError::InvalidHeader);
906 }
907
908 let page_size = u16::from_be_bytes([bytes[16], bytes[17]]);
911 let page_size = if page_size == 1 {
912 65536
913 } else {
914 usize::from(page_size)
915 };
916
917 Ok(page_size)
918}
919
920pub fn check_db_and_page_size(db_size: usize, page_size: usize) -> Result<(), ImportDbError> {
922 if !(page_size.is_power_of_two() && (512..=65536).contains(&page_size)) {
923 return Err(ImportDbError::InvalidPageSize);
924 }
925 if db_size % page_size != 0 {
926 return Err(ImportDbError::InvalidDbSize);
927 }
928 Ok(())
929}
930
931#[cfg(test)]
932pub mod test_suite {
933 use super::{
934 sqlite3_file, sqlite3_vfs, SQLiteVfsFile, VfsAppData, VfsError, VfsFile, VfsResult,
935 VfsStore, SQLITE_IOERR, SQLITE_OK, SQLITE_OPEN_CREATE, SQLITE_OPEN_MAIN_DB,
936 SQLITE_OPEN_READWRITE,
937 };
938
939 fn test_vfs_file<File: VfsFile>(file: &mut File) -> VfsResult<i32> {
940 let base_offset = 1024 * 1024;
941
942 let mut write_buffer = vec![42; 64 * 1024];
943 let mut read_buffer = vec![42; base_offset + write_buffer.len()];
944 let hw = "hello world!";
945 write_buffer[0..hw.len()].copy_from_slice(hw.as_bytes());
946
947 file.write(&write_buffer, 0)?;
948 assert!(!file.read(&mut read_buffer, 0)?);
949 if &read_buffer[0..hw.len()] != hw.as_bytes()
950 || read_buffer[hw.len()..write_buffer.len()]
951 .iter()
952 .any(|&x| x != 42)
953 || read_buffer[write_buffer.len()..].iter().any(|&x| x != 0)
954 {
955 return Err(VfsError::new(SQLITE_IOERR, "incorrect buffer data".into()))?;
956 }
957 if file.size()? != write_buffer.len() {
958 return Err(VfsError::new(
959 SQLITE_IOERR,
960 "incorrect buffer length".into(),
961 ))?;
962 }
963
964 file.write(&write_buffer, base_offset)?;
965 if file.size()? != base_offset + write_buffer.len() {
966 return Err(VfsError::new(
967 SQLITE_IOERR,
968 "incorrect buffer length".into(),
969 ))?;
970 }
971 assert!(file.read(&mut read_buffer, 0)?);
972 if &read_buffer[0..hw.len()] != hw.as_bytes()
973 || read_buffer[hw.len()..write_buffer.len()]
974 .iter()
975 .any(|&x| x != 42)
976 || read_buffer[write_buffer.len()..base_offset]
977 .iter()
978 .all(|&x| x != 0)
979 || &read_buffer[base_offset..base_offset + hw.len()] != hw.as_bytes()
980 || read_buffer[base_offset + hw.len()..]
981 .iter()
982 .any(|&x| x != 42)
983 {
984 return Err(VfsError::new(SQLITE_IOERR, "incorrect buffer data".into()))?;
985 }
986
987 Ok(SQLITE_OK)
988 }
989
990 pub fn test_vfs_store<AppData, File: VfsFile, Store: VfsStore<File, AppData>>(
991 vfs_data: VfsAppData<AppData>,
992 ) -> VfsResult<()> {
993 let layout = std::alloc::Layout::new::<sqlite3_vfs>();
994 let vfs = unsafe {
995 let vfs = std::alloc::alloc(layout) as *mut sqlite3_vfs;
996 (*vfs).pAppData = vfs_data.leak().cast();
997 vfs
998 };
999
1000 let test_file = |filename: &str, flags: i32| {
1001 if Store::contains_file(vfs, filename)? {
1002 return Err(VfsError::new(SQLITE_IOERR, "found file before test".into()))?;
1003 }
1004
1005 let vfs_file = SQLiteVfsFile {
1006 io_methods: sqlite3_file {
1007 pMethods: std::ptr::null(),
1008 },
1009 vfs,
1010 flags,
1011 name_ptr: filename.as_ptr(),
1012 name_length: filename.len(),
1013 };
1014
1015 Store::add_file(vfs, filename, flags)?;
1016
1017 if !Store::contains_file(vfs, filename)? {
1018 return Err(VfsError::new(
1019 SQLITE_IOERR,
1020 "not found file after create".into(),
1021 ))?;
1022 }
1023
1024 Store::with_file_mut(&vfs_file, |file| test_vfs_file(file))?;
1025
1026 Store::delete_file(vfs, filename)?;
1027
1028 if Store::contains_file(vfs, filename)? {
1029 return Err(VfsError::new(
1030 SQLITE_IOERR,
1031 "found file after delete".into(),
1032 ))?;
1033 }
1034
1035 Ok(())
1036 };
1037
1038 test_file(
1039 "___test_vfs_store#1___",
1040 SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_MAIN_DB,
1041 )?;
1042
1043 test_file(
1044 "___test_vfs_store#2___",
1045 SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE,
1046 )?;
1047
1048 unsafe {
1049 drop(VfsAppData::<AppData>::from_raw((*vfs).pAppData as *mut _));
1050 std::alloc::dealloc(vfs.cast(), layout);
1051 }
1052
1053 Ok(())
1054 }
1055}
1056
1057#[cfg(test)]
1058mod tests {
1059 use super::{MemChunksFile, VfsFile};
1060 use wasm_bindgen_test::wasm_bindgen_test;
1061
1062 #[wasm_bindgen_test]
1063 fn test_chunks_file() {
1064 let mut file = MemChunksFile::new(512);
1065 file.write(&[], 0).unwrap();
1066 assert!(file.size().unwrap() == 0);
1067
1068 let mut buffer = [1; 2];
1069 let ret = file.read(&mut buffer, 0).unwrap();
1070 assert_eq!(ret, false);
1071 assert_eq!([0; 2], buffer);
1072
1073 file.write(&[1], 0).unwrap();
1074 assert!(file.size().unwrap() == 1);
1075 let mut buffer = [2; 2];
1076 let ret = file.read(&mut buffer, 0).unwrap();
1077 assert_eq!(ret, false);
1078 assert_eq!([1, 0], buffer);
1079
1080 let mut file = MemChunksFile::new(512);
1081 file.write(&[1; 512], 0).unwrap();
1082 assert!(file.size().unwrap() == 512);
1083 assert!(file.chunks.len() == 1);
1084
1085 file.truncate(512).unwrap();
1086 assert!(file.size().unwrap() == 512);
1087 assert!(file.chunks.len() == 1);
1088
1089 file.write(&[41, 42, 43], 511).unwrap();
1090 assert!(file.size().unwrap() == 514);
1091 assert!(file.chunks.len() == 2);
1092
1093 let mut buffer = [0; 3];
1094 let ret = file.read(&mut buffer, 511).unwrap();
1095 assert_eq!(ret, true);
1096 assert_eq!(buffer, [41, 42, 43]);
1097
1098 file.truncate(513).unwrap();
1099 assert!(file.size().unwrap() == 513);
1100 assert!(file.chunks.len() == 2);
1101
1102 file.write(&[1], 2048).unwrap();
1103 assert!(file.size().unwrap() == 2049);
1104 assert!(file.chunks.len() == 5);
1105
1106 file.truncate(0).unwrap();
1107 assert!(file.size().unwrap() == 0);
1108 assert!(file.chunks.len() == 0);
1109 }
1110}