#[allow(dead_code)]
#[derive(Copy, Clone)]
enum MmapFileMutType {
Cow,
Normal,
}
#[allow(dead_code)]
pub(crate) const LOCK_UNLOCKED: u8 = 0;
#[allow(dead_code)]
pub(crate) const LOCK_SHARED: u8 = 1;
#[allow(dead_code)]
pub(crate) const LOCK_EXCLUSIVE: u8 = 2;
#[allow(dead_code)]
#[inline]
fn validate_mapping_range(
file_len: u64,
offset: u64,
len: Option<usize>,
) -> Result<(), ::std::io::Error> {
if offset > file_len {
return Err(::std::io::Error::new(
::std::io::ErrorKind::InvalidInput,
format!("Options::offset ({offset}) exceeds file length ({file_len})"),
));
}
let effective_len: u64 = match len {
Some(n) => n as u64,
None => file_len - offset, };
if let Some(n) = len {
let end = offset.checked_add(n as u64).ok_or_else(|| {
::std::io::Error::new(
::std::io::ErrorKind::InvalidInput,
"Options::offset + Options::len overflows u64",
)
})?;
if end > file_len {
return Err(::std::io::Error::new(
::std::io::ErrorKind::InvalidInput,
format!("Options::offset ({offset}) + Options::len ({n}) exceeds file length ({file_len})"),
));
}
}
if effective_len > isize::MAX as u64 {
return Err(::std::io::Error::new(
::std::io::ErrorKind::InvalidInput,
format!(
"effective mapping length ({effective_len}) exceeds isize::MAX ({}); a Rust slice cannot represent it",
isize::MAX,
),
));
}
Ok(())
}
#[allow(dead_code)]
#[inline]
fn remmap<T: ::memmapix::MmapAsRawDesc>(
file: T,
opts: Option<&::memmapix::MmapOptions>,
typ: MmapFileMutType,
) -> Result<::memmapix::MmapMut, ::std::io::Error> {
unsafe {
match opts {
None => match typ {
MmapFileMutType::Cow => ::memmapix::MmapOptions::new().map_copy(file),
MmapFileMutType::Normal => ::memmapix::MmapMut::map_mut(file),
},
Some(opts) => {
let opts = opts.clone();
match typ {
MmapFileMutType::Cow => opts.map_copy(file),
MmapFileMutType::Normal => opts.map_mut(file),
}
}
}
}
}
macro_rules! impl_flush {
() => {
fn flush(&self) -> crate::error::Result<()> {
if self.poisoned {
return Err(Self::poison_err());
}
if self.is_cow() {
return Ok(());
}
self.mmap.flush()
}
fn flush_async(&self) -> crate::error::Result<()> {
if self.poisoned {
return Err(Self::poison_err());
}
if self.is_cow() {
return Ok(());
}
self.mmap.flush_async()
}
fn flush_range(&self, offset: usize, len: usize) -> crate::error::Result<()> {
if self.poisoned {
return Err(Self::poison_err());
}
if self.is_cow() {
return Ok(());
}
self.mmap.flush_range(offset, len)
}
fn flush_async_range(&self, offset: usize, len: usize) -> crate::error::Result<()> {
if self.poisoned {
return Err(Self::poison_err());
}
if self.is_cow() {
return Ok(());
}
self.mmap.flush_async_range(offset, len)
}
};
}
macro_rules! impl_file_lock {
($ext: path) => {
#[inline]
fn lock(&mut self) -> crate::error::Result<()> {
match self.lock_state {
crate::disk::LOCK_EXCLUSIVE => Ok(()),
crate::disk::LOCK_SHARED => Err(::std::io::Error::new(
::std::io::ErrorKind::WouldBlock,
"shared lock currently held; call `unlock()` first to upgrade",
)),
_ => {
<_ as $ext>::lock(&self.file)?;
self.lock_state = crate::disk::LOCK_EXCLUSIVE;
Ok(())
}
}
}
#[inline]
unsafe fn lock_shared(&mut self) -> crate::error::Result<()> {
match self.lock_state {
crate::disk::LOCK_SHARED => Ok(()),
crate::disk::LOCK_EXCLUSIVE => Err(::std::io::Error::new(
::std::io::ErrorKind::WouldBlock,
"exclusive lock currently held; call `unlock()` first to downgrade",
)),
_ => {
<_ as $ext>::lock_shared(&self.file)?;
self.lock_state = crate::disk::LOCK_SHARED;
Ok(())
}
}
}
#[inline]
fn try_lock(&mut self) -> crate::error::Result<()> {
match self.lock_state {
crate::disk::LOCK_EXCLUSIVE => Ok(()),
crate::disk::LOCK_SHARED => Err(::std::io::Error::new(
::std::io::ErrorKind::WouldBlock,
"shared lock currently held; call `unlock()` first to upgrade",
)),
_ => {
<_ as $ext>::try_lock(&self.file).map_err(::std::io::Error::from)?;
self.lock_state = crate::disk::LOCK_EXCLUSIVE;
Ok(())
}
}
}
#[inline]
unsafe fn try_lock_shared(&mut self) -> crate::error::Result<()> {
match self.lock_state {
crate::disk::LOCK_SHARED => Ok(()),
crate::disk::LOCK_EXCLUSIVE => Err(::std::io::Error::new(
::std::io::ErrorKind::WouldBlock,
"exclusive lock currently held; call `unlock()` first to downgrade",
)),
_ => {
<_ as $ext>::try_lock_shared(&self.file).map_err(::std::io::Error::from)?;
self.lock_state = crate::disk::LOCK_SHARED;
Ok(())
}
}
}
#[inline]
unsafe fn unlock(&mut self) -> crate::error::Result<()> {
if self.lock_state == crate::disk::LOCK_UNLOCKED {
return Ok(());
}
<_ as $ext>::unlock(&self.file)?;
self.lock_state = crate::disk::LOCK_UNLOCKED;
Ok(())
}
};
}
cfg_sync! {
macro_rules! impl_mmap_file_ext {
($name: ident) => {
impl MmapFileExt for $name {
fn len(&self) -> usize {
self.mmap.len()
}
fn as_slice(&self) -> &[u8] {
self.mmap.as_ref()
}
fn path(&self) -> &Path {
self.path.as_path()
}
fn metadata(&self) -> crate::error::Result<MetaData> {
self.file.metadata().map(MetaData::disk)
}
impl_file_lock!(::fs4::FileExt);
#[inline]
fn is_exec(&self) -> bool {
self.exec
}
}
};
}
macro_rules! impl_mmap_file_ext_for_mut {
($name: ident) => {
impl MmapFileExt for $name {
fn len(&self) -> usize {
if self.poisoned { 0 } else { self.mmap.len() }
}
fn as_slice(&self) -> &[u8] {
if self.poisoned { &[] } else { self.mmap.as_ref() }
}
fn path(&self) -> &Path {
self.path.as_path()
}
fn metadata(&self) -> crate::error::Result<MetaData> {
self.file.metadata().map(MetaData::disk)
}
impl_file_lock!(::fs4::FileExt);
#[inline]
fn is_exec(&self) -> bool {
false
}
}
};
}
}
#[cfg(feature = "sync")]
#[cfg_attr(docsrs, doc(cfg(feature = "sync")))]
mod sync_impl;
#[cfg(feature = "sync")]
#[cfg_attr(docsrs, doc(cfg(feature = "sync")))]
pub use sync_impl::{DiskMmapFile, DiskMmapFileMut};
cfg_async! {
macro_rules! impl_async_mmap_file_ext {
($name: ident) => {
impl AsyncMmapFileExt for $name {
fn len(&self) -> usize {
self.mmap.len()
}
fn as_slice(&self) -> &[u8] {
self.mmap.as_ref()
}
fn path(&self) -> &Path {
self.path.as_path()
}
#[inline]
async fn metadata(&self) -> crate::error::Result<MetaData> {
self.file
.metadata()
.await
.map(MetaData::disk)
}
#[inline]
fn is_exec(&self) -> bool {
self.exec
}
impl_file_lock!(::fs4::AsyncFileExt);
}
};
}
macro_rules! impl_async_mmap_file_ext_for_mut {
($name: ident) => {
impl AsyncMmapFileExt for $name {
fn len(&self) -> usize {
if self.poisoned { 0 } else { self.mmap.len() }
}
fn as_slice(&self) -> &[u8] {
if self.poisoned { &[] } else { self.mmap.as_ref() }
}
fn path(&self) -> &Path {
self.path.as_path()
}
#[inline]
async fn metadata(&self) -> crate::error::Result<MetaData> {
self.file
.metadata()
.await
.map(MetaData::disk)
}
#[inline]
fn is_exec(&self) -> bool {
false
}
impl_file_lock!(::fs4::AsyncFileExt);
}
};
}
macro_rules! declare_and_impl_async_fmmap_file {
($filename_prefix: literal, $doc_test_runtime: literal, $path_str: literal, $base_file: ty) => {
pub struct AsyncDiskMmapFile {
pub(crate) mmap: Mmap,
pub(crate) file: $base_file,
pub(crate) path: PathBuf,
exec: bool,
pub(crate) lock_state: u8,
}
impl_async_mmap_file_ext!(AsyncDiskMmapFile);
impl AsyncDiskMmapFile {
#[doc = "```ignore"]
#[doc = concat!("use fmmap::", $path_str, "::AsyncMmapFileExt;")]
#[doc = concat!("use fmmap::raw::", $path_str, "::AsyncDiskMmapFile;")]
#[doc = concat!("# use fmmap::", $path_str, "::{AsyncMmapFileMut, AsyncMmapFileMutExt};")]
#[doc = " # use scopeguard::defer;"]
#[doc = concat!("# ", $doc_test_runtime, "::block_on(async {")]
#[doc = concat!("# let mut file = AsyncMmapFileMut::create(\"", $filename_prefix, "_disk_open_test.txt\").await.unwrap();")]
#[doc = concat!(" # defer!(std::fs::remove_file(\"", $filename_prefix, "_disk_open_test.txt\").unwrap());")]
#[doc = concat!("# file.truncate(100).await.unwrap();")]
#[doc = concat!("# file.write_all(\"some data...\".as_bytes(), 0).unwrap();")]
#[doc = concat!("# file.flush().unwrap();")]
#[doc = "# drop(file);"]
#[doc = "// mmap the file"]
#[doc = concat!("let mut file = AsyncDiskMmapFile::open(\"", $filename_prefix, "_disk_open_test.txt\").await.unwrap();")]
#[doc = "let mut buf = vec![0; \"some data...\".len()];"]
#[doc = "file.read_exact(buf.as_mut_slice(), 0).unwrap();"]
#[doc = "assert_eq!(buf.as_slice(), \"some data...\".as_bytes());"]
#[doc = "# })"]
#[doc = "```"]
#[doc = ""]
#[doc = "# Safety"]
#[doc = ""]
#[doc = "See the [crate-level safety section](crate) for the full contract."]
pub async unsafe fn open<P: AsRef<Path>>(path: P,) -> Result<Self, Error> {
Self::open_in(path, None).await
}
#[doc = "```ignore"]
#[doc = concat!("use fmmap::", $path_str, "::{AsyncOptions, AsyncMmapFileExt};")]
#[doc = concat!("use fmmap::raw::", $path_str, "::AsyncDiskMmapFile;")]
#[doc = concat!("# use fmmap::", $path_str, "::{AsyncMmapFileMut, AsyncMmapFileMutExt};")]
#[doc = " # use scopeguard::defer;"]
#[doc = concat!("# ", $doc_test_runtime, "::block_on(async {")]
#[doc = concat!("# let mut file = AsyncMmapFileMut::create(\"", $filename_prefix, "_disk_open_with_options_test.txt\").await.unwrap();")]
#[doc = concat!(" # defer!(std::fs::remove_file(\"", $filename_prefix, "_disk_open_with_options_test.txt\").unwrap());")]
#[doc = concat!("# file.truncate(23).await.unwrap();")]
#[doc = concat!("# file.write_all(\"sanity text\".as_bytes(), 0).unwrap();")]
#[doc = concat!("# file.write_all(\"some data...\".as_bytes(), \"sanity text\".as_bytes().len()).unwrap();")]
#[doc = concat!("# file.flush().unwrap();")]
#[doc = "# drop(file);"]
#[doc = "// mmap the file"]
#[doc = "let opts = AsyncOptions::new()"]
#[doc = " // mmap content after the sanity text"]
#[doc = " .offset(\"sanity text\".as_bytes().len() as u64);"]
#[doc = "// mmap the file"]
#[doc = concat!("let mut file = AsyncDiskMmapFile::open_with_options(\"", $filename_prefix, "_disk_open_with_options_test.txt\", opts).await.unwrap();")]
#[doc = "let mut buf = vec![0; \"some data...\".len()];"]
#[doc = "file.read_exact(buf.as_mut_slice(), 0).unwrap();"]
#[doc = "assert_eq!(buf.as_slice(), \"some data...\".as_bytes());"]
#[doc = "# })"]
#[doc = "```"]
#[doc = ""]
#[doc = "# Safety"]
#[doc = ""]
#[doc = "See the [crate-level safety section](crate) for the full contract."]
pub async unsafe fn open_with_options<P: AsRef<Path>>(path: P, opts: AsyncOptions) -> Result<Self, Error> {
Self::open_in(path, Some(opts)).await
}
#[doc = "```ignore"]
#[doc = concat!("use fmmap::", $path_str, "::AsyncMmapFileExt;")]
#[doc = concat!("use fmmap::raw::", $path_str, "::AsyncDiskMmapFile;")]
#[doc = concat!("# use fmmap::", $path_str, "::{AsyncMmapFileMut, AsyncMmapFileMutExt};")]
#[doc = " # use scopeguard::defer;"]
#[doc = concat!("# ", $doc_test_runtime, "::block_on(async {")]
#[doc = concat!("# let mut file = AsyncMmapFileMut::create(\"", $filename_prefix, "_disk_open_exec_test.txt\").await.unwrap();")]
#[doc = concat!("# defer!(std::fs::remove_file(\"", $filename_prefix, "_disk_open_exec_test.txt\").unwrap());")]
#[doc = concat!("# file.truncate(100).await.unwrap();")]
#[doc = concat!("# file.write_all(\"some data...\".as_bytes(), 0).unwrap();")]
#[doc = concat!("# file.flush().unwrap();")]
#[doc = "# drop(file);"]
#[doc = "// mmap the file"]
#[doc = concat!("let mut file = AsyncDiskMmapFile::open_exec(\"", $filename_prefix, "_disk_open_exec_test.txt\").await.unwrap();")]
#[doc = "let mut buf = vec![0; \"some data...\".len()];"]
#[doc = "file.read_exact(buf.as_mut_slice(), 0).unwrap();"]
#[doc = "assert_eq!(buf.as_slice(), \"some data...\".as_bytes());"]
#[doc = "# })"]
#[doc = "```"]
#[doc = ""]
#[doc = "# Safety"]
#[doc = ""]
#[doc = "See the [crate-level safety section](crate) for the full contract."]
pub async unsafe fn open_exec<P: AsRef<Path>>(path: P,) -> Result<Self, Error> {
Self::open_exec_in(path, None).await
}
#[doc = "```ignore"]
#[doc = concat!("use fmmap::", $path_str, "::{AsyncOptions, AsyncMmapFileExt};")]
#[doc = concat!("use fmmap::raw::", $path_str, "::AsyncDiskMmapFile;")]
#[doc = concat!("# use fmmap::", $path_str, "::{AsyncMmapFileMut, AsyncMmapFileMutExt};")]
#[doc = " # use scopeguard::defer;"]
#[doc = concat!("# ", $doc_test_runtime, "::block_on(async {")]
#[doc = concat!("# let mut file = AsyncMmapFileMut::create(\"", $filename_prefix, "_disk_open_exec_with_options_test.txt\").await.unwrap();")]
#[doc = concat!(" # defer!(std::fs::remove_file(\"", $filename_prefix, "_disk_open_exec_with_options_test.txt\").unwrap());")]
#[doc = concat!("# file.truncate(23).await.unwrap();")]
#[doc = concat!("# file.write_all(\"sanity text\".as_bytes(), 0).unwrap();")]
#[doc = concat!("# file.write_all(\"some data...\".as_bytes(), \"sanity text\".as_bytes().len()).unwrap();")]
#[doc = concat!("# file.flush().unwrap();")]
#[doc = "# drop(file);"]
#[doc = "// mmap the file"]
#[doc = "let opts = AsyncOptions::new()"]
#[doc = " // mmap content after the sanity text"]
#[doc = " .offset(\"sanity text\".as_bytes().len() as u64);"]
#[doc = "// mmap the file"]
#[doc = concat!("let mut file = AsyncDiskMmapFile::open_exec_with_options(\"", $filename_prefix, "_disk_open_exec_with_options_test.txt\", opts).await.unwrap();")]
#[doc = "let mut buf = vec![0; \"some data...\".len()];"]
#[doc = "file.read_exact(buf.as_mut_slice(), 0).unwrap();"]
#[doc = "assert_eq!(buf.as_slice(), \"some data...\".as_bytes());"]
#[doc = "# })"]
#[doc = "```"]
#[doc = ""]
#[doc = "# Safety"]
#[doc = ""]
#[doc = "See the [crate-level safety section](crate) for the full contract."]
pub async unsafe fn open_exec_with_options<P: AsRef<Path>>(path: P, opts: AsyncOptions) -> Result<Self, Error> {
Self::open_exec_in(path, Some(opts)).await
}
async fn open_in<P: AsRef<Path>>(path: P, opts: Option<AsyncOptions>) -> Result<Self, Error> {
let path_ref = path.as_ref();
let file = open_read_only_file_async(path_ref).await?;
::fs4::AsyncFileExt::try_lock_shared(&file)
.map_err(Error::from)?;
if let Some(opts) = opts.as_ref() {
crate::disk::validate_mapping_range(file.metadata().await?.len(), opts.offset, opts.len)?;
}
let mmap = match &opts {
None => unsafe {
Mmap::map(&file)?
},
Some(opts) => unsafe {
opts.mmap_opts.map(&file)?
},
};
Ok(Self {
mmap,
file,
path: path_ref.to_path_buf(),
exec: false,
lock_state: crate::disk::LOCK_SHARED,
})
}
async fn open_exec_in<P: AsRef<Path>>(path: P, opts: Option<AsyncOptions>) -> Result<Self, Error> {
let path_ref = path.as_ref();
let file = open_read_only_file_async(path_ref).await?;
::fs4::AsyncFileExt::try_lock_shared(&file)
.map_err(Error::from)?;
if let Some(opts) = opts.as_ref() {
crate::disk::validate_mapping_range(file.metadata().await?.len(), opts.offset, opts.len)?;
}
let mmap = match &opts {
None => unsafe {
MmapOptions::new().map_exec(&file)?
},
Some(opts) => unsafe {
opts.mmap_opts.map_exec(&file)?
},
};
Ok(Self {
mmap,
file,
path: path_ref.to_path_buf(),
exec: true,
lock_state: crate::disk::LOCK_SHARED,
})
}
}
};
}
macro_rules! impl_async_mmap_file_mut_ext_for_mut {
($filename_prefix: literal, $doc_test_runtime: literal, $path_str: literal) => {
impl AsyncMmapFileMutExt for AsyncDiskMmapFileMut {
fn as_mut_slice(&mut self) -> &mut [u8] {
if self.poisoned {
&mut []
} else {
self.mmap.as_mut()
}
}
fn is_cow(&self) -> bool {
matches!(self.typ, MmapFileMutType::Cow)
}
impl_flush!();
async fn truncate(&mut self, max_sz: u64) -> Result<(), Error> {
if self.poisoned {
return Err(Self::poison_err());
}
if self.is_cow() {
return Err(Error::new(ErrorKind::Unsupported, "cannot truncate a copy-on-write mmap file"));
}
if self.offset > max_sz {
return Err(Error::new(
ErrorKind::InvalidInput,
"truncate would leave mapping offset past EOF",
));
}
let meta = self.file.metadata().await?;
if meta.len() > 0 {
self.flush()?;
}
let placeholder = MmapOptions::new()
.len(1)
.map_anon()?;
drop(::core::mem::replace(&mut self.mmap, placeholder));
let outcome: Result<(), Error> = async {
self.file.set_len(max_sz).await?;
self.file.sync_all().await?;
sync_parent_async(&self.path).await?;
let mut opts = self.opts.clone().unwrap_or_default();
if let Some(user_len) = self.len {
let cap = (max_sz - self.offset) as usize;
opts.len(user_len.min(cap));
}
self.opts = Some(opts.clone());
self.mmap = remmap(&self.file, Some(&opts), self.typ)?;
Ok(())
}.await;
if let Err(e) = outcome {
self.poisoned = true;
return Err(e);
}
Ok(())
}
#[doc = "```ignore"]
#[doc = concat!("use fmmap::", $path_str, "::AsyncMmapFileMutExt;")]
#[doc = concat!("use fmmap::raw::", $path_str, "::AsyncDiskMmapFileMut;")]
#[doc = ""]
#[doc = concat!("# ", $doc_test_runtime, "::block_on(async {")]
#[doc = concat!("let mut file = AsyncDiskMmapFileMut::create(\"", $filename_prefix, "_disk_remove_test.txt\").await.unwrap();")]
#[doc = ""]
#[doc = "file.truncate(100).await;"]
#[doc = "file.write_all(\"some data...\".as_bytes(), 0).unwrap();"]
#[doc = "file.flush().unwrap();"]
#[doc = ""]
#[doc = "file.drop_remove().await.unwrap();"]
#[doc = ""]
#[doc = concat!("let err = ", $path_str, "::fs::File::open(\"", $filename_prefix, "_disk_remove_test.txt\").await;")]
#[doc = "assert_eq!(err.unwrap_err().kind(), std::io::ErrorKind::NotFound);"]
#[doc = "# })"]
#[doc = "```"]
async fn drop_remove(self) -> crate::error::Result<()> {
self
.try_drop_remove()
.await
.map_err(crate::error::DropRemoveError::into_error)
}
#[doc = "```ignore"]
#[doc = "use fmmap::MetaDataExt;"]
#[doc = concat!("use fmmap::", $path_str, "::{AsyncMmapFileExt, AsyncMmapFileMutExt};")]
#[doc = concat!("use fmmap::raw::", $path_str, "::AsyncDiskMmapFileMut;")]
#[doc = "# use scopeguard::defer;"]
#[doc = ""]
#[doc = concat!("# ", $doc_test_runtime, "::block_on(async {")]
#[doc = concat!("let mut file = AsyncDiskMmapFileMut::create(\"", $filename_prefix, "_disk_close_with_truncate_test.txt\").await.unwrap();")]
#[doc = concat!("# defer!(std::fs::remove_file(\"", $filename_prefix, "_disk_close_with_truncate_test.txt\").unwrap());")]
#[doc = "file.truncate(100).await;"]
#[doc = "file.write_all(\"some data...\".as_bytes(), 0).unwrap();"]
#[doc = "file.flush().unwrap();"]
#[doc = "file.close_with_truncate(50).await.unwrap();"]
#[doc = ""]
#[doc = concat!("let file = AsyncDiskMmapFileMut::open(\"", $filename_prefix, "_disk_close_with_truncate_test.txt\").await.unwrap();")]
#[doc = "let meta = file.metadata().await.unwrap();"]
#[doc = "assert_eq!(meta.len(), 50);"]
#[doc = "# })"]
#[doc = "```"]
async fn close_with_truncate(self, max_sz: i64) -> crate::error::Result<()> {
if max_sz >= 0 && self.is_cow() {
return Err(Error::new(
ErrorKind::Unsupported,
"cannot truncate a copy-on-write mmap file",
));
}
let meta = self.file.metadata().await?;
if meta.len() > 0 {
self.flush()?;
}
let path = self.path;
drop(self.mmap);
if max_sz >= 0 {
self.file.set_len(max_sz as u64).await?;
self.file.sync_all().await?;
sync_parent_async(&path).await?;
}
Ok(())
}
}
};
}
macro_rules! declare_and_impl_async_fmmap_file_mut {
($filename_prefix: literal, $doc_test_runtime: literal, $path_str: literal, $base_file: ty, $immutable_file: ident) => {
pub struct AsyncDiskMmapFileMut {
pub(crate) mmap: MmapMut,
pub(crate) file: $base_file,
pub(crate) path: PathBuf,
opts: Option<MmapOptions>,
offset: u64,
len: Option<usize>,
typ: MmapFileMutType,
poisoned: bool,
pub(crate) lock_state: u8,
pub(crate) file_identity: crate::utils::FileIdentity,
}
impl AsyncDiskMmapFileMut {
#[inline]
pub fn is_poisoned(&self) -> bool {
self.poisoned
}
fn poison_err() -> Error {
Error::other("mmap file was poisoned by a previous failed truncate")
}
#[cfg(test)]
#[allow(dead_code)]
pub(crate) fn force_poison_for_test(&mut self) {
self.poisoned = true;
}
pub async fn try_drop_remove(
#[cfg_attr(not(unix), allow(unused_mut))] mut self,
) -> std::result::Result<(), crate::error::DropRemoveError<Self>> {
let parent_handle = match crate::utils::open_parent_for_sync(&self.path) {
Ok(h) => h,
Err(e) => return Err(crate::error::DropRemoveError::Recoverable(self, e)),
};
#[cfg(unix)]
let pin: std::fs::File = {
let async_file = self.file;
match extract_pin_or_err(async_file).await {
Ok(p) => p,
Err((file_back, e)) => {
self.file = file_back;
drop(parent_handle);
return Err(crate::error::DropRemoveError::Recoverable(self, e));
}
}
};
#[cfg(not(unix))]
drop(self.file);
let path = self.path;
let identity = self.file_identity;
drop(self.mmap);
#[cfg(unix)]
let inode_pin: Option<std::fs::File> = Some(pin);
#[cfg(not(unix))]
let inode_pin: Option<std::fs::File> = None;
run_blocking_io(move || -> crate::error::Result<()> {
let _inode_pin = inode_pin;
match crate::utils::identity_at_or_path(&parent_handle, &path) {
Err(e) if e.kind() == ErrorKind::NotFound => return Err(e),
Err(e) => return Err(e),
Ok(probe_id) => {
if !identity.is_known_equal(&probe_id) {
return Err(Error::other(format!(
"cannot unlink '{}': path no longer names the original file (path-reuse detected between handle drop and unlink)",
path.display(),
)));
}
}
}
crate::utils::unlink_at_or_path(&parent_handle, &path, identity)?;
crate::utils::sync_parent_handle(&parent_handle).map_err(|e| {
Error::new(
e.kind(),
format!(
"file '{}' unlinked but parent dir fsync failed: {e}; \
the unlink is committed but not yet crash-durable",
path.display(),
),
)
})
})
.await
.map_err(crate::error::DropRemoveError::Terminal)
}
pub(crate) async fn close_with_truncate_in_place(
&mut self,
max_sz: u64,
) -> Result<(), Error> {
if self.poisoned {
return Err(Self::poison_err());
}
self.flush()?;
let placeholder = MmapOptions::new().len(1).map_anon()?;
drop(std::mem::replace(&mut self.mmap, placeholder));
let result: Result<(), Error> = async {
self.file.set_len(max_sz).await?;
self.file.sync_all().await?;
sync_parent_async(&self.path).await
}
.await;
if result.is_err() {
self.poisoned = true;
}
result
}
}
impl_async_mmap_file_ext_for_mut!(AsyncDiskMmapFileMut);
impl_async_mmap_file_mut_ext_for_mut!($filename_prefix, $doc_test_runtime, $path_str);
impl AsyncDiskMmapFileMut {
#[doc = "```ignore"]
#[doc = concat!("use fmmap::", $path_str, "::AsyncMmapFileMutExt;")]
#[doc = concat!("use fmmap::raw::", $path_str, "::AsyncDiskMmapFileMut;")]
#[doc = " # use scopeguard::defer;"]
#[doc = ""]
#[doc = concat!("# ", $doc_test_runtime, "::block_on(async {")]
#[doc = concat!("let mut file = AsyncDiskMmapFileMut::create(\"", $filename_prefix, "_disk_create_test.txt\").await.unwrap();")]
#[doc = concat!("# defer!(std::fs::remove_file(\"", $filename_prefix, "_disk_create_test.txt\").unwrap());")]
#[doc = "file.truncate(100).await;"]
#[doc = "file.write_all(\"some data...\".as_bytes(), 0).unwrap();"]
#[doc = "file.flush().unwrap();"]
#[doc = "# })"]
#[doc = "```"]
#[doc = ""]
#[doc = concat!("[`create_with_options`]: raw/", $path_str, "/struct.AsyncDiskMmapFileMut.html#method.create_with_options")]
#[doc = concat!("[`AsyncOptions`]: ", $path_str, "/struct.AsyncOptions.html")]
#[doc = ""]
#[doc = "# Safety"]
#[doc = ""]
#[doc = "See the [crate-level safety section](crate) for the full contract."]
pub async unsafe fn create<P: AsRef<Path>>(path: P) -> Result<Self, Error> {
Self::create_in(path, None).await
}
#[doc = "```ignore"]
#[doc = concat!("use fmmap::", $path_str, "::{AsyncOptions, AsyncMmapFileMutExt};")]
#[doc = concat!("use fmmap::raw::", $path_str, "::AsyncDiskMmapFileMut;")]
#[doc = " # use scopeguard::defer;"]
#[doc = ""]
#[doc = concat!("# ", $doc_test_runtime, "::block_on(async {")]
#[doc = "let opts = AsyncOptions::new()"]
#[doc = " // truncate to 100"]
#[doc = " .max_size(100);"]
#[doc = concat!("let mut file = AsyncDiskMmapFileMut::create_with_options(\"", $filename_prefix, "_disk_create_with_options_test.txt\", opts).await.unwrap();")]
#[doc = concat!("# defer!(std::fs::remove_file(\"", $filename_prefix, "_disk_create_with_options_test.txt\").unwrap());")]
#[doc = "file.write_all(\"some data...\".as_bytes(), 0).unwrap();"]
#[doc = "file.flush().unwrap();"]
#[doc = "# })"]
#[doc = "```"]
#[doc = ""]
#[doc = concat!("[`AsyncOptions`]: ", $path_str, "/struct.AsyncOptions.html")]
#[doc = ""]
#[doc = "# Safety"]
#[doc = ""]
#[doc = "See the [crate-level safety section](crate) for the full contract."]
pub async unsafe fn create_with_options<P: AsRef<Path>>(path: P, opts: AsyncOptions) -> Result<Self, Error> {
Self::create_in(path, Some(opts)).await
}
#[doc = "```ignore"]
#[doc = concat!("use fmmap::", $path_str, "::{AsyncMmapFileExt, AsyncMmapFileMutExt};")]
#[doc = concat!("use fmmap::raw::", $path_str, "::AsyncDiskMmapFileMut;")]
#[doc = " # use scopeguard::defer;"]
#[doc = ""]
#[doc = concat!("# ", $doc_test_runtime, "::block_on(async {")]
#[doc = concat!("# let mut file = AsyncDiskMmapFileMut::create(\"", $filename_prefix, "_disk_open_test.txt\").await.unwrap();")]
#[doc = concat!("# defer!(std::fs::remove_file(\"", $filename_prefix, "_disk_open_test.txt\").unwrap());")]
#[doc = concat!("# file.truncate(100).await.unwrap();")]
#[doc = concat!("# file.write_all(\"some data...\".as_bytes(), 0).unwrap();")]
#[doc = concat!("# file.flush().unwrap();")]
#[doc = "# drop(file);"]
#[doc = ""]
#[doc = "// mmap the file"]
#[doc = concat!("let mut file = AsyncDiskMmapFileMut::open(\"", $filename_prefix, "_disk_open_test.txt\").await.unwrap();")]
#[doc = "let mut buf = vec![0; \"some data...\".len()];"]
#[doc = "file.read_exact(buf.as_mut_slice(), 0).unwrap();"]
#[doc = "assert_eq!(buf.as_slice(), \"some data...\".as_bytes());"]
#[doc = ""]
#[doc = "// modify the file data"]
#[doc = "file.truncate(\"some modified data...\".len() as u64).await.unwrap();"]
#[doc = "file.write_all(\"some modified data...\".as_bytes(), 0).unwrap();"]
#[doc = "file.flush().unwrap();"]
#[doc = "drop(file);"]
#[doc = ""]
#[doc = "// reopen to check content"]
#[doc = "let mut buf = vec![0; \"some modified data...\".len()];"]
#[doc = concat!("let file = AsyncDiskMmapFileMut::open(\"", $filename_prefix, "_disk_open_test.txt\").await.unwrap();")]
#[doc = "file.read_exact(buf.as_mut_slice(), 0).unwrap();"]
#[doc = "assert_eq!(buf.as_slice(), \"some modified data...\".as_bytes());"]
#[doc = "# })"]
#[doc = "```"]
#[doc = ""]
#[doc = "File does not exists"]
#[doc = ""]
#[doc = "```ignore"]
#[doc = concat!("use fmmap::", $path_str, "::{AsyncMmapFileExt, AsyncMmapFileMutExt};")]
#[doc = concat!("use fmmap::raw::", $path_str, "::AsyncDiskMmapFileMut;")]
#[doc = " # use scopeguard::defer;"]
#[doc = ""]
#[doc = concat!("# ", $doc_test_runtime, "::block_on(async {")]
#[doc = "// mmap the file"]
#[doc = concat!("let mut file = AsyncDiskMmapFileMut::open(\"", $filename_prefix, "_disk_open_test.txt\").await.unwrap();")]
#[doc = concat!("# defer!(std::fs::remove_file(\"", $filename_prefix, "_disk_open_test.txt\").unwrap());")]
#[doc = "file.truncate(100).await.unwrap();"]
#[doc = "file.write_all(\"some data...\".as_bytes(), 0).unwrap();"]
#[doc = ""]
#[doc = "let mut buf = vec![0; \"some data...\".len()];"]
#[doc = "file.read_exact(buf.as_mut_slice(), 0).unwrap();"]
#[doc = "assert_eq!(buf.as_slice(), \"some data...\".as_bytes());"]
#[doc = ""]
#[doc = "// modify the file data"]
#[doc = "file.truncate(\"some modified data...\".len() as u64).await.unwrap();"]
#[doc = "file.write_all(\"some modified data...\".as_bytes(), 0).unwrap();"]
#[doc = "file.flush().unwrap();"]
#[doc = "drop(file);"]
#[doc = ""]
#[doc = "// reopen to check content"]
#[doc = "let mut buf = vec![0; \"some modified data...\".len()];"]
#[doc = concat!("let file = AsyncDiskMmapFileMut::open(\"", $filename_prefix, "_disk_open_test.txt\").await.unwrap();")]
#[doc = "file.read_exact(buf.as_mut_slice(), 0).unwrap();"]
#[doc = "assert_eq!(buf.as_slice(), \"some modified data...\".as_bytes());"]
#[doc = "# })"]
#[doc = "```"]
#[doc = ""]
#[doc = concat!("[`open_with_options`]: raw/", $path_str, "/struct.AsyncDiskMmapFileMut.html#method.open_with_options")]
#[doc = concat!("[`AsyncOptions`]: ", $path_str, "/struct.AsyncOptions.html")]
#[doc = ""]
#[doc = "# Safety"]
#[doc = ""]
#[doc = "See the [crate-level safety section](crate) for the full contract."]
pub async unsafe fn open<P: AsRef<Path>>(path: P) -> Result<Self, Error> {
Self::open_in(path, None).await
}
#[doc = "```ignore"]
#[doc = concat!("use fmmap::", $path_str, "::{AsyncMmapFileMut, AsyncMmapFileExt, AsyncMmapFileMutExt, AsyncOptions};")]
#[doc = "# use scopeguard::defer;"]
#[doc = ""]
#[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());")]
#[doc = "# file.truncate(23).await.unwrap();"]
#[doc = "# file.write_all(\"sanity text\".as_bytes(), 0).unwrap();"]
#[doc = "# file.write_all(\"some data...\".as_bytes(), \"sanity text\".as_bytes().len()).unwrap();"]
#[doc = "# file.flush().unwrap();"]
#[doc = "# drop(file);"]
#[doc = ""]
#[doc = "let opts = AsyncOptions::new()"]
#[doc = " // allow read"]
#[doc = " .read(true)"]
#[doc = " // allow write"]
#[doc = " .write(true)"]
#[doc = " // allow append"]
#[doc = " .append(true)"]
#[doc = " // truncate to 100"]
#[doc = " .max_size(100)"]
#[doc = " // mmap content after the sanity text"]
#[doc = " .offset(\"sanity text\".as_bytes().len() as u64);"]
#[doc = concat!("let mut file = AsyncMmapFileMut::open_with_options(\"", $filename_prefix, "_open_with_options_test.txt\", opts).await.unwrap();")]
#[doc = "let mut buf = vec![0; \"some data...\".len()];"]
#[doc = "file.read_exact(buf.as_mut_slice(), 0).unwrap();"]
#[doc = "assert_eq!(buf.as_slice(), \"some data...\".as_bytes());"]
#[doc = ""]
#[doc = "// modify the file data"]
#[doc = "file.truncate((\"some modified data...\".len() + \"sanity text\".len()) as u64).await.unwrap();"]
#[doc = "file.write_all(\"some modified data...\".as_bytes(), 0).unwrap();"]
#[doc = "file.flush().unwrap();"]
#[doc = "drop(file);"]
#[doc = ""]
#[doc = "// reopen to check content"]
#[doc = "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();")]
#[doc = "// skip the sanity text"]
#[doc = "file.read_exact(buf.as_mut_slice(), \"sanity text\".as_bytes().len()).unwrap();"]
#[doc = "assert_eq!(buf.as_slice(), \"some modified data...\".as_bytes());"]
#[doc = "# })"]
#[doc = "```"]
#[doc = ""]
#[doc = "File does not exists"]
#[doc = ""]
#[doc = "```ignore"]
#[doc = concat!("use fmmap::", $path_str, "::{AsyncMmapFileMut, AsyncMmapFileExt, AsyncMmapFileMutExt, AsyncOptions};")]
#[doc = "# use scopeguard::defer;"]
#[doc = ""]
#[doc = concat!("# ", $doc_test_runtime, "::block_on(async {")]
#[doc = "// mmap the file with options"]
#[doc = "let opts = AsyncOptions::new()"]
#[doc = " // allow read"]
#[doc = " .read(true)"]
#[doc = " // allow write"]
#[doc = " .write(true)"]
#[doc = " // allow append"]
#[doc = " .append(true)"]
#[doc = " // truncate to 100"]
#[doc = " .max_size(100);"]
#[doc = concat!("let mut file = AsyncMmapFileMut::open_with_options(\"", $filename_prefix, "_open_with_options_test.txt\", opts).await.unwrap();")]
#[doc = concat!("# defer!(std::fs::remove_file(\"", $filename_prefix, "_open_with_options_test.txt\").unwrap());")]
#[doc = "file.write_all(\"some data...\".as_bytes(), 0).unwrap();"]
#[doc = ""]
#[doc = "let mut buf = vec![0; \"some data...\".len()];"]
#[doc = "file.read_exact(buf.as_mut_slice(), 0).unwrap();"]
#[doc = "assert_eq!(buf.as_slice(), \"some data...\".as_bytes());"]
#[doc = ""]
#[doc = "// modify the file data"]
#[doc = "file.truncate(\"some modified data...\".len() as u64).await.unwrap();"]
#[doc = "file.write_all(\"some modified data...\".as_bytes(), 0).unwrap();"]
#[doc = "file.flush().unwrap();"]
#[doc = "drop(file);"]
#[doc = ""]
#[doc = "// reopen to check content"]
#[doc = "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();")]
#[doc = "file.read_exact(buf.as_mut_slice(), 0).unwrap();"]
#[doc = "assert_eq!(buf.as_slice(), \"some modified data...\".as_bytes());"]
#[doc = "# })"]
#[doc = "```"]
#[doc = ""]
#[doc = concat!("[`AsyncOptions`]: ", $path_str, "/struct.AsyncOptions.html")]
#[doc = ""]
#[doc = "# Safety"]
#[doc = ""]
#[doc = "See the [crate-level safety section](crate) for the full contract."]
pub async unsafe fn open_with_options<P: AsRef<Path>>(path: P, opts: AsyncOptions) -> Result<Self, Error> {
Self::open_in(path, Some(opts)).await
}
#[doc = "```ignore"]
#[doc = concat!("use fmmap::", $path_str, "::{AsyncMmapFileExt, AsyncMmapFileMutExt};")]
#[doc = concat!("use fmmap::raw::", $path_str, "::AsyncDiskMmapFileMut;")]
#[doc = " # use scopeguard::defer;"]
#[doc = ""]
#[doc = concat!("# ", $doc_test_runtime, "::block_on(async {")]
#[doc = "// create a temp file"]
#[doc = concat!("let mut file = AsyncDiskMmapFileMut::create(\"", $filename_prefix, "_disk_open_existing_test.txt\").await.unwrap();")]
#[doc = "file.truncate(100).await.unwrap();"]
#[doc = "file.write_all(\"some data...\".as_bytes(), 0).unwrap();"]
#[doc = "file.flush().unwrap();"]
#[doc = concat!("# defer!(std::fs::remove_file(\"", $filename_prefix, "_disk_open_existing_test.txt\").unwrap());")]
#[doc = "drop(file);"]
#[doc = ""]
#[doc = "// mmap the file"]
#[doc = concat!("let mut file = AsyncDiskMmapFileMut::open_exist(\"", $filename_prefix, "_disk_open_existing_test.txt\").await.unwrap();")]
#[doc = "let mut buf = vec![0; \"some data...\".len()];"]
#[doc = "file.read_exact(buf.as_mut_slice(), 0).unwrap();"]
#[doc = "assert_eq!(buf.as_slice(), \"some data...\".as_bytes());"]
#[doc = ""]
#[doc = "// modify the file data"]
#[doc = "file.truncate(\"some modified data...\".len() as u64).await.unwrap();"]
#[doc = "file.write_all(\"some modified data...\".as_bytes(), 0).unwrap();"]
#[doc = "file.flush().unwrap();"]
#[doc = "drop(file);"]
#[doc = ""]
#[doc = ""]
#[doc = "// reopen to check content"]
#[doc = "let mut buf = vec![0; \"some modified data...\".len()];"]
#[doc = concat!("let mut file = AsyncDiskMmapFileMut::open_exist(\"", $filename_prefix, "_disk_open_existing_test.txt\").await.unwrap();")]
#[doc = "file.read_exact(buf.as_mut_slice(), 0).unwrap();"]
#[doc = "assert_eq!(buf.as_slice(), \"some modified data...\".as_bytes());"]
#[doc = "# })"]
#[doc = "```"]
#[doc = ""]
#[doc = concat!("[`AsyncOptions`]: ", $path_str, "/struct.AsyncOptions.html")]
#[doc = ""]
#[doc = "# Safety"]
#[doc = ""]
#[doc = "See the [crate-level safety section](crate) for the full contract."]
pub async unsafe fn open_exist<P: AsRef<Path>>(path: P) -> Result<Self, Error> {
Self::open_exist_in(path, None).await
}
#[doc = "```ignore"]
#[doc = concat!("use fmmap::", $path_str, "::{AsyncMmapFileExt, AsyncMmapFileMutExt, AsyncOptions};")]
#[doc = concat!("use fmmap::raw::", $path_str, "::AsyncDiskMmapFileMut;")]
#[doc = " # use scopeguard::defer;"]
#[doc = ""]
#[doc = concat!("# ", $doc_test_runtime, "::block_on(async {")]
#[doc = "// create a temp file"]
#[doc = concat!("let mut file = AsyncDiskMmapFileMut::create(\"", $filename_prefix, "_disk_open_existing_test_with_options.txt\").await.unwrap();")]
#[doc = concat!("# defer!(std::fs::remove_file(\"", $filename_prefix, "_disk_open_existing_test_with_options.txt\").unwrap());")]
#[doc = "file.truncate(23).await.unwrap();"]
#[doc = "file.write_all(\"sanity text\".as_bytes(), 0).unwrap();"]
#[doc = "file.write_all(\"some data...\".as_bytes(), \"sanity text\".as_bytes().len()).unwrap();"]
#[doc = "file.flush().unwrap();"]
#[doc = "drop(file);"]
#[doc = ""]
#[doc = "// mmap the file"]
#[doc = "let opts = AsyncOptions::new()"]
#[doc = " // truncate to 100"]
#[doc = " .max_size(100)"]
#[doc = " // mmap content after the sanity text"]
#[doc = " .offset(\"sanity text\".as_bytes().len() as u64);"]
#[doc = ""]
#[doc = concat!("let mut file = AsyncDiskMmapFileMut::open_exist_with_options(\"", $filename_prefix, "_disk_open_existing_test_with_options.txt\", opts).await.unwrap();")]
#[doc = "let mut buf = vec![0; \"some data...\".len()];"]
#[doc = "file.read_exact(buf.as_mut_slice(), 0).unwrap();"]
#[doc = "assert_eq!(buf.as_slice(), \"some data...\".as_bytes());"]
#[doc = ""]
#[doc = "// modify the file data"]
#[doc = "file.truncate((\"some modified data...\".len() + \"sanity text\".len()) as u64).await.unwrap();"]
#[doc = "file.write_all(\"some modified data...\".as_bytes(), 0).unwrap();"]
#[doc = "file.flush().unwrap();"]
#[doc = ""]
#[doc = ""]
#[doc = "// reopen to check content, cow will not change the content."]
#[doc = concat!("let mut file = AsyncDiskMmapFileMut::open(\"", $filename_prefix, "_disk_open_existing_test_with_options.txt\").await.unwrap();")]
#[doc = "let mut buf = vec![0; \"some modified data...\".len()];"]
#[doc = "// skip the sanity text"]
#[doc = "file.read_exact(buf.as_mut_slice(), \"sanity text\".as_bytes().len()).unwrap();"]
#[doc = "assert_eq!(buf.as_slice(), \"some modified data...\".as_bytes());"]
#[doc = "# })"]
#[doc = "```"]
#[doc = ""]
#[doc = concat!("[`AsyncOptions`]: ", $path_str, "/struct.AsyncOptions.html")]
#[doc = ""]
#[doc = "# Safety"]
#[doc = ""]
#[doc = "See the [crate-level safety section](crate) for the full contract."]
pub async unsafe fn open_exist_with_options<P: AsRef<Path>>(path: P, opts: AsyncOptions) -> Result<Self, Error> {
Self::open_exist_in(path, Some(opts)).await
}
#[doc = "```ignore"]
#[doc = concat!("use fmmap::", $path_str, "::{AsyncMmapFileExt, AsyncMmapFileMutExt};")]
#[doc = concat!("use fmmap::raw::", $path_str, "::AsyncDiskMmapFileMut;")]
#[doc = "# use scopeguard::defer;"]
#[doc = ""]
#[doc = concat!("# ", $doc_test_runtime, "::block_on(async {")]
#[doc = "// create a temp file"]
#[doc = concat!("let mut file = AsyncDiskMmapFileMut::create(\"", $filename_prefix, "_disk_open_cow_test.txt\").await.unwrap();")]
#[doc = concat!("# defer!(std::fs::remove_file(\"", $filename_prefix, "_disk_open_cow_test.txt\").unwrap());")]
#[doc = "file.truncate(12).await.unwrap();"]
#[doc = "file.write_all(\"some data...\".as_bytes(), 0).unwrap();"]
#[doc = "file.flush().unwrap();"]
#[doc = "drop(file);"]
#[doc = ""]
#[doc = "// mmap the file"]
#[doc = concat!("let mut file = AsyncDiskMmapFileMut::open_cow(\"", $filename_prefix, "_disk_open_cow_test.txt\").await.unwrap();")]
#[doc = "let mut buf = vec![0; \"some data...\".len()];"]
#[doc = "file.read_exact(buf.as_mut_slice(), 0).unwrap();"]
#[doc = "assert_eq!(buf.as_slice(), \"some data...\".as_bytes());"]
#[doc = ""]
#[doc = "// modify the file data"]
#[doc = "file.write_all(\"some data!!!\".as_bytes(), 0).unwrap();"]
#[doc = "file.flush().unwrap();"]
#[doc = ""]
#[doc = "// cow, change will only be seen in current caller"]
#[doc = "assert_eq!(file.as_slice(), \"some data!!!\".as_bytes());"]
#[doc = "drop(file);"]
#[doc = ""]
#[doc = "// reopen to check content, cow will not change the content."]
#[doc = concat!("let mut file = AsyncDiskMmapFileMut::open(\"", $filename_prefix, "_disk_open_cow_test.txt\").await.unwrap();")]
#[doc = "let mut buf = vec![0; \"some data...\".len()];"]
#[doc = "file.read_exact(buf.as_mut_slice(), 0).unwrap();"]
#[doc = "assert_eq!(buf.as_slice(), \"some data...\".as_bytes());"]
#[doc = "# })"]
#[doc = "```"]
#[doc = ""]
#[doc = concat!("[`AsyncOptions`]: ", $path_str, "/struct.AsyncOptions.html")]
#[doc = ""]
#[doc = "# Safety"]
#[doc = ""]
#[doc = "See the [crate-level safety section](crate) for the full contract."]
pub async unsafe fn open_cow<P: AsRef<Path>>(path: P) -> Result<Self, Error> {
Self::open_cow_in(path, None).await
}
#[doc = "```ignore"]
#[doc = concat!("use fmmap::", $path_str, "::{AsyncMmapFileExt, AsyncMmapFileMutExt, AsyncOptions};")]
#[doc = concat!("use fmmap::raw::", $path_str, "::AsyncDiskMmapFileMut;")]
#[doc = concat!("use ", $path_str, "::fs::File;")]
#[doc = "# use scopeguard::defer;"]
#[doc = ""]
#[doc = concat!("# ", $doc_test_runtime, "::block_on(async {")]
#[doc = "// create a temp file"]
#[doc = concat!("let mut file = AsyncDiskMmapFileMut::create(\"", $filename_prefix, "_disk_open_cow_with_options_test.txt\").await.unwrap();")]
#[doc = concat!("# defer!(std::fs::remove_file(\"", $filename_prefix, "_disk_open_cow_with_options_test.txt\").unwrap());")]
#[doc = "file.truncate(23).await.unwrap();"]
#[doc = "file.write_all(\"sanity text\".as_bytes(), 0).unwrap();"]
#[doc = "file.write_all(\"some data...\".as_bytes(), \"sanity text\".as_bytes().len()).unwrap();"]
#[doc = "file.flush().unwrap();"]
#[doc = "drop(file);"]
#[doc = ""]
#[doc = "// mmap the file"]
#[doc = "let opts = AsyncOptions::new()"]
#[doc = " // mmap content after the sanity text"]
#[doc = " .offset(\"sanity text\".as_bytes().len() as u64);"]
#[doc = ""]
#[doc = concat!("let mut file = AsyncDiskMmapFileMut::open_cow_with_options(\"", $filename_prefix, "_disk_open_cow_with_options_test.txt\", opts).await.unwrap();")]
#[doc = "let mut buf = vec![0; \"some data...\".len()];"]
#[doc = "file.read_exact(buf.as_mut_slice(), 0).unwrap();"]
#[doc = "assert_eq!(buf.as_slice(), \"some data...\".as_bytes());"]
#[doc = ""]
#[doc = "// modify the file data"]
#[doc = "file.write_all(\"some data!!!\".as_bytes(), 0).unwrap();"]
#[doc = "file.flush().unwrap();"]
#[doc = ""]
#[doc = "// cow, change will only be seen in current caller"]
#[doc = "assert_eq!(file.as_slice(), \"some data!!!\".as_bytes());"]
#[doc = "drop(file);"]
#[doc = ""]
#[doc = "// reopen to check content, cow will not change the content."]
#[doc = concat!("let mut file = AsyncDiskMmapFileMut::open(\"", $filename_prefix, "_disk_open_cow_with_options_test.txt\").await.unwrap();")]
#[doc = "let mut buf = vec![0; \"some data...\".len()];"]
#[doc = "// skip the sanity text"]
#[doc = "file.read_exact(buf.as_mut_slice(), \"sanity text\".as_bytes().len()).unwrap();"]
#[doc = "assert_eq!(buf.as_slice(), \"some data...\".as_bytes());"]
#[doc = "# })"]
#[doc = "```"]
#[doc = ""]
#[doc = concat!("[`AsyncOptions`]: ", $path_str, "/struct.AsyncOptions.html")]
#[doc = ""]
#[doc = "# Safety"]
#[doc = ""]
#[doc = "See the [crate-level safety section](crate) for the full contract."]
pub async unsafe fn open_cow_with_options<P: AsRef<Path>>(path: P, opts: AsyncOptions) -> Result<Self, Error> {
Self::open_cow_in(path, Some(opts)).await
}
#[doc = "```ignore"]
#[doc = concat!("use fmmap::", $path_str, "::AsyncMmapFileMutExt;")]
#[doc = concat!("use fmmap::raw::", $path_str, "::AsyncDiskMmapFileMut;")]
#[doc = "# use scopeguard::defer;"]
#[doc = ""]
#[doc = concat!("# ", $doc_test_runtime, "::block_on(async {")]
#[doc = concat!("let mut file = AsyncDiskMmapFileMut::create(\"", $filename_prefix, "_disk_freeze_test.txt\").await.unwrap();")]
#[doc = concat!("# defer!(std::fs::remove_file(\"", $filename_prefix, "_disk_freeze_test.txt\").unwrap());")]
#[doc = "file.truncate(100).await;"]
#[doc = "file.write_all(\"some data...\".as_bytes(), 0).unwrap();"]
#[doc = "file.flush().unwrap();"]
#[doc = "// freeze"]
#[doc = "file.freeze().unwrap();"]
#[doc = "# })"]
#[doc = "```"]
pub fn freeze(self) -> Result<$immutable_file, Error> {
if self.poisoned {
return Err(Self::poison_err());
}
Ok($immutable_file {
mmap: self.mmap.make_read_only()?,
file: self.file,
path: self.path,
exec: false,
lock_state: self.lock_state,
})
}
#[doc = "```ignore"]
#[doc = concat!("use fmmap::", $path_str, "::AsyncMmapFileMutExt;")]
#[doc = concat!("use fmmap::raw::", $path_str, "::AsyncDiskMmapFileMut;")]
#[doc = "# use scopeguard::defer;"]
#[doc = ""]
#[doc = concat!("# ", $doc_test_runtime, "::block_on(async {")]
#[doc = concat!("let mut file = AsyncDiskMmapFileMut::create(\"", $filename_prefix, "_disk_freeze_exec_test.txt\").await.unwrap();")]
#[doc = concat!("# defer!(std::fs::remove_file(\"", $filename_prefix, "_disk_freeze_exec_test.txt\").unwrap());")]
#[doc = "file.truncate(100).await;"]
#[doc = "file.write_all(\"some data...\".as_bytes(), 0).unwrap();"]
#[doc = "file.flush().unwrap();"]
#[doc = "// freeze_exec"]
#[doc = "file.freeze_exec().unwrap();"]
#[doc = "# })"]
#[doc = "```"]
pub fn freeze_exec(self) -> Result<$immutable_file, Error> {
if self.poisoned {
return Err(Self::poison_err());
}
Ok($immutable_file {
mmap: self.mmap.make_exec()?,
file: self.file,
path: self.path,
exec: true,
lock_state: self.lock_state,
})
}
}
};
}
macro_rules! impl_async_fmmap_file_mut_private {
($name: ident) => {
impl $name {
async fn create_in<P: AsRef<Path>>(path: P, opts: Option<AsyncOptions>) -> Result<Self, Error> {
let path_ref = path.as_ref();
let (file, opts_bk, max_size, offset, len) = match opts {
None => (create_file_async(path_ref).await?, None, 0u64, 0u64, None),
Some(mut opts) => {
let max_size = opts.max_size;
let offset = opts.offset;
let len = opts.len;
let file = opts
.file_opts
.read(true)
.write(true)
.append(false)
.create_new(true)
.open(path_ref)
.await?;
(file, Some(opts.mmap_opts), max_size, offset, len)
}
};
::fs4::AsyncFileExt::try_lock(&file)
.map_err(Error::from)?;
crate::disk::validate_mapping_range(max_size, offset, len)?;
if max_size > 0 {
file.set_len(max_size).await?;
file.sync_all().await?;
sync_parent_async(&path).await?;
}
let file_identity = {
#[cfg(unix)]
{
use std::os::fd::AsRawFd;
unsafe { crate::utils::FileIdentity::from_raw_fd(file.as_raw_fd()) }?
}
#[cfg(windows)]
{
use std::os::windows::io::AsRawHandle;
unsafe { crate::utils::FileIdentity::from_raw_handle(file.as_raw_handle()) }?
}
#[cfg(not(any(unix, windows)))]
crate::utils::FileIdentity::from_path(path_ref)?
};
let mmap = unsafe {
match &opts_bk {
None => MmapMut::map_mut(&file)?,
Some(o) => o.clone().map_mut(&file)?,
}
};
Ok(Self {
mmap,
file,
path: path_ref.to_path_buf(),
opts: opts_bk,
offset,
len,
typ: MmapFileMutType::Normal,
poisoned: false,
lock_state: crate::disk::LOCK_EXCLUSIVE,
file_identity,
})
}
async fn open_in<P: AsRef<Path>>(path: P, opts: Option<AsyncOptions>) -> Result<Self, Error> {
let path_ref = path.as_ref();
let (file, opts_bk, truncate, max_size, offset, len) = match opts {
None => (open_or_create_file_async(path_ref).await?, None, false, 0, 0u64, None),
Some(mut opts) => {
let truncate = opts.truncate_flag;
let max_size = opts.max_size;
let offset = opts.offset;
let len = opts.len;
let file = opts
.file_opts
.read(true)
.write(true)
.append(false)
.create(true)
.open(path_ref)
.await?;
(file, Some(opts.mmap_opts), truncate, max_size, offset, len)
}
};
::fs4::AsyncFileExt::try_lock(&file)
.map_err(Error::from)?;
let current_len = file.metadata().await?.len();
let post_truncate_len = if truncate { 0 } else { current_len };
let planned_len = if post_truncate_len == 0 && max_size > 0 {
max_size
} else {
post_truncate_len
};
crate::disk::validate_mapping_range(planned_len, offset, len)?;
if truncate {
file.set_len(0).await?;
file.sync_all().await?;
sync_parent_async(&path).await?;
}
let meta = file.metadata().await?;
if meta.len() == 0 && max_size > 0 {
file.set_len(max_size).await?;
file.sync_all().await?;
sync_parent_async(&path).await?;
}
let file_identity = {
#[cfg(unix)]
{
use std::os::fd::AsRawFd;
unsafe { crate::utils::FileIdentity::from_raw_fd(file.as_raw_fd()) }?
}
#[cfg(windows)]
{
use std::os::windows::io::AsRawHandle;
unsafe { crate::utils::FileIdentity::from_raw_handle(file.as_raw_handle()) }?
}
#[cfg(not(any(unix, windows)))]
crate::utils::FileIdentity::from_path(path_ref)?
};
let mmap = match &opts_bk {
None => unsafe {
MmapMut::map_mut(&file)?
},
Some(o) => unsafe {
o.clone().map_mut(&file)?
},
};
Ok(Self {
mmap,
file,
path: path_ref.to_path_buf(),
opts: opts_bk,
offset,
len,
typ: MmapFileMutType::Normal,
poisoned: false,
lock_state: crate::disk::LOCK_EXCLUSIVE,
file_identity,
})
}
async fn open_exist_in<P: AsRef<Path>>(path: P, opts: Option<AsyncOptions>) -> Result<Self, Error> {
let path_ref = path.as_ref();
let file = open_exist_file_async(path_ref).await?;
::fs4::AsyncFileExt::try_lock(&file)
.map_err(Error::from)?;
let (mmap, opts_bk, offset, len) = match opts {
None => {
let mmap = unsafe {
MmapMut::map_mut(&file)?
};
(mmap, None, 0u64, None)
}
Some(opts) => {
let opts_bk = opts.mmap_opts.clone();
let offset = opts.offset;
let len = opts.len;
let current_len = file.metadata().await?.len();
let planned_len = if current_len == 0 && opts.max_size > 0 {
opts.max_size
} else {
current_len
};
crate::disk::validate_mapping_range(planned_len, offset, len)?;
if current_len == 0 && opts.max_size > 0 {
file.set_len(opts.max_size).await?;
file.sync_all().await?;
sync_parent_async(&path).await?;
}
let mmap = unsafe {
opts.mmap_opts.map_mut(&file)?
};
(mmap, Some(opts_bk), offset, len)
}
};
let file_identity = {
#[cfg(unix)]
{
use std::os::fd::AsRawFd;
unsafe { crate::utils::FileIdentity::from_raw_fd(file.as_raw_fd()) }?
}
#[cfg(windows)]
{
use std::os::windows::io::AsRawHandle;
unsafe { crate::utils::FileIdentity::from_raw_handle(file.as_raw_handle()) }?
}
#[cfg(not(any(unix, windows)))]
crate::utils::FileIdentity::from_path(path_ref)?
};
Ok(Self {
mmap,
file,
path: path_ref.to_path_buf(),
opts: opts_bk,
offset,
len,
typ: MmapFileMutType::Normal,
poisoned: false,
lock_state: crate::disk::LOCK_EXCLUSIVE,
file_identity,
})
}
async fn open_cow_in<P: AsRef<Path>>(path: P, opts: Option<AsyncOptions>) -> Result<Self, Error> {
let path_ref = path.as_ref();
let file = open_exist_file_async(path_ref).await?;
::fs4::AsyncFileExt::try_lock_shared(&file)
.map_err(Error::from)?;
let (mmap, opts_bk, offset, len) = match opts {
None => {
let mmap = unsafe {
MmapOptions::new().map_copy(&file)?
};
(mmap, None, 0u64, None)
}
Some(opts) => {
let opts_bk = opts.mmap_opts.clone();
let offset = opts.offset;
let len = opts.len;
crate::disk::validate_mapping_range(file.metadata().await?.len(), offset, len)?;
let mmap = unsafe {
opts.mmap_opts.map_copy(&file)?
};
(mmap, Some(opts_bk), offset, len)
}
};
let file_identity = {
#[cfg(unix)]
{
use std::os::fd::AsRawFd;
unsafe { crate::utils::FileIdentity::from_raw_fd(file.as_raw_fd()) }?
}
#[cfg(windows)]
{
use std::os::windows::io::AsRawHandle;
unsafe { crate::utils::FileIdentity::from_raw_handle(file.as_raw_handle()) }?
}
#[cfg(not(any(unix, windows)))]
crate::utils::FileIdentity::from_path(path_ref)?
};
Ok(Self {
mmap,
file,
path: path_ref.to_path_buf(),
opts: opts_bk,
offset,
len,
typ: MmapFileMutType::Cow,
poisoned: false,
lock_state: crate::disk::LOCK_SHARED,
file_identity,
})
}
}
};
}
}
#[cfg(feature = "tokio")]
#[cfg_attr(docsrs, doc(cfg(feature = "tokio")))]
pub(crate) mod tokio_impl;
#[cfg(feature = "smol")]
#[cfg_attr(docsrs, doc(cfg(feature = "smol")))]
pub(crate) mod smol_impl;