Skip to main content

MemoryMappedFile

Struct MemoryMappedFile 

Source
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>

Source

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:

  1. Just a filename, if the namespace is either Namespace::GLOBAL or Namespace::LOCAL
  2. A namespaced filename if using Namespace::CUSTOM and you know what you’re doing
  3. Just a filename if using Namespace::CUSTOM and 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 pages is explicitly true
  • the requested size + 4 is at least as big as a large page, and large_pages isn’t Some(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.

Source

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.

Source

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

Source

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

Source

pub fn is_writable(&self) -> bool

Check if this MMF can be written to

Source

pub fn is_readable(&self) -> bool

Check if this MMF can be read from

Source

pub fn namespace(&self) -> String

Get the namespace of the file, if any. If an empty string is returned, it’s Local.

Source

pub fn filename(&self) -> String

Return the filename the MMF is bound to, which is only the whole name if no namespace is provided.

Source

pub fn fullname(&self) -> String

Returns the stored name, which should be [Namespace\]<FileName>

Source

pub fn close(&self) -> MMFResult<()>

Close the MMF. Don’t worry about calling this, it’s handled in Drop.

Trait Implementations§

Source§

impl<LOCK: Debug + MMFLock> Debug for MemoryMappedFile<LOCK>

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
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.

Source§

fn drop(&mut self)

Ignore any errors when closing the handle.

Source§

fn pin_drop(self: Pin<&mut Self>)

🔬This is a nightly-only experimental API. (pin_ergonomics)
Execute the destructor for this type, but different to Drop::drop, it requires self to be pinned. Read more
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.

Source§

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_spin(&self, count: usize, tries: usize) -> MMFResult<Vec<u8>>

Spinning form of read

Source§

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<()>

Spinning version of read_to_buf

Source§

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<()>

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<()>

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.
Source§

fn size(&self) -> usize

Returns the size of the data portion of the MMF.

This allows users to know the MMF’s size without exposing it publicly in case someone has a &mut MMF because that would be very very dangerous.

Source§

fn write_spin( &self, buffer: impl Deref<Target = [u8]>, tries: usize, ) -> MMFResult<()>

Spin for tries times max, or until writing is allowed. Read more
Source§

impl<LOCK: MMFLock + Send + Sync> Send for MemoryMappedFile<LOCK>

Available on crate features 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.

Source§

impl<LOCK: MMFLock + Send + Sync> Sync for MemoryMappedFile<LOCK>

Available on crate features 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.

Auto Trait Implementations§

§

impl<LOCK> !Freeze for MemoryMappedFile<LOCK>

§

impl<LOCK> !RefUnwindSafe for MemoryMappedFile<LOCK>

§

impl<LOCK> Unpin for MemoryMappedFile<LOCK>
where LOCK: Unpin,

§

impl<LOCK> UnsafeUnpin for MemoryMappedFile<LOCK>
where LOCK: UnsafeUnpin,

§

impl<LOCK> UnwindSafe for MemoryMappedFile<LOCK>
where LOCK: UnwindSafe,

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.