pub struct MemoryMappedFile<LOCK: MMFLock> { /* private fields */ }Expand description
A simple struct wrapping a Memory Mapped File.
It contains all the data required to create and keep alive a [HANDLE] to a Memory Mapped File. The [HANDLE] is
required to create a [MEMORY_MAPPED_VIEW_ADDRESS] to read and write data from. In order to expose reading and
writing functionality in a safe manner, these are wrapped in a safe API that prefers more short blocks of unsafe
over a larger block that also does safe operations.
Once this reaches a stable-ish state, an unsafe function feature will be added to get access to the raw handle and
map view.
Supports both x86 and AMD64 by leveraging usize, to allow target-sized ints to be used everywhere.
Implementations§
Source§impl<LOCK: MMFLock> MemoryMappedFile<LOCK>
impl<LOCK: MMFLock> MemoryMappedFile<LOCK>
Sourcepub fn new(
size: NonZeroUsize,
name: impl Into<ztr64>,
namespace: Namespace,
large_pages: Option<bool>,
) -> MMFResult<Self>
pub fn new( size: NonZeroUsize, name: impl Into<ztr64>, namespace: Namespace, large_pages: Option<bool>, ) -> MMFResult<Self>
Attempt to create a new Memory Mapped File. Or fail graciously if we can’t.
The size will be automatically divided into the upper and lower halves, as the function to allocate this memory requires them to be split. The name of the file should be any one of:
- Just a filename, if the namespace is either
Namespace::GLOBALorNamespace::LOCAL - A namespaced filename if using
Namespace::CUSTOMand you know what you’re doing - Just a filename if using
Namespace::CUSTOMand you don’t need other processes to access it.
Violating these constraints should result in a local namespace, but no guarantees are given and if it leads to UB, the heat death of the universe, panics or errors or a change in the answer to a value other than 42. you’re on your own.
The size MUST be a non-zero value; allocating zero bytes errors on the OS end of things. Allocating too much will make a part of the file inaccessible to other code trying to read it from a 32-bit process. The total size allocated will be 4 bytes larger than the specified size, but only after checking the input size is non-zero.
Windows will, by default, overcommit filemappings to be multiples of the used page size. It does this silently for normal pages, but requires it for large pages. As of 0.5.0 (when large page support was added), this crate will mimic this behavior and provide users with the next (checked to not overflow) integer multiple of the selected page size, minus the 4 bytes for the lock. Large page usage requiring mapping them in full is a technical limitation.
To support use cases where permissions for large pages might not be available, it is possible to explicitly prevent their use. As such, large page usage is enabled if the system supports it, and
large pagesis explicitly true- the requested size + 4 is at least as big as a large page, and
large_pagesisn’tSome(false)
In most cases large pages will be 2MB or 4MB. If your architecture doesn’t support large pages, it makes a best effort to get an exact multiple of your page size close to, but larger than, the requested size + 4. The exact sizes for (large) pages in Windows can be found in this Old New Thing from 2021.
Sourcepub fn open(
size: NonZeroUsize,
name: impl Into<ztr64>,
namespace: Namespace,
readonly: bool,
large_pages: Option<bool>,
) -> MMFResult<Self>
pub fn open( size: NonZeroUsize, name: impl Into<ztr64>, namespace: Namespace, readonly: bool, large_pages: Option<bool>, ) -> MMFResult<Self>
Open an existing MMF, if it exists.
Defaults to read and write permissions, use the exposed wrappers to open R or RW I have no idea what happens if you call this on a fake name. Code responsibly. In all reality though, it should return an error that you can handle.
Sourcepub fn open_read(
size: NonZeroUsize,
name: &str,
namespace: Namespace,
large_pages: Option<bool>,
) -> MMFResult<Self>
pub fn open_read( size: NonZeroUsize, name: &str, namespace: Namespace, large_pages: Option<bool>, ) -> MMFResult<Self>
Open an MMF for reading
Wrapper around open that always passes true
Sourcepub fn open_write(
size: NonZeroUsize,
name: &str,
namespace: Namespace,
large_pages: Option<bool>,
) -> MMFResult<Self>
pub fn open_write( size: NonZeroUsize, name: &str, namespace: Namespace, large_pages: Option<bool>, ) -> MMFResult<Self>
Open an MMF for reading and writing
Wrapper around open that always passes false
Sourcepub fn is_writable(&self) -> bool
pub fn is_writable(&self) -> bool
Check if this MMF can be written to
Sourcepub fn is_readable(&self) -> bool
pub fn is_readable(&self) -> bool
Check if this MMF can be read from
Sourcepub fn namespace(&self) -> String
pub fn namespace(&self) -> String
Get the namespace of the file, if any. If an empty string is returned, it’s Local.
Sourcepub fn filename(&self) -> String
pub fn filename(&self) -> String
Return the filename the MMF is bound to, which is only the whole name if no namespace is provided.
Trait Implementations§
Source§impl<LOCK: MMFLock> Drop for MemoryMappedFile<LOCK>
Available on crate feature impl_mmf only.Implement closing the handle to the MMF before dropping it, so the system can clean up resources.
impl<LOCK: MMFLock> Drop for MemoryMappedFile<LOCK>
impl_mmf only.Implement closing the handle to the MMF before dropping it, so the system can clean up resources.
Source§impl<LOCK: MMFLock> Mmf for MemoryMappedFile<LOCK>
Available on crate feature impl_mmf only.Implements a usable file-like interface for working with an MMF. Pass all input as bytes, please.
impl<LOCK: MMFLock> Mmf for MemoryMappedFile<LOCK>
impl_mmf only.Implements a usable file-like interface for working with an MMF. Pass all input as bytes, please.
Source§fn read(&self, count: usize) -> Result<Vec<u8>, MMFError>
fn read(&self, count: usize) -> Result<Vec<u8>, MMFError>
Attempts to read bytes up to the entirety of the data as defined in Self::size.
This function succeeds if there is a value in Self::map_view but it cannot guarantee the data returned is
correct. This is an unfortunate side effect of having to work with raw pointers and bytes in memory.
Assuming nothing external has touched the memory region other than this class, it should be valid data unless
it’s marked as uninitialized. The returned error for this is an instance of the
crate’s error enum
- 1: Write Protected; the file has a write lock on it which means reading might return incomplete data, or the maximum amount of readers has been reached (this should not happen assuming all implementations are clean).
- 2: Invalid block; the lock is telling us this data has not yet been initialized.
- 5: File not found; the MMF isn’t opened yet or no map view exists.
Source§fn read_to_buf(&self, buffer: &mut Vec<u8>, count: usize) -> MMFResult<()>
fn read_to_buf(&self, buffer: &mut Vec<u8>, count: usize) -> MMFResult<()>
See the documentation for Self::read(), except this takes a buffer to write to.
If the count is 0, the entire MMF will be read into the buffer. If the buffer is smaller than the amount of data
to be read, it will be grown to fit the requested data, using Vec::reserve_exact. The returned error for
this is an instance of the crate’s error enum
Source§fn read_to_buf_spin(
&self,
buffer: &mut Vec<u8>,
count: usize,
tries: usize,
) -> MMFResult<()>
fn read_to_buf_spin( &self, buffer: &mut Vec<u8>, count: usize, tries: usize, ) -> MMFResult<()>
Spinning version of read_to_buf
Source§unsafe fn read_to_raw(
&self,
buffer: *mut u8,
count: usize,
) -> Result<(), MMFError>
unsafe fn read_to_raw( &self, buffer: *mut u8, count: usize, ) -> Result<(), MMFError>
Read into a raw pointer and pray it’s valid for count bytes.
If the count is 0, this operation will exit with a non-specific error. This method
provides a best effort to ensure that the read portion of the copy is sound by clamping count to the MMFs
size. This prevents, at the very least, UB from reading beyond the end of the MMF. It also ensures the MMF is
opened and initialized, with the usual errors from read to make these problems known to callers.
§Safety
It is the caller’s responsibility to ensure that buffer is valid for at least count bytes. Failing to do so
is UB. See the documentation for std::ptr::copy for safety concerns, the provided buffer is the dst.
Source§unsafe fn read_to_raw_spin(
&self,
buffer: *mut u8,
count: usize,
tries: usize,
) -> MMFResult<()>
unsafe fn read_to_raw_spin( &self, buffer: *mut u8, count: usize, tries: usize, ) -> MMFResult<()>
Spinning version of read_to_raw
§Safety
It is the caller’s responsibility to ensure that buffer is valid for at least count bytes. Failing to do so
is UB. See the documentation for std::ptr::copy for safety concerns, the provided buffer is the dst.
Source§fn write(&self, buffer: impl Deref<Target = [u8]>) -> MMFResult<()>
fn write(&self, buffer: impl Deref<Target = [u8]>) -> MMFResult<()>
Attempt to write a complete buffer into the MMF. Uses pointers and memcpy to be fast.
This function errors only if the lock could not be acquired or when trying to write more data than fits. Writing more data than the MMF can hold is UB so this is prevented by erroring out instead. If the input buffer is smaller than the destination file, the end is zeroed out. The start of the buffer is also padded by the lock bytes to signal and flag locking.The returned error for this is an instance of the crate’s error enum
Error codes produced by this function:
- 0 or 1: Access denied; the lock could not be acquired or the MMF is read-only.
- 4: Not enough memory; the write was blocked because it was too large.
- All errors from Self::read() as a read is required to update the lock.
impl<LOCK: MMFLock + Send + Sync> Send for MemoryMappedFile<LOCK>
impl_mmf and mmf_send only.Send marker for use in shared contexts
§Safety
The default MMF implementation doesn’t do anything unless the lock gives an all clear, so it’s safe to mark it
Send when the lock itself is.
impl<LOCK: MMFLock + Send + Sync> Sync for MemoryMappedFile<LOCK>
impl_mmf and mmf_send only.Sync marker for use in shared contexts
§Safety
The default MMF implementation doesn’t do anything unless the lock gives an all clear, so it’s safe to mark it
Sync when the lock itself is.