1use crate::libsqlite3::*;
4
5use fragile::Fragile;
6use js_sys::{Date, Math, Number, Uint8Array};
7use parking_lot::Mutex;
8use std::{
9 ffi::{CStr, CString},
10 ops::{Deref, DerefMut},
11};
12use wasm_array_cp::ArrayBufferCopy;
13
14pub const SQLITE3_HEADER: &str = "SQLite format 3";
16
17pub struct FragileComfirmed<T> {
22 fragile: Fragile<T>,
23}
24
25unsafe impl<T> Send for FragileComfirmed<T> {}
26unsafe impl<T> Sync for FragileComfirmed<T> {}
27
28impl<T> FragileComfirmed<T> {
29 pub fn new(t: T) -> Self {
30 FragileComfirmed {
31 fragile: Fragile::new(t),
32 }
33 }
34}
35
36impl<T> Deref for FragileComfirmed<T> {
37 type Target = T;
38 fn deref(&self) -> &Self::Target {
39 self.fragile.get()
40 }
41}
42
43impl<T> DerefMut for FragileComfirmed<T> {
44 fn deref_mut(&mut self) -> &mut Self::Target {
45 self.fragile.get_mut()
46 }
47}
48
49pub fn get_random_name() -> String {
51 let random = Number::from(Math::random()).to_string(36).unwrap();
52 random.slice(2, random.length()).as_string().unwrap()
53}
54
55pub fn copy_to_vec(src: &Uint8Array) -> Vec<u8> {
57 ArrayBufferCopy::to_vec(src)
58}
59
60pub fn copy_to_slice(src: &Uint8Array, dst: &mut [u8]) {
62 ArrayBufferCopy::copy_to(src, dst);
63}
64
65pub fn copy_to_uint8_array(src: &[u8]) -> Uint8Array {
67 ArrayBufferCopy::from_slice(src)
68}
69
70pub fn copy_to_uint8_array_subarray(src: &[u8], dst: &Uint8Array) {
72 ArrayBufferCopy::copy_from(dst, src)
73}
74
75#[macro_export]
79macro_rules! bail {
80 ($ex:expr) => {
81 bail!($ex, SQLITE_ERROR);
82 };
83 ($ex:expr, $code: expr) => {
84 if $ex {
85 return $code;
86 }
87 };
88}
89
90#[macro_export]
96macro_rules! check_option {
97 ($ex:expr) => {
98 check_option!($ex, SQLITE_ERROR)
99 };
100 ($ex:expr, $code: expr) => {
101 if let Some(v) = $ex {
102 v
103 } else {
104 return $code;
105 }
106 };
107}
108
109#[macro_export]
115macro_rules! check_result {
116 ($ex:expr) => {
117 check_result!($ex, SQLITE_ERROR)
118 };
119 ($ex:expr, $code: expr) => {
120 if let Ok(v) = $ex {
121 v
122 } else {
123 return $code;
124 }
125 };
126}
127
128#[macro_export]
130macro_rules! unused {
131 ($ex:expr) => {
132 let _ = $ex;
133 };
134}
135
136#[repr(C)]
140pub struct SQLiteVfsFile {
141 pub io_methods: sqlite3_file,
144 pub vfs: *mut sqlite3_vfs,
146 pub flags: i32,
148 pub name_ptr: *const u8,
151 pub name_length: usize,
153}
154
155impl SQLiteVfsFile {
156 pub unsafe fn from_file(file: *mut sqlite3_file) -> &'static SQLiteVfsFile {
162 &*file.cast::<Self>()
163 }
164
165 pub unsafe fn name(&self) -> &'static mut str {
173 std::str::from_utf8_unchecked_mut(std::slice::from_raw_parts_mut(
175 self.name_ptr.cast_mut(),
176 self.name_length,
177 ))
178 }
179
180 pub fn sqlite3_file(&'static self) -> *mut sqlite3_file {
182 self as *const SQLiteVfsFile as *mut sqlite3_file
183 }
184}
185
186#[derive(thiserror::Error, Debug)]
188pub enum RegisterVfsError {
189 #[error("An error occurred converting the given vfs name to a CStr")]
190 ToCStr,
191 #[error("An error occurred while registering vfs with sqlite")]
192 RegisterVfs,
193}
194
195pub fn register_vfs<IO: SQLiteIoMethods, V: SQLiteVfs<IO>>(
197 vfs_name: &str,
198 app_data: IO::AppData,
199 default_vfs: bool,
200) -> Result<*mut sqlite3_vfs, RegisterVfsError> {
201 let name = CString::new(vfs_name).map_err(|_| RegisterVfsError::ToCStr)?;
202 let name_ptr = name.into_raw();
203 let app_data = VfsAppData::new(app_data).leak();
204
205 let vfs = Box::leak(Box::new(V::vfs(name_ptr, app_data.cast())));
206 let ret = unsafe { sqlite3_vfs_register(vfs, i32::from(default_vfs)) };
207
208 if ret != SQLITE_OK {
209 unsafe {
210 drop(Box::from_raw(vfs));
211 drop(CString::from_raw(name_ptr));
212 drop(VfsAppData::from_raw(app_data));
213 }
214 return Err(RegisterVfsError::RegisterVfs);
215 }
216
217 Ok(vfs as *mut sqlite3_vfs)
218}
219
220pub fn page_read<T, G: Fn(usize) -> Option<T>, R: Fn(T, &mut [u8], (usize, usize))>(
222 buf: &mut [u8],
223 page_size: usize,
224 file_size: usize,
225 offset: usize,
226 get_page: G,
227 read_fn: R,
228) -> i32 {
229 if page_size == 0 || file_size == 0 {
230 buf.fill(0);
231 return SQLITE_IOERR_SHORT_READ;
232 }
233
234 let mut bytes_read = 0;
235 let mut p_data_offset = 0;
236 let p_data_length = buf.len();
237 let i_offset = offset;
238
239 while p_data_offset < p_data_length {
240 let file_offset = i_offset + p_data_offset;
241 let page_idx = file_offset / page_size;
242 let page_offset = file_offset % page_size;
243 let page_addr = page_idx * page_size;
244
245 let Some(page) = get_page(page_addr) else {
246 break;
247 };
248
249 let page_length = (page_size - page_offset).min(p_data_length - p_data_offset);
250 read_fn(
251 page,
252 &mut buf[p_data_offset..p_data_offset + page_length],
253 (page_offset, page_offset + page_length),
254 );
255
256 p_data_offset += page_length;
257 bytes_read += page_length;
258 }
259
260 if bytes_read < p_data_length {
261 buf[bytes_read..].fill(0);
262 return SQLITE_IOERR_SHORT_READ;
263 }
264
265 SQLITE_OK
266}
267
268pub struct MemChunksFile {
270 chunks: Vec<Vec<u8>>,
271 chunk_size: Option<usize>,
272 file_size: usize,
273}
274
275impl Default for MemChunksFile {
276 fn default() -> Self {
277 Self::new(512)
278 }
279}
280
281impl MemChunksFile {
282 pub fn new(chunk_size: usize) -> Self {
284 assert!(chunk_size != 0, "chunk size can't be zero");
285 MemChunksFile {
286 chunks: Vec::new(),
287 chunk_size: Some(chunk_size),
288 file_size: 0,
289 }
290 }
291
292 pub fn waiting_for_write() -> Self {
296 MemChunksFile {
297 chunks: Vec::new(),
298 chunk_size: None,
299 file_size: 0,
300 }
301 }
302}
303
304impl VfsFile for MemChunksFile {
305 fn read(&self, buf: &mut [u8], offset: usize) -> VfsResult<i32> {
306 let Some(chunk_size) = self.chunk_size else {
307 buf.fill(0);
308 return Ok(SQLITE_IOERR_SHORT_READ);
309 };
310
311 if self.file_size <= offset {
312 buf.fill(0);
313 return Ok(SQLITE_IOERR_SHORT_READ);
314 }
315
316 if chunk_size == buf.len() && offset % chunk_size == 0 {
317 buf.copy_from_slice(&self.chunks[offset / chunk_size]);
318 Ok(SQLITE_OK)
319 } else {
320 let mut size = buf.len();
321 let chunk_idx = offset / chunk_size;
322 let mut remaining_idx = offset % chunk_size;
323 let mut offset = 0;
324
325 for chunk in &self.chunks[chunk_idx..] {
326 let n = std::cmp::min(chunk_size.min(self.file_size) - remaining_idx, size);
327 buf[offset..offset + n].copy_from_slice(&chunk[remaining_idx..remaining_idx + n]);
328 offset += n;
329 size -= n;
330 remaining_idx = 0;
331 if size == 0 {
332 break;
333 }
334 }
335
336 if offset < buf.len() {
337 buf[offset..].fill(0);
338 Ok(SQLITE_IOERR_SHORT_READ)
339 } else {
340 Ok(SQLITE_OK)
341 }
342 }
343 }
344
345 fn write(&mut self, buf: &[u8], offset: usize) -> VfsResult<()> {
346 if buf.is_empty() {
347 return Ok(());
348 }
349
350 let chunk_size = if let Some(chunk_size) = self.chunk_size {
351 chunk_size
352 } else {
353 let size = buf.len();
354 self.chunk_size = Some(size);
355 size
356 };
357
358 let new_length = self.file_size.max(offset + buf.len());
359
360 if chunk_size == buf.len() && offset % chunk_size == 0 {
361 for _ in self.chunks.len()..offset / chunk_size {
362 self.chunks.push(vec![0; chunk_size]);
363 }
364 if let Some(buffer) = self.chunks.get_mut(offset / chunk_size) {
365 buffer.copy_from_slice(buf);
366 } else {
367 self.chunks.push(buf.to_vec());
368 }
369 } else {
370 let mut size = buf.len();
371 let chunk_start_idx = offset / chunk_size;
372 let chunk_end_idx = (offset + size - 1) / chunk_size;
373 let chunks_length = self.chunks.len();
374
375 for _ in chunks_length..=chunk_end_idx {
376 self.chunks.push(vec![0; chunk_size]);
377 }
378
379 let mut remaining_idx = offset % chunk_size;
380 let mut offset = 0;
381
382 for idx in chunk_start_idx..=chunk_end_idx {
383 let n = std::cmp::min(chunk_size - remaining_idx, size);
384 self.chunks[idx][remaining_idx..remaining_idx + n]
385 .copy_from_slice(&buf[offset..offset + n]);
386 offset += n;
387 size -= n;
388 remaining_idx = 0;
389 if size == 0 {
390 break;
391 }
392 }
393 }
394
395 self.file_size = new_length;
396
397 Ok(())
398 }
399
400 fn truncate(&mut self, size: usize) -> VfsResult<()> {
401 if let Some(chunk_size) = self.chunk_size {
402 if size == 0 {
403 std::mem::take(&mut self.chunks);
404 } else {
405 let idx = ((size - 1) / chunk_size) + 1;
406 self.chunks.drain(idx..);
407 }
408 } else if size != 0 {
409 assert_eq!(self.file_size, 0);
410 return Err(VfsError::new(SQLITE_IOERR, "Failed to truncate".into()));
411 }
412 self.file_size = size;
413 Ok(())
414 }
415
416 fn flush(&mut self) -> VfsResult<()> {
417 Ok(())
418 }
419
420 fn size(&self) -> VfsResult<usize> {
421 Ok(self.file_size)
422 }
423}
424
425#[derive(Default)]
427pub struct MemLinearFile(Vec<u8>);
428
429impl VfsFile for MemLinearFile {
430 fn read(&self, buf: &mut [u8], offset: usize) -> VfsResult<i32> {
431 let size = buf.len();
432 let end = size + offset;
433 if self.0.len() <= offset {
434 buf.fill(0);
435 return Ok(SQLITE_IOERR_SHORT_READ);
436 }
437
438 let read_end = end.min(self.0.len());
439 let read_size = read_end - offset;
440 buf[..read_size].copy_from_slice(&self.0[offset..read_end]);
441
442 if read_size < size {
443 buf[read_size..].fill(0);
444 return Ok(SQLITE_IOERR_SHORT_READ);
445 }
446 Ok(SQLITE_OK)
447 }
448
449 fn write(&mut self, buf: &[u8], offset: usize) -> VfsResult<()> {
450 let end = buf.len() + offset;
451 if end > self.0.len() {
452 self.0.resize(end, 0);
453 }
454 self.0[offset..end].copy_from_slice(buf);
455 Ok(())
456 }
457
458 fn truncate(&mut self, size: usize) -> VfsResult<()> {
459 self.0.truncate(size);
460 Ok(())
461 }
462
463 fn flush(&mut self) -> VfsResult<()> {
464 Ok(())
465 }
466
467 fn size(&self) -> VfsResult<usize> {
468 Ok(self.0.len())
469 }
470}
471
472#[derive(Debug)]
474pub struct VfsError {
475 code: i32,
476 message: String,
477}
478
479impl VfsError {
480 pub fn new(code: i32, message: String) -> Self {
481 VfsError { code, message }
482 }
483}
484
485pub type VfsResult<T> = Result<T, VfsError>;
487
488pub struct VfsAppData<T> {
490 data: T,
491 last_err: Mutex<Option<(i32, String)>>,
492}
493
494impl<T> VfsAppData<T> {
495 pub fn new(t: T) -> Self {
496 VfsAppData {
497 data: t,
498 last_err: Mutex::new(None),
499 }
500 }
501
502 pub fn leak(self) -> *mut Self {
504 Box::into_raw(Box::new(self))
505 }
506
507 pub unsafe fn from_raw(t: *mut Self) -> VfsAppData<T> {
511 *Box::from_raw(t)
512 }
513
514 pub fn pop_err(&self) -> Option<(i32, String)> {
516 self.last_err.lock().take()
517 }
518
519 pub fn store_err(&self, err: VfsError) -> i32 {
521 let VfsError { code, message } = err;
522 self.last_err.lock().replace((code, message));
523 code
524 }
525}
526
527impl<T> Deref for VfsAppData<T> {
529 type Target = T;
530
531 fn deref(&self) -> &Self::Target {
532 &self.data
533 }
534}
535
536pub trait VfsFile {
538 fn read(&self, buf: &mut [u8], offset: usize) -> VfsResult<i32>;
540 fn write(&mut self, buf: &[u8], offset: usize) -> VfsResult<()>;
542 fn truncate(&mut self, size: usize) -> VfsResult<()>;
544 fn flush(&mut self) -> VfsResult<()>;
546 fn size(&self) -> VfsResult<usize>;
548}
549
550pub trait VfsStore<File, AppData> {
552 unsafe fn app_data(vfs: *mut sqlite3_vfs) -> &'static VfsAppData<AppData> {
558 &*(*vfs).pAppData.cast()
559 }
560 fn name2path(vfs: *mut sqlite3_vfs, file: &str) -> VfsResult<String> {
562 unused!(vfs);
563 Ok(file.into())
564 }
565 fn add_file(vfs: *mut sqlite3_vfs, file: &str, flags: i32) -> VfsResult<()>;
567 fn contains_file(vfs: *mut sqlite3_vfs, file: &str) -> VfsResult<bool>;
569 fn delete_file(vfs: *mut sqlite3_vfs, file: &str) -> VfsResult<()>;
571 fn with_file<F: Fn(&File) -> i32>(vfs_file: &SQLiteVfsFile, f: F) -> VfsResult<i32>;
573 fn with_file_mut<F: Fn(&mut File) -> i32>(vfs_file: &SQLiteVfsFile, f: F) -> VfsResult<i32>;
575}
576
577#[allow(clippy::missing_safety_doc)]
579pub trait SQLiteVfs<IO: SQLiteIoMethods> {
580 const VERSION: ::std::os::raw::c_int;
581 const MAX_PATH_SIZE: ::std::os::raw::c_int = 1024;
582
583 fn vfs(
584 vfs_name: *const ::std::os::raw::c_char,
585 app_data: *mut VfsAppData<IO::AppData>,
586 ) -> sqlite3_vfs {
587 sqlite3_vfs {
588 iVersion: Self::VERSION,
589 szOsFile: std::mem::size_of::<SQLiteVfsFile>() as i32,
590 mxPathname: Self::MAX_PATH_SIZE,
591 pNext: std::ptr::null_mut(),
592 zName: vfs_name,
593 pAppData: app_data.cast(),
594 xOpen: Some(Self::xOpen),
595 xDelete: Some(Self::xDelete),
596 xAccess: Some(Self::xAccess),
597 xFullPathname: Some(Self::xFullPathname),
598 xDlOpen: None,
599 xDlError: None,
600 xDlSym: None,
601 xDlClose: None,
602 xRandomness: Some(x_methods_shim::xRandomness),
603 xSleep: Some(x_methods_shim::xSleep),
604 xCurrentTime: Some(x_methods_shim::xCurrentTime),
605 xGetLastError: Some(Self::xGetLastError),
606 xCurrentTimeInt64: Some(x_methods_shim::xCurrentTimeInt64),
607 xSetSystemCall: None,
608 xGetSystemCall: None,
609 xNextSystemCall: None,
610 }
611 }
612
613 unsafe extern "C" fn xOpen(
614 pVfs: *mut sqlite3_vfs,
615 zName: sqlite3_filename,
616 pFile: *mut sqlite3_file,
617 flags: ::std::os::raw::c_int,
618 pOutFlags: *mut ::std::os::raw::c_int,
619 ) -> ::std::os::raw::c_int {
620 let app_data = IO::Store::app_data(pVfs);
621
622 let name = if zName.is_null() {
623 get_random_name()
624 } else {
625 check_result!(CStr::from_ptr(zName).to_str()).into()
626 };
627
628 let name = match IO::Store::name2path(pVfs, &name) {
629 Ok(name) => name,
630 Err(err) => return app_data.store_err(err),
631 };
632
633 let exist = match IO::Store::contains_file(pVfs, &name) {
634 Ok(exist) => exist,
635 Err(err) => return app_data.store_err(err),
636 };
637
638 if !exist {
639 if flags & SQLITE_OPEN_CREATE == 0 {
640 return app_data.store_err(VfsError::new(
641 SQLITE_CANTOPEN,
642 format!("file not found: {name}"),
643 ));
644 }
645 if let Err(err) = IO::Store::add_file(pVfs, &name, flags) {
646 return app_data.store_err(err);
647 }
648 }
649
650 let leak = name.leak();
651 let vfs_file = pFile.cast::<SQLiteVfsFile>();
652 (*vfs_file).vfs = pVfs;
653 (*vfs_file).flags = flags;
654 (*vfs_file).name_ptr = leak.as_ptr();
655 (*vfs_file).name_length = leak.len();
656
657 (*pFile).pMethods = &IO::METHODS;
658
659 if !pOutFlags.is_null() {
660 *pOutFlags = flags;
661 }
662
663 SQLITE_OK
664 }
665
666 unsafe extern "C" fn xDelete(
667 pVfs: *mut sqlite3_vfs,
668 zName: *const ::std::os::raw::c_char,
669 syncDir: ::std::os::raw::c_int,
670 ) -> ::std::os::raw::c_int {
671 unused!(syncDir);
672
673 let app_data = IO::Store::app_data(pVfs);
674 bail!(zName.is_null(), SQLITE_IOERR_DELETE);
675 let s = check_result!(CStr::from_ptr(zName).to_str());
676 if let Err(err) = IO::Store::delete_file(pVfs, s) {
677 app_data.store_err(err)
678 } else {
679 SQLITE_OK
680 }
681 }
682
683 unsafe extern "C" fn xAccess(
684 pVfs: *mut sqlite3_vfs,
685 zName: *const ::std::os::raw::c_char,
686 flags: ::std::os::raw::c_int,
687 pResOut: *mut ::std::os::raw::c_int,
688 ) -> ::std::os::raw::c_int {
689 unused!(flags);
690
691 *pResOut = if zName.is_null() {
692 0
693 } else {
694 let app_data = IO::Store::app_data(pVfs);
695 let file = check_result!(CStr::from_ptr(zName).to_str());
696 let exist = match IO::Store::contains_file(pVfs, file) {
697 Ok(exist) => exist,
698 Err(err) => return app_data.store_err(err),
699 };
700 i32::from(exist)
701 };
702
703 SQLITE_OK
704 }
705
706 unsafe extern "C" fn xFullPathname(
707 pVfs: *mut sqlite3_vfs,
708 zName: *const ::std::os::raw::c_char,
709 nOut: ::std::os::raw::c_int,
710 zOut: *mut ::std::os::raw::c_char,
711 ) -> ::std::os::raw::c_int {
712 unused!(pVfs);
713 bail!(zName.is_null() || zOut.is_null(), SQLITE_CANTOPEN);
714 let len = CStr::from_ptr(zName).to_bytes_with_nul().len();
715 bail!(len > nOut as usize, SQLITE_CANTOPEN);
716 zName.copy_to(zOut, len);
717 SQLITE_OK
718 }
719
720 unsafe extern "C" fn xGetLastError(
721 pVfs: *mut sqlite3_vfs,
722 nOut: ::std::os::raw::c_int,
723 zOut: *mut ::std::os::raw::c_char,
724 ) -> ::std::os::raw::c_int {
725 let app_data = IO::Store::app_data(pVfs);
726 let Some((code, msg)) = app_data.pop_err() else {
727 return SQLITE_OK;
728 };
729 if !zOut.is_null() {
730 let nOut = nOut as usize;
731 let count = msg.len().min(nOut);
732 msg.as_ptr().copy_to(zOut.cast(), count);
733 let zero = match nOut.cmp(&msg.len()) {
734 std::cmp::Ordering::Less | std::cmp::Ordering::Equal => nOut,
735 std::cmp::Ordering::Greater => msg.len() + 1,
736 };
737 if zero > 0 {
738 std::ptr::write(zOut.add(zero - 1), 0);
739 }
740 }
741 code
742 }
743}
744
745#[allow(clippy::missing_safety_doc)]
747pub trait SQLiteIoMethods {
748 type File: VfsFile;
749 type AppData: 'static;
750 type Store: VfsStore<Self::File, Self::AppData>;
751
752 const VERSION: ::std::os::raw::c_int;
753
754 const METHODS: sqlite3_io_methods = sqlite3_io_methods {
755 iVersion: Self::VERSION,
756 xClose: Some(Self::xClose),
757 xRead: Some(Self::xRead),
758 xWrite: Some(Self::xWrite),
759 xTruncate: Some(Self::xTruncate),
760 xSync: Some(Self::xSync),
761 xFileSize: Some(Self::xFileSize),
762 xLock: Some(Self::xLock),
763 xUnlock: Some(Self::xUnlock),
764 xCheckReservedLock: Some(Self::xCheckReservedLock),
765 xFileControl: Some(Self::xFileControl),
766 xSectorSize: Some(Self::xSectorSize),
767 xDeviceCharacteristics: Some(Self::xDeviceCharacteristics),
768 xShmMap: None,
769 xShmLock: None,
770 xShmBarrier: None,
771 xShmUnmap: None,
772 xFetch: None,
773 xUnfetch: None,
774 };
775
776 unsafe extern "C" fn xClose(pFile: *mut sqlite3_file) -> ::std::os::raw::c_int {
777 let vfs_file = SQLiteVfsFile::from_file(pFile);
778 let app_data = Self::Store::app_data(vfs_file.vfs);
779
780 if vfs_file.flags & SQLITE_OPEN_DELETEONCLOSE != 0 {
781 if let Err(err) = Self::Store::delete_file(vfs_file.vfs, vfs_file.name()) {
782 return app_data.store_err(err);
783 }
784 }
785
786 drop(Box::from_raw(vfs_file.name()));
787
788 SQLITE_OK
789 }
790
791 unsafe extern "C" fn xRead(
792 pFile: *mut sqlite3_file,
793 zBuf: *mut ::std::os::raw::c_void,
794 iAmt: ::std::os::raw::c_int,
795 iOfst: sqlite3_int64,
796 ) -> ::std::os::raw::c_int {
797 let vfs_file = SQLiteVfsFile::from_file(pFile);
798 let app_data = Self::Store::app_data(vfs_file.vfs);
799
800 let f = |file: &Self::File| {
801 let size = iAmt as usize;
802 let offset = iOfst as usize;
803 let slice = std::slice::from_raw_parts_mut(zBuf.cast::<u8>(), size);
804 match file.read(slice, offset) {
805 Ok(code) => code,
806 Err(err) => app_data.store_err(err),
807 }
808 };
809
810 match Self::Store::with_file(vfs_file, f) {
811 Ok(code) => code,
812 Err(err) => app_data.store_err(err),
813 }
814 }
815
816 unsafe extern "C" fn xWrite(
817 pFile: *mut sqlite3_file,
818 zBuf: *const ::std::os::raw::c_void,
819 iAmt: ::std::os::raw::c_int,
820 iOfst: sqlite3_int64,
821 ) -> ::std::os::raw::c_int {
822 let vfs_file = SQLiteVfsFile::from_file(pFile);
823 let app_data = Self::Store::app_data(vfs_file.vfs);
824
825 let f = |file: &mut Self::File| {
826 let (offset, size) = (iOfst as usize, iAmt as usize);
827 let slice = std::slice::from_raw_parts(zBuf.cast::<u8>(), size);
828 if let Err(err) = file.write(slice, offset) {
829 app_data.store_err(err)
830 } else {
831 SQLITE_OK
832 }
833 };
834
835 match Self::Store::with_file_mut(vfs_file, f) {
836 Ok(code) => code,
837 Err(err) => app_data.store_err(err),
838 }
839 }
840
841 unsafe extern "C" fn xTruncate(
842 pFile: *mut sqlite3_file,
843 size: sqlite3_int64,
844 ) -> ::std::os::raw::c_int {
845 let vfs_file = SQLiteVfsFile::from_file(pFile);
846 let app_data = Self::Store::app_data(vfs_file.vfs);
847
848 let f = |file: &mut Self::File| {
849 if let Err(err) = file.truncate(size as usize) {
850 app_data.store_err(err)
851 } else {
852 SQLITE_OK
853 }
854 };
855
856 match Self::Store::with_file_mut(vfs_file, f) {
857 Ok(code) => code,
858 Err(err) => app_data.store_err(err),
859 }
860 }
861
862 unsafe extern "C" fn xSync(
863 pFile: *mut sqlite3_file,
864 flags: ::std::os::raw::c_int,
865 ) -> ::std::os::raw::c_int {
866 unused!(flags);
867
868 let vfs_file = SQLiteVfsFile::from_file(pFile);
869 let app_data = Self::Store::app_data(vfs_file.vfs);
870
871 let f = |file: &mut Self::File| {
872 if let Err(err) = file.flush() {
873 app_data.store_err(err)
874 } else {
875 SQLITE_OK
876 }
877 };
878
879 match Self::Store::with_file_mut(vfs_file, f) {
880 Ok(code) => code,
881 Err(err) => app_data.store_err(err),
882 }
883 }
884
885 unsafe extern "C" fn xFileSize(
886 pFile: *mut sqlite3_file,
887 pSize: *mut sqlite3_int64,
888 ) -> ::std::os::raw::c_int {
889 let vfs_file = SQLiteVfsFile::from_file(pFile);
890 let app_data = Self::Store::app_data(vfs_file.vfs);
891
892 let f = |file: &Self::File| match file.size() {
893 Ok(size) => {
894 *pSize = size as sqlite3_int64;
895 SQLITE_OK
896 }
897 Err(err) => app_data.store_err(err),
898 };
899
900 match Self::Store::with_file(vfs_file, f) {
901 Ok(code) => code,
902 Err(err) => app_data.store_err(err),
903 }
904 }
905
906 unsafe extern "C" fn xLock(
907 pFile: *mut sqlite3_file,
908 eLock: ::std::os::raw::c_int,
909 ) -> ::std::os::raw::c_int {
910 unused!((pFile, eLock));
911 SQLITE_OK
912 }
913
914 unsafe extern "C" fn xUnlock(
915 pFile: *mut sqlite3_file,
916 eLock: ::std::os::raw::c_int,
917 ) -> ::std::os::raw::c_int {
918 unused!((pFile, eLock));
919 SQLITE_OK
920 }
921
922 unsafe extern "C" fn xCheckReservedLock(
923 pFile: *mut sqlite3_file,
924 pResOut: *mut ::std::os::raw::c_int,
925 ) -> ::std::os::raw::c_int {
926 unused!(pFile);
927 *pResOut = 0;
928 SQLITE_OK
929 }
930
931 unsafe extern "C" fn xFileControl(
932 pFile: *mut sqlite3_file,
933 op: ::std::os::raw::c_int,
934 pArg: *mut ::std::os::raw::c_void,
935 ) -> ::std::os::raw::c_int {
936 unused!((pFile, op, pArg));
937 SQLITE_NOTFOUND
938 }
939
940 unsafe extern "C" fn xSectorSize(pFile: *mut sqlite3_file) -> ::std::os::raw::c_int {
941 unused!(pFile);
942 512
943 }
944
945 unsafe extern "C" fn xDeviceCharacteristics(pFile: *mut sqlite3_file) -> ::std::os::raw::c_int {
946 unused!(pFile);
947 0
948 }
949}
950
951#[allow(clippy::missing_safety_doc)]
953pub mod x_methods_shim {
954 use super::*;
955
956 #[cfg(target_feature = "atomics")]
958 pub unsafe extern "C" fn xSleep(
959 _pVfs: *mut sqlite3_vfs,
960 microseconds: ::std::os::raw::c_int,
961 ) -> ::std::os::raw::c_int {
962 use std::{thread, time::Duration};
963 thread::sleep(Duration::from_micros(microseconds as u64));
964 SQLITE_OK
965 }
966
967 #[cfg(not(target_feature = "atomics"))]
968 pub unsafe extern "C" fn xSleep(
969 _pVfs: *mut sqlite3_vfs,
970 _microseconds: ::std::os::raw::c_int,
971 ) -> ::std::os::raw::c_int {
972 SQLITE_OK
973 }
974
975 pub unsafe extern "C" fn xRandomness(
977 _pVfs: *mut sqlite3_vfs,
978 nByte: ::std::os::raw::c_int,
979 zOut: *mut ::std::os::raw::c_char,
980 ) -> ::std::os::raw::c_int {
981 for i in 0..nByte as usize {
982 *zOut.add(i) = (Math::random() * 255000.0) as _;
983 }
984 nByte
985 }
986
987 pub unsafe extern "C" fn xCurrentTime(
989 _pVfs: *mut sqlite3_vfs,
990 pTimeOut: *mut f64,
991 ) -> ::std::os::raw::c_int {
992 *pTimeOut = 2440587.5 + (Date::new_0().get_time() / 86400000.0);
993 SQLITE_OK
994 }
995
996 pub unsafe extern "C" fn xCurrentTimeInt64(
998 _pVfs: *mut sqlite3_vfs,
999 pOut: *mut sqlite3_int64,
1000 ) -> ::std::os::raw::c_int {
1001 *pOut = ((2440587.5 * 86400000.0) + Date::new_0().get_time()) as sqlite3_int64;
1002 SQLITE_OK
1003 }
1004}
1005
1006#[derive(thiserror::Error, Debug)]
1007pub enum ImportDbError {
1008 #[error("Byte array size is invalid for an SQLite db.")]
1009 InvalidDbSize,
1010 #[error("Input does not contain an SQLite database header.")]
1011 InvalidHeader,
1012 #[error("Page size must be a power of two between 512 and 65536 inclusive")]
1013 InvalidPageSize,
1014}
1015
1016pub fn check_import_db(bytes: &[u8]) -> Result<usize, ImportDbError> {
1018 let length = bytes.len();
1019
1020 if length < 512 || length % 512 != 0 {
1021 return Err(ImportDbError::InvalidDbSize);
1022 }
1023
1024 if SQLITE3_HEADER
1025 .as_bytes()
1026 .iter()
1027 .zip(bytes)
1028 .any(|(x, y)| x != y)
1029 {
1030 return Err(ImportDbError::InvalidHeader);
1031 }
1032
1033 let page_size = u16::from_be_bytes([bytes[16], bytes[17]]);
1036 let page_size = if page_size == 1 {
1037 65536
1038 } else {
1039 usize::from(page_size)
1040 };
1041
1042 Ok(page_size)
1043}
1044
1045pub fn check_db_and_page_size(db_size: usize, page_size: usize) -> Result<(), ImportDbError> {
1047 if !(page_size.is_power_of_two() && (512..=65536).contains(&page_size)) {
1048 return Err(ImportDbError::InvalidPageSize);
1049 }
1050 if db_size % page_size != 0 {
1051 return Err(ImportDbError::InvalidDbSize);
1052 }
1053 Ok(())
1054}
1055
1056#[cfg(test)]
1057mod tests {
1058 use crate::{
1059 vfs::utils::{copy_to_slice, copy_to_uint8_array_subarray},
1060 SQLITE_IOERR_SHORT_READ,
1061 };
1062
1063 use super::{copy_to_uint8_array, copy_to_vec, MemChunksFile, VfsFile};
1064 use js_sys::Uint8Array;
1065 use wasm_bindgen_test::wasm_bindgen_test;
1066
1067 #[wasm_bindgen_test]
1068 fn test_js_utils() {
1069 let buf1 = vec![1, 2, 3, 4];
1070 let uint8 = copy_to_uint8_array(&buf1);
1071 let buf2 = copy_to_vec(&uint8);
1072 assert_eq!(buf1, buf2);
1073
1074 let mut buf3 = vec![0u8; 2];
1075 copy_to_slice(&uint8.subarray(0, 2), &mut buf3);
1076 assert_eq!(buf3, vec![1, 2]);
1077
1078 let buf4 = Uint8Array::new_with_length(3);
1079 copy_to_uint8_array_subarray(&buf3, &buf4.subarray(1, 3));
1080 assert!(buf4.get_index(0) == 0);
1081 assert!(buf4.get_index(1) == 1);
1082 assert!(buf4.get_index(2) == 2);
1083 }
1084
1085 #[wasm_bindgen_test]
1086 fn test_chunks_file() {
1087 let mut file = MemChunksFile::new(512);
1088 file.write(&[], 0).unwrap();
1089 assert!(file.size().unwrap() == 0);
1090
1091 let mut buffer = [1; 2];
1092 let ret = file.read(&mut buffer, 0).unwrap();
1093 assert_eq!(ret, SQLITE_IOERR_SHORT_READ);
1094 assert_eq!([0; 2], buffer);
1095
1096 file.write(&[1], 0).unwrap();
1097 assert!(file.size().unwrap() == 1);
1098 let mut buffer = [2; 2];
1099 let ret = file.read(&mut buffer, 0).unwrap();
1100 assert_eq!(ret, SQLITE_IOERR_SHORT_READ);
1101 assert_eq!([1, 0], buffer);
1102
1103 let mut file = MemChunksFile::new(512);
1104 file.write(&[1; 512], 0).unwrap();
1105 assert!(file.size().unwrap() == 512);
1106 assert!(file.chunks.len() == 1);
1107
1108 file.truncate(512).unwrap();
1109 assert!(file.size().unwrap() == 512);
1110 assert!(file.chunks.len() == 1);
1111
1112 file.write(&[41, 42, 43], 511).unwrap();
1113 assert!(file.size().unwrap() == 514);
1114 assert!(file.chunks.len() == 2);
1115
1116 let mut buffer = [0; 3];
1117 let ret = file.read(&mut buffer, 511).unwrap();
1118 assert_eq!(ret, 0);
1119 assert_eq!(buffer, [41, 42, 43]);
1120
1121 file.truncate(513).unwrap();
1122 assert!(file.size().unwrap() == 513);
1123 assert!(file.chunks.len() == 2);
1124
1125 file.write(&[1], 2048).unwrap();
1126 assert!(file.size().unwrap() == 2049);
1127 assert!(file.chunks.len() == 5);
1128
1129 file.truncate(0).unwrap();
1130 assert!(file.size().unwrap() == 0);
1131 assert!(file.chunks.len() == 0);
1132 }
1133}