macro_rules! declare_and_impl_options { ($name: ident, $file_open_options: ident) => { /// A memory map builder, providing advanced options and flags for specifying memory map file behavior. /// // TODO: support file lock options #[derive(Clone)] pub struct $name { pub(crate) mmap_opts: MmapOptions, pub(crate) file_opts: $file_open_options, pub(crate) max_size: u64, } impl Default for $name { fn default() -> Self { Self::new() } } impl $name { /// Creates a new set of options for configuring and creating a memory map. pub fn new() -> Self { Self { mmap_opts: MmapOptions::new(), file_opts: <$file_open_options>::new(), max_size: 0, } } /// Configures the memory map to start at byte offset from the beginning of the file. /// This option has no effect on anonymous memory maps. /// By default, the offset is 0. pub fn offset(mut self, offset: u64) -> Self { self.mmap_opts.offset(offset); self } /// Configures the created memory mapped buffer to be len bytes long. /// This option is mandatory for anonymous memory maps. /// For file-backed memory maps, the length will default to the file length. pub fn len(mut self, len: usize) -> Self { self.mmap_opts.len(len); self } /// Populate (prefault) page tables for a mapping. /// For a file mapping, this causes read-ahead on the file. This will help to reduce blocking on page faults later. /// This option corresponds to the MAP_POPULATE flag on Linux. It has no effect on Windows pub fn populate(mut self) -> Self { self.mmap_opts.populate(); self } /// Configures the anonymous memory map to be suitable for a process or thread stack. /// This option corresponds to the MAP_STACK flag on Linux. It has no effect on Windows. /// This option has no effect on file-backed memory maps pub fn stack(mut self) -> Self { self.mmap_opts.stack(); self } /// Configures the max size of the file. /// /// This option only has effect when mmaping a real file in write mode. /// /// This field is ignored when opening [`DiskMmapFile`], [`AsyncDiskMmapFile`], [`MmapFile`] and [`AsyncMmapFile`]. /// /// [`DiskMmapFile`]: fmmap::raw::DiskMmapFile /// [`AsyncDiskMmapFile`]: fmmap::raw::AsyncDiskMmapFile /// [`MmapFile`]: struct.MmapFile.html /// [`AsyncMmapFile`]: struct.AsyncMmapFile.html pub fn max_size(mut self, max_sz: u64) -> Self { self.max_size = max_sz; self } /// Sets the option for read access. For details, please see [`std::fs::OpenOptions::read`] /// /// [`std::fs::OpenOptions::read`]: https://doc.rust-lang.org/std/fs/struct.OpenOptions.html#method.read pub fn read(mut self, val: bool) -> Self { self.file_opts.read(val); self } /// Sets the option for write access. For details, please see [`std::fs::OpenOptions::write`]. /// /// This field is ignored when opening [`DiskMmapFile`], [`AsyncDiskMmapFile`], [`MmapFile`] and [`AsyncMmapFile`]. /// /// [`DiskMmapFile`]: fmmap::raw::DiskMmapFile /// [`AsyncDiskMmapFile`]: fmmap::raw::AsyncDiskMmapFile /// [`MmapFile`]: struct.MmapFile.html /// [`AsyncMmapFile`]: struct.AsyncMmapFile.html /// [`std::fs::OpenOptions::write`]: https://doc.rust-lang.org/std/fs/struct.OpenOptions.html#method.write pub fn write(mut self, val: bool) -> Self { self.file_opts.write(val); self } /// Sets the option to create a new file, or open it if it already exists. For details, please see [`std::fs::OpenOptions::create`]. /// /// This field is ignored when opening [`DiskMmapFile`], [`AsyncDiskMmapFile`], [`MmapFile`] and [`AsyncMmapFile`]. /// /// [`DiskMmapFile`]: fmmap::raw::DiskMmapFile /// [`AsyncDiskMmapFile`]: fmmap::raw::AsyncDiskMmapFile /// [`MmapFile`]: struct.MmapFile.html /// [`AsyncMmapFile`]: struct.AsyncMmapFile.html /// [`std::fs::OpenOptions::create`]: https://doc.rust-lang.org/std/fs/struct.OpenOptions.html#method.create pub fn create(mut self, val: bool) -> Self { self.file_opts.create(val); self } /// Sets the option to create a new file, failing if it already exists. For details, please see [`std::fs::OpenOptions::create_new`] /// /// This field is ignored when opening [`DiskMmapFile`], [`AsyncDiskMmapFile`], [`MmapFile`] and [`AsyncMmapFile`]. /// /// [`DiskMmapFile`]: fmmap::raw::DiskMmapFile /// [`AsyncDiskMmapFile`]: fmmap::raw::AsyncDiskMmapFile /// [`MmapFile`]: struct.MmapFile.html /// [`AsyncMmapFile`]: struct.AsyncMmapFile.html /// [`std::fs::OpenOptions::create_new`]: https://doc.rust-lang.org/std/fs/struct.OpenOptions.html#method.create_new pub fn create_new(mut self, val: bool) -> Self { self.file_opts.create_new(val); self } /// Sets the option for the append mode. For details, please see [`std::fs::OpenOptions::append`] /// /// This field is ignored when opening [`DiskMmapFile`], [`AsyncDiskMmapFile`], [`MmapFile`] and [`AsyncMmapFile`]. /// /// [`DiskMmapFile`]: fmmap::raw::DiskMmapFile /// [`AsyncDiskMmapFile`]: fmmap::raw::AsyncDiskMmapFile /// [`MmapFile`]: struct.MmapFile.html /// [`AsyncMmapFile`]: struct.AsyncMmapFile.html /// [`std::fs::OpenOptions::append`]: https://doc.rust-lang.org/std/fs/struct.OpenOptions.html#method.append pub fn append(mut self, val: bool) -> Self { self.file_opts.append(val); self } /// Sets the option for truncating a previous file. For details, please see [`std::fs::OpenOptions::truncate`] /// /// This field is ignored when opening [`DiskMmapFile`], [`AsyncDiskMmapFile`], [`MmapFile`] and [`AsyncMmapFile`]. /// /// [`DiskMmapFile`]: fmmap::raw::DiskMmapFile /// [`AsyncDiskMmapFile`]: fmmap::raw::AsyncDiskMmapFile /// [`MmapFile`]: struct.MmapFile.html /// [`AsyncMmapFile`]: struct.AsyncMmapFile.html /// [`std::fs::OpenOptions::truncate`]: https://doc.rust-lang.org/std/fs/struct.OpenOptions.html#method.truncate pub fn truncate(mut self, val: bool) -> Self { self.file_opts.truncate(val); self } } }; } #[cfg(unix)] macro_rules! impl_options_unix_ext { ($name: ident) => { #[cfg(unix)] impl $name { /// Sets the mode bits that a new file will be created with. [Read more] /// /// [Read more]: https://doc.rust-lang.org/std/os/unix/fs/trait.OpenOptionsExt.html#tymethod.mode #[cfg(unix)] pub fn mode(mut self, mode: u32) -> Self { self.file_opts.mode(mode); self } /// Pass custom flags to the `flags` argument of `open`. [Read more] /// /// [Read more]: https://doc.rust-lang.org/std/os/unix/fs/trait.OpenOptionsExt.html#tymethod.mode #[cfg(unix)] pub fn custom_flags(mut self, flags: i32) -> Self { self.file_opts.custom_flags(flags); self } } }; } #[cfg(windows)] macro_rules! impl_options_windows_ext { ($name: ident) => { #[cfg(windows)] impl $name { /// Overrides the `dwDesiredAccess` argument to the call to [`CreateFile`] with the specified value. [Read more] /// /// [`CreateFile`]: https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilea /// [Read more]: https://doc.rust-lang.org/std/os/windows/fs/trait.OpenOptionsExt.html#tymethod.security_qos_flags #[cfg(windows)] pub fn access_mode(mut self, access: u32) -> Self { self.file_opts.access_mode(access); self } /// Overrides the `dwShareMode` argument to the call to [`CreateFile`] with the specified value. [Read more] /// /// [`CreateFile`]: https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilea /// [Read more]: https://doc.rust-lang.org/std/os/windows/fs/trait.OpenOptionsExt.html#tymethod.security_qos_flags #[cfg(windows)] pub fn share_mode(mut self, val: u32) -> Self { self.file_opts.share_mode(val); self } /// Sets extra flags for the dwFileFlags argument to the /// call to [`CreateFile2`] to the specified value (or combines /// it with `attributes` and `security_qos_flags` to set the `dwFlagsAndAttributes` for [`CreateFile`]). [Read more] /// /// [`CreateFile2`]: https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfile2 /// [`CreateFile`]: https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilea /// [Read more]: https://doc.rust-lang.org/std/os/windows/fs/trait.OpenOptionsExt.html#tymethod.security_qos_flags #[cfg(windows)] pub fn custom_flags(mut self, flag: u32) -> Self { self.file_opts.custom_flags(flag); self } /// Overrides the `dwDesiredAccess` argument to the call to [`CreateFile`] with the specified value. [Read more] /// /// [`CreateFile`]: https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilea /// [Read more]: https://doc.rust-lang.org/std/os/windows/fs/trait.OpenOptionsExt.html#tymethod.security_qos_flags #[cfg(windows)] pub fn attributes(mut self, val: u32) -> Self { self.file_opts.attributes(val); self } /// Sets the `dwSecurityQosFlags` argument to the call to /// [`CreateFile2`] to the specified value (or combines it with `custom_flags` /// and `attributes` to set the `dwFlagsAndAttributes` for [`CreateFile`]). [Read more] /// /// [`CreateFile2`]: https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfile2 /// [`CreateFile`]: https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilea /// [Read more]: https://doc.rust-lang.org/std/os/windows/fs/trait.OpenOptionsExt.html#tymethod.security_qos_flags #[cfg(windows)] pub fn security_qos_flags(mut self, flags: u32) -> Self { self.file_opts.security_qos_flags(flags); self } } }; } cfg_sync!( mod sync_impl; pub use sync_impl::Options; ); cfg_async! { macro_rules! declare_and_impl_async_options { ($filename_prefix: literal, $doc_test_runtime: literal, $path_str: literal) => { declare_and_impl_options!(AsyncOptions, OpenOptions); impl AsyncOptions { /// Create a new file and mmap this file with [`AsyncOptions`] /// /// # Example #[doc = "```ignore"] #[doc = concat!("use fmmap::", $path_str, "::{AsyncOptions, AsyncMmapFileMut, AsyncMmapFileExt, AsyncMmapFileMutExt};")] /// # use scopeguard::defer; /// #[doc = concat!("# ", $doc_test_runtime, "::block_on(async {")] /// let mut file = AsyncOptions::new() /// // truncate to 100 /// .max_size(100) #[doc = concat!(".create_mmap_file_mut(\"", $filename_prefix, "_create_with_options_test.txt\").await.unwrap();")] #[doc = concat!("# defer!(std::fs::remove_file(\"", $filename_prefix, "_create_with_options_test.txt\").unwrap());")] /// assert!(!file.is_empty()); /// file.write_all("some data...".as_bytes(), 0).unwrap(); /// file.flush().unwrap(); /// # }) #[doc = "```"] /// /// [`AsyncOptions`]: struct.AsyncOptions.html pub async fn create_mmap_file_mut>(self, path: P) -> Result { Ok(AsyncMmapFileMut::from(AsyncDiskMmapFileMut::create_with_options(path, self).await?)) } /// Open a readable memory map backed by a file with [`Options`] /// /// # Example /// #[doc = "```ignore"] #[doc = concat!("use fmmap::", $path_str, "::{AsyncOptions, AsyncMmapFile, AsyncMmapFileExt};")] #[doc = concat!("# use fmmap::", $path_str, "::{AsyncMmapFileMut, AsyncMmapFileMutExt};")] /// # use scopeguard::defer; /// #[doc = concat!("# ", $doc_test_runtime, "::block_on(async {")] #[doc = concat!("# let mut file = AsyncMmapFileMut::create(\"", $filename_prefix, "_open_with_options_test.txt\").await.unwrap();")] #[doc = concat!("# defer!(std::fs::remove_file(\"", $filename_prefix, "_open_with_options_test.txt\").unwrap());")] /// # file.truncate(23).await.unwrap(); /// # file.write_all("sanity text".as_bytes(), 0).unwrap(); /// # file.write_all("some data...".as_bytes(), "sanity text".as_bytes().len()).unwrap(); /// # file.flush().unwrap(); /// # drop(file); /// /// // mmap the file /// let file = AsyncOptions::new() /// // mmap content after the sanity text /// .offset("sanity text".as_bytes().len() as u64) #[doc = concat!(".open_mmap_file(\"", $filename_prefix, "_open_with_options_test.txt\").await.unwrap();")] /// let mut buf = vec![0; "some data...".len()]; /// file.read_exact(buf.as_mut_slice(), 0).unwrap(); /// assert_eq!(buf.as_slice(), "some data...".as_bytes()); /// # }) #[doc = "```"] /// /// [`AsyncOptions`]: struct.AsyncOptions.html pub async fn open_mmap_file>(self, path: P) -> Result { Ok(AsyncMmapFile::from(AsyncDiskMmapFile::open_with_options(path, self).await?)) } /// Open a readable and executable memory map backed by a file with [`AsyncOptions`]. /// /// # Examples /// #[doc = "```ignore"] #[doc = concat!("use fmmap::", $path_str, "::{AsyncOptions, AsyncMmapFile, AsyncMmapFileExt};")] #[doc = concat!("# use fmmap::", $path_str, "::{AsyncMmapFileMut, AsyncMmapFileMutExt};")] /// # use scopeguard::defer; /// #[doc = concat!("# ", $doc_test_runtime, "::block_on(async {")] #[doc = concat!("# let mut file = AsyncMmapFileMut::create(\"", $filename_prefix, "_open_exec_with_options_test.txt\").await.unwrap();")] #[doc = concat!(" # defer!(std::fs::remove_file(\"", $filename_prefix, "_open_exec_with_options_test.txt\").unwrap());")] /// # file.truncate(23).await.unwrap(); /// # file.write_all("sanity text".as_bytes(), 0).unwrap(); /// # file.write_all("some data...".as_bytes(), "sanity text".as_bytes().len()).unwrap(); /// # file.flush().unwrap(); /// # drop(file); /// /// // mmap the file /// let file = AsyncOptions::new() /// // mmap content after the sanity text /// .offset("sanity text".as_bytes().len() as u64) #[doc = concat!(".open_exec_mmap_file(\"", $filename_prefix, "_open_exec_with_options_test.txt\").await.unwrap();")] /// let mut buf = vec![0; "some data...".len()]; /// file.read_exact(buf.as_mut_slice(), 0).unwrap(); /// assert_eq!(buf.as_slice(), "some data...".as_bytes()); /// # }) #[doc = "```"] /// /// [`AsyncOptions`]: struct.AsyncOptions.html pub async fn open_exec_mmap_file>(self, path: P) -> Result { Ok(AsyncMmapFile::from(AsyncDiskMmapFile::open_exec_with_options(path, self).await?)) } /// Open or Create(if not exists) a file and mmap this file with [`AsyncOptions`]. /// /// # Examples /// /// File already exists /// /// ```ignore #[doc = concat!("use fmmap::", $path_str, "::{AsyncMmapFileMut, AsyncMmapFileExt, AsyncMmapFileMutExt, AsyncOptions};")] /// # use scopeguard::defer; /// #[doc = concat!("# ", $doc_test_runtime, "::block_on(async {")] #[doc = concat!("# let mut file = AsyncMmapFileMut::create(\"", $filename_prefix, "_open_with_options_test.txt\").await.unwrap();")] #[doc = concat!("# defer!(std::fs::remove_file(\"", $filename_prefix, "_open_with_options_test.txt\").unwrap());")] /// # file.truncate(23).await.unwrap(); /// # file.write_all("sanity text".as_bytes(), 0).unwrap(); /// # file.write_all("some data...".as_bytes(), "sanity text".as_bytes().len()).unwrap(); /// # file.flush().unwrap(); /// # drop(file); /// /// let mut file = AsyncOptions::new() /// // allow read /// .read(true) /// // allow write /// .write(true) /// // allow append /// .append(true) /// // truncate to 100 /// .max_size(100) /// // mmap content after the sanity text /// .offset("sanity text".as_bytes().len() as u64) #[doc = concat!(".open_mmap_file_mut(\"", $filename_prefix, "_open_with_options_test.txt\").await.unwrap();")] /// let mut buf = vec![0; "some data...".len()]; /// file.read_exact(buf.as_mut_slice(), 0).unwrap(); /// assert_eq!(buf.as_slice(), "some data...".as_bytes()); /// /// // modify the file data /// file.truncate(("some modified data...".len() + "sanity text".len()) as u64).await.unwrap(); /// file.write_all("some modified data...".as_bytes(), 0).unwrap(); /// file.flush().unwrap(); /// drop(file); /// /// // reopen to check content /// let mut buf = vec![0; "some modified data...".len()]; #[doc = concat!("let mut file = AsyncMmapFileMut::open(\"", $filename_prefix, "_open_with_options_test.txt\").await.unwrap();")] /// // skip the sanity text /// file.read_exact(buf.as_mut_slice(), "sanity text".as_bytes().len()).unwrap(); /// assert_eq!(buf.as_slice(), "some modified data...".as_bytes()); /// # }) #[doc = "```"] /// /// File does not exists /// /// ```ignore #[doc = concat!("use fmmap::", $path_str, "::{AsyncMmapFileMut, AsyncMmapFileExt, AsyncMmapFileMutExt, AsyncOptions};")] /// # use scopeguard::defer; /// #[doc = concat!("# ", $doc_test_runtime, "::block_on(async {")] /// // mmap the file with options /// let mut file = AsyncOptions::new() /// // allow read /// .read(true) /// // allow write /// .write(true) /// // allow append /// .append(true) /// // truncate to 100 /// .max_size(100) #[doc = concat!(".open_mmap_file_mut(\"", $filename_prefix, "_open_with_options_test.txt\").await.unwrap();")] #[doc = concat!("# defer!(std::fs::remove_file(\"", $filename_prefix, "_open_with_options_test.txt\").unwrap());")] /// file.write_all("some data...".as_bytes(), 0).unwrap(); /// /// let mut buf = vec![0; "some data...".len()]; /// file.read_exact(buf.as_mut_slice(), 0).unwrap(); /// assert_eq!(buf.as_slice(), "some data...".as_bytes()); /// /// // modify the file data /// file.truncate("some modified data...".len() as u64).await.unwrap(); /// file.write_all("some modified data...".as_bytes(), 0).unwrap(); /// file.flush().unwrap(); /// drop(file); /// /// // reopen to check content /// let mut buf = vec![0; "some modified data...".len()]; #[doc = concat!("let mut file = AsyncMmapFileMut::open(\"", $filename_prefix, "_open_with_options_test.txt\").await.unwrap();")] /// file.read_exact(buf.as_mut_slice(), 0).unwrap(); /// assert_eq!(buf.as_slice(), "some modified data...".as_bytes()); /// # }) #[doc = "```"] /// /// [`AsyncOptions`]: struct.AsyncOptions.html pub async fn open_mmap_file_mut>(self, path: P) -> Result { Ok(AsyncMmapFileMut::from(AsyncDiskMmapFileMut::open_with_options(path, self).await?)) } /// Open an existing file and mmap this file with [`AsyncOptions`] /// /// # Example /// /// ```ignore #[doc = concat!("use fmmap::", $path_str, "::{AsyncMmapFileMut, AsyncMmapFileExt, AsyncMmapFileMutExt, AsyncOptions};")] /// # use scopeguard::defer; /// #[doc = concat!("# ", $doc_test_runtime, "::block_on(async {")] /// // create a temp file #[doc = concat!("let mut file = AsyncMmapFileMut::create(\"", $filename_prefix, "_open_existing_test_with_options.txt\").await.unwrap();")] #[doc = concat!("# defer!(std::fs::remove_file(\"", $filename_prefix, "_open_existing_test_with_options.txt\").unwrap());")] /// file.truncate(23).await.unwrap(); /// file.write_all("sanity text".as_bytes(), 0).unwrap(); /// file.write_all("some data...".as_bytes(), "sanity text".as_bytes().len()).unwrap(); /// file.flush().unwrap(); /// drop(file); /// /// // mmap the file /// let mut file = AsyncOptions::new() /// // truncate to 100 /// .max_size(100) /// // mmap content after the sanity text /// .offset("sanity text".as_bytes().len() as u64) #[doc = concat!(".open_exist_mmap_file_mut(\"", $filename_prefix, "_open_existing_test_with_options.txt\").await.unwrap();")] /// /// let mut buf = vec![0; "some data...".len()]; /// file.read_exact(buf.as_mut_slice(), 0).unwrap(); /// assert_eq!(buf.as_slice(), "some data...".as_bytes()); /// /// // modify the file data /// file.truncate(("some modified data...".len() + "sanity text".len()) as u64).await.unwrap(); /// file.write_all("some modified data...".as_bytes(), 0).unwrap(); /// file.flush().unwrap(); /// /// // reopen to check content, cow will not change the content. #[doc = concat!("let mut file = AsyncMmapFileMut::open(\"", $filename_prefix, "_open_existing_test_with_options.txt\").await.unwrap();")] /// let mut buf = vec![0; "some modified data...".len()]; /// // skip the sanity text /// file.read_exact(buf.as_mut_slice(), "sanity text".as_bytes().len()).unwrap(); /// assert_eq!(buf.as_slice(), "some modified data...".as_bytes()); /// # }) #[doc = "```"] /// /// [`AsyncOptions`]: struct.AsyncOptions.html pub async fn open_exist_mmap_file_mut>(self, path: P) -> Result { Ok(AsyncMmapFileMut::from(AsyncDiskMmapFileMut::open_exist_with_options(path, self).await?)) } /// Open and mmap an existing file in copy-on-write mode(copy-on-write memory map backed by a file) with [`AsyncOptions`]. /// Data written to the memory map will not be visible by other processes, and will not be carried through to the underlying file. /// /// # Examples /// #[doc = "```ignore"] #[doc = concat!("use fmmap::", $path_str, "::{AsyncMmapFileMut, AsyncMmapFileExt, AsyncMmapFileMutExt, AsyncOptions};")] /// # use scopeguard::defer; /// #[doc = concat!("# ", $doc_test_runtime, "::block_on(async {")] /// // create a temp file #[doc = concat!("let mut file = AsyncMmapFileMut::create(\"", $filename_prefix, "_open_cow_with_options_test.txt\").await.unwrap();")] #[doc = concat!("# defer!(std::fs::remove_file(\"", $filename_prefix, "_open_cow_with_options_test.txt\").unwrap());")] /// file.truncate(23).await.unwrap(); /// file.write_all("sanity text".as_bytes(), 0).unwrap(); /// file.write_all("some data...".as_bytes(), "sanity text".as_bytes().len()).unwrap(); /// file.flush().unwrap(); /// drop(file); /// /// // mmap the file /// let mut file = AsyncOptions::new() /// // mmap content after the sanity text /// .offset("sanity text".as_bytes().len() as u64) #[doc = concat!(".open_cow_mmap_file_mut(\"", $filename_prefix, "_open_cow_with_options_test.txt\").await.unwrap();")] /// assert!(file.is_cow()); /// /// let mut buf = vec![0; "some data...".len()]; /// file.read_exact(buf.as_mut_slice(), 0).unwrap(); /// assert_eq!(buf.as_slice(), "some data...".as_bytes()); /// /// // modify the file data /// file.write_all("some data!!!".as_bytes(), 0).unwrap(); /// file.flush().unwrap(); /// /// // cow, change will only be seen in current caller /// assert_eq!(file.as_slice(), "some data!!!".as_bytes()); /// drop(file); /// /// // reopen to check content, cow will not change the content. #[doc = concat!("let mut file = AsyncMmapFileMut::open(\"", $filename_prefix, "_open_cow_with_options_test.txt\").await.unwrap();")] /// let mut buf = vec![0; "some data...".len()]; /// // skip the sanity text /// file.read_exact(buf.as_mut_slice(), "sanity text".as_bytes().len()).unwrap(); /// assert_eq!(buf.as_slice(), "some data...".as_bytes()); /// # }) #[doc = "```"] /// /// /// [`AsyncOptions`]: struct.AsyncOptions.html pub async fn open_cow_mmap_file_mut>(self, path: P) -> Result { Ok(AsyncMmapFileMut::from(AsyncDiskMmapFileMut::open_cow_with_options(path, self).await?)) } } }; } macro_rules! impl_async_options_tests { ($filename_prefix: literal, $runtime: meta, $path_str: ident) => { #[cfg(test)] mod tests { use crate::$path_str::{AsyncOptions, AsyncMmapFileMut, AsyncMmapFileMutExt, AsyncMmapFileExt}; use scopeguard::defer; #[$runtime] async fn test_create_mmap_file_mut() { let path = concat!($filename_prefix, "_options_create_mmap_file_mut.txt"); defer!(std::fs::remove_file(path).unwrap()); let mut file = AsyncOptions::new() // truncate to 100 .max_size(100) .create_mmap_file_mut(path).await.unwrap(); assert!(!file.is_empty()); file.write_all("some data...".as_bytes(), 0).unwrap(); file.flush().unwrap(); } #[$runtime] async fn test_open_mmap_file() { let path = concat!($filename_prefix, "_options_open_mmap_file.txt"); defer!(std::fs::remove_file(path).unwrap()); let mut file = AsyncMmapFileMut::create(path).await.unwrap(); file.truncate(23).await.unwrap(); file.write_all("sanity text".as_bytes(), 0).unwrap(); file.write_all("some data...".as_bytes(), "sanity text".as_bytes().len()).unwrap(); file.flush().unwrap(); drop(file); // mmap the file let file = AsyncOptions::new() // mmap content after the sanity text .offset("sanity text".as_bytes().len() as u64) .open_mmap_file(path).await.unwrap(); let mut buf = vec![0; "some data...".len()]; file.read_exact(buf.as_mut_slice(), 0).unwrap(); assert_eq!(buf.as_slice(), "some data...".as_bytes()); } #[$runtime] async fn test_open_mmap_file_exec() { let path = concat!($filename_prefix, "_options_open_exec_mmap_file.txt"); defer!(std::fs::remove_file(path).unwrap()); let mut file = AsyncMmapFileMut::create(path).await.unwrap(); file.truncate(23).await.unwrap(); file.write_all("sanity text".as_bytes(), 0).unwrap(); file.write_all("some data...".as_bytes(), "sanity text".as_bytes().len()).unwrap(); file.flush().unwrap(); drop(file); // mmap the file let file = AsyncOptions::new() // mmap content after the sanity text .offset("sanity text".as_bytes().len() as u64) .open_exec_mmap_file(path).await.unwrap(); let mut buf = vec![0; "some data...".len()]; file.read_exact(buf.as_mut_slice(), 0).unwrap(); assert_eq!(buf.as_slice(), "some data...".as_bytes()); } #[$runtime] async fn test_open_mmap_file_mut() { let path = concat!($filename_prefix, "_options_open_mmap_file_mut.txt"); defer!(std::fs::remove_file(path).unwrap()); let mut file = AsyncMmapFileMut::create(path).await.unwrap(); file.truncate(23).await.unwrap(); file.write_all("sanity text".as_bytes(), 0).unwrap(); file.write_all("some data...".as_bytes(), "sanity text".as_bytes().len()).unwrap(); file.flush().unwrap(); drop(file); let mut file = AsyncOptions::new() // allow read .read(true) // allow write .write(true) // allow append .append(true) // truncate to 100 .max_size(100) // mmap content after the sanity text .offset("sanity text".as_bytes().len() as u64) .open_mmap_file_mut(path).await.unwrap(); let mut buf = vec![0; "some data...".len()]; file.read_exact(buf.as_mut_slice(), 0).unwrap(); assert_eq!(buf.as_slice(), "some data...".as_bytes()); // modify the file data file.truncate(("some modified data...".len() + "sanity text".len()) as u64).await.unwrap(); file.write_all("some modified data...".as_bytes(), 0).unwrap(); file.flush().unwrap(); drop(file); // reopen to check content let mut buf = vec![0; "some modified data...".len()]; let file = AsyncMmapFileMut::open(path).await.unwrap(); // skip the sanity text file.read_exact(buf.as_mut_slice(), "sanity text".as_bytes().len()).unwrap(); assert_eq!(buf.as_slice(), "some modified data...".as_bytes()); } #[$runtime] async fn open_exist_mmap_file_mut() { let path = concat!($filename_prefix, "_options_open_exist_mmap_file_mut.txt"); defer!(std::fs::remove_file(path).unwrap()); let mut file = AsyncMmapFileMut::create(path).await.unwrap(); file.truncate(23).await.unwrap(); file.write_all("sanity text".as_bytes(), 0).unwrap(); file.write_all("some data...".as_bytes(), "sanity text".as_bytes().len()).unwrap(); file.flush().unwrap(); drop(file); // mmap the file let mut file = AsyncOptions::new() // truncate to 100 .max_size(100) // mmap content after the sanity text .offset("sanity text".as_bytes().len() as u64) .open_exist_mmap_file_mut(path).await.unwrap(); let mut buf = vec![0; "some data...".len()]; file.read_exact(buf.as_mut_slice(), 0).unwrap(); assert_eq!(buf.as_slice(), "some data...".as_bytes()); // modify the file data file.truncate(("some modified data...".len() + "sanity text".len()) as u64).await.unwrap(); file.write_all("some modified data...".as_bytes(), 0).unwrap(); file.flush().unwrap(); // reopen to check content, cow will not change the content. let file = AsyncMmapFileMut::open(path).await.unwrap(); let mut buf = vec![0; "some modified data...".len()]; // skip the sanity text file.read_exact(buf.as_mut_slice(), "sanity text".as_bytes().len()).unwrap(); assert_eq!(buf.as_slice(), "some modified data...".as_bytes()); } #[$runtime] async fn open_cow_mmap_file_mut() { let path = concat!($filename_prefix, "_options_open_cow_mmap_file_mut.txt"); defer!(std::fs::remove_file(path).unwrap()); let mut file = AsyncMmapFileMut::create(path).await.unwrap(); file.truncate(23).await.unwrap(); file.write_all("sanity text".as_bytes(), 0).unwrap(); file.write_all("some data...".as_bytes(), "sanity text".as_bytes().len()).unwrap(); file.flush().unwrap(); drop(file); // mmap the file let mut file = AsyncOptions::new() // mmap content after the sanity text .offset("sanity text".as_bytes().len() as u64) .open_cow_mmap_file_mut(path).await.unwrap(); assert!(file.is_cow()); let mut buf = vec![0; "some data...".len()]; file.read_exact(buf.as_mut_slice(), 0).unwrap(); assert_eq!(buf.as_slice(), "some data...".as_bytes()); // modify the file data file.write_all("some data!!!".as_bytes(), 0).unwrap(); file.flush().unwrap(); // cow, change will only be seen in current caller assert_eq!(file.as_slice(), "some data!!!".as_bytes()); drop(file); // reopen to check content, cow will not change the content. let file = AsyncMmapFileMut::open(path).await.unwrap(); let mut buf = vec![0; "some data...".len()]; // skip the sanity text file.read_exact(buf.as_mut_slice(), "sanity text".as_bytes().len()).unwrap(); assert_eq!(buf.as_slice(), "some data...".as_bytes()); } } }; } } cfg_async_std!( pub mod async_std_impl; ); cfg_smol!( pub mod smol_impl; ); cfg_tokio!( pub mod tokio_impl; );