//! A cross-platform Rust API for memory mapped buffers. //! //! The core functionality is provided by either [`Mmap`] or [`MmapMut`], //! which correspond to mapping a [`File`] to a [`&[u8]`](https://doc.rust-lang.org/std/primitive.slice.html) //! or [`&mut [u8]`](https://doc.rust-lang.org/std/primitive.slice.html) //! respectively. Both function by dereferencing to a slice, allowing the //! [`Mmap`]/[`MmapMut`] to be used in the same way you would the equivalent slice //! types. //! //! [`File`]: std::fs::File //! //! # Examples //! //! For simple cases [`Mmap`] can be used directly: //! //! ``` //! use std::fs::File; //! use std::io::Read; //! //! use memmap2::Mmap; //! //! # fn main() -> std::io::Result<()> { //! let mut file = File::open("LICENSE-APACHE")?; //! //! let mut contents = Vec::new(); //! file.read_to_end(&mut contents)?; //! //! let mmap = unsafe { Mmap::map(&file)? }; //! //! assert_eq!(&contents[..], &mmap[..]); //! # Ok(()) //! # } //! ``` //! //! However for cases which require configuration of the mapping, then //! you can use [`MmapOptions`] in order to further configure a mapping //! before you create it.
#[cfg(unix)] mod advice; #[cfg(unix)] pubusecrate::advice::{Advice, UncheckedAdvice};
use std::fmt; #[cfg(not(any(unix, windows)))] use std::fs::File; use std::io::{Error, ErrorKind, Result}; use std::isize; use std::mem; use std::ops::{Deref, DerefMut}; #[cfg(unix)] use std::os::unix::io::{AsRawFd, RawFd}; #[cfg(windows)] use std::os::windows::io::{AsRawHandle, RawHandle}; use std::slice;
#[cfg(windows)] impl<'a, T> MmapAsRawDesc for &'a T where
T: AsRawHandle,
{ fn as_raw_desc(&self) -> MmapRawDescriptor {
MmapRawDescriptor(self.as_raw_handle())
}
}
/// A memory map builder, providing advanced options and flags for specifying memory map behavior. /// /// `MmapOptions` can be used to create an anonymous memory map using [`map_anon()`], or a /// file-backed memory map using one of [`map()`], [`map_mut()`], [`map_exec()`], /// [`map_copy()`], or [`map_copy_read_only()`]. /// /// ## Safety /// /// All file-backed memory map constructors are marked `unsafe` because of the potential for /// *Undefined Behavior* (UB) using the map if the underlying file is subsequently modified, in or /// out of process. Applications must consider the risk and take appropriate precautions when /// using file-backed maps. Solutions such as file permissions, locks or process-private (e.g. /// unlinked) files exist but are platform specific and limited. /// /// [`map_anon()`]: MmapOptions::map_anon() /// [`map()`]: MmapOptions::map() /// [`map_mut()`]: MmapOptions::map_mut() /// [`map_exec()`]: MmapOptions::map_exec() /// [`map_copy()`]: MmapOptions::map_copy() /// [`map_copy_read_only()`]: MmapOptions::map_copy_read_only() #[derive(Clone, Debug, Default)] pubstruct MmapOptions {
offset: u64,
len: Option<usize>,
huge: Option<u8>,
stack: bool,
populate: bool,
}
impl MmapOptions { /// Creates a new set of options for configuring and creating a memory map. /// /// # Example /// /// ``` /// use memmap2::{MmapMut, MmapOptions}; /// # use std::io::Result; /// /// # fn main() -> Result<()> { /// // Create a new memory map builder. /// let mut mmap_options = MmapOptions::new(); /// /// // Configure the memory map builder using option setters, then create /// // a memory map using one of `mmap_options.map_anon`, `mmap_options.map`, /// // `mmap_options.map_mut`, `mmap_options.map_exec`, or `mmap_options.map_copy`: /// let mut mmap: MmapMut = mmap_options.len(36).map_anon()?; /// /// // Use the memory map: /// mmap.copy_from_slice(b"...data to copy to the memory map..."); /// # Ok(()) /// # } /// ``` pubfn new() -> MmapOptions {
MmapOptions::default()
}
/// Configures the memory map to start at byte `offset` from the beginning of the file. /// /// This option has no effect on anonymous memory maps. /// /// By default, the offset is 0. /// /// # Example /// /// ``` /// use memmap2::MmapOptions; /// use std::fs::File; /// /// # fn main() -> std::io::Result<()> { /// let mmap = unsafe { /// MmapOptions::new() /// .offset(30) /// .map(&File::open("LICENSE-APACHE")?)? /// }; /// assert_eq!(&b"Apache License"[..], /// &mmap[..14]); /// # Ok(()) /// # } /// ``` pubfn offset(&mutself, offset: u64) -> &mutSelf { self.offset = offset; self
}
/// Configures the created memory mapped buffer to be `len` bytes long. /// /// This option is mandatory for anonymous memory maps. /// /// For file-backed memory maps, the length will default to the file length. /// /// # Example /// /// ``` /// use memmap2::MmapOptions; /// use std::fs::File; /// /// # fn main() -> std::io::Result<()> { /// let mmap = unsafe { /// MmapOptions::new() /// .len(9) /// .map(&File::open("README.md")?)? /// }; /// assert_eq!(&b"# memmap2"[..], &mmap[..]); /// # Ok(()) /// # } /// ``` pubfn len(&mutself, len: usize) -> &mutSelf { self.len = Some(len); self
}
/// Returns the configured length, or the length of the provided file. fn get_len<T: MmapAsRawDesc>(&self, file: &T) -> Result<usize> { self.len.map(Ok).unwrap_or_else(|| { let desc = file.as_raw_desc(); let file_len = file_len(desc.0)?;
if file_len < self.offset { return Err(Error::new(
ErrorKind::InvalidData, "memory map offset is larger than length",
));
} let len = file_len - self.offset;
/// Configures the anonymous memory map to be suitable for a process or thread stack. /// /// This option corresponds to the `MAP_STACK` flag on Linux. It has no effect on Windows. /// /// This option has no effect on file-backed memory maps. /// /// # Example /// /// ``` /// use memmap2::MmapOptions; /// /// # fn main() -> std::io::Result<()> { /// let stack = MmapOptions::new().stack().len(4096).map_anon(); /// # Ok(()) /// # } /// ``` pubfn stack(&mutself) -> &mutSelf { self.stack = true; self
}
/// Configures the anonymous memory map to be allocated using huge pages. /// /// This option corresponds to the `MAP_HUGETLB` flag on Linux. It has no effect on Windows. /// /// The size of the requested page can be specified in page bits. If not provided, the system /// default is requested. The requested length should be a multiple of this, or the mapping /// will fail. /// /// This option has no effect on file-backed memory maps. /// /// # Example /// /// ``` /// use memmap2::MmapOptions; /// /// # fn main() -> std::io::Result<()> { /// let stack = MmapOptions::new().huge(Some(21)).len(2*1024*1024).map_anon(); /// # Ok(()) /// # } /// ``` pubfn huge(&mutself, page_bits: Option<u8>) -> &mutSelf { self.huge = Some(page_bits.unwrap_or(0)); self
} /// Populate (prefault) page tables for a mapping. /// /// For a file mapping, this causes read-ahead on the file. This will help to reduce blocking on page faults later. /// /// This option corresponds to the `MAP_POPULATE` flag on Linux. It has no effect on Windows. /// /// # Example /// /// ``` /// use memmap2::MmapOptions; /// use std::fs::File; /// /// # fn main() -> std::io::Result<()> { /// let file = File::open("LICENSE-MIT")?; /// /// let mmap = unsafe { /// MmapOptions::new().populate().map(&file)? /// }; /// /// assert_eq!(&b"Copyright"[..], &mmap[..9]); /// # Ok(()) /// # } /// ``` pubfn populate(&mutself) -> &mutSelf { self.populate = true; self
}
/// Creates a read-only memory map backed by a file. /// /// # 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 is not open with read permissions. /// /// # Example /// /// ``` /// use memmap2::MmapOptions; /// use std::fs::File; /// use std::io::Read; /// /// # fn main() -> std::io::Result<()> { /// let mut file = File::open("LICENSE-APACHE")?; /// /// let mut contents = Vec::new(); /// file.read_to_end(&mut contents)?; /// /// let mmap = unsafe { /// MmapOptions::new().map(&file)? /// }; /// /// assert_eq!(&contents[..], &mmap[..]); /// # Ok(()) /// # } /// ``` pubunsafefn map<T: MmapAsRawDesc>(&self, file: T) -> Result<Mmap> { let desc = file.as_raw_desc();
/// Creates a readable and executable memory map backed by a file. /// /// # 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 is not open with read permissions. pubunsafefn map_exec<T: MmapAsRawDesc>(&self, file: T) -> Result<Mmap> { let desc = file.as_raw_desc();
/// Creates a writeable memory map backed by a file. /// /// # 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 is not open with read and write permissions. /// /// # Example /// /// ``` /// # extern crate memmap2; /// # extern crate tempfile; /// # /// use std::fs::OpenOptions; /// use std::path::PathBuf; /// /// use memmap2::MmapOptions; /// # /// # fn main() -> std::io::Result<()> { /// # let tempdir = tempfile::tempdir()?; /// let path: PathBuf = /* path to file */ /// # tempdir.path().join("map_mut"); /// let file = OpenOptions::new().read(true).write(true).create(true).open(&path)?; /// file.set_len(13)?; /// /// let mut mmap = unsafe { /// MmapOptions::new().map_mut(&file)? /// }; /// /// mmap.copy_from_slice(b"Hello, world!"); /// # Ok(()) /// # } /// ``` pubunsafefn map_mut<T: MmapAsRawDesc>(&self, file: T) -> Result<MmapMut> { let desc = file.as_raw_desc();
/// Creates a 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. /// /// # 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 is not open with writable permissions. /// /// # Example /// /// ``` /// use memmap2::MmapOptions; /// use std::fs::File; /// use std::io::Write; /// /// # fn main() -> std::io::Result<()> { /// let file = File::open("LICENSE-APACHE")?; /// let mut mmap = unsafe { MmapOptions::new().map_copy(&file)? }; /// (&mut mmap[..]).write_all(b"Hello, world!")?; /// # Ok(()) /// # } /// ``` pubunsafefn map_copy<T: MmapAsRawDesc>(&self, file: T) -> Result<MmapMut> { let desc = file.as_raw_desc();
/// Creates a copy-on-write read-only memory map backed by a file. /// /// # 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 is not open with read permissions. /// /// # Example /// /// ``` /// use memmap2::MmapOptions; /// use std::fs::File; /// use std::io::Read; /// /// # fn main() -> std::io::Result<()> { /// let mut file = File::open("README.md")?; /// /// let mut contents = Vec::new(); /// file.read_to_end(&mut contents)?; /// /// let mmap = unsafe { /// MmapOptions::new().map_copy_read_only(&file)? /// }; /// /// assert_eq!(&contents[..], &mmap[..]); /// # Ok(()) /// # } /// ``` pubunsafefn map_copy_read_only<T: MmapAsRawDesc>(&self, file: T) -> Result<Mmap> { let desc = file.as_raw_desc();
/// Creates an anonymous memory map. /// /// The memory map length should be configured using [`MmapOptions::len()`] /// before creating an anonymous memory map, otherwise a zero-length mapping /// will be crated. /// /// # Errors /// /// This method returns an error when the underlying system call fails or /// when `len > isize::MAX`. pubfn map_anon(&self) -> Result<MmapMut> { let len = self.len.unwrap_or(0);
// See get_len() for details. if mem::size_of::<usize>() < 8 && len > isize::MAX as usize { return Err(Error::new(
ErrorKind::InvalidData, "memory map length overflows isize",
));
}
/// Creates a raw memory map. /// /// # 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 is not open with read and write permissions. pubfn map_raw<T: MmapAsRawDesc>(&self, file: T) -> Result<MmapRaw> { let desc = file.as_raw_desc();
/// Creates a read-only raw memory map /// /// This is primarily useful to avoid intermediate `Mmap` instances when /// read-only access to files modified elsewhere are required. /// /// # Errors /// /// This method returns an error when the underlying system call fails pubfn map_raw_read_only<T: MmapAsRawDesc>(&self, file: T) -> Result<MmapRaw> { let desc = file.as_raw_desc();
/// A handle to an immutable memory mapped buffer. /// /// A `Mmap` may be backed by a file, or it can be anonymous map, backed by volatile memory. Use /// [`MmapOptions`] or [`map()`] to create a file-backed memory map. To create an immutable /// anonymous memory map, first create a mutable anonymous memory map, and then make it immutable /// with [`MmapMut::make_read_only()`]. /// /// A file backed `Mmap` is created by `&File` reference, and will remain valid even after the /// `File` is dropped. In other words, the `Mmap` handle is completely independent of the `File` /// used to create it. For consistency, on some platforms this is achieved by duplicating the /// underlying file handle. The memory will be unmapped when the `Mmap` handle is dropped. /// /// Dereferencing and accessing the bytes of the buffer may result in page faults (e.g. swapping /// the mapped pages into physical memory) though the details of this are platform specific. /// /// `Mmap` is [`Sync`] and [`Send`]. /// /// ## Safety /// /// All file-backed memory map constructors are marked `unsafe` because of the potential for /// *Undefined Behavior* (UB) using the map if the underlying file is subsequently modified, in or /// out of process. Applications must consider the risk and take appropriate precautions when using /// file-backed maps. Solutions such as file permissions, locks or process-private (e.g. unlinked) /// files exist but are platform specific and limited. /// /// ## Example /// /// ``` /// use memmap2::MmapOptions; /// use std::io::Write; /// use std::fs::File; /// /// # fn main() -> std::io::Result<()> { /// let file = File::open("README.md")?; /// let mmap = unsafe { MmapOptions::new().map(&file)? }; /// assert_eq!(b"# memmap2", &mmap[0..9]); /// # Ok(()) /// # } /// ``` /// /// See [`MmapMut`] for the mutable version. /// /// [`map()`]: Mmap::map() pubstruct Mmap {
inner: MmapInner,
}
impl Mmap { /// Creates a read-only memory map backed by a file. /// /// This is equivalent to calling `MmapOptions::new().map(file)`. /// /// # 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 is not open with read permissions. /// /// # Example /// /// ``` /// use std::fs::File; /// use std::io::Read; /// /// use memmap2::Mmap; /// /// # fn main() -> std::io::Result<()> { /// let mut file = File::open("LICENSE-APACHE")?; /// /// let mut contents = Vec::new(); /// file.read_to_end(&mut contents)?; /// /// let mmap = unsafe { Mmap::map(&file)? }; /// /// assert_eq!(&contents[..], &mmap[..]); /// # Ok(()) /// # } /// ``` pubunsafefn map<T: MmapAsRawDesc>(file: T) -> Result<Mmap> {
MmapOptions::new().map(file)
}
/// Transition the memory map to be writable. /// /// If the memory map is file-backed, the file must have been opened with write 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 is not open with writable permissions. /// /// # Example /// /// ``` /// # extern crate memmap2; /// # extern crate tempfile; /// # /// use memmap2::Mmap; /// use std::ops::DerefMut; /// use std::io::Write; /// # use std::fs::OpenOptions; /// /// # fn main() -> std::io::Result<()> { /// # let tempdir = tempfile::tempdir()?; /// let file = /* file opened with write permissions */ /// # OpenOptions::new() /// # .read(true) /// # .write(true) /// # .create(true) /// # .open(tempdir.path() /// # .join("make_mut"))?; /// # file.set_len(128)?; /// let mmap = unsafe { Mmap::map(&file)? }; /// // ... use the read-only memory map ... /// let mut mut_mmap = mmap.make_mut()?; /// mut_mmap.deref_mut().write_all(b"hello, world!")?; /// # Ok(()) /// # } /// ``` pubfn make_mut(mutself) -> Result<MmapMut> { self.inner.make_mut()?;
Ok(MmapMut { inner: self.inner })
}
/// Advise OS how this memory map will be accessed. /// /// Only supported on Unix. /// /// See [madvise()](https://man7.org/linux/man-pages/man2/madvise.2.html) map page. #[cfg(unix)] pubfn advise(&self, advice: Advice) -> Result<()> { self.inner
.advise(advice as libc::c_int, 0, self.inner.len())
}
/// Advise OS how this memory map will be accessed. /// /// Used with the [unchecked flags][UncheckedAdvice]. Only supported on Unix. /// /// See [madvise()](https://man7.org/linux/man-pages/man2/madvise.2.html) map page. #[cfg(unix)] pubunsafefn unchecked_advise(&self, advice: UncheckedAdvice) -> Result<()> { self.inner
.advise(advice as libc::c_int, 0, self.inner.len())
}
/// Advise OS how this range of memory map will be accessed. /// /// Only supported on Unix. /// /// The offset and length must be in the bounds of the memory map. /// /// See [madvise()](https://man7.org/linux/man-pages/man2/madvise.2.html) map page. #[cfg(unix)] pubfn advise_range(&self, advice: Advice, offset: usize, len: usize) -> Result<()> { self.inner.advise(advice as libc::c_int, offset, len)
}
/// Advise OS how this range of memory map will be accessed. /// /// Used with the [unchecked flags][UncheckedAdvice]. Only supported on Unix. /// /// The offset and length must be in the bounds of the memory map. /// /// See [madvise()](https://man7.org/linux/man-pages/man2/madvise.2.html) map page. #[cfg(unix)] pubunsafefn unchecked_advise_range(
&self,
advice: UncheckedAdvice,
offset: usize,
len: usize,
) -> Result<()> { self.inner.advise(advice as libc::c_int, offset, len)
}
/// Lock the whole memory map into RAM. Only supported on Unix. /// /// See [mlock()](https://man7.org/linux/man-pages/man2/mlock.2.html) map page. #[cfg(unix)] pubfn lock(&self) -> Result<()> { self.inner.lock()
}
/// Unlock the whole memory map. Only supported on Unix. /// /// See [munlock()](https://man7.org/linux/man-pages/man2/munlock.2.html) map page. #[cfg(unix)] pubfn unlock(&self) -> Result<()> { self.inner.unlock()
}
/// Adjust the size of the memory mapping. /// /// This will try to resize the memory mapping in place. If /// [`RemapOptions::may_move`] is specified it will move the mapping if it /// could not resize in place, otherwise it will error. /// /// Only supported on Linux. /// /// See the [`mremap(2)`] man page. /// /// # Safety /// /// Resizing the memory mapping beyond the end of the mapped file will /// result in UB should you happen to access memory beyond the end of the /// file. /// /// [`mremap(2)`]: https://man7.org/linux/man-pages/man2/mremap.2.html #[cfg(target_os = "linux")] pubunsafefn remap(&mutself, new_len: usize, options: RemapOptions) -> Result<()> { self.inner.remap(new_len, options)
}
}
#[cfg(feature = "stable_deref_trait")] unsafeimpl stable_deref_trait::StableDeref for Mmap {}
/// A handle to a raw memory mapped buffer. /// /// This struct never hands out references to its interior, only raw pointers. /// This can be helpful when creating shared memory maps between untrusted processes. pubstruct MmapRaw {
inner: MmapInner,
}
impl MmapRaw { /// Creates a writeable memory map backed by a file. /// /// This is equivalent to calling `MmapOptions::new().map_raw(file)`. /// /// # 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 is not open with read and write permissions. pubfn map_raw<T: MmapAsRawDesc>(file: T) -> Result<MmapRaw> {
MmapOptions::new().map_raw(file)
}
/// Returns a raw pointer to the memory mapped file. /// /// Before dereferencing this pointer, you have to make sure that the file has not been /// truncated since the memory map was created. /// Avoiding this will not introduce memory safety issues in Rust terms, /// but will cause SIGBUS (or equivalent) signal. #[inline] pubfn as_ptr(&self) -> *const u8 { self.inner.ptr()
}
/// Returns an unsafe mutable pointer to the memory mapped file. /// /// Before dereferencing this pointer, you have to make sure that the file has not been /// truncated since the memory map was created. /// Avoiding this will not introduce memory safety issues in Rust terms, /// but will cause SIGBUS (or equivalent) signal. #[inline] pubfn as_mut_ptr(&self) -> *mut u8 { self.inner.ptr() as _
}
/// Returns the length in bytes of the memory map. /// /// Note that truncating the file can cause the length to change (and render this value unusable). #[inline] pubfn len(&self) -> usize { self.inner.len()
}
/// Flushes outstanding memory map modifications to disk. /// /// 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. /// /// # Example /// /// ``` /// # extern crate memmap2; /// # extern crate tempfile; /// # /// use std::fs::OpenOptions; /// use std::io::Write; /// use std::path::PathBuf; /// use std::slice; /// /// use memmap2::MmapRaw; /// /// # fn main() -> std::io::Result<()> { /// let tempdir = tempfile::tempdir()?; /// let path: PathBuf = /* path to file */ /// # tempdir.path().join("flush"); /// let file = OpenOptions::new().read(true).write(true).create(true).open(&path)?; /// file.set_len(128)?; /// /// let mut mmap = unsafe { MmapRaw::map_raw(&file)? }; /// /// let mut memory = unsafe { slice::from_raw_parts_mut(mmap.as_mut_ptr(), 128) }; /// memory.write_all(b"Hello, world!")?; /// mmap.flush()?; /// # Ok(()) /// # } /// ``` pubfn flush(&self) -> Result<()> { let len = self.len(); self.inner.flush(0, len)
}
/// Asynchronously flushes outstanding memory map modifications to disk. /// /// 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. pubfn flush_async(&self) -> Result<()> { let len = self.len(); self.inner.flush_async(0, len)
}
/// Flushes outstanding memory map modifications in the range to disk. /// /// 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. pubfn flush_range(&self, offset: usize, len: usize) -> Result<()> { self.inner.flush(offset, len)
}
/// Asynchronously flushes outstanding memory map modifications in the range to disk. /// /// 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. pubfn flush_async_range(&self, offset: usize, len: usize) -> Result<()> { self.inner.flush_async(offset, len)
}
/// Advise OS how this memory map will be accessed. /// /// Only supported on Unix. /// /// See [madvise()](https://man7.org/linux/man-pages/man2/madvise.2.html) map page. #[cfg(unix)] pubfn advise(&self, advice: Advice) -> Result<()> { self.inner
.advise(advice as libc::c_int, 0, self.inner.len())
}
/// Advise OS how this memory map will be accessed. /// /// Used with the [unchecked flags][UncheckedAdvice]. Only supported on Unix. /// /// See [madvise()](https://man7.org/linux/man-pages/man2/madvise.2.html) map page. #[cfg(unix)] pubunsafefn unchecked_advise(&self, advice: UncheckedAdvice) -> Result<()> { self.inner
.advise(advice as libc::c_int, 0, self.inner.len())
}
/// Advise OS how this range of memory map will be accessed. /// /// The offset and length must be in the bounds of the memory map. /// /// Only supported on Unix. /// /// See [madvise()](https://man7.org/linux/man-pages/man2/madvise.2.html) map page. #[cfg(unix)] pubfn advise_range(&self, advice: Advice, offset: usize, len: usize) -> Result<()> { self.inner.advise(advice as libc::c_int, offset, len)
}
/// Advise OS how this range of memory map will be accessed. /// /// Used with the [unchecked flags][UncheckedAdvice]. Only supported on Unix. /// /// The offset and length must be in the bounds of the memory map. /// /// See [madvise()](https://man7.org/linux/man-pages/man2/madvise.2.html) map page. #[cfg(unix)] pubunsafefn unchecked_advise_range(
&self,
advice: UncheckedAdvice,
offset: usize,
len: usize,
) -> Result<()> { self.inner.advise(advice as libc::c_int, offset, len)
}
/// Lock the whole memory map into RAM. Only supported on Unix. /// /// See [mlock()](https://man7.org/linux/man-pages/man2/mlock.2.html) map page. #[cfg(unix)] pubfn lock(&self) -> Result<()> { self.inner.lock()
}
/// Unlock the whole memory map. Only supported on Unix. /// /// See [munlock()](https://man7.org/linux/man-pages/man2/munlock.2.html) map page. #[cfg(unix)] pubfn unlock(&self) -> Result<()> { self.inner.unlock()
}
/// Adjust the size of the memory mapping. /// /// This will try to resize the memory mapping in place. If /// [`RemapOptions::may_move`] is specified it will move the mapping if it /// could not resize in place, otherwise it will error. /// /// Only supported on Linux. /// /// See the [`mremap(2)`] man page. /// /// # Safety /// /// Resizing the memory mapping beyond the end of the mapped file will /// result in UB should you happen to access memory beyond the end of the /// file. /// /// [`mremap(2)`]: https://man7.org/linux/man-pages/man2/mremap.2.html #[cfg(target_os = "linux")] pubunsafefn remap(&mutself, new_len: usize, options: RemapOptions) -> Result<()> { self.inner.remap(new_len, options)
}
}
/// A handle to a mutable memory mapped buffer. /// /// A file-backed `MmapMut` buffer may be used to read from or write to a file. An anonymous /// `MmapMut` buffer may be used any place that an in-memory byte buffer is needed. Use /// [`MmapMut::map_mut()`] and [`MmapMut::map_anon()`] to create a mutable memory map of the /// respective types, or [`MmapOptions::map_mut()`] and [`MmapOptions::map_anon()`] if non-default /// options are required. /// /// A file backed `MmapMut` is created by `&File` reference, and will remain valid even after the /// `File` is dropped. In other words, the `MmapMut` handle is completely independent of the `File` /// used to create it. For consistency, on some platforms this is achieved by duplicating the /// underlying file handle. The memory will be unmapped when the `MmapMut` handle is dropped. /// /// Dereferencing and accessing the bytes of the buffer may result in page faults (e.g. swapping /// the mapped pages into physical memory) though the details of this are platform specific. /// /// `Mmap` is [`Sync`] and [`Send`]. /// /// See [`Mmap`] for the immutable version. /// /// ## Safety /// /// All file-backed memory map constructors are marked `unsafe` because of the potential for /// *Undefined Behavior* (UB) using the map if the underlying file is subsequently modified, in or /// out of process. Applications must consider the risk and take appropriate precautions when using /// file-backed maps. Solutions such as file permissions, locks or process-private (e.g. unlinked) /// files exist but are platform specific and limited. pubstruct MmapMut {
inner: MmapInner,
}
impl MmapMut { /// Creates a writeable memory map backed by a file. /// /// This is equivalent to calling `MmapOptions::new().map_mut(file)`. /// /// # 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 is not open with read and write permissions. /// /// # Example /// /// ``` /// # extern crate memmap2; /// # extern crate tempfile; /// # /// use std::fs::OpenOptions; /// use std::path::PathBuf; /// /// use memmap2::MmapMut; /// # /// # fn main() -> std::io::Result<()> { /// # let tempdir = tempfile::tempdir()?; /// let path: PathBuf = /* path to file */ /// # tempdir.path().join("map_mut"); /// let file = OpenOptions::new() /// .read(true) /// .write(true) /// .create(true) /// .open(&path)?; /// file.set_len(13)?; /// /// let mut mmap = unsafe { MmapMut::map_mut(&file)? }; /// /// mmap.copy_from_slice(b"Hello, world!"); /// # Ok(()) /// # } /// ``` pubunsafefn map_mut<T: MmapAsRawDesc>(file: T) -> Result<MmapMut> {
MmapOptions::new().map_mut(file)
}
/// Creates an anonymous memory map. /// /// This is equivalent to calling `MmapOptions::new().len(length).map_anon()`. /// /// # Errors /// /// This method returns an error when the underlying system call fails or /// when `len > isize::MAX`. pubfn map_anon(length: usize) -> Result<MmapMut> {
MmapOptions::new().len(length).map_anon()
}
/// Flushes outstanding memory map modifications to disk. /// /// 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. /// /// # Example /// /// ``` /// # extern crate memmap2; /// # extern crate tempfile; /// # /// use std::fs::OpenOptions; /// use std::io::Write; /// use std::path::PathBuf; /// /// use memmap2::MmapMut; /// /// # fn main() -> std::io::Result<()> { /// # let tempdir = tempfile::tempdir()?; /// let path: PathBuf = /* path to file */ /// # tempdir.path().join("flush"); /// let file = OpenOptions::new().read(true).write(true).create(true).open(&path)?; /// file.set_len(128)?; /// /// let mut mmap = unsafe { MmapMut::map_mut(&file)? }; /// /// (&mut mmap[..]).write_all(b"Hello, world!")?; /// mmap.flush()?; /// # Ok(()) /// # } /// ``` pubfn flush(&self) -> Result<()> { let len = self.len(); self.inner.flush(0, len)
}
/// Asynchronously flushes outstanding memory map modifications to disk. /// /// 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. pubfn flush_async(&self) -> Result<()> { let len = self.len(); self.inner.flush_async(0, len)
}
/// Flushes outstanding memory map modifications in the range to disk. /// /// 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. pubfn flush_range(&self, offset: usize, len: usize) -> Result<()> { self.inner.flush(offset, len)
}
/// Asynchronously flushes outstanding memory map modifications in the range to disk. /// /// 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. pubfn flush_async_range(&self, offset: usize, len: usize) -> Result<()> { self.inner.flush_async(offset, len)
}
/// 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. /// /// # Example /// /// ``` /// # extern crate memmap2; /// # /// use std::io::Write; /// use std::path::PathBuf; /// /// use memmap2::{Mmap, MmapMut}; /// /// # fn main() -> std::io::Result<()> { /// let mut mmap = MmapMut::map_anon(128)?; /// /// (&mut mmap[..]).write(b"Hello, world!")?; /// /// let mmap: Mmap = mmap.make_read_only()?; /// # Ok(()) /// # } /// ``` pubfn make_read_only(mutself) -> Result<Mmap> { self.inner.make_read_only()?;
Ok(Mmap { inner: self.inner })
}
/// 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. /// /// On systems with separate instructions and data caches (a category that includes many ARM /// chips), a platform-specific call may be needed to ensure that the changes are visible to the /// execution unit (e.g. when using this function to implement a JIT compiler). For more /// details, see [this ARM write-up](https://community.arm.com/arm-community-blogs/b/architectures-and-processors-blog/posts/caches-and-self-modifying-code) /// or the `man` page for [`sys_icache_invalidate`](https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man3/sys_icache_invalidate.3.html). /// /// # 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. pubfn make_exec(mutself) -> Result<Mmap> { self.inner.make_exec()?;
Ok(Mmap { inner: self.inner })
}
/// Advise OS how this memory map will be accessed. /// /// Only supported on Unix. /// /// See [madvise()](https://man7.org/linux/man-pages/man2/madvise.2.html) map page. #[cfg(unix)] pubfn advise(&self, advice: Advice) -> Result<()> { self.inner
.advise(advice as libc::c_int, 0, self.inner.len())
}
/// Advise OS how this memory map will be accessed. /// /// Used with the [unchecked flags][UncheckedAdvice]. Only supported on Unix. /// /// See [madvise()](https://man7.org/linux/man-pages/man2/madvise.2.html) map page. #[cfg(unix)] pubunsafefn unchecked_advise(&self, advice: UncheckedAdvice) -> Result<()> { self.inner
.advise(advice as libc::c_int, 0, self.inner.len())
}
/// Advise OS how this range of memory map will be accessed. /// /// Only supported on Unix. /// /// The offset and length must be in the bounds of the memory map. /// /// See [madvise()](https://man7.org/linux/man-pages/man2/madvise.2.html) map page. #[cfg(unix)] pubfn advise_range(&self, advice: Advice, offset: usize, len: usize) -> Result<()> { self.inner.advise(advice as libc::c_int, offset, len)
}
/// Advise OS how this range of memory map will be accessed. /// /// Used with the [unchecked flags][UncheckedAdvice]. Only supported on Unix. /// /// The offset and length must be in the bounds of the memory map. /// /// See [madvise()](https://man7.org/linux/man-pages/man2/madvise.2.html) map page. #[cfg(unix)] pubfn unchecked_advise_range(
&self,
advice: UncheckedAdvice,
offset: usize,
len: usize,
) -> Result<()> { self.inner.advise(advice as libc::c_int, offset, len)
}
/// Lock the whole memory map into RAM. Only supported on Unix. /// /// See [mlock()](https://man7.org/linux/man-pages/man2/mlock.2.html) map page. #[cfg(unix)] pubfn lock(&self) -> Result<()> { self.inner.lock()
}
/// Unlock the whole memory map. Only supported on Unix. /// /// See [munlock()](https://man7.org/linux/man-pages/man2/munlock.2.html) map page. #[cfg(unix)] pubfn unlock(&self) -> Result<()> { self.inner.unlock()
}
/// Adjust the size of the memory mapping. /// /// This will try to resize the memory mapping in place. If /// [`RemapOptions::may_move`] is specified it will move the mapping if it /// could not resize in place, otherwise it will error. /// /// Only supported on Linux. /// /// See the [`mremap(2)`] man page. /// /// # Safety /// /// Resizing the memory mapping beyond the end of the mapped file will /// result in UB should you happen to access memory beyond the end of the /// file. /// /// [`mremap(2)`]: https://man7.org/linux/man-pages/man2/mremap.2.html #[cfg(target_os = "linux")] pubunsafefn remap(&mutself, new_len: usize, options: RemapOptions) -> Result<()> { self.inner.remap(new_len, options)
}
}
#[cfg(feature = "stable_deref_trait")] unsafeimpl stable_deref_trait::StableDeref for MmapMut {}
/// Options for [`Mmap::remap`] and [`MmapMut::remap`]. #[derive(Copy, Clone, Default, Debug)] #[cfg(target_os = "linux")] pubstruct RemapOptions {
may_move: bool,
}
#[cfg(target_os = "linux")] impl RemapOptions { /// Creates a mew set of options for resizing a memory map. pubfn new() -> Self { Self::default()
}
/// Controls whether the memory map can be moved if it is not possible to /// resize it in place. /// /// If false then the memory map is guaranteed to remain at the same /// address when being resized but attempting to resize will return an /// error if the new memory map would overlap with something else in the /// current process' memory. /// /// By default this is false. /// /// # `may_move` and `StableDeref` /// If the `stable_deref_trait` feature is enabled then [`Mmap`] and /// [`MmapMut`] implement `StableDeref`. `StableDeref` promises that the /// memory map dereferences to a fixed address, however, calling `remap` /// with `may_move` set may result in the backing memory of the mapping /// being moved to a new address. This may cause UB in other code /// depending on the `StableDeref` guarantees. pubfn may_move(mutself, may_move: bool) -> Self { self.may_move = may_move; self
}
#[cfg(unix)] usecrate::advice::Advice; use std::fs::{File, OpenOptions}; use std::io::{Read, Write}; use std::mem; #[cfg(unix)] use std::os::unix::io::AsRawFd; #[cfg(windows)] use std::os::windows::fs::OpenOptionsExt;
#[test] fn map_file() { let expected_len = 128; let tempdir = tempfile::tempdir().unwrap(); let path = tempdir.path().join("mmap");
let file = OpenOptions::new()
.read(true)
.write(true)
.create(true)
.open(path)
.unwrap();
file.set_len(expected_len as u64).unwrap();
letmut mmap = unsafe { MmapMut::map_mut(&file).unwrap() }; let len = mmap.len();
assert_eq!(expected_len, len);
let zeros = vec![0; len]; let incr: Vec<u8> = (0..len as u8).collect();
// check that the mmap is empty
assert_eq!(&zeros[..], &mmap[..]);
// write values into the mmap
(&mut mmap[..]).write_all(&incr[..]).unwrap();
// read values back
assert_eq!(&incr[..], &mmap[..]);
}
#[test] #[cfg(unix)] fn map_fd() { let expected_len = 128; let tempdir = tempfile::tempdir().unwrap(); let path = tempdir.path().join("mmap");
let file = OpenOptions::new()
.read(true)
.write(true)
.create(true)
.open(path)
.unwrap();
file.set_len(expected_len as u64).unwrap();
letmut mmap = unsafe { MmapMut::map_mut(file.as_raw_fd()).unwrap() }; let len = mmap.len();
assert_eq!(expected_len, len);
let zeros = vec![0; len]; let incr: Vec<u8> = (0..len as u8).collect();
// check that the mmap is empty
assert_eq!(&zeros[..], &mmap[..]);
// write values into the mmap
(&mut mmap[..]).write_all(&incr[..]).unwrap();
// read values back
assert_eq!(&incr[..], &mmap[..]);
}
/// Checks that "mapping" a 0-length file derefs to an empty slice. #[test] fn map_empty_file() { let tempdir = tempfile::tempdir().unwrap(); let path = tempdir.path().join("mmap");
let file = OpenOptions::new()
.read(true)
.write(true)
.create(true)
.open(path)
.unwrap(); let mmap = unsafe { Mmap::map(&file).unwrap() };
assert!(mmap.is_empty());
assert_eq!(mmap.as_ptr().align_offset(mem::size_of::<usize>()), 0); let mmap = unsafe { MmapMut::map_mut(&file).unwrap() };
assert!(mmap.is_empty());
assert_eq!(mmap.as_ptr().align_offset(mem::size_of::<usize>()), 0);
}
#[test] fn map_anon() { let expected_len = 128; letmut mmap = MmapMut::map_anon(expected_len).unwrap(); let len = mmap.len();
assert_eq!(expected_len, len);
let zeros = vec![0; len]; let incr: Vec<u8> = (0..len as u8).collect();
// check that the mmap is empty
assert_eq!(&zeros[..], &mmap[..]);
// write values into the mmap
(&mut mmap[..]).write_all(&incr[..]).unwrap();
// read values back
assert_eq!(&incr[..], &mmap[..]);
}
// The mmap contains the write
(&mmap[..]).read_exact(&mut read).unwrap();
assert_eq!(write, &read);
// The file does not contain the write
file.read_exact(&mut read).unwrap();
assert_eq!(nulls, &read);
// another mmap does not contain the write let mmap2 = unsafe { MmapOptions::new().map(&file).unwrap() };
(&mmap2[..]).read_exact(&mut read).unwrap();
assert_eq!(nulls, &read);
}
#[test] fn map_copy_read_only() { let tempdir = tempfile::tempdir().unwrap(); let path = tempdir.path().join("mmap");
let file = OpenOptions::new()
.read(true)
.write(true)
.create(true)
.open(path)
.unwrap();
file.set_len(128).unwrap();
let nulls = b"\0\0\0\0\0\0"; letmut read = [0u8; 6];
// The mmap contains the write
(&mmap[..]).read_exact(&mut read).unwrap();
assert_eq!(write, &read);
// The file should contain the write
file.read_exact(&mut read).unwrap();
assert_eq!(write, &read);
// another mmap should contain the write let mmap2 = unsafe { MmapOptions::new().map(&file).unwrap() };
(&mmap2[..]).read_exact(&mut read).unwrap();
assert_eq!(write, &read);
let mmap = mmap.make_exec().expect("make_exec");
drop(mmap);
}
#[test] fn mprotect_copy() { let tempdir = tempfile::tempdir().unwrap(); let path = tempdir.path().join("mmap");
// The mmap contains the write
(&mmap[..]).read_exact(&mut read).unwrap();
assert_eq!(write, &read);
// The file does not contain the write
file.read_exact(&mut read).unwrap();
assert_eq!(nulls, &read);
// another mmap does not contain the write let mmap2 = unsafe { MmapOptions::new().map(&file).unwrap() };
(&mmap2[..]).read_exact(&mut read).unwrap();
assert_eq!(nulls, &read);
let mmap = mmap.make_exec().expect("make_exec");
drop(mmap);
}
#[test] fn mprotect_anon() { let mmap = MmapMut::map_anon(256).expect("map_mut");
let mmap = mmap.make_read_only().expect("make_read_only"); let mmap = mmap.make_mut().expect("make_mut"); let mmap = mmap.make_exec().expect("make_exec");
drop(mmap);
}
#[test] fn raw() { let tempdir = tempfile::tempdir().unwrap(); let path = tempdir.path().join("mmapraw");
/// Something that relies on StableDeref #[test] #[cfg(feature = "stable_deref_trait")] fn owning_ref() { externcrate owning_ref;
letmut map = MmapMut::map_anon(128).unwrap();
map[10] = 42; let owning = owning_ref::OwningRef::new(map); let sliced = owning.map(|map| &map[10..20]);
assert_eq!(42, sliced[0]);
let map = sliced.into_owner().make_read_only().unwrap(); let owning = owning_ref::OwningRef::new(map); let sliced = owning.map(|map| &map[10..20]);
assert_eq!(42, sliced[0]);
}
#[test] #[cfg(unix)] fn advise() { let expected_len = 128; let tempdir = tempfile::tempdir().unwrap(); let path = tempdir.path().join("mmap_advise");
let file = OpenOptions::new()
.read(true)
.write(true)
.create(true)
.open(path)
.unwrap();
file.set_len(expected_len as u64).unwrap();
// Test MmapMut::advise letmut mmap = unsafe { MmapMut::map_mut(&file).unwrap() };
mmap.advise(Advice::Random)
.expect("mmap advising should be supported on unix");
let len = mmap.len();
assert_eq!(expected_len, len);
let zeros = vec![0; len]; let incr: Vec<u8> = (0..len as u8).collect();
// check that the mmap is empty
assert_eq!(&zeros[..], &mmap[..]);
mmap.advise_range(Advice::Sequential, 0, mmap.len())
.expect("mmap advising should be supported on unix");
// write values into the mmap
(&mut mmap[..]).write_all(&incr[..]).unwrap();
// read values back
assert_eq!(&incr[..], &mmap[..]);
// Set advice and Read from the read-only map let mmap = unsafe { Mmap::map(&file).unwrap() };
mmap.advise(Advice::Random)
.expect("mmap advising should be supported on unix");
// read values back
assert_eq!(&incr[..], &mmap[..]);
}
#[test] #[cfg(target_os = "linux")] fn advise_writes_unsafely() { let page_size = unsafe { libc::sysconf(libc::_SC_PAGESIZE) as usize };
letmut mmap = MmapMut::map_anon(page_size).unwrap();
mmap.as_mut().fill(255); let mmap = mmap.make_read_only().unwrap();
let a = mmap.as_ref()[0]; unsafe {
mmap.unchecked_advise(crate::UncheckedAdvice::DontNeed)
.unwrap();
} let b = mmap.as_ref()[0];
assert_eq!(a, 255);
assert_eq!(b, 0);
}
#[test] #[cfg(target_os = "linux")] fn advise_writes_unsafely_to_part_of_map() { let page_size = unsafe { libc::sysconf(libc::_SC_PAGESIZE) as usize };
let a = mmap.as_ref()[0]; let b = mmap.as_ref()[page_size]; unsafe {
mmap.unchecked_advise_range(crate::UncheckedAdvice::DontNeed, page_size, page_size)
.unwrap();
} let c = mmap.as_ref()[0]; let d = mmap.as_ref()[page_size];
/// Returns true if a non-zero amount of memory is locked. #[cfg(target_os = "linux")] fn is_locked() -> bool { let status = &std::fs::read_to_string("/proc/self/status")
.expect("/proc/self/status should be available"); for line in status.lines() { if line.starts_with("VmLck:") { let numbers = line.replace(|c: char| !c.is_ascii_digit(), ""); return numbers != "0";
}
}
panic!("cannot get VmLck information")
}
#[test] #[cfg(unix)] fn lock() { let tempdir = tempfile::tempdir().unwrap(); let path = tempdir.path().join("mmap_lock");
let file = OpenOptions::new()
.read(true)
.write(true)
.create(true)
.open(path)
.unwrap();
file.set_len(128).unwrap();
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.