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 FragileComfirmed<T> {
82 fragile: Fragile<T>,
83}
84
85unsafe impl<T> Send for FragileComfirmed<T> {}
86unsafe impl<T> Sync for FragileComfirmed<T> {}
87
88impl<T> FragileComfirmed<T> {
89 pub fn new(t: T) -> Self {
90 FragileComfirmed {
91 fragile: Fragile::new(t),
92 }
93 }
94}
95
96impl<T> Deref for FragileComfirmed<T> {
97 type Target = T;
98 fn deref(&self) -> &Self::Target {
99 self.fragile.get()
100 }
101}
102
103impl<T> DerefMut for FragileComfirmed<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<i32> {
153 let Some(chunk_size) = self.chunk_size else {
154 buf.fill(0);
155 return Ok(SQLITE_IOERR_SHORT_READ);
156 };
157
158 if self.file_size <= offset {
159 buf.fill(0);
160 return Ok(SQLITE_IOERR_SHORT_READ);
161 }
162
163 if chunk_size == buf.len() && offset % chunk_size == 0 {
164 buf.copy_from_slice(&self.chunks[offset / chunk_size]);
165 Ok(SQLITE_OK)
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(SQLITE_IOERR_SHORT_READ)
186 } else {
187 Ok(SQLITE_OK)
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<i32>;
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 name2path(vfs: *mut sqlite3_vfs, file: &str) -> VfsResult<String> {
446 unused!(vfs);
447 Ok(file.into())
448 }
449 fn add_file(vfs: *mut sqlite3_vfs, file: &str, flags: i32) -> VfsResult<()>;
451 fn contains_file(vfs: *mut sqlite3_vfs, file: &str) -> VfsResult<bool>;
453 fn delete_file(vfs: *mut sqlite3_vfs, file: &str) -> VfsResult<()>;
455 fn with_file<F: Fn(&File) -> i32>(vfs_file: &SQLiteVfsFile, f: F) -> VfsResult<i32>;
457 fn with_file_mut<F: Fn(&mut File) -> i32>(vfs_file: &SQLiteVfsFile, f: F) -> VfsResult<i32>;
459}
460
461#[allow(clippy::missing_safety_doc)]
463pub trait SQLiteVfs<IO: SQLiteIoMethods> {
464 const VERSION: ::std::os::raw::c_int;
465 const MAX_PATH_SIZE: ::std::os::raw::c_int = 1024;
466
467 fn vfs(
468 vfs_name: *const ::std::os::raw::c_char,
469 app_data: *mut VfsAppData<IO::AppData>,
470 ) -> sqlite3_vfs {
471 sqlite3_vfs {
472 iVersion: Self::VERSION,
473 szOsFile: std::mem::size_of::<SQLiteVfsFile>() as i32,
474 mxPathname: Self::MAX_PATH_SIZE,
475 pNext: std::ptr::null_mut(),
476 zName: vfs_name,
477 pAppData: app_data.cast(),
478 xOpen: Some(Self::xOpen),
479 xDelete: Some(Self::xDelete),
480 xAccess: Some(Self::xAccess),
481 xFullPathname: Some(Self::xFullPathname),
482 xDlOpen: None,
483 xDlError: None,
484 xDlSym: None,
485 xDlClose: None,
486 xRandomness: Some(x_methods_shim::xRandomness),
487 xSleep: Some(x_methods_shim::xSleep),
488 xCurrentTime: Some(x_methods_shim::xCurrentTime),
489 xGetLastError: Some(Self::xGetLastError),
490 xCurrentTimeInt64: Some(x_methods_shim::xCurrentTimeInt64),
491 xSetSystemCall: None,
492 xGetSystemCall: None,
493 xNextSystemCall: None,
494 }
495 }
496
497 unsafe extern "C" fn xOpen(
498 pVfs: *mut sqlite3_vfs,
499 zName: sqlite3_filename,
500 pFile: *mut sqlite3_file,
501 flags: ::std::os::raw::c_int,
502 pOutFlags: *mut ::std::os::raw::c_int,
503 ) -> ::std::os::raw::c_int {
504 let app_data = IO::Store::app_data(pVfs);
505
506 let name = if zName.is_null() {
507 random_name()
508 } else {
509 check_result!(CStr::from_ptr(zName).to_str()).into()
510 };
511
512 let name = match IO::Store::name2path(pVfs, &name) {
513 Ok(name) => name,
514 Err(err) => return app_data.store_err(err),
515 };
516
517 let exist = match IO::Store::contains_file(pVfs, &name) {
518 Ok(exist) => exist,
519 Err(err) => return app_data.store_err(err),
520 };
521
522 if !exist {
523 if flags & SQLITE_OPEN_CREATE == 0 {
524 return app_data.store_err(VfsError::new(
525 SQLITE_CANTOPEN,
526 format!("file not found: {name}"),
527 ));
528 }
529 if let Err(err) = IO::Store::add_file(pVfs, &name, flags) {
530 return app_data.store_err(err);
531 }
532 }
533
534 let leak = name.leak();
535 let vfs_file = pFile.cast::<SQLiteVfsFile>();
536 (*vfs_file).vfs = pVfs;
537 (*vfs_file).flags = flags;
538 (*vfs_file).name_ptr = leak.as_ptr();
539 (*vfs_file).name_length = leak.len();
540
541 (*pFile).pMethods = &IO::METHODS;
542
543 if !pOutFlags.is_null() {
544 *pOutFlags = flags;
545 }
546
547 SQLITE_OK
548 }
549
550 unsafe extern "C" fn xDelete(
551 pVfs: *mut sqlite3_vfs,
552 zName: *const ::std::os::raw::c_char,
553 syncDir: ::std::os::raw::c_int,
554 ) -> ::std::os::raw::c_int {
555 unused!(syncDir);
556
557 let app_data = IO::Store::app_data(pVfs);
558 bail!(zName.is_null(), SQLITE_IOERR_DELETE);
559 let s = check_result!(CStr::from_ptr(zName).to_str());
560 if let Err(err) = IO::Store::delete_file(pVfs, s) {
561 app_data.store_err(err)
562 } else {
563 SQLITE_OK
564 }
565 }
566
567 unsafe extern "C" fn xAccess(
568 pVfs: *mut sqlite3_vfs,
569 zName: *const ::std::os::raw::c_char,
570 flags: ::std::os::raw::c_int,
571 pResOut: *mut ::std::os::raw::c_int,
572 ) -> ::std::os::raw::c_int {
573 unused!(flags);
574
575 *pResOut = if zName.is_null() {
576 0
577 } else {
578 let app_data = IO::Store::app_data(pVfs);
579 let file = check_result!(CStr::from_ptr(zName).to_str());
580 let exist = match IO::Store::contains_file(pVfs, file) {
581 Ok(exist) => exist,
582 Err(err) => return app_data.store_err(err),
583 };
584 i32::from(exist)
585 };
586
587 SQLITE_OK
588 }
589
590 unsafe extern "C" fn xFullPathname(
591 pVfs: *mut sqlite3_vfs,
592 zName: *const ::std::os::raw::c_char,
593 nOut: ::std::os::raw::c_int,
594 zOut: *mut ::std::os::raw::c_char,
595 ) -> ::std::os::raw::c_int {
596 unused!(pVfs);
597 bail!(zName.is_null() || zOut.is_null(), SQLITE_CANTOPEN);
598 let len = CStr::from_ptr(zName).to_bytes_with_nul().len();
599 bail!(len > nOut as usize, SQLITE_CANTOPEN);
600 zName.copy_to(zOut, len);
601 SQLITE_OK
602 }
603
604 unsafe extern "C" fn xGetLastError(
605 pVfs: *mut sqlite3_vfs,
606 nOut: ::std::os::raw::c_int,
607 zOut: *mut ::std::os::raw::c_char,
608 ) -> ::std::os::raw::c_int {
609 let app_data = IO::Store::app_data(pVfs);
610 let Some((code, msg)) = app_data.pop_err() else {
611 return SQLITE_OK;
612 };
613 if !zOut.is_null() {
614 let nOut = nOut as usize;
615 let count = msg.len().min(nOut);
616 msg.as_ptr().copy_to(zOut.cast(), count);
617 let zero = match nOut.cmp(&msg.len()) {
618 std::cmp::Ordering::Less | std::cmp::Ordering::Equal => nOut,
619 std::cmp::Ordering::Greater => msg.len() + 1,
620 };
621 if zero > 0 {
622 std::ptr::write(zOut.add(zero - 1), 0);
623 }
624 }
625 code
626 }
627}
628
629#[allow(clippy::missing_safety_doc)]
631pub trait SQLiteIoMethods {
632 type File: VfsFile;
633 type AppData: 'static;
634 type Store: VfsStore<Self::File, Self::AppData>;
635
636 const VERSION: ::std::os::raw::c_int;
637
638 const METHODS: sqlite3_io_methods = sqlite3_io_methods {
639 iVersion: Self::VERSION,
640 xClose: Some(Self::xClose),
641 xRead: Some(Self::xRead),
642 xWrite: Some(Self::xWrite),
643 xTruncate: Some(Self::xTruncate),
644 xSync: Some(Self::xSync),
645 xFileSize: Some(Self::xFileSize),
646 xLock: Some(Self::xLock),
647 xUnlock: Some(Self::xUnlock),
648 xCheckReservedLock: Some(Self::xCheckReservedLock),
649 xFileControl: Some(Self::xFileControl),
650 xSectorSize: Some(Self::xSectorSize),
651 xDeviceCharacteristics: Some(Self::xDeviceCharacteristics),
652 xShmMap: None,
653 xShmLock: None,
654 xShmBarrier: None,
655 xShmUnmap: None,
656 xFetch: None,
657 xUnfetch: None,
658 };
659
660 unsafe extern "C" fn xClose(pFile: *mut sqlite3_file) -> ::std::os::raw::c_int {
661 let vfs_file = SQLiteVfsFile::from_file(pFile);
662 let app_data = Self::Store::app_data(vfs_file.vfs);
663
664 if vfs_file.flags & SQLITE_OPEN_DELETEONCLOSE != 0 {
665 if let Err(err) = Self::Store::delete_file(vfs_file.vfs, vfs_file.name()) {
666 return app_data.store_err(err);
667 }
668 }
669
670 drop(Box::from_raw(vfs_file.name()));
671
672 SQLITE_OK
673 }
674
675 unsafe extern "C" fn xRead(
676 pFile: *mut sqlite3_file,
677 zBuf: *mut ::std::os::raw::c_void,
678 iAmt: ::std::os::raw::c_int,
679 iOfst: sqlite3_int64,
680 ) -> ::std::os::raw::c_int {
681 let vfs_file = SQLiteVfsFile::from_file(pFile);
682 let app_data = Self::Store::app_data(vfs_file.vfs);
683
684 let f = |file: &Self::File| {
685 let size = iAmt as usize;
686 let offset = iOfst as usize;
687 let slice = std::slice::from_raw_parts_mut(zBuf.cast::<u8>(), size);
688 match file.read(slice, offset) {
689 Ok(code) => code,
690 Err(err) => app_data.store_err(err),
691 }
692 };
693
694 match Self::Store::with_file(vfs_file, f) {
695 Ok(code) => code,
696 Err(err) => app_data.store_err(err),
697 }
698 }
699
700 unsafe extern "C" fn xWrite(
701 pFile: *mut sqlite3_file,
702 zBuf: *const ::std::os::raw::c_void,
703 iAmt: ::std::os::raw::c_int,
704 iOfst: sqlite3_int64,
705 ) -> ::std::os::raw::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 let (offset, size) = (iOfst as usize, iAmt as usize);
711 let slice = std::slice::from_raw_parts(zBuf.cast::<u8>(), size);
712 if let Err(err) = file.write(slice, offset) {
713 app_data.store_err(err)
714 } else {
715 SQLITE_OK
716 }
717 };
718
719 match Self::Store::with_file_mut(vfs_file, f) {
720 Ok(code) => code,
721 Err(err) => app_data.store_err(err),
722 }
723 }
724
725 unsafe extern "C" fn xTruncate(
726 pFile: *mut sqlite3_file,
727 size: sqlite3_int64,
728 ) -> ::std::os::raw::c_int {
729 let vfs_file = SQLiteVfsFile::from_file(pFile);
730 let app_data = Self::Store::app_data(vfs_file.vfs);
731
732 let f = |file: &mut Self::File| {
733 if let Err(err) = file.truncate(size as usize) {
734 app_data.store_err(err)
735 } else {
736 SQLITE_OK
737 }
738 };
739
740 match Self::Store::with_file_mut(vfs_file, f) {
741 Ok(code) => code,
742 Err(err) => app_data.store_err(err),
743 }
744 }
745
746 unsafe extern "C" fn xSync(
747 pFile: *mut sqlite3_file,
748 flags: ::std::os::raw::c_int,
749 ) -> ::std::os::raw::c_int {
750 unused!(flags);
751
752 let vfs_file = SQLiteVfsFile::from_file(pFile);
753 let app_data = Self::Store::app_data(vfs_file.vfs);
754
755 let f = |file: &mut Self::File| {
756 if let Err(err) = file.flush() {
757 app_data.store_err(err)
758 } else {
759 SQLITE_OK
760 }
761 };
762
763 match Self::Store::with_file_mut(vfs_file, f) {
764 Ok(code) => code,
765 Err(err) => app_data.store_err(err),
766 }
767 }
768
769 unsafe extern "C" fn xFileSize(
770 pFile: *mut sqlite3_file,
771 pSize: *mut sqlite3_int64,
772 ) -> ::std::os::raw::c_int {
773 let vfs_file = SQLiteVfsFile::from_file(pFile);
774 let app_data = Self::Store::app_data(vfs_file.vfs);
775
776 let f = |file: &Self::File| match file.size() {
777 Ok(size) => {
778 *pSize = size as sqlite3_int64;
779 SQLITE_OK
780 }
781 Err(err) => app_data.store_err(err),
782 };
783
784 match Self::Store::with_file(vfs_file, f) {
785 Ok(code) => code,
786 Err(err) => app_data.store_err(err),
787 }
788 }
789
790 unsafe extern "C" fn xLock(
791 pFile: *mut sqlite3_file,
792 eLock: ::std::os::raw::c_int,
793 ) -> ::std::os::raw::c_int {
794 unused!((pFile, eLock));
795 SQLITE_OK
796 }
797
798 unsafe extern "C" fn xUnlock(
799 pFile: *mut sqlite3_file,
800 eLock: ::std::os::raw::c_int,
801 ) -> ::std::os::raw::c_int {
802 unused!((pFile, eLock));
803 SQLITE_OK
804 }
805
806 unsafe extern "C" fn xCheckReservedLock(
807 pFile: *mut sqlite3_file,
808 pResOut: *mut ::std::os::raw::c_int,
809 ) -> ::std::os::raw::c_int {
810 unused!(pFile);
811 *pResOut = 0;
812 SQLITE_OK
813 }
814
815 unsafe extern "C" fn xFileControl(
816 pFile: *mut sqlite3_file,
817 op: ::std::os::raw::c_int,
818 pArg: *mut ::std::os::raw::c_void,
819 ) -> ::std::os::raw::c_int {
820 unused!((pFile, op, pArg));
821 SQLITE_NOTFOUND
822 }
823
824 unsafe extern "C" fn xSectorSize(pFile: *mut sqlite3_file) -> ::std::os::raw::c_int {
825 unused!(pFile);
826 512
827 }
828
829 unsafe extern "C" fn xDeviceCharacteristics(pFile: *mut sqlite3_file) -> ::std::os::raw::c_int {
830 unused!(pFile);
831 0
832 }
833}
834
835#[allow(clippy::missing_safety_doc)]
837pub mod x_methods_shim {
838 use super::*;
839
840 #[cfg(target_feature = "atomics")]
842 pub unsafe extern "C" fn xSleep(
843 _pVfs: *mut sqlite3_vfs,
844 microseconds: ::std::os::raw::c_int,
845 ) -> ::std::os::raw::c_int {
846 use std::{thread, time::Duration};
847 thread::sleep(Duration::from_micros(microseconds as u64));
848 SQLITE_OK
849 }
850
851 #[cfg(not(target_feature = "atomics"))]
852 pub unsafe extern "C" fn xSleep(
853 _pVfs: *mut sqlite3_vfs,
854 _microseconds: ::std::os::raw::c_int,
855 ) -> ::std::os::raw::c_int {
856 SQLITE_OK
857 }
858
859 pub unsafe extern "C" fn xRandomness(
861 _pVfs: *mut sqlite3_vfs,
862 nByte: ::std::os::raw::c_int,
863 zOut: *mut ::std::os::raw::c_char,
864 ) -> ::std::os::raw::c_int {
865 for i in 0..nByte as usize {
866 *zOut.add(i) = (Math::random() * 255000.0) as _;
867 }
868 nByte
869 }
870
871 pub unsafe extern "C" fn xCurrentTime(
873 _pVfs: *mut sqlite3_vfs,
874 pTimeOut: *mut f64,
875 ) -> ::std::os::raw::c_int {
876 *pTimeOut = 2440587.5 + (Date::new_0().get_time() / 86400000.0);
877 SQLITE_OK
878 }
879
880 pub unsafe extern "C" fn xCurrentTimeInt64(
882 _pVfs: *mut sqlite3_vfs,
883 pOut: *mut sqlite3_int64,
884 ) -> ::std::os::raw::c_int {
885 *pOut = ((2440587.5 * 86400000.0) + Date::new_0().get_time()) as sqlite3_int64;
886 SQLITE_OK
887 }
888}
889
890#[derive(thiserror::Error, Debug)]
891pub enum ImportDbError {
892 #[error("Byte array size is invalid for an SQLite db.")]
893 InvalidDbSize,
894 #[error("Input does not contain an SQLite database header.")]
895 InvalidHeader,
896 #[error("Page size must be a power of two between 512 and 65536 inclusive")]
897 InvalidPageSize,
898}
899
900pub fn check_import_db(bytes: &[u8]) -> Result<usize, ImportDbError> {
902 let length = bytes.len();
903
904 if length < 512 || length % 512 != 0 {
905 return Err(ImportDbError::InvalidDbSize);
906 }
907
908 if SQLITE3_HEADER
909 .as_bytes()
910 .iter()
911 .zip(bytes)
912 .any(|(x, y)| x != y)
913 {
914 return Err(ImportDbError::InvalidHeader);
915 }
916
917 let page_size = u16::from_be_bytes([bytes[16], bytes[17]]);
920 let page_size = if page_size == 1 {
921 65536
922 } else {
923 usize::from(page_size)
924 };
925
926 Ok(page_size)
927}
928
929pub fn check_db_and_page_size(db_size: usize, page_size: usize) -> Result<(), ImportDbError> {
931 if !(page_size.is_power_of_two() && (512..=65536).contains(&page_size)) {
932 return Err(ImportDbError::InvalidPageSize);
933 }
934 if db_size % page_size != 0 {
935 return Err(ImportDbError::InvalidDbSize);
936 }
937 Ok(())
938}
939
940#[cfg(test)]
941mod tests {
942 use crate::SQLITE_IOERR_SHORT_READ;
943
944 use super::{MemChunksFile, VfsFile};
945 use wasm_bindgen_test::wasm_bindgen_test;
946
947 #[wasm_bindgen_test]
948 fn test_chunks_file() {
949 let mut file = MemChunksFile::new(512);
950 file.write(&[], 0).unwrap();
951 assert!(file.size().unwrap() == 0);
952
953 let mut buffer = [1; 2];
954 let ret = file.read(&mut buffer, 0).unwrap();
955 assert_eq!(ret, SQLITE_IOERR_SHORT_READ);
956 assert_eq!([0; 2], buffer);
957
958 file.write(&[1], 0).unwrap();
959 assert!(file.size().unwrap() == 1);
960 let mut buffer = [2; 2];
961 let ret = file.read(&mut buffer, 0).unwrap();
962 assert_eq!(ret, SQLITE_IOERR_SHORT_READ);
963 assert_eq!([1, 0], buffer);
964
965 let mut file = MemChunksFile::new(512);
966 file.write(&[1; 512], 0).unwrap();
967 assert!(file.size().unwrap() == 512);
968 assert!(file.chunks.len() == 1);
969
970 file.truncate(512).unwrap();
971 assert!(file.size().unwrap() == 512);
972 assert!(file.chunks.len() == 1);
973
974 file.write(&[41, 42, 43], 511).unwrap();
975 assert!(file.size().unwrap() == 514);
976 assert!(file.chunks.len() == 2);
977
978 let mut buffer = [0; 3];
979 let ret = file.read(&mut buffer, 511).unwrap();
980 assert_eq!(ret, 0);
981 assert_eq!(buffer, [41, 42, 43]);
982
983 file.truncate(513).unwrap();
984 assert!(file.size().unwrap() == 513);
985 assert!(file.chunks.len() == 2);
986
987 file.write(&[1], 2048).unwrap();
988 assert!(file.size().unwrap() == 2049);
989 assert!(file.chunks.len() == 5);
990
991 file.truncate(0).unwrap();
992 assert!(file.size().unwrap() == 0);
993 assert!(file.chunks.len() == 0);
994 }
995}