use std::{io, path::Path};
use js_sys::{Function, Reflect, Uint8Array};
use wasm_bindgen::JsCast;
use wasm_bindgen_futures::JsFuture;
use web_sys::{Blob, File};
use super::opfs::{CreateFileMode, get_fs_handle, opfs_err};
const LARGE_FILE_THRESHOLD: usize = 10 * 1024 * 1024;
const CHUNK_SIZE: usize = 2 * 1024 * 1024;
pub async fn read(path: impl AsRef<Path>) -> io::Result<Vec<u8>> {
let handle = get_fs_handle(&path, CreateFileMode::NotCreate).await?;
let file: File = JsFuture::from(handle.get_file())
.await
.map_err(opfs_err)?
.unchecked_into();
let size = file.size() as usize;
if size == 0 {
return Ok(Vec::new());
}
if size <= LARGE_FILE_THRESHOLD {
return read_small_file(&file, size).await;
}
crate::console::warning!(
"Reading large file: {}, size: {} MB",
path.as_ref().display(),
size / 1024 / 1024
);
read_large_file(&file, size).await
}
async fn read_small_file(file: &File, size: usize) -> io::Result<Vec<u8>> {
let array_buffer = JsFuture::from(file.array_buffer())
.await
.map_err(opfs_err)?;
let uint8_array = Uint8Array::new(&array_buffer);
let len = uint8_array.length() as usize;
let mut output = Vec::<u8>::new();
output
.try_reserve_exact(size)
.map_err(|e| io::Error::new(io::ErrorKind::OutOfMemory, e))?;
unsafe {
uint8_array.raw_copy_to_ptr(output.as_mut_ptr());
output.set_len(len);
}
Ok(output)
}
async fn read_large_file(file: &File, size: usize) -> io::Result<Vec<u8>> {
let mut output = Vec::<u8>::new();
output
.try_reserve_exact(size)
.map_err(|e| io::Error::new(io::ErrorKind::OutOfMemory, e))?;
let slice_method: Function = Reflect::get(file, &"slice".into())
.map_err(opfs_err)?
.unchecked_into();
let mut offset = 0;
while offset < size {
let end = (offset + CHUNK_SIZE).min(size);
let chunk_blob: Blob = slice_method
.call2(file, &offset.into(), &end.into())
.map_err(opfs_err)?
.unchecked_into();
let array_buffer = JsFuture::from(chunk_blob.array_buffer())
.await
.map_err(opfs_err)?;
let uint8_array = Uint8Array::new(&array_buffer);
let chunk_len = uint8_array.length() as usize;
unsafe {
uint8_array.raw_copy_to_ptr(output.as_mut_ptr().add(offset));
output.set_len(offset + chunk_len);
}
offset += chunk_len;
}
Ok(output)
}