macro_rules! read_impl {
($this:ident, $offset: tt, $typ:tt::$conv:tt) => {{
const SIZE: usize = mem::size_of::<$typ>();
// try to convert directly from the bytes
// this Option<ret> trick is to avoid keeping a borrow on self
// when advance() is called (mut borrow) and to call bytes() only once
let mut buf = [0; SIZE];
$this
.read_exact(&mut buf, $offset)
.map(|_| unsafe { $typ::$conv(*(&buf as *const _ as *const [_; SIZE])) })
}};
}
macro_rules! impl_from {
($outer: ident, $enum_inner: ident, [$($inner: ident), +$(,)?]) => {
$(
impl From<$inner> for $outer {
fn from(file: $inner) -> Self {
$outer{ inner: <$enum_inner>::from(file) }
}
}
)*
};
}
macro_rules! impl_from_mut {
($outer: ident, $enum_inner: ident, [$($inner: ident), +$(,)?]) => {
$(
impl From<$inner> for $outer {
fn from(file: $inner) -> Self {
$outer{
inner: <$enum_inner>::from(file),
remove_on_drop: false,
deleted: false,
}
}
}
)*
};
}
macro_rules! impl_drop {
($name: ident, $inner: ident, $empty: ident) => {
impl Drop for $name {
fn drop(&mut self) {
if self.remove_on_drop && !self.deleted {
let empty = <$inner>::Empty(<$empty>::default());
// swap the inner to empty
let inner = mem::replace(&mut self.inner, empty);
// do remove and ignore the result
let path = inner.path_buf();
drop(inner);
let _ = std::fs::remove_file(path);
}
}
}
};
}
macro_rules! impl_flush {
() => {
fn flush(&self) -> Result<()> {
self.inner.flush()
}
fn flush_async(&self) -> Result<()> {
self.inner.flush_async()
}
fn flush_range(&self, offset: usize, len: usize) -> Result<()> {
self.inner.flush_range(offset, len)
}
fn flush_async_range(&self, offset: usize, len: usize) -> Result<()> {
self.inner.flush_async_range(offset, len)
}
};
}
macro_rules! impl_file_lock {
() => {
#[inline]
fn lock_exclusive(&self) -> crate::error::Result<()> {
self.inner.lock_exclusive()
}
#[inline]
fn lock_shared(&self) -> crate::error::Result<()> {
self.inner.lock_shared()
}
#[inline]
fn try_lock_exclusive(&self) -> crate::error::Result<()> {
self.inner.try_lock_exclusive()
}
#[inline]
fn try_lock_shared(&self) -> crate::error::Result<()> {
self.inner.try_lock_shared()
}
#[inline]
fn unlock(&self) -> crate::error::Result<()> {
self.inner.unlock()
}
};
}
macro_rules! impl_constructor_for_memory_mmap_file {
($memory_base: ident, $name: ident, $name_str: literal, $path_str: literal) => {
use bytes::Bytes;
impl $name {
#[doc = concat!("Create a in-memory ", $name_str)]
#[doc = "# Examples"]
#[doc = "```ignore"]
#[doc = "use bytes::{BufMut, BytesMut};"]
#[doc = concat!("use fmmap::", $path_str, "::", $name_str, ";")]
#[doc = ""]
#[doc = "let mut data = BytesMut::with_capacity(100);"]
#[doc = "data.put_slice(\"some data...\".as_bytes());"]
#[doc = concat!($name_str, "::memory(\"foo.mem\", data.freeze());")]
#[doc = "```"]
pub fn memory<P: AsRef<Path>>(path: P, data: Bytes) -> Self {
Self::from(<$memory_base>::new(path, data))
}
#[doc = concat!("Create a in-memory ", $name_str, " from Vec")]
#[doc = "# Examples"]
#[doc = "```ignore"]
#[doc = concat!("use fmmap::", $path_str, "::", $name_str, ";")]
#[doc = ""]
#[doc = "let data = (0..=255u8).collect::<Vec<_>>();"]
#[doc = concat!($name_str, "::memory_from_vec(\"foo.mem\", data);")]
#[doc = "```"]
pub fn memory_from_vec<P: AsRef<Path>>(path: P, src: Vec<u8>) -> Self {
Self::from(<$memory_base>::from_vec(path, src))
}
#[doc = concat!("Create a in-memory ", $name_str, " from String")]
#[doc = "# Examples"]
#[doc = "```ignore"]
#[doc = concat!("use fmmap::", $path_str, "::", $name_str, ";")]
#[doc = ""]
#[doc = "let data: &'static str = \"some data...\";"]
#[doc = concat!($name_str, "::memory_from_string(\"foo.mem\", data.to_string());")]
#[doc = "```"]
pub fn memory_from_string<P: AsRef<Path>>(path: P, src: String) -> Self {
Self::from(<$memory_base>::from_string(path, src))
}
#[doc = concat!("Create a in-memory ", $name_str, " from static slice")]
#[doc = "# Examples"]
#[doc = "```ignore"]
#[doc = "use bytes::Bytes;"]
#[doc = concat!("use fmmap::", $path_str, "::", $name_str, ";")]
#[doc = ""]
#[doc = "let data: &'static [u8] = \"some data...\".as_bytes();"]
#[doc = concat!($name_str, "::memory_from_slice(\"foo.mem\", data);")]
#[doc = "```"]
pub fn memory_from_slice<P: AsRef<Path>>(path: P, src: &'static [u8]) -> Self {
Self::from(<$memory_base>::from_slice(path, src))
}
#[doc = concat!("Create a in-memory ", $name_str, " from static str")]
#[doc = "# Examples"]
#[doc = "```ignore"]
#[doc = "use bytes::Bytes;"]
#[doc = concat!("use fmmap::", $path_str, "::", $name_str, ";")]
#[doc = ""]
#[doc = "let data: &'static str = \"some data...\";"]
#[doc = concat!($name_str, "::memory_from_str(\"foo.mem\", data);")]
#[doc = "```"]
pub fn memory_from_str<P: AsRef<Path>>(path: P, src: &'static str) -> Self {
Self::from(<$memory_base>::from_str(path, src))
}
#[doc = concat!("Create a in-memory ", $name_str, " by copy from slice")]
#[doc = "# Examples"]
#[doc = "```ignore"]
#[doc = concat!("use fmmap::", $path_str, "::", $name_str, ";")]
#[doc = ""]
#[doc = concat!($name_str, "::memory_copy_from_slice(\"foo.mem\", \"some data...\".as_bytes());")]
#[doc = "```"]
pub fn memory_copy_from_slice<P: AsRef<Path>>(path: P, src: &[u8]) -> Self {
Self::from(<$memory_base>::copy_from_slice(path, src))
}
}
};
}
macro_rules! impl_constructor_for_memory_mmap_file_mut {
($memory_base: ident, $name: ident, $name_str: literal, $path_str: literal) => {
impl $name {
#[doc = concat!("Create a in-memory ", $name_str)]
#[doc = "# Examples"]
#[doc = "```ignore"]
#[doc = concat!("use fmmap::", $path_str, "::", $name_str, ";")]
#[doc = ""]
#[doc = concat!($name_str, "::memory(\"foo.mem\");")]
#[doc = "```"]
pub fn memory<P: AsRef<Path>>(path: P) -> Self {
Self::from(<$memory_base>::new(path))
}
#[doc = concat!("Create a in-memory ", $name_str, "with capacity")]
#[doc = "# Examples"]
#[doc = "```ignore"]
#[doc = concat!("use fmmap::", $path_str, "::", $name_str, ";")]
#[doc = ""]
#[doc = concat!($name_str, "::memory_with_capacity(\"foo.mem\", 1000);")]
#[doc = "```"]
pub fn memory_with_capacity<P: AsRef<Path>>(path: P, cap: usize) -> Self {
Self::from(<$memory_base>::with_capacity(path, cap))
}
#[doc = concat!("Create a in-memory ", $name_str, " from Vec")]
#[doc = "# Examples"]
#[doc = "```ignore"]
#[doc = concat!("use fmmap::", $path_str, "::", $name_str, ";")]
#[doc = ""]
#[doc = "let data = (0..=255u8).collect::<Vec<_>>();"]
#[doc = concat!($name_str, "::memory_from_vec(\"foo.mem\", data);")]
#[doc = "```"]
pub fn memory_from_vec<P: AsRef<Path>>(path: P, src: Vec<u8>) -> Self {
Self::from(<$memory_base>::from_vec(path, src))
}
#[doc = concat!("Create a in-memory ", $name_str, " from String")]
#[doc = "# Examples"]
#[doc = "```ignore"]
#[doc = concat!("use fmmap::", $path_str, "::", $name_str, ";")]
#[doc = ""]
#[doc = "let data: &'static str = \"some data...\";"]
#[doc = concat!($name_str, "::memory_from_string(\"foo.mem\", data.to_string());")]
#[doc = "```"]
pub fn memory_from_string<P: AsRef<Path>>(path: P, src: String) -> Self {
Self::from(<$memory_base>::from_string(path, src))
}
#[doc = concat!("Create a in-memory ", $name_str, " from static str")]
#[doc = "# Examples"]
#[doc = "```ignore"]
#[doc = "use bytes::Bytes;"]
#[doc = concat!("use fmmap::", $path_str, "::", $name_str, ";")]
#[doc = ""]
#[doc = "let data: &'static str = \"some data...\";"]
#[doc = concat!($name_str, "::memory_from_str(\"foo.mem\", data);")]
#[doc = "```"]
pub fn memory_from_str<P: AsRef<Path>>(path: P, src: &'static str) -> Self {
Self::from(<$memory_base>::from_str(path, src))
}
#[doc = concat!("Create a in-memory ", $name_str, " by from slice")]
#[doc = "# Examples"]
#[doc = "```ignore"]
#[doc = concat!("use fmmap::", $path_str, "::", $name_str, ";")]
#[doc = ""]
#[doc = concat!($name_str, "::memory_from_slice(\"foo.mem\", \"some data...\".as_bytes());")]
#[doc = "```"]
pub fn memory_from_slice<P: AsRef<Path>>(path: P, src: &[u8]) -> Self {
Self::from(<$memory_base>::from_slice(path, src))
}
}
};
}
cfg_sync! {
macro_rules! impl_mmap_file_ext {
($name: ident) => {
impl MmapFileExt for $name {
#[inline]
fn len(&self) -> usize {
self.inner.len()
}
#[inline]
fn as_slice(&self) -> &[u8] {
self.inner.as_slice()
}
#[inline]
fn path(&self) -> &Path {
self.inner.path()
}
#[inline]
fn is_exec(&self) -> bool {
self.inner.is_exec()
}
#[inline]
fn metadata(&self) -> Result<MetaData> {
self.inner.metadata()
}
impl_file_lock!();
}
};
}
mod sync_impl;
pub use sync_impl::{MmapFileExt, MmapFileMutExt, MmapFile, MmapFileMut};
}
cfg_async! {
macro_rules! impl_async_mmap_file_ext {
($name: ident) => {
#[async_trait]
impl AsyncMmapFileExt for $name {
#[inline]
fn len(&self) -> usize {
self.inner.len()
}
#[inline]
fn as_slice(&self) -> &[u8] {
self.inner.as_slice()
}
#[inline]
fn path(&self) -> &Path {
self.inner.path()
}
#[inline]
fn is_exec(&self) -> bool {
self.inner.is_exec()
}
#[inline]
async fn metadata(&self) -> Result<MetaData> {
self.inner.metadata().await
}
impl_file_lock!();
}
};
}
macro_rules! impl_async_mmap_file_mut_ext {
($filename_prefix: literal, $doc_test_runtime: literal, $path_str: literal) => {
#[async_trait]
impl AsyncMmapFileMutExt for AsyncMmapFileMut {
#[inline]
fn as_mut_slice(&mut self) -> &mut [u8] {
self.inner.as_mut_slice()
}
#[inline]
fn is_cow(&self) -> bool {
self.inner.is_cow()
}
impl_flush!();
#[inline]
async fn truncate(&mut self, max_sz: u64) -> Result<()> {
self.inner.truncate(max_sz).await
}
/// Remove the underlying file
///
/// # Example
///
/// ```ignore
#[doc = concat!("use fmmap::", $path_str, "::{AsyncMmapFileMut, AsyncMmapFileMutExt};")]
#[doc = concat!("use ", $path_str, "::fs::File;")]
#[doc = concat!("# ", $doc_test_runtime, "::block_on(async {")]
#[doc = concat!("let mut file = AsyncMmapFileMut::create(\"", $filename_prefix, "_remove_test.txt\").await.unwrap();")]
///
/// file.truncate(12).await;
/// file.write_all("some data...".as_bytes(), 0).unwrap();
/// file.flush().unwrap();
///
/// file.drop_remove().await.unwrap();
///
#[doc = concat!("let err = File::open(\"", $filename_prefix, "_remove_test.txt\").await;")]
/// assert_eq!(err.unwrap_err().kind(), std::io::ErrorKind::NotFound);
/// # })
/// ```
async fn drop_remove(mut self) -> Result<()> {
let empty = AsyncMmapFileMutInner::Empty(AsyncEmptyMmapFile::default());
// swap the inner to empty
let inner = mem::replace(&mut self.inner, empty);
if !self.remove_on_drop {
// do remove
inner.drop_remove().await?;
self.deleted = true;
}
Ok(())
}
/// Close and truncate the underlying file
///
/// # Examples
///
/// ```ignore
#[doc = concat!("use fmmap::{MetaDataExt,", $path_str, "::{AsyncMmapFileMut, AsyncMmapFileExt, AsyncMmapFileMutExt}};")]
/// # use scopeguard::defer;
///
#[doc = concat!("# ", $doc_test_runtime, "::block_on(async {")]
#[doc = concat!("let mut file = AsyncMmapFileMut::create(\"", $filename_prefix, "_close_with_truncate_test.txt\").await.unwrap();")]
#[doc = concat!("# defer!(std::fs::remove_file(\"", $filename_prefix, "_close_with_truncate_test.txt\").unwrap());")]
/// file.truncate(100).await;
/// file.write_all("some data...".as_bytes(), 0).unwrap();
/// file.flush().unwrap();
///
/// file.close_with_truncate(50).await.unwrap();
///
#[doc = concat!("let file = AsyncMmapFileMut::open(\"", $filename_prefix, "_close_with_truncate_test.txt\").await.unwrap();")]
/// let meta = file.metadata().await.unwrap();
/// assert_eq!(meta.len(), 50);
/// # })
/// ```
async fn close_with_truncate(mut self, max_sz: i64) -> Result<()> {
let empty = AsyncMmapFileMutInner::Empty(AsyncEmptyMmapFile::default());
// swap the inner to empty
let inner = mem::replace(&mut self.inner, empty);
inner.close_with_truncate(max_sz).await
}
}
};
}
macro_rules! declare_async_mmap_file_ext {
($disk_file_mut: ty, $opts: ty, $reader: ty) => {
/// Utility methods to [`AsyncMmapFile`]
///
/// [`AsyncMmapFile`]: structs.AsyncMmapFile.html
#[async_trait]
#[enum_dispatch]
pub trait AsyncMmapFileExt {
/// Returns the current mmap length
fn len(&self) -> usize;
/// Returns the mmap is empty of not.
fn is_empty(&self) -> bool {
self.len() == 0
}
/// Returns the underlying slice of the mmap
fn as_slice(&self) -> &[u8];
/// slice returns data starting from offset off of size sz.
///
/// # Panics
/// If there's not enough data, it would
/// panic.
fn slice(&self, offset: usize, sz: usize) -> &[u8] {
&self.as_slice()[offset..offset+sz]
}
/// bytes returns data starting from offset off of size sz.
///
/// # Errors
/// If there's not enough data, it would return
/// `Err(Error::from(ErrorKind::EOF))`.
fn bytes(&self, offset: usize, sz: usize) -> Result<&[u8]> {
let buf = self.as_slice();
if buf.len() < offset + sz {
Err(Error::from(ErrorKind::EOF))
} else {
Ok(&buf[offset..offset+sz])
}
}
/// Returns the path of the inner file.
fn path(&self) -> &Path;
/// Returns the path buf of the inner file.
fn path_buf(&self) -> PathBuf {
self.path().to_path_buf()
}
/// Returns the path lossy string of the inner file.
fn path_lossy(&self) -> Cow<'_, str> {
self.path().to_string_lossy()
}
/// Returns the path string of the inner file.
fn path_string(&self) -> String {
self.path_lossy().to_string()
}
/// Whether the mmap is executable
fn is_exec(&self) -> bool;
/// Returns the metadata of file metadata
///
/// Metadata information about a file.
/// This structure is returned from the metadata or
/// symlink_metadata function or method and represents
/// known metadata about a file such as its permissions, size, modification times, etc
async fn metadata(&self) -> Result<MetaData>;
/// Copy the content of the mmap file to Vec
#[inline]
fn copy_all_to_vec(&self) -> Vec<u8> {
self.as_slice().to_vec()
}
/// Copy a range of content of the mmap file to Vec
#[inline]
fn copy_range_to_vec(&self, offset: usize, len: usize) -> Vec<u8> {
self.slice(offset, len).to_vec()
}
/// Write the content of the mmap file to a new file.
#[inline]
async fn write_all_to_new_file<P: AsRef<Path> + Send + Sync>(&self, new_file_path: P) -> Result<()> {
let buf = self.as_slice();
let opts = <$opts>::new().max_size(buf.len() as u64);
let mut mmap = <$disk_file_mut>::create_with_options(new_file_path, opts).await?;
mmap.writer(0)?.write_all(buf).await?;
mmap.flush()
}
/// Write a range of content of the mmap file to new file.
#[inline]
async fn write_range_to_new_file<P: AsRef<Path> + Send + Sync>(&self, new_file_path: P, offset: usize, len: usize) -> Result<()> {
let buf = self.as_slice();
if buf.len() < offset + len {
return Err(Error::from(ErrorKind::EOF));
}
let opts = <$opts>::new().max_size(len as u64);
let mut mmap = <$disk_file_mut>::create_with_options(new_file_path, opts).await?;
mmap.writer(0)?.write_all(&buf[offset..offset + len]).await?;
mmap.flush()
}
/// Returns a [`AsyncMmapFileReader`] which helps read data from mmap like a normal File.
///
/// # Errors
/// If there's not enough data, it would return
/// `Err(Error::from(ErrorKind::EOF))`.
///
/// [`AsyncMmapFileReader`]: structs.AsyncMmapFileReader.html
fn reader(&self, offset: usize) -> Result<$reader> {
let buf = self.as_slice();
if buf.len() < offset {
Err(Error::from(ErrorKind::EOF))
} else {
Ok(<$reader>::new(Cursor::new(&buf[offset..]), offset, buf.len() - offset))
}
}
/// Returns a [`AsyncMmapFileReader`] base on the given `offset` and `len`, which helps read data from mmap like a normal File.
///
/// # Errors
/// If there's not enough data, it would return
/// `Err(Error::from(ErrorKind::EOF))`.
///
/// [`AsyncMmapFileReader`]: structs.AsyncMmapFileReader.html
fn range_reader(&self, offset: usize, len: usize) -> Result<$reader> {
let buf = self.as_slice();
if buf.len() < offset + len {
Err(Error::from(ErrorKind::EOF))
} else {
Ok(<$reader>::new(Cursor::new(&buf[offset.. offset + len]), offset, len))
}
}
/// Locks the file for shared usage, blocking if the file is currently locked exclusively.
///
/// # Notes
/// This function will do nothing if the underlying is not a real file, e.g. in-memory.
fn lock_exclusive(&self) -> Result<()>;
/// Locks the file for exclusive usage, blocking if the file is currently locked.
///
/// # Notes
/// This function will do nothing if the underlying is not a real file, e.g. in-memory.
fn lock_shared(&self) -> Result<()>;
/// Locks the file for shared usage, or returns a an error if the file is currently locked (see lock_contended_error).
///
/// # Notes
/// This function will do nothing if the underlying is not a real file, e.g. in-memory.
fn try_lock_exclusive(&self) -> Result<()>;
/// Locks the file for shared usage, or returns a an error if the file is currently locked (see lock_contended_error).Locks the file for shared usage, or returns a an error if the file is currently locked (see lock_contended_error).
///
/// # Notes
/// This function will do nothing if the underlying is not a real file, e.g. in-memory.
fn try_lock_shared(&self) -> Result<()>;
/// Unlocks the file.
///
/// # Notes
/// This function will do nothing if the underlying is not a real file, e.g. in-memory.
fn unlock(&self) -> Result<()>;
/// Read bytes to the dst buf from the offset, returns how many bytes read.
fn read(&self, dst: &mut [u8], offset: usize) -> usize {
let buf = self.as_slice();
if buf.len() < offset {
0
} else {
let remaining = buf.len() - offset;
let dst_len = dst.len();
if remaining > dst_len {
dst.copy_from_slice(&buf[offset..offset + dst_len]);
dst_len
} else {
dst.copy_from_slice(&buf[offset..offset + remaining]);
remaining
}
}
}
/// Read the exact number of bytes required to fill buf.
fn read_exact(&self, dst: &mut [u8], offset: usize) -> Result<()> {
let buf = self.as_slice();
let remaining = buf.len().checked_sub(offset);
match remaining {
None => Err(Error::from(ErrorKind::EOF)),
Some(remaining) => {
let dst_len = dst.len();
if remaining < dst_len {
Err(Error::from(ErrorKind::EOF))
} else {
dst.copy_from_slice(&buf[offset..offset + dst_len]);
Ok(())
}
}
}
}
/// Read a signed 8 bit integer from offset.
fn read_i8(&self, offset: usize) -> Result<i8> {
let buf = self.as_slice();
let remaining = buf.len().checked_sub(offset);
match remaining {
None => Err(Error::from(ErrorKind::EOF)),
Some(remaining) => {
if remaining < 1 {
Err(Error::from(ErrorKind::EOF))
} else {
Ok(buf[offset] as i8)
}
}
}
}
/// Read a signed 16 bit integer from offset in big-endian byte order.
fn read_i16(&self, offset: usize) -> Result<i16> {
read_impl!(self, offset, i16::from_be_bytes)
}
/// Read a signed 16 bit integer from offset in little-endian byte order.
fn read_i16_le(&self, offset: usize) -> Result<i16> {
read_impl!(self, offset, i16::from_le_bytes)
}
/// Read a signed integer from offset in big-endian byte order.
fn read_isize(&self, offset: usize) -> Result<isize> {
read_impl!(self, offset, isize::from_be_bytes)
}
/// Read a signed integer from offset in little-endian byte order.
fn read_isize_le(&self, offset: usize) -> Result<isize> {
read_impl!(self, offset, isize::from_le_bytes)
}
/// Read a signed 32 bit integer from offset in big-endian byte order.
fn read_i32(&self, offset: usize) -> Result<i32> {
read_impl!(self, offset, i32::from_be_bytes)
}
/// Read a signed 32 bit integer from offset in little-endian byte order.
fn read_i32_le(&self, offset: usize) -> Result<i32> {
read_impl!(self, offset, i32::from_le_bytes)
}
/// Read a signed 64 bit integer from offset in big-endian byte order.
fn read_i64(&self, offset: usize) -> Result<i64> {
read_impl!(self, offset, i64::from_be_bytes)
}
/// Read a signed 64 bit integer from offset in little-endian byte order.
fn read_i64_le(&self, offset: usize) -> Result<i64> {
read_impl!(self, offset, i64::from_le_bytes)
}
/// Read a signed 128 bit integer from offset in big-endian byte order.
fn read_i128(&self, offset: usize) -> Result<i128> {
read_impl!(self, offset, i128::from_be_bytes)
}
/// Read a signed 128 bit integer from offset in little-endian byte order.
fn read_i128_le(&self, offset: usize) -> Result<i128> {
read_impl!(self, offset, i128::from_le_bytes)
}
/// Read an unsigned 8 bit integer from offset.
fn read_u8(&self, offset: usize) -> Result<u8> {
let buf = self.as_slice();
let remaining = buf.len().checked_sub(offset);
match remaining {
None => Err(Error::from(ErrorKind::EOF)),
Some(remaining) => {
if remaining < 1 {
Err(Error::from(ErrorKind::EOF))
} else {
Ok(buf[offset])
}
}
}
}
/// Read an unsigned 16 bit integer from offset in big-endian.
fn read_u16(&self, offset: usize) -> Result<u16> {
read_impl!(self, offset, u16::from_be_bytes)
}
/// Read an unsigned 16 bit integer from offset in little-endian.
fn read_u16_le(&self, offset: usize) -> Result<u16> {
read_impl!(self, offset, u16::from_le_bytes)
}
/// Read an unsigned integer from offset in big-endian byte order.
fn read_usize(&self, offset: usize) -> Result<usize> {
read_impl!(self, offset, usize::from_be_bytes)
}
/// Read an unsigned integer from offset in little-endian byte order.
fn read_usize_le(&self, offset: usize) -> Result<usize> {
read_impl!(self, offset, usize::from_le_bytes)
}
/// Read an unsigned 32 bit integer from offset in big-endian.
fn read_u32(&self, offset: usize) -> Result<u32> {
read_impl!(self, offset, u32::from_be_bytes)
}
/// Read an unsigned 32 bit integer from offset in little-endian.
fn read_u32_le(&self, offset: usize) -> Result<u32> {
read_impl!(self, offset, u32::from_le_bytes)
}
/// Read an unsigned 64 bit integer from offset in big-endian.
fn read_u64(&self, offset: usize) -> Result<u64> {
read_impl!(self, offset, u64::from_be_bytes)
}
/// Read an unsigned 64 bit integer from offset in little-endian.
fn read_u64_le(&self, offset: usize) -> Result<u64> {
read_impl!(self, offset, u64::from_le_bytes)
}
/// Read an unsigned 128 bit integer from offset in big-endian.
fn read_u128(&self, offset: usize) -> Result<u128> {
read_impl!(self, offset, u128::from_be_bytes)
}
/// Read an unsigned 128 bit integer from offset in little-endian.
fn read_u128_le(&self, offset: usize) -> Result<u128> {
read_impl!(self, offset, u128::from_le_bytes)
}
/// Read an IEEE754 single-precision (4 bytes) floating point number from
/// offset in big-endian byte order.
fn read_f32(&self, offset: usize) -> Result<f32> {
read_impl!(self, offset, f32::from_be_bytes)
}
/// Read an IEEE754 single-precision (4 bytes) floating point number from
/// offset in little-endian byte order.
fn read_f32_le(&self, offset: usize) -> Result<f32> {
read_impl!(self, offset, f32::from_le_bytes)
}
/// Read an IEEE754 single-precision (8 bytes) floating point number from
/// offset in big-endian byte order.
fn read_f64(&self, offset: usize) -> Result<f64> {
read_impl!(self, offset, f64::from_be_bytes)
}
/// Read an IEEE754 single-precision (8 bytes) floating point number from
/// offset in little-endian byte order.
fn read_f64_le(&self, offset: usize) -> Result<f64> {
read_impl!(self, offset, f64::from_le_bytes)
}
}
};
}
macro_rules! declare_async_mmap_file_mut_ext {
($writer: ty) => {
/// Utility methods to [`AsyncMmapFileMut`]
///
/// [`AsyncMmapFileMut`]: structs.AsyncMmapFileMut.html
#[async_trait]
#[enum_dispatch]
pub trait AsyncMmapFileMutExt {
/// Returns the mutable underlying slice of the mmap
fn as_mut_slice(&mut self) -> &mut [u8];
/// slice_mut returns mutable data starting from offset off of size sz.
///
/// # Panics
/// If there's not enough data, it would
/// panic.
fn slice_mut(&mut self, offset: usize, sz: usize) -> &mut [u8] {
&mut self.as_mut_slice()[offset..offset+sz]
}
/// Whether mmap is copy on write
fn is_cow(&self) -> bool;
/// bytes_mut returns mutable data starting from offset off of size sz.
///
/// # Errors
/// If there's not enough data, it would return
/// `Err(Error::from(ErrorKind::EOF))`.
fn bytes_mut(&mut self, offset: usize, sz: usize) -> Result<&mut [u8]> {
let buf = self.as_mut_slice();
if buf.len() < offset + sz {
Err(Error::from(ErrorKind::EOF))
} else {
Ok(&mut buf[offset..offset+sz])
}
}
/// Fill 0 to the specific range
fn zero_range(&mut self, start: usize, end: usize) {
let buf = self.as_mut_slice();
let end = end.min(buf.len());
buf[start..end].fill(0);
}
/// Flushes outstanding memory map modifications to disk (if the inner is a real file).
///
/// When this method returns with a non-error result,
/// all outstanding changes to a file-backed memory map are guaranteed to be durably stored.
/// The file’s metadata (including last modification timestamp) may not be updated.
fn flush(&self) -> Result<()>;
/// Asynchronously flushes outstanding memory map modifications to disk(if the inner is a real file).
///
/// This method initiates flushing modified pages to durable storage,
/// but it will not wait for the operation to complete before returning.
/// The file’s metadata (including last modification timestamp) may not be updated.
fn flush_async(&self) -> Result<()>;
/// Flushes outstanding memory map modifications in the range to disk(if the inner is a real file).
///
/// The offset and length must be in the bounds of the memory map.
///
/// When this method returns with a non-error result,
/// all outstanding changes to a file-backed memory
/// in the range are guaranteed to be durable stored.
/// The file’s metadata (including last modification timestamp) may not be updated.
/// It is not guaranteed the only the changes in the specified range are flushed;
/// other outstanding changes to the memory map may be flushed as well.
fn flush_range(&self, offset: usize, len: usize) -> Result<()>;
/// Asynchronously flushes outstanding memory map modifications in the range to disk(if the inner is a real file).
///
/// The offset and length must be in the bounds of the memory map.
///
/// This method initiates flushing modified pages to durable storage,
/// but it will not wait for the operation to complete before returning.
/// The file’s metadata (including last modification timestamp) may not be updated.
/// It is not guaranteed that the only changes flushed are those in the specified range;
/// other outstanding changes to the memory map may be flushed as well.
fn flush_async_range(&self, offset: usize, len: usize) -> Result<()>;
/// Truncates the file to the `max_size`, which will lead to
/// do re-mmap and sync_dir if the inner is a real file.
async fn truncate(&mut self, max_sz: u64) -> Result<()>;
/// Remove the underlying file
async fn drop_remove(self) -> Result<()>;
/// Close and truncate the underlying file
async fn close_with_truncate(self, max_sz: i64) -> Result<()>;
/// Returns a [`AsyncMmapFileWriter`] base on the given `offset`, which helps read or write data from mmap like a normal File.
///
/// # Notes
/// If you use a writer to write data to mmap, there is no guarantee all
/// data will be durably stored. So you need to call [`flush`]/[`flush_range`]/[`flush_async`]/[`flush_async_range`] in [`AsyncMmapFileMutExt`]
/// to guarantee all data will be durably stored.
///
/// # Errors
/// If there's not enough data, it would return
/// `Err(Error::from(ErrorKind::EOF))`.
///
/// [`flush`]: traits.MmapFileMutExt.html#methods.flush
/// [`flush_range`]: traits.MmapFileMutExt.html#methods.flush_range
/// [`flush_async`]: traits.MmapFileMutExt.html#methods.flush_async
/// [`flush_async_range`]: traits.MmapFileMutExt.html#methods.flush_async_range
/// [`MmapFileWriter`]: structs.MmapFileWriter.html
fn writer(&mut self, offset: usize) -> Result<$writer> {
let buf = self.as_mut_slice();
let buf_len = buf.len();
if buf_len < offset {
Err(Error::from(ErrorKind::EOF))
} else {
Ok(<$writer>::new(Cursor::new(&mut buf[offset..]), offset, buf_len - offset))
}
}
/// Returns a [`AsyncMmapFileWriter`] base on the given `offset` and `len`, which helps read or write data from mmap like a normal File.
///
/// # Notes
/// If you use a writer to write data to mmap, there is no guarantee all
/// data will be durably stored. So you need to call [`flush`]/[`flush_range`]/[`flush_async`]/[`flush_async_range`] in [`MmapFileMutExt`]
/// to guarantee all data will be durably stored.
///
/// # Errors
/// If there's not enough data, it would return
/// `Err(Error::from(ErrorKind::EOF))`.
///
/// [`flush`]: traits.AsyncMmapFileMutExt.html#methods.flush
/// [`flush_range`]: traits.AsyncMmapFileMutExt.html#methods.flush_range
/// [`flush_async`]: traits.AsyncMmapFileMutExt.html#methods.flush_async
/// [`flush_async_range`]: traits.AsyncMmapFileMutExt.html#methods.flush_async_range
/// [`AsyncMmapFileWriter`]: structs.AsyncMmapFileWriter.html
fn range_writer(&mut self, offset: usize, len: usize) -> Result<$writer> {
let buf = self.as_mut_slice();
if buf.len() < offset + len {
Err(Error::from(ErrorKind::EOF))
} else {
Ok(<$writer>::new(
Cursor::new(&mut buf[offset..offset + len]), offset, len))
}
}
/// Write bytes to the mmap from the offset.
fn write(&mut self, src: &[u8], offset: usize) -> usize {
let buf = self.as_mut_slice();
if buf.len() <= offset {
0
} else {
let remaining = buf.len() - offset;
let src_len = src.len();
if remaining > src_len {
buf[offset..offset + src_len].copy_from_slice(src);
src_len
} else {
buf[offset..offset + remaining].copy_from_slice(&src[..remaining]);
remaining
}
}
}
/// Write the all of bytes in `src` to the mmap from the offset.
fn write_all(&mut self, src: &[u8], offset: usize) -> Result<()> {
let buf = self.as_mut_slice();
let remaining = buf.len().checked_sub(offset);
match remaining {
None => Err(Error::from(ErrorKind::EOF)),
Some(remaining) => {
let src_len = src.len();
if remaining < src_len {
Err(Error::from(ErrorKind::EOF))
} else {
buf[offset..offset + src_len].copy_from_slice(src);
Ok(())
}
}
}
}
/// Writes a signed 8 bit integer to mmap from the offset.
fn write_i8(&mut self, val: i8, offset: usize) -> Result<()> {
self.write_all(&[val as u8], offset)
}
/// Writes a signed 16 bit integer to mmap from the offset in the big-endian byte order.
fn write_i16(&mut self, val: i16, offset: usize) -> Result<()> {
self.write_all(&val.to_be_bytes(), offset)
}
/// Writes a signed 16 bit integer to mmap from the offset in the little-endian byte order.
fn write_i16_le(&mut self, val: i16, offset: usize) -> Result<()> {
self.write_all(&val.to_le_bytes(), offset)
}
/// Writes a signed integer to mmap from the offset in the big-endian byte order.
fn write_isize(&mut self, val: isize, offset: usize) -> Result<()> {
self.write_all(&val.to_be_bytes(), offset)
}
/// Writes a signed integer to mmap from the offset in the little-endian byte order.
fn write_isize_le(&mut self, val: isize, offset: usize) -> Result<()> {
self.write_all(&val.to_le_bytes(), offset)
}
/// Writes a signed 32 bit integer to mmap from the offset in the big-endian byte order.
fn write_i32(&mut self, val: i32, offset: usize) -> Result<()> {
self.write_all(&val.to_be_bytes(), offset)
}
/// Writes a signed 32 bit integer to mmap from the offset in the little-endian byte order.
fn write_i32_le(&mut self, val: i32, offset: usize) -> Result<()> {
self.write_all(&val.to_le_bytes(), offset)
}
/// Writes a signed 64 bit integer to mmap from the offset in the big-endian byte order.
fn write_i64(&mut self, val: i64, offset: usize) -> Result<()> {
self.write_all(&val.to_be_bytes(), offset)
}
/// Writes a signed 64 bit integer to mmap from the offset in the little-endian byte order.
fn write_i64_le(&mut self, val: i64, offset: usize) -> Result<()> {
self.write_all(&val.to_le_bytes(), offset)
}
/// Writes a signed 128 bit integer to mmap from the offset in the big-endian byte order.
fn write_i128(&mut self, val: i128, offset: usize) -> Result<()> {
self.write_all(&val.to_be_bytes(), offset)
}
/// Writes a signed 128 bit integer to mmap from the offset in the little-endian byte order.
fn write_i128_le(&mut self, val: i128, offset: usize) -> Result<()> {
self.write_all(&val.to_le_bytes(), offset)
}
/// Writes an unsigned 8 bit integer to mmap from the offset.
fn write_u8(&mut self, val: u8, offset: usize) -> Result<()> {
self.write_all(&[val], offset)
}
/// Writes an unsigned 16 bit integer to mmap from the offset in the big-endian byte order.
fn write_u16(&mut self, val: u16, offset: usize) -> Result<()> {
self.write_all(&val.to_be_bytes(), offset)
}
/// Writes an unsigned 16 bit integer to mmap from the offset in the little-endian byte order.
fn write_u16_le(&mut self, val: u16, offset: usize) -> Result<()> {
self.write_all(&val.to_le_bytes(), offset)
}
/// Writes an unsigned integer to mmap from the offset in the big-endian byte order.
fn write_usize(&mut self, val: usize, offset: usize) -> Result<()> {
self.write_all(&val.to_be_bytes(), offset)
}
/// Writes an unsigned integer to mmap from the offset in the little-endian byte order.
fn write_usize_le(&mut self, val: usize, offset: usize) -> Result<()> {
self.write_all(&val.to_le_bytes(), offset)
}
/// Writes an unsigned 32 bit integer to mmap from the offset in the big-endian byte order.
fn write_u32(&mut self, val: u32, offset: usize) -> Result<()> {
self.write_all(&val.to_be_bytes(), offset)
}
/// Writes an unsigned 32 bit integer to mmap from the offset in the little-endian byte order.
fn write_u32_le(&mut self, val: u32, offset: usize) -> Result<()> {
self.write_all(&val.to_le_bytes(), offset)
}
/// Writes an unsigned 64 bit integer to mmap from the offset in the big-endian byte order.
fn write_u64(&mut self, val: u64, offset: usize) -> Result<()> {
self.write_all(&val.to_be_bytes(), offset)
}
/// Writes an unsigned 64 bit integer to mmap from the offset in the little-endian byte order.
fn write_u64_le(&mut self, val: u64, offset: usize) -> Result<()> {
self.write_all(&val.to_le_bytes(), offset)
}
/// Writes an unsigned 128 bit integer to mmap from the offset in the big-endian byte order.
fn write_u128(&mut self, val: u128, offset: usize) -> Result<()> {
self.write_all(&val.to_be_bytes(), offset)
}
/// Writes an unsigned 128 bit integer to mmap from the offset in the little-endian byte order.
fn write_u128_le(&mut self, val: u128, offset: usize) -> Result<()> {
self.write_all(&val.to_le_bytes(), offset)
}
/// Writes an IEEE754 single-precision (4 bytes) floating point number to mmap from the offset in big-endian byte order.
fn write_f32(&mut self, val: f32, offset: usize) -> Result<()> {
self.write_all(&val.to_be_bytes(), offset)
}
/// Writes an IEEE754 single-precision (4 bytes) floating point number to mmap from the offset in little-endian byte order.
fn write_f32_le(&mut self, val: f32, offset: usize) -> Result<()> {
self.write_all(&val.to_le_bytes(), offset)
}
/// Writes an IEEE754 single-precision (8 bytes) floating point number to mmap from the offset in big-endian byte order.
fn write_f64(&mut self, val: f64, offset: usize) -> Result<()> {
self.write_all(&val.to_be_bytes(), offset)
}
/// Writes an IEEE754 single-precision (8 bytes) floating point number to mmap from the offset in little-endian byte order.
fn write_f64_le(&mut self, val: f64, offset: usize) -> Result<()> {
self.write_all(&val.to_le_bytes(), offset)
}
}
};
}
macro_rules! declare_and_impl_inners {
() => {
enum AsyncMmapFileInner {
Empty(AsyncEmptyMmapFile),
Memory(AsyncMemoryMmapFile),
Disk(AsyncDiskMmapFile)
}
impl From<AsyncEmptyMmapFile> for AsyncMmapFileInner {
fn from(v: AsyncEmptyMmapFile) -> AsyncMmapFileInner {
AsyncMmapFileInner::Empty(v)
}
}
impl From<AsyncMemoryMmapFile> for AsyncMmapFileInner {
fn from(v: AsyncMemoryMmapFile) -> AsyncMmapFileInner {
AsyncMmapFileInner::Memory(v)
}
}
impl From<AsyncDiskMmapFile> for AsyncMmapFileInner {
fn from(v: AsyncDiskMmapFile) -> AsyncMmapFileInner {
AsyncMmapFileInner::Disk(v)
}
}
#[async_trait]
impl AsyncMmapFileExt for AsyncMmapFileInner {
#[inline]
fn len(&self) -> usize {
match self {
AsyncMmapFileInner::Empty(inner) => AsyncMmapFileExt::len(inner),
AsyncMmapFileInner::Memory(inner) => AsyncMmapFileExt::len(inner),
AsyncMmapFileInner::Disk(inner) => AsyncMmapFileExt::len(inner),
}
}
#[inline]
fn as_slice(&self) -> &[u8] {
match self {
AsyncMmapFileInner::Empty(inner) => AsyncMmapFileExt::as_slice(inner),
AsyncMmapFileInner::Memory(inner) => AsyncMmapFileExt::as_slice(inner),
AsyncMmapFileInner::Disk(inner) => AsyncMmapFileExt::as_slice(inner),
}
}
#[inline]
fn path(&self) -> &Path {
match self {
AsyncMmapFileInner::Empty(inner) => AsyncMmapFileExt::path(inner),
AsyncMmapFileInner::Memory(inner) => AsyncMmapFileExt::path(inner),
AsyncMmapFileInner::Disk(inner) => AsyncMmapFileExt::path(inner),
}
}
#[inline]
fn is_exec(&self) -> bool {
match self {
AsyncMmapFileInner::Empty(inner) => AsyncMmapFileExt::is_exec(inner),
AsyncMmapFileInner::Memory(inner) => AsyncMmapFileExt::is_exec(inner),
AsyncMmapFileInner::Disk(inner) => AsyncMmapFileExt::is_exec(inner),
}
}
#[inline]
async fn metadata(&self) -> Result<MetaData> {
match self {
AsyncMmapFileInner::Empty(inner) => AsyncMmapFileExt::metadata(inner).await,
AsyncMmapFileInner::Memory(inner) => AsyncMmapFileExt::metadata(inner).await,
AsyncMmapFileInner::Disk(inner) => AsyncMmapFileExt::metadata(inner).await,
}
}
#[inline]
fn lock_exclusive(&self) -> Result<()> {
match self {
AsyncMmapFileInner::Empty(inner) => AsyncMmapFileExt::lock_exclusive(inner),
AsyncMmapFileInner::Memory(inner) => AsyncMmapFileExt::lock_exclusive(inner),
AsyncMmapFileInner::Disk(inner) => AsyncMmapFileExt::lock_exclusive(inner),
}
}
#[inline]
fn lock_shared(&self) -> Result<()> {
match self {
AsyncMmapFileInner::Empty(inner) => AsyncMmapFileExt::lock_shared(inner),
AsyncMmapFileInner::Memory(inner) => AsyncMmapFileExt::lock_shared(inner),
AsyncMmapFileInner::Disk(inner) => AsyncMmapFileExt::lock_shared(inner),
}
}
#[inline]
fn try_lock_exclusive(&self) -> Result<()> {
match self {
AsyncMmapFileInner::Empty(inner) => AsyncMmapFileExt::try_lock_exclusive(inner),
AsyncMmapFileInner::Memory(inner) => {
AsyncMmapFileExt::try_lock_exclusive(inner)
}
AsyncMmapFileInner::Disk(inner) => AsyncMmapFileExt::try_lock_exclusive(inner),
}
}
#[inline]
fn try_lock_shared(&self) -> Result<()> {
match self {
AsyncMmapFileInner::Empty(inner) => AsyncMmapFileExt::try_lock_shared(inner),
AsyncMmapFileInner::Memory(inner) => AsyncMmapFileExt::try_lock_shared(inner),
AsyncMmapFileInner::Disk(inner) => AsyncMmapFileExt::try_lock_shared(inner),
}
}
#[inline]
fn unlock(&self) -> Result<()> {
match self {
AsyncMmapFileInner::Empty(inner) => AsyncMmapFileExt::unlock(inner),
AsyncMmapFileInner::Memory(inner) => AsyncMmapFileExt::unlock(inner),
AsyncMmapFileInner::Disk(inner) => AsyncMmapFileExt::unlock(inner),
}
}
}
enum AsyncMmapFileMutInner {
Empty(AsyncEmptyMmapFile),
Memory(AsyncMemoryMmapFileMut),
Disk(AsyncDiskMmapFileMut)
}
impl From<AsyncEmptyMmapFile> for AsyncMmapFileMutInner {
fn from(v: AsyncEmptyMmapFile) -> AsyncMmapFileMutInner {
AsyncMmapFileMutInner::Empty(v)
}
}
impl From<AsyncMemoryMmapFileMut> for AsyncMmapFileMutInner {
fn from(v: AsyncMemoryMmapFileMut) -> AsyncMmapFileMutInner {
AsyncMmapFileMutInner::Memory(v)
}
}
impl From<AsyncDiskMmapFileMut> for AsyncMmapFileMutInner {
fn from(v: AsyncDiskMmapFileMut) -> AsyncMmapFileMutInner {
AsyncMmapFileMutInner::Disk(v)
}
}
#[async_trait]
impl AsyncMmapFileExt for AsyncMmapFileMutInner {
#[inline]
fn len(&self) -> usize {
match self {
AsyncMmapFileMutInner::Empty(inner) => AsyncMmapFileExt::len(inner),
AsyncMmapFileMutInner::Memory(inner) => AsyncMmapFileExt::len(inner),
AsyncMmapFileMutInner::Disk(inner) => AsyncMmapFileExt::len(inner),
}
}
#[inline]
fn as_slice(&self) -> &[u8] {
match self {
AsyncMmapFileMutInner::Empty(inner) => AsyncMmapFileExt::as_slice(inner),
AsyncMmapFileMutInner::Memory(inner) => AsyncMmapFileExt::as_slice(inner),
AsyncMmapFileMutInner::Disk(inner) => AsyncMmapFileExt::as_slice(inner),
}
}
#[inline]
fn path(&self) -> &Path {
match self {
AsyncMmapFileMutInner::Empty(inner) => AsyncMmapFileExt::path(inner),
AsyncMmapFileMutInner::Memory(inner) => AsyncMmapFileExt::path(inner),
AsyncMmapFileMutInner::Disk(inner) => AsyncMmapFileExt::path(inner),
}
}
#[inline]
fn is_exec(&self) -> bool {
match self {
AsyncMmapFileMutInner::Empty(inner) => AsyncMmapFileExt::is_exec(inner),
AsyncMmapFileMutInner::Memory(inner) => AsyncMmapFileExt::is_exec(inner),
AsyncMmapFileMutInner::Disk(inner) => AsyncMmapFileExt::is_exec(inner),
}
}
#[inline]
async fn metadata(&self) -> Result<MetaData> {
match self {
AsyncMmapFileMutInner::Empty(inner) => AsyncMmapFileExt::metadata(inner).await,
AsyncMmapFileMutInner::Memory(inner) => AsyncMmapFileExt::metadata(inner).await,
AsyncMmapFileMutInner::Disk(inner) => AsyncMmapFileExt::metadata(inner).await,
}
}
#[inline]
fn lock_exclusive(&self) -> Result<()> {
match self {
AsyncMmapFileMutInner::Empty(inner) => AsyncMmapFileExt::lock_exclusive(inner),
AsyncMmapFileMutInner::Memory(inner) => AsyncMmapFileExt::lock_exclusive(inner),
AsyncMmapFileMutInner::Disk(inner) => AsyncMmapFileExt::lock_exclusive(inner),
}
}
#[inline]
fn lock_shared(&self) -> Result<()> {
match self {
AsyncMmapFileMutInner::Empty(inner) => AsyncMmapFileExt::lock_shared(inner),
AsyncMmapFileMutInner::Memory(inner) => AsyncMmapFileExt::lock_shared(inner),
AsyncMmapFileMutInner::Disk(inner) => AsyncMmapFileExt::lock_shared(inner),
}
}
#[inline]
fn try_lock_exclusive(&self) -> Result<()> {
match self {
AsyncMmapFileMutInner::Empty(inner) => AsyncMmapFileExt::try_lock_exclusive(inner),
AsyncMmapFileMutInner::Memory(inner) => {
AsyncMmapFileExt::try_lock_exclusive(inner)
}
AsyncMmapFileMutInner::Disk(inner) => AsyncMmapFileExt::try_lock_exclusive(inner),
}
}
#[inline]
fn try_lock_shared(&self) -> Result<()> {
match self {
AsyncMmapFileMutInner::Empty(inner) => AsyncMmapFileExt::try_lock_shared(inner),
AsyncMmapFileMutInner::Memory(inner) => AsyncMmapFileExt::try_lock_shared(inner),
AsyncMmapFileMutInner::Disk(inner) => AsyncMmapFileExt::try_lock_shared(inner),
}
}
#[inline]
fn unlock(&self) -> Result<()> {
match self {
AsyncMmapFileMutInner::Empty(inner) => AsyncMmapFileExt::unlock(inner),
AsyncMmapFileMutInner::Memory(inner) => AsyncMmapFileExt::unlock(inner),
AsyncMmapFileMutInner::Disk(inner) => AsyncMmapFileExt::unlock(inner),
}
}
}
#[async_trait]
impl AsyncMmapFileMutExt for AsyncMmapFileMutInner {
#[inline]
fn as_mut_slice(&mut self) -> &mut [u8] {
match self {
AsyncMmapFileMutInner::Empty(inner) => AsyncMmapFileMutExt::as_mut_slice(inner),
AsyncMmapFileMutInner::Memory(inner) => {
AsyncMmapFileMutExt::as_mut_slice(inner)
}
AsyncMmapFileMutInner::Disk(inner) => AsyncMmapFileMutExt::as_mut_slice(inner),
}
}
#[inline]
fn is_cow(&self) -> bool {
match self {
AsyncMmapFileMutInner::Empty(inner) => AsyncMmapFileMutExt::is_cow(inner),
AsyncMmapFileMutInner::Memory(inner) => AsyncMmapFileMutExt::is_cow(inner),
AsyncMmapFileMutInner::Disk(inner) => AsyncMmapFileMutExt::is_cow(inner),
}
}
#[inline]
fn flush(&self) -> Result<()> {
match self {
AsyncMmapFileMutInner::Empty(inner) => AsyncMmapFileMutExt::flush(inner),
AsyncMmapFileMutInner::Memory(inner) => AsyncMmapFileMutExt::flush(inner),
AsyncMmapFileMutInner::Disk(inner) => AsyncMmapFileMutExt::flush(inner),
}
}
#[inline]
fn flush_async(&self) -> Result<()> {
match self {
AsyncMmapFileMutInner::Empty(inner) => AsyncMmapFileMutExt::flush_async(inner),
AsyncMmapFileMutInner::Memory(inner) => AsyncMmapFileMutExt::flush_async(inner),
AsyncMmapFileMutInner::Disk(inner) => AsyncMmapFileMutExt::flush_async(inner),
}
}
#[inline]
fn flush_range(&self, offset: usize, len: usize) -> Result<()> {
match self {
AsyncMmapFileMutInner::Empty(inner) => AsyncMmapFileMutExt::flush_range(
inner,
offset,
len,
),
AsyncMmapFileMutInner::Memory(inner) => AsyncMmapFileMutExt::flush_range(
inner,
offset,
len,
),
AsyncMmapFileMutInner::Disk(inner) => AsyncMmapFileMutExt::flush_range(
inner,
offset,
len,
),
}
}
#[inline]
fn flush_async_range(&self, offset: usize, len: usize) -> Result<()> {
match self {
AsyncMmapFileMutInner::Empty(inner) => AsyncMmapFileMutExt::flush_async_range(
inner,
offset,
len,
),
AsyncMmapFileMutInner::Memory(inner) => AsyncMmapFileMutExt::flush_async_range(
inner,
offset,
len,
),
AsyncMmapFileMutInner::Disk(inner) => AsyncMmapFileMutExt::flush_async_range(
inner,
offset,
len,
),
}
}
async fn truncate(&mut self, max_sz: u64) -> Result<()> {
match self {
AsyncMmapFileMutInner::Empty(inner) => {
AsyncMmapFileMutExt::truncate(inner, max_sz).await
}
AsyncMmapFileMutInner::Memory(inner) => {
AsyncMmapFileMutExt::truncate(inner, max_sz).await
}
AsyncMmapFileMutInner::Disk(inner) => {
AsyncMmapFileMutExt::truncate(inner, max_sz).await
}
}
}
async fn drop_remove(self) -> Result<()> {
match self {
AsyncMmapFileMutInner::Empty(inner) => AsyncMmapFileMutExt::drop_remove(inner).await,
AsyncMmapFileMutInner::Memory(inner) => AsyncMmapFileMutExt::drop_remove(inner).await,
AsyncMmapFileMutInner::Disk(inner) => AsyncMmapFileMutExt::drop_remove(inner).await,
}
}
async fn close_with_truncate(self, max_sz: i64) -> Result<()> {
match self {
AsyncMmapFileMutInner::Empty(inner) => {
AsyncMmapFileMutExt::close_with_truncate(inner, max_sz).await
}
AsyncMmapFileMutInner::Memory(inner) => {
AsyncMmapFileMutExt::close_with_truncate(inner, max_sz).await
}
AsyncMmapFileMutInner::Disk(inner) => {
AsyncMmapFileMutExt::close_with_truncate(inner, max_sz).await
}
}
}
}
};
}
macro_rules! declare_and_impl_async_mmap_file {
($filename_prefix: literal, $doc_test_runtime: literal, $path_str: literal) => {
/// A read-only memory map file.
///
/// There is 3 status of this struct:
/// - __Disk__: mmap to a real file
/// - __Memory__: use [`Bytes`] to mock a mmap, which is useful for test and in-memory storage engine
/// - __Empty__: a state represents null mmap, which is helpful for drop, close the `AsyncMmapFile`. This state cannot be constructed directly.
///
/// [`Bytes`]: https://docs.rs/bytes/1.1.0/bytes/struct.Bytes.html
#[repr(transparent)]
pub struct AsyncMmapFile {
inner: AsyncMmapFileInner
}
impl_from!(AsyncMmapFile, AsyncMmapFileInner, [AsyncEmptyMmapFile, AsyncMemoryMmapFile, AsyncDiskMmapFile]);
impl_async_mmap_file_ext!(AsyncMmapFile);
impl AsyncMmapFile {
/// Open a readable memory map backed by a file
///
/// # Examples
///
#[doc = "```ignore"]
#[doc = concat!("use fmmap::", $path_str, "::{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_test.txt\").await.unwrap();")]
#[doc = concat!(" # defer!(std::fs::remove_file(\"", $filename_prefix, "_open_test.txt\").unwrap());")]
/// # file.truncate(12).await.unwrap();
/// # file.write_all("some data...".as_bytes(), 0).unwrap();
/// # file.flush().unwrap();
/// # drop(file);
/// // mmap the file
#[doc = concat!("let mut file = AsyncMmapFile::open(\"", $filename_prefix, "_open_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 = "```"]
pub async fn open<P: AsRef<Path>>(path: P) -> Result<Self> {
Ok(Self::from(AsyncDiskMmapFile::open(path).await?))
}
/// Open a readable memory map backed by a file with [`Options`]
///
/// # 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_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 opts = AsyncOptions::new()
/// // mmap content after the sanity text
/// .offset("sanity text".as_bytes().len() as u64);
/// // mmap the file
#[doc = concat!("let mut file = AsyncMmapFile::open_with_options(\"", $filename_prefix, "_open_with_options_test.txt\", opts).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 = "```"]
///
#[doc = concat!("[`AsyncOptions`]: ", $path_str, "/struct.AsyncOptions.html")]
pub async fn open_with_options<P: AsRef<Path>>(path: P, opts: AsyncOptions) -> Result<Self> {
Ok(Self::from(AsyncDiskMmapFile::open_with_options(path, opts).await?))
}
/// Open a readable and executable memory map backed by a file
///
/// # Examples
///
#[doc = "```ignore"]
#[doc = concat!("use fmmap::", $path_str, "::{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_test.txt\").await.unwrap();")]
#[doc = concat!(" # defer!(std::fs::remove_file(\"", $filename_prefix, "_open_exec_test.txt\").unwrap());")]
/// # file.truncate(12).await.unwrap();
/// # file.write_all("some data...".as_bytes(), 0).unwrap();
/// # file.flush().unwrap();
/// # drop(file);
/// // mmap the file
#[doc = concat!("let mut file = AsyncMmapFile::open_exec(\"", $filename_prefix, "_open_exec_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 = "```"]
pub async fn open_exec<P: AsRef<Path>>(path: P) -> Result<Self> {
Ok(Self::from(AsyncDiskMmapFile::open_exec(path).await?))
}
/// Open a readable and executable memory map backed by a file with [`Options`].
///
/// # 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 opts = AsyncOptions::new()
/// // mmap content after the sanity text
/// .offset("sanity text".as_bytes().len() as u64);
/// // mmap the file
#[doc = concat!("let mut file = AsyncMmapFile::open_exec_with_options(\"", $filename_prefix, "_open_exec_with_options_test.txt\", opts).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 = "```"]
///
#[doc = concat!("[`AsyncOptions`]: ", $path_str, "/struct.AsyncOptions.html")]
pub async fn open_exec_with_options<P: AsRef<Path>>(path: P, opts: AsyncOptions) -> Result<Self> {
Ok(Self::from(AsyncDiskMmapFile::open_exec_with_options(path, opts).await?))
}
}
impl_constructor_for_memory_mmap_file!(AsyncMemoryMmapFile, AsyncMmapFile, "AsyncMmapFile", $path_str);
};
}
macro_rules! delcare_and_impl_async_mmap_file_mut {
($filename_prefix: literal, $doc_test_runtime: literal, $path_str: literal) => {
/// A writable memory map file.
///
/// There is 3 status of this struct:
/// - __Disk__: mmap to a real file
/// - __Memory__: use [`BytesMut`] to mock a mmap, which is useful for test and in-memory storage engine
/// - __Empty__: a state represents null mmap, which is helpful for drop, remove, close the `AsyncMmapFileMut`. This state cannot be constructed directly.
///
/// [`BytesMut`]: https://docs.rs/bytes/1.1.0/bytes/struct.BytesMut.html
pub struct AsyncMmapFileMut {
inner: AsyncMmapFileMutInner,
remove_on_drop: bool,
deleted: bool,
}
impl_from_mut!(AsyncMmapFileMut, AsyncMmapFileMutInner, [AsyncEmptyMmapFile, AsyncMemoryMmapFileMut, AsyncDiskMmapFileMut]);
impl_async_mmap_file_ext!(AsyncMmapFileMut);
impl_async_mmap_file_mut_ext!($filename_prefix, $doc_test_runtime, $path_str);
impl AsyncMmapFileMut {
/// Create a new file and mmap this file
///
/// # Notes
/// The new file is zero size, so, before write, you should truncate first.
/// Or you can use [`create_with_options`] and set `max_size` field for [`AsyncOptions`] to enable directly write
/// without truncating.
/// # Examples
///
#[doc = "```ignore"]
#[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, "_create_test.txt\").await.unwrap();")]
#[doc = concat!("# defer!(std::fs::remove_file(\"", $filename_prefix, "_create_test.txt\").unwrap());")]
/// file.truncate(12).await;
/// file.write_all("some data...".as_bytes(), 0).unwrap();
/// file.flush().unwrap();
/// # })
#[doc = "```"]
///
#[doc = concat!("[`create_with_options`]: ", $path_str, "/struct.AsyncMmapFileMut.html#method.create_with_options")]
#[doc = concat!("[`AsyncOptions`]: ", $path_str, "/struct.AsyncOptions.html")]
pub async fn create<P: AsRef<Path>>(path: P) -> Result<Self> {
Ok(Self::from(AsyncDiskMmapFileMut::create(path).await?))
}
/// Create a new file and mmap this file with [`AsyncOptions`]
///
/// # Example
///
#[doc = "```ignore"]
#[doc = concat!("use fmmap::", $path_str, "::{AsyncOptions, AsyncMmapFileMut, AsyncMmapFileMutExt};")]
/// # use scopeguard::defer;
///
#[doc = concat!("# ", $doc_test_runtime, "::block_on(async {")]
/// let opts = AsyncOptions::new()
/// // truncate to 100
/// .max_size(100);
#[doc = concat!("let mut file = AsyncMmapFileMut::create_with_options(\"", $filename_prefix, "_create_with_options_test.txt\", opts).await.unwrap();")]
#[doc = concat!("# defer!(std::fs::remove_file(\"", $filename_prefix, "_create_with_options_test.txt\").unwrap());")]
/// file.write_all("some data...".as_bytes(), 0).unwrap();
/// file.flush().unwrap();
/// # })
#[doc = "```"]
///
#[doc = concat!("[`AsyncOptions`]: ", $path_str, "/struct.AsyncOptions.html")]
pub async fn create_with_options<P: AsRef<Path>>(path: P, opts: AsyncOptions) -> Result<Self> {
Ok(Self::from(AsyncDiskMmapFileMut::create_with_options(path, opts).await?))
}
/// Open or Create(if not exists) a file and mmap this file.
///
/// # Notes
/// If the file does not exist, then the new file will be open in zero size, so before do write, you should truncate first.
/// Or you can use [`open_with_options`] and set `max_size` field for [`AsyncOptions`] to enable directly write
/// without truncating.
///
/// # Examples
///
/// File already exists
///
#[doc = "```ignore"]
#[doc = concat!("use fmmap::", $path_str, "::{AsyncMmapFileMut, AsyncMmapFileExt, AsyncMmapFileMutExt};")]
/// # use scopeguard::defer;
///
#[doc = concat!("# ", $doc_test_runtime, "::block_on(async {")]
#[doc = concat!("# let mut file = AsyncMmapFileMut::create(\"", $filename_prefix, "_open_test.txt\").await.unwrap();")]
#[doc = concat!("# defer!(std::fs::remove_file(\"", $filename_prefix, "_open_test.txt\").unwrap());")]
/// # file.truncate(12).await.unwrap();
/// # file.write_all("some data...".as_bytes(), 0).unwrap();
/// # file.flush().unwrap();
/// # drop(file);
///
/// // mmap the file
#[doc = concat!("let mut file = AsyncMmapFileMut::open(\"", $filename_prefix, "_open_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() 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_test.txt\").await.unwrap();")]
/// file.read_exact(buf.as_mut_slice(), 0).unwrap();
/// assert_eq!(buf.as_slice(), "some modified data...".as_bytes());
/// # })
#[doc = "```"]
///
/// File does not exists
///
#[doc = "```ignore"]
#[doc = concat!("use fmmap::", $path_str, "::{AsyncMmapFileMut, AsyncMmapFileExt, AsyncMmapFileMutExt};")]
/// # use scopeguard::defer;
///
#[doc = concat!("# ", $doc_test_runtime, "::block_on(async {")]
/// // mmap the file
#[doc = concat!("let mut file = AsyncMmapFileMut::open(\"", $filename_prefix, "_open_test.txt\").await.unwrap();")]
#[doc = concat!("# defer!(std::fs::remove_file(\"", $filename_prefix, "_open_test.txt\").unwrap());")]
/// file.truncate(100).await.unwrap();
/// file.write_all("some data...".as_bytes(), 0).unwrap();
/// file.flush().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_test.txt\").await.unwrap();")]
/// file.read_exact(buf.as_mut_slice(), 0).unwrap();
/// assert_eq!(buf.as_slice(), "some modified data...".as_bytes());
/// # })
#[doc = "```"]
///
#[doc = concat!("[`open_with_options`]: ", $path_str, "/struct.AsyncMmapFileMut.html#method.open_with_options")]
#[doc = concat!("[`AsyncOptions`]: ", $path_str, "/struct.AsyncOptions.html")]
pub async fn open<P: AsRef<Path>>(path: P) -> Result<Self> {
Ok(Self::from(AsyncDiskMmapFileMut::open(path).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);
///
/// // mmap the file
/// let opts = 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!("let mut file = AsyncMmapFileMut::open_with_options(\"", $filename_prefix, "_open_with_options_test.txt\", opts).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 opts = AsyncOptions::new()
/// // allow read
/// .read(true)
/// // allow write
/// .write(true)
/// // allow append
/// .append(true)
/// // truncate to 100
/// .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());")]
/// 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 = "```"]
///
#[doc = concat!("[`AsyncOptions`]: ", $path_str, "/struct.AsyncOptions.html")]
pub async fn open_with_options<P: AsRef<Path>>(path: P, opts: AsyncOptions) -> Result<Self> {
Ok(Self::from(AsyncDiskMmapFileMut::open_with_options(path, opts).await?))
}
/// Open an existing file and mmap this file
///
/// # Examples
#[doc = "```ignore"]
#[doc = concat!("use fmmap::", $path_str, "::{AsyncMmapFileMut, AsyncMmapFileExt, AsyncMmapFileMutExt};")]
/// # 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.txt\").await.unwrap();")]
#[doc = concat!("# defer!(std::fs::remove_file(\"", $filename_prefix, "_open_existing_test.txt\").unwrap());")]
/// file.truncate(12).await.unwrap();
/// file.write_all("some data...".as_bytes(), 0).unwrap();
/// file.flush().unwrap();
/// drop(file);
///
/// // mmap the file
#[doc = concat!("let mut file = AsyncMmapFileMut::open_exist(\"", $filename_prefix, "_open_existing_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() 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_existing_test.txt\").await.unwrap();")]
/// file.read_exact(buf.as_mut_slice(), 0).unwrap();
/// assert_eq!(buf.as_slice(), "some modified data...".as_bytes());
/// # })
#[doc = "```"]
///
#[doc = concat!("[`AsyncOptions`]: ", $path_str, "/struct.AsyncOptions.html")]
pub async fn open_exist<P: AsRef<Path>>(path: P) -> Result<Self> {
Ok(Self::from(AsyncDiskMmapFileMut::open_exist(path).await?))
}
/// Open an existing file and mmap this file with [`AsyncOptions`]
///
/// # Examples
///
/// ```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 opts = AsyncOptions::new()
/// // truncate to 100
/// .max_size(100)
/// // mmap content after the sanity text
/// .offset("sanity text".as_bytes().len() as u64);
///
#[doc = concat!("let mut file = AsyncMmapFileMut::open_exist_with_options(\"", $filename_prefix, "_open_existing_test_with_options.txt\", opts).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 = "```"]
///
#[doc = concat!("[`AsyncOptions`]: ", $path_str, "/struct.AsyncOptions.html")]
pub async fn open_exist_with_options<P: AsRef<Path>>(path: P, opts: AsyncOptions) -> Result<Self> {
Ok(Self::from(AsyncDiskMmapFileMut::open_exist_with_options(path, opts).await?))
}
/// Open and mmap an existing file in copy-on-write mode(copy-on-write memory map backed by a file).
/// 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};")]
/// # 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_test.txt\").await.unwrap();")]
#[doc = concat!("# defer!(std::fs::remove_file(\"", $filename_prefix, "_open_cow_test.txt\").unwrap());")]
/// file.truncate(12).await.unwrap();
/// file.write_all("some data...".as_bytes(), 0).unwrap();
/// file.flush().unwrap();
/// drop(file);
///
/// // mmap the file
#[doc = concat!("let mut file = AsyncMmapFileMut::open_cow(\"", $filename_prefix, "_open_cow_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.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_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 = "```"]
///
#[doc = concat!("[`AsyncOptions`]: ", $path_str, "/struct.AsyncOptions.html")]
pub async fn open_cow<P: AsRef<Path>>(path: P) -> Result<Self> {
Ok(Self::from(AsyncDiskMmapFileMut::open_cow(path).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 std::io::SeekFrom;
/// # 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 opts = AsyncOptions::new()
/// // mmap content after the sanity text
/// .offset("sanity text".as_bytes().len() as u64);
///
#[doc = concat!("let mut file = AsyncMmapFileMut::open_cow_with_options(\"", $filename_prefix, "_open_cow_with_options_test.txt\", opts).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.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 = "```"]
///
#[doc = concat!("[`AsyncOptions`]: ", $path_str, "/struct.AsyncOptions.html")]
pub async fn open_cow_with_options<P: AsRef<Path>>(path: P, opts: AsyncOptions) -> Result<Self> {
Ok(Self::from(AsyncDiskMmapFileMut::open_cow_with_options(path, opts).await?))
}
/// Make the mmap file read-only.
///
/// # Notes
/// If `remove_on_drop` is set to `true`, then the underlying file will not be removed on drop if this function is invoked. [Read more]
///
/// Returns an immutable version of this memory mapped buffer.
/// If the memory map is file-backed, the file must have been opened with read permissions.
///
/// # Errors
/// This method returns an error when the underlying system call fails,
/// which can happen for a variety of reasons,
/// such as when the file has not been opened with read permissions.
///
/// # Examples
///
#[doc = "```ignore"]
#[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, "_freeze_test.txt\").await.unwrap();")]
#[doc = concat!("# defer!(std::fs::remove_file(\"", $filename_prefix, "_freeze_test.txt\").unwrap());")]
/// file.truncate(12).await;
/// file.write_all("some data...".as_bytes(), 0).unwrap();
/// file.flush().unwrap();
/// // freeze
/// file.freeze().unwrap();
/// # })
#[doc = "```"]
///
#[doc = concat!("[Read more]: ", $path_str, "/struct.AsyncMmapFileMut.html#methods.set_remove_on_drop")]
///
#[inline]
pub fn freeze(mut self) -> Result<AsyncMmapFile> {
let empty = AsyncMmapFileMutInner::Empty(AsyncEmptyMmapFile::default());
// swap the inner to empty
let inner = mem::replace(&mut self.inner, empty);
match inner {
AsyncMmapFileMutInner::Empty(empty) => Ok(AsyncMmapFile::from(empty)), // unreachable, keep this for good measure
AsyncMmapFileMutInner::Memory(memory) => Ok(AsyncMmapFile::from(memory.freeze())),
AsyncMmapFileMutInner::Disk(disk) => Ok(AsyncMmapFile::from(disk.freeze()?)),
}
}
/// Transition the memory map to be readable and executable.
/// If the memory map is file-backed, the file must have been opened with execute permissions.
///
/// # Notes
/// If `remove_on_drop` is set to `true`, then the underlying file will not be removed on drop if this function is invoked. [Read more]
///
/// # Errors
/// This method returns an error when the underlying system call fails,
/// which can happen for a variety of reasons,
/// such as when the file has not been opened with execute permissions
///
/// # Examples
///
#[doc = "```ignore"]
#[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, "_freeze_exec_test.txt\").await.unwrap();")]
#[doc = concat!("# defer!(std::fs::remove_file(\"", $filename_prefix, "_freeze_exec_test.txt\").unwrap());")]
/// file.truncate(12).await;
/// file.write_all("some data...".as_bytes(), 0).unwrap();
/// file.flush().unwrap();
/// // freeze_exec
/// file.freeze_exec().unwrap();
/// # })
#[doc = "```"]
///
#[doc = concat!("[Read more]: ", $path_str, "/struct.AsyncMmapFileMut.html#methods.set_remove_on_drop")]
///
#[inline]
pub fn freeze_exec(mut self) -> Result<AsyncMmapFile> {
let empty = AsyncMmapFileMutInner::Empty(AsyncEmptyMmapFile::default());
// swap the inner to empty
let inner = mem::replace(&mut self.inner, empty);
match inner {
AsyncMmapFileMutInner::Empty(empty) => Ok(AsyncMmapFile::from(empty)), // unreachable, keep this for good measure
AsyncMmapFileMutInner::Memory(memory) => Ok(AsyncMmapFile::from(memory.freeze())),
AsyncMmapFileMutInner::Disk(disk) => Ok(AsyncMmapFile::from(disk.freeze_exec()?)),
}
}
/// Returns whether remove the underlying file on drop.
#[inline]
pub fn get_remove_on_drop(&self) -> bool {
self.remove_on_drop
}
/// Whether remove the underlying file on drop.
/// Default is false.
///
/// # Notes
/// If invoke [`AsyncMmapFileMut::freeze`], then the file will
/// not be removed even though the field `remove_on_drop` is true.
///
/// [`AsyncMmapFileMut::freeze`]: structs.AsyncMmapFileMut.html#methods.freeze
#[inline]
pub fn set_remove_on_drop(&mut self, val: bool) {
self.remove_on_drop = val;
}
/// Close the file. It would also truncate the file if max_sz >= 0.
#[inline]
pub async fn close(&mut self, max_sz: i64) -> Result<()> {
let empty = AsyncMmapFileMutInner::Empty(AsyncEmptyMmapFile::default());
// swap the inner to empty
let inner = mem::replace(&mut self.inner, empty);
match inner {
AsyncMmapFileMutInner::Disk(disk) => {
disk.flush()?;
if max_sz >= 0 {
disk.file.set_len(max_sz as u64).await.map_err(From::from)
} else {
Ok(())
}
},
_ => Ok(()),
}
}
/// Remove the underlying file without dropping, leaving an [`AsyncEmptyMmapFile`].
#[inline]
pub async fn remove(&mut self) -> Result<()> {
let empty = AsyncMmapFileMutInner::Empty(AsyncEmptyMmapFile::default());
// swap the inner to empty
let inner = mem::replace(&mut self.inner, empty);
match inner {
AsyncMmapFileMutInner::Disk(disk) => {
let path = disk.path;
drop(disk.mmap);
disk.file
.set_len(0)
.await?;
drop(disk.file);
remove_file(path)
.await
.map_err(From::from)
},
_ => Ok(()),
}
}
}
impl_constructor_for_memory_mmap_file_mut!(AsyncMemoryMmapFileMut, AsyncMmapFileMut, "AsyncMmapFileMut", $path_str);
impl_drop!(AsyncMmapFileMut, AsyncMmapFileMutInner, AsyncEmptyMmapFile);
};
}
macro_rules! file_lock_tests {
($filename_prefix: literal, $runtime: meta) => {
#[$runtime]
async fn test_flush() {
let path = concat!($filename_prefix, "_flush.txt");
let mut file1 = AsyncMmapFileMut::create_with_options(path, AsyncOptions::new().max_size(100)).await.unwrap();
file1.set_remove_on_drop(true);
file1.write_all(vec![1; 100].as_slice(), 0).unwrap();
file1.flush_range(0, 10).unwrap();
file1.flush_async_range(11, 20).unwrap();
file1.flush_async().unwrap();
}
#[$runtime]
async fn test_lock_shared() {
let path = concat!($filename_prefix, "_lock_shared.txt");
let file1 = AsyncMmapFileMut::open(path).await.unwrap();
let file2 = AsyncMmapFileMut::open(path).await.unwrap();
let file3 = AsyncMmapFileMut::open(path).await.unwrap();
defer!(std::fs::remove_file(path).unwrap());
// Concurrent shared access is OK, but not shared and exclusive.
file1.lock_shared().unwrap();
file2.lock_shared().unwrap();
assert!(file3.try_lock_exclusive().is_err());
file1.unlock().unwrap();
assert!(file3.try_lock_exclusive().is_err());
// Once all shared file locks are dropped, an exclusive lock may be created;
file2.unlock().unwrap();
file3.lock_exclusive().unwrap();
}
#[$runtime]
async fn test_lock_exclusive() {
let path = concat!($filename_prefix, "_lock_exclusive.txt");
defer!(std::fs::remove_file(path).unwrap());
let file1 = AsyncMmapFileMut::open(path).await.unwrap();
let file2 = AsyncMmapFileMut::open(path).await.unwrap();
// No other access is possible once an exclusive lock is created.
file1.lock_exclusive().unwrap();
assert!(file2.try_lock_exclusive().is_err());
assert!(file2.try_lock_shared().is_err());
// Once the exclusive lock is dropped, the second file is able to create a lock.
file1.unlock().unwrap();
file2.lock_exclusive().unwrap();
}
#[$runtime]
async fn test_lock_cleanup() {
let path = concat!($filename_prefix, "_lock_cleanup.txt");
defer!(std::fs::remove_file(path).unwrap());
let file1 = AsyncMmapFileMut::open(path).await.unwrap();
let file2 = AsyncMmapFileMut::open(path).await.unwrap();
// No other access is possible once an exclusive lock is created.
file1.lock_exclusive().unwrap();
assert!(file2.try_lock_exclusive().is_err());
assert!(file2.try_lock_shared().is_err());
// Drop file1; the lock should be released.
drop(file1);
file2.lock_shared().unwrap();
}
};
}
}
cfg_async_std!(
pub(crate) mod async_std_impl;
);
cfg_smol!(
pub(crate) mod smol_impl;
);
cfg_tokio!(
pub(crate) mod tokio_impl;
);