#![cfg(all(target_arch = "wasm32", feature = "opfs"))]
#![allow(unsafe_code)]
use std::sync::Arc;
use crate::Result;
use crate::errors::PagedbError;
use crate::vfs::traits::{
VfsFile, checked_opfs_byte_count, checked_opfs_file_size, checked_opfs_js_range, write_all_at,
};
use crate::vfs::types::{ReadReq, WriteReq};
use super::protocol::{ErrKind, OpfsOp, OpfsResult};
use super::vfs_impl::OpfsVfs;
pub struct OpfsFile {
pub(crate) handle_id: u32,
pub(crate) vfs: Arc<OpfsVfs>,
pub(crate) read_only: bool,
}
unsafe impl Send for OpfsFile {}
impl Drop for OpfsFile {
fn drop(&mut self) {
let vfs = Arc::clone(&self.vfs);
let handle_id = self.handle_id;
wasm_bindgen_futures::spawn_local(async move {
let _ = vfs.dispatch(OpfsOp::Close { handle_id }).await;
});
}
}
impl VfsFile for OpfsFile {
async fn read_at(&self, offset: u64, buf: &mut [u8]) -> Result<usize> {
let len = buf.len();
checked_opfs_js_range(offset, len)?;
let result = self
.vfs
.dispatch(OpfsOp::Read {
handle_id: self.handle_id,
offset,
len,
})
.await?;
match result {
OpfsResult::Data { bytes, count } => {
let n = checked_opfs_byte_count("read", count, buf.len())?;
if bytes.len() != n {
return Err(PagedbError::Io(std::io::Error::new(
std::io::ErrorKind::InvalidData,
"OPFS read payload length did not match its byte count",
)));
}
buf[..n].copy_from_slice(&bytes[..n]);
Ok(n)
}
OpfsResult::Err { reason, kind } => Err(map_err(&reason, kind)),
_ => Err(PagedbError::Unsupported),
}
}
async fn read_at_vectored(&self, reqs: &mut [ReadReq<'_>]) -> Result<()> {
for req in reqs.iter() {
checked_opfs_js_range(req.offset, req.buf.len())?;
}
for req in reqs.iter_mut() {
let n = self.read_at(req.offset, req.buf).await?;
for b in &mut req.buf[n..] {
*b = 0;
}
}
Ok(())
}
async fn write_at(&mut self, offset: u64, buf: &[u8]) -> Result<usize> {
if self.read_only {
return Err(PagedbError::ReadOnly);
}
let data = buf.to_vec();
let len = data.len();
checked_opfs_js_range(offset, len)?;
let result = self
.vfs
.dispatch(OpfsOp::Write {
handle_id: self.handle_id,
offset,
data,
})
.await?;
match result {
OpfsResult::Written { count } => checked_opfs_byte_count("write", count, len),
OpfsResult::Err { reason, kind } => Err(map_err(&reason, kind)),
_ => Err(PagedbError::Unsupported),
}
}
async fn write_at_vectored(&mut self, reqs: &[WriteReq<'_>]) -> Result<()> {
if self.read_only {
return Err(PagedbError::ReadOnly);
}
for req in reqs {
checked_opfs_js_range(req.offset, req.buf.len())?;
}
for req in reqs {
write_all_at(self, req.offset, req.buf).await?;
}
Ok(())
}
async fn sync(&mut self) -> Result<()> {
let result = self
.vfs
.dispatch(OpfsOp::Flush {
handle_id: self.handle_id,
})
.await?;
match result {
OpfsResult::Ok => Ok(()),
OpfsResult::Err { reason, kind } => Err(map_err(&reason, kind)),
_ => Err(PagedbError::Unsupported),
}
}
async fn truncate(&mut self, len: u64) -> Result<()> {
if self.read_only {
return Err(PagedbError::ReadOnly);
}
checked_opfs_js_range(len, 0)?;
let result = self
.vfs
.dispatch(OpfsOp::Truncate {
handle_id: self.handle_id,
len,
})
.await?;
match result {
OpfsResult::Ok => Ok(()),
OpfsResult::Err { reason, kind } => Err(map_err(&reason, kind)),
_ => Err(PagedbError::Unsupported),
}
}
async fn len(&self) -> Result<u64> {
let result = self
.vfs
.dispatch(OpfsOp::GetSize {
handle_id: self.handle_id,
})
.await?;
match result {
OpfsResult::Size { len } => checked_opfs_file_size(len),
OpfsResult::Err { reason, kind } => Err(map_err(&reason, kind)),
_ => Err(PagedbError::Unsupported),
}
}
async fn is_empty(&self) -> Result<bool> {
Ok(self.len().await? == 0)
}
fn supports_direct_io(&self) -> bool {
false
}
}
pub(crate) fn map_err(reason: &str, kind: ErrKind) -> PagedbError {
match kind {
ErrKind::NotFound => PagedbError::Io(std::io::Error::from(std::io::ErrorKind::NotFound)),
ErrKind::AlreadyExists => {
PagedbError::Io(std::io::Error::from(std::io::ErrorKind::AlreadyExists))
}
ErrKind::PermissionDenied => PagedbError::ReadOnly,
ErrKind::Io | ErrKind::Other => PagedbError::Io(std::io::Error::other(reason.to_string())),
}
}