diff --git a/crates/librqbit/src/storage/filesystem/fs.rs b/crates/librqbit/src/storage/filesystem/fs.rs index ae99843..dd63448 100644 --- a/crates/librqbit/src/storage/filesystem/fs.rs +++ b/crates/librqbit/src/storage/filesystem/fs.rs @@ -62,8 +62,21 @@ impl StorageFactory for FilesystemStorageFactory { } pub struct FilesystemStorage { - output_folder: PathBuf, - opened_files: Vec, + pub(super) output_folder: PathBuf, + pub(super) opened_files: Vec, +} + +impl FilesystemStorage { + pub(super) fn take_fs(&self) -> anyhow::Result { + Ok(Self { + opened_files: self + .opened_files + .iter() + .map(|f| f.take_clone()) + .collect::>>()?, + output_folder: self.output_folder.clone(), + }) + } } impl TorrentStorage for FilesystemStorage { diff --git a/crates/librqbit/src/storage/filesystem/mmap.rs b/crates/librqbit/src/storage/filesystem/mmap.rs new file mode 100644 index 0000000..87dc044 --- /dev/null +++ b/crates/librqbit/src/storage/filesystem/mmap.rs @@ -0,0 +1,97 @@ +use std::path::Path; + +use anyhow::Context; +use memmap2::{MmapMut, MmapOptions}; +use parking_lot::RwLock; + +use crate::torrent_state::ManagedTorrentInfo; + +use crate::storage::{StorageFactory, TorrentStorage}; + +use super::{FilesystemStorage, FilesystemStorageFactory}; + +#[derive(Default)] +pub struct MmapFilesystemStorageFactory {} + +type OpenedMmap = RwLock; + +fn dummy_mmap() -> anyhow::Result { + Ok(memmap2::MmapOptions::new().len(1).map_anon()?) +} + +impl StorageFactory for MmapFilesystemStorageFactory { + type Storage = MmapFilesystemStorage; + + fn init_storage(&self, meta: &ManagedTorrentInfo) -> anyhow::Result { + let fs_storage = FilesystemStorageFactory::default().init_storage(meta)?; + let mut mmaps = Vec::new(); + for file in fs_storage.opened_files.iter() { + let mmap = unsafe { MmapOptions::new().map_mut(&*file.file.lock()) } + .context("error mapping file")?; + mmaps.push(RwLock::new(mmap)); + } + + Ok(MmapFilesystemStorage { + opened_mmaps: mmaps, + fs: fs_storage, + }) + } +} + +pub struct MmapFilesystemStorage { + opened_mmaps: Vec, + fs: FilesystemStorage, +} + +impl TorrentStorage for MmapFilesystemStorage { + fn pread_exact(&self, file_id: usize, offset: u64, buf: &mut [u8]) -> anyhow::Result<()> { + let g = self + .opened_mmaps + .get(file_id) + .context("no such file")? + .read(); + let start = offset; + let end = offset + buf.len() as u64; + let start = start.try_into()?; + let end = end.try_into()?; + buf.copy_from_slice(g.get(start..end).context("bug")?); + Ok(()) + } + + fn pwrite_all(&self, file_id: usize, offset: u64, buf: &[u8]) -> anyhow::Result<()> { + let mut g = self + .opened_mmaps + .get(file_id) + .context("no such file")? + .write(); + let start = offset; + let end = offset + buf.len() as u64; + let start = start.try_into()?; + let end = end.try_into()?; + g.get_mut(start..end).context("bug")?.copy_from_slice(buf); + Ok(()) + } + + fn remove_file(&self, file_id: usize, filename: &Path) -> anyhow::Result<()> { + self.fs.remove_file(file_id, filename) + } + + fn ensure_file_length(&self, file_id: usize, len: u64) -> anyhow::Result<()> { + self.fs.ensure_file_length(file_id, len) + } + + fn take(&self) -> anyhow::Result> { + Ok(Box::new(Self { + opened_mmaps: self + .opened_mmaps + .iter() + .map(|m| { + let d = dummy_mmap()?; + let mut g = m.write(); + Ok::<_, anyhow::Error>(RwLock::new(std::mem::replace(&mut *g, d))) + }) + .collect::>()?, + fs: self.fs.take_fs()?, + })) + } +} diff --git a/crates/librqbit/src/storage/filesystem/mod.rs b/crates/librqbit/src/storage/filesystem/mod.rs index eb634b5..9b3df6b 100644 --- a/crates/librqbit/src/storage/filesystem/mod.rs +++ b/crates/librqbit/src/storage/filesystem/mod.rs @@ -1,4 +1,5 @@ mod fs; +mod mmap; mod opened_file; pub use fs::{FilesystemStorage, FilesystemStorageFactory};