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