MemoryMappedFile

Struct MemoryMappedFile 

Source
pub struct MemoryMappedFile { /* private fields */ }
Expand description

Memory-mapped file with safe, zero-copy region access.

This is the core type for memory-mapped file operations. It provides:

  • Safe concurrent access through interior mutability
  • Zero-copy reads and writes
  • Automatic bounds checking
  • Cross-platform compatibility

§Examples

use mmap_io::{MemoryMappedFile, MmapMode};

// Create a new 1KB file
let mmap = MemoryMappedFile::create_rw("data.bin", 1024)?;

// Write some data
mmap.update_region(0, b"Hello, world!")?;
mmap.flush()?;

// Open existing file read-only
let ro_mmap = MemoryMappedFile::open_ro("data.bin")?;
let data = ro_mmap.as_slice(0, 13)?;
assert_eq!(data, b"Hello, world!");

Cloning this struct is cheap; it clones an Arc to the inner state. For read-write mappings, interior mutability is protected with an RwLock.

Implementations§

Source§

impl MemoryMappedFile

Source

pub fn builder<P: AsRef<Path>>(path: P) -> MemoryMappedFileBuilder

Builder for constructing a MemoryMappedFile with custom options.

Example:

// let mmap = MemoryMappedFile::builder("file.bin")
//     .mode(MmapMode::ReadWrite)
//     .size(1_000_000)
//     .flush_policy(FlushPolicy::EveryBytes(1_000_000))
//     .create().unwrap();
Source

pub fn create_rw<P: AsRef<Path>>(path: P, size: u64) -> Result<Self>

Create a new file (truncating if exists) and memory-map it in read-write mode with the given size.

§Performance
  • Time Complexity: O(1) for mapping creation
  • Memory Usage: Virtual address space of size bytes (physical memory allocated on demand)
  • I/O Operations: One file creation, one truncate, one mmap syscall
§Errors

Returns MmapIoError::ResizeFailed if size is zero or exceeds the maximum safe limit. Returns MmapIoError::Io if file creation or mapping fails.

Source

pub fn open_ro<P: AsRef<Path>>(path: P) -> Result<Self>

Open an existing file and memory-map it read-only.

§Errors

Returns MmapIoError::Io if file opening or mapping fails.

Source

pub fn open_rw<P: AsRef<Path>>(path: P) -> Result<Self>

Open an existing file and memory-map it read-write.

§Errors

Returns MmapIoError::ResizeFailed if file is zero-length. Returns MmapIoError::Io if file opening or mapping fails.

Source

pub fn mode(&self) -> MmapMode

Return current mapping mode.

Source

pub fn len(&self) -> u64

Total length of the mapped file in bytes (cached).

Source

pub fn is_empty(&self) -> bool

Whether the mapped file is empty.

Source

pub fn as_slice(&self, offset: u64, len: u64) -> Result<&[u8]>

Get a zero-copy read-only slice for the given [offset, offset+len). For RW mappings, cannot return a reference bound to a temporary guard; use read_into instead.

§Performance
  • Time Complexity: O(1) - direct pointer access
  • Memory Usage: No additional allocation (zero-copy)
  • Cache Behavior: May trigger page faults on first access
§Errors

Returns MmapIoError::OutOfBounds if range exceeds file bounds. Returns MmapIoError::InvalidMode for RW mappings (use read_into instead).

Source

pub fn as_slice_mut(&self, offset: u64, len: u64) -> Result<MappedSliceMut<'_>>

Get a zero-copy mutable slice for the given [offset, offset+len). Only available in ReadWrite mode.

§Errors

Returns MmapIoError::InvalidMode if not in ReadWrite mode. Returns MmapIoError::OutOfBounds if range exceeds file bounds.

Source

pub fn update_region(&self, offset: u64, data: &[u8]) -> Result<()>

Copy the provided bytes into the mapped file at the given offset. Bounds-checked, zero-copy write.

§Performance
  • Time Complexity: O(n) where n is data.len()
  • Memory Usage: No additional allocation
  • I/O Operations: May trigger flush based on flush policy
§Errors

Returns MmapIoError::InvalidMode if not in ReadWrite mode. Returns MmapIoError::OutOfBounds if range exceeds file bounds.

Source

pub async fn update_region_async(&self, offset: u64, data: &[u8]) -> Result<()>

Async write that enforces Async-Only Flushing semantics: always flush after write. Uses spawn_blocking to avoid blocking the async scheduler.

Source

pub fn flush(&self) -> Result<()>

Flush changes to disk. For read-only mappings, this is a no-op.

Smart internal guards:

  • Skip I/O when there are no pending writes (accumulator is zero)
  • On Linux, use msync(MS_ASYNC) as a cheaper hint; fall back to full flush on error
§Performance
  • Time Complexity: O(n) where n is the size of dirty pages
  • I/O Operations: Triggers disk write of modified pages
  • Optimization: Skips flush if no writes since last flush
  • Platform: Linux uses async msync for better performance
§Errors

Returns MmapIoError::FlushFailed if flush operation fails.

Source

pub async fn flush_async(&self) -> Result<()>

Async flush changes to disk. For read-only or COW mappings, this is a no-op. This method enforces “async-only flushing” semantics for async paths.

Source

pub async fn flush_range_async(&self, offset: u64, len: u64) -> Result<()>

Async flush a specific byte range to disk.

Source

pub fn flush_range(&self, offset: u64, len: u64) -> Result<()>

Flush a specific byte range to disk.

Smart internal guards:

  • Skip I/O when there are no pending writes in accumulator
  • Optimize microflushes (< page size) with page-aligned batching
  • On Linux, prefer msync(MS_ASYNC) for the range; fall back to full range flush on error
§Performance Optimizations
  • Microflush Detection: Ranges smaller than page size are batched
  • Page Alignment: Small ranges are expanded to page boundaries
  • Async Hints: Linux uses MS_ASYNC for better performance
  • Zero-Copy: No data copying during flush operations
§Errors

Returns MmapIoError::OutOfBounds if range exceeds file bounds. Returns MmapIoError::FlushFailed if flush operation fails.

Source

pub fn resize(&self, new_size: u64) -> Result<()>

Resize (grow or shrink) the mapped file (RW only). This remaps the file internally.

§Performance
  • Time Complexity: O(1) for the remap operation
  • Memory Usage: Allocates new virtual address space of new_size
  • I/O Operations: File truncate/extend + new mmap syscall
  • Note: Existing pointers/slices become invalid after resize
§Errors

Returns MmapIoError::InvalidMode if not in ReadWrite mode. Returns MmapIoError::ResizeFailed if new size is zero or exceeds the maximum safe limit. Returns MmapIoError::Io if resize operation fails.

Source

pub fn path(&self) -> &Path

Path to the underlying file.

Source

pub fn touch_pages(&self) -> Result<()>

Touch (prewarm) pages by reading the first byte of each page. This forces the OS to load all pages into physical memory, eliminating page faults during subsequent access. Useful for benchmarking and performance-critical sections.

§Performance
  • Time Complexity: O(n) where n is the number of pages
  • Memory Usage: Forces all pages into physical memory
  • I/O Operations: May trigger disk reads for unmapped pages
  • Cache Behavior: Optimizes subsequent access patterns
§Examples
use mmap_io::MemoryMappedFile;

let mmap = MemoryMappedFile::open_ro("data.bin")?;

// Prewarm all pages before performance-critical section
mmap.touch_pages()?;

// Now all subsequent accesses will be fast (no page faults)
let data = mmap.as_slice(0, 1024)?;
§Errors

Returns MmapIoError::Io if memory access fails.

Source

pub fn touch_pages_range(&self, offset: u64, len: u64) -> Result<()>

Touch (prewarm) a specific range of pages. Similar to touch_pages() but only affects the specified range.

§Arguments
  • offset - Starting offset in bytes
  • len - Length of range to touch in bytes
§Errors

Returns MmapIoError::OutOfBounds if range exceeds file bounds. Returns MmapIoError::Io if memory access fails.

Source§

impl MemoryMappedFile

Source

pub fn open_cow<P: AsRef<Path>>(path: P) -> Result<Self>

Open an existing file and memory-map it copy-on-write (private). Changes through this mapping are visible only within this process; the underlying file remains unchanged.

Source§

impl MemoryMappedFile

Source

pub fn current_len(&self) -> Result<u64>

Return the up-to-date file length (cached). This ensures length remains correct even after resize.

§Errors

Returns MmapIoError::Io if metadata query fails (not expected in current implementation).

Source

pub fn read_into(&self, offset: u64, buf: &mut [u8]) -> Result<()>

Read bytes from the mapping into the provided buffer starting at offset. Length is buf.len(); performs bounds checks.

§Performance
  • Time Complexity: O(n) where n is buf.len()
  • Memory Usage: Uses provided buffer, no additional allocation
  • Cache Behavior: Sequential access pattern is cache-friendly
§Errors

Returns MmapIoError::OutOfBounds if range exceeds file bounds.

Source§

impl MemoryMappedFile

Source

pub fn advise(&self, offset: u64, len: u64, advice: MmapAdvice) -> Result<()>

Advise the OS about expected access patterns for a memory range.

This can help the OS optimize memory management, prefetching, and caching. The advice is a hint and may be ignored by the OS.

§Platform-specific behavior
  • Unix: Uses madvise system call
  • Windows: Uses PrefetchVirtualMemory for WillNeed, no-op for others
§Errors

Returns MmapIoError::OutOfBounds if the range exceeds file bounds. Returns MmapIoError::AdviceFailed if the system call fails.

Source§

impl MemoryMappedFile

Source

pub fn chunks(&self, chunk_size: usize) -> ChunkIterator<'_>

Create an iterator over fixed-size chunks of the file.

For read-only and copy-on-write mappings, this returns immutable slices. For read-write mappings, use chunks_mut() for mutable access.

§Arguments
  • chunk_size - Size of each chunk in bytes
§Examples
use mmap_io::MemoryMappedFile;

let mmap = MemoryMappedFile::open_ro("data.bin")?;

// Process file in 1MB chunks
for chunk in mmap.chunks(1024 * 1024) {
    let data = chunk?;
    // Process chunk...
}
Source

pub fn pages(&self) -> PageIterator<'_>

Create an iterator over page-aligned chunks of the file.

Pages are aligned to the system’s page size, which is typically 4KB on most systems. This can provide better performance for certain access patterns.

§Examples
use mmap_io::MemoryMappedFile;

let mmap = MemoryMappedFile::open_ro("data.bin")?;

// Process file page by page
for page in mmap.pages() {
    let data = page?;
    // Process page...
}
Source

pub fn chunks_mut(&self, chunk_size: usize) -> ChunkIteratorMut<'_>

Create a mutable iterator over fixed-size chunks of the file.

This is only available for read-write mappings. Due to Rust’s borrowing rules, this returns an iterator that processes chunks through a callback.

§Arguments
  • chunk_size - Size of each chunk in bytes
§Examples
use mmap_io::{MemoryMappedFile, MmapMode};

let mmap = MemoryMappedFile::open_rw("data.bin")?;

// Zero out file in 4KB chunks
mmap.chunks_mut(4096).for_each_mut(|offset, chunk| {
    chunk.fill(0);
    Ok::<(), std::io::Error>(())
})??;
Source§

impl MemoryMappedFile

Source

pub fn lock(&self, offset: u64, len: u64) -> Result<()>

Lock memory pages to prevent them from being swapped to disk.

This operation requires appropriate permissions (typically root/admin). Locked pages count against system limits.

§Platform-specific behavior
  • Unix: Uses mlock system call
  • Windows: Uses VirtualLock
§Errors

Returns MmapIoError::OutOfBounds if the range exceeds file bounds. Returns MmapIoError::LockFailed if the lock operation fails (often due to permissions).

Source

pub fn unlock(&self, offset: u64, len: u64) -> Result<()>

Unlock previously locked memory pages.

This allows the pages to be swapped out again if needed.

§Platform-specific behavior
  • Unix: Uses munlock system call
  • Windows: Uses VirtualUnlock
§Errors

Returns MmapIoError::OutOfBounds if the range exceeds file bounds. Returns MmapIoError::UnlockFailed if the unlock operation fails.

Source

pub fn lock_all(&self) -> Result<()>

Lock all pages of the memory-mapped file.

Convenience method that locks the entire file.

§Errors

Returns MmapIoError::LockFailed if the lock operation fails.

Source

pub fn unlock_all(&self) -> Result<()>

Unlock all pages of the memory-mapped file.

Convenience method that unlocks the entire file.

§Errors

Returns MmapIoError::UnlockFailed if the unlock operation fails.

Source§

impl MemoryMappedFile

Source

pub fn atomic_u64(&self, offset: u64) -> Result<&AtomicU64>

Get an atomic view of a u64 value at the specified offset.

The offset must be properly aligned for atomic operations (8-byte alignment for u64). This allows lock-free concurrent access to the value.

§Safety

The returned reference is valid for the lifetime of the memory mapping. The caller must ensure that the memory at this offset is not concurrently modified through non-atomic operations.

§Errors

Returns MmapIoError::Misaligned if the offset is not 8-byte aligned. Returns MmapIoError::OutOfBounds if the offset + 8 exceeds file bounds.

Source

pub fn atomic_u32(&self, offset: u64) -> Result<&AtomicU32>

Get an atomic view of a u32 value at the specified offset.

The offset must be properly aligned for atomic operations (4-byte alignment for u32). This allows lock-free concurrent access to the value.

§Safety

The returned reference is valid for the lifetime of the memory mapping. The caller must ensure that the memory at this offset is not concurrently modified through non-atomic operations.

§Errors

Returns MmapIoError::Misaligned if the offset is not 4-byte aligned. Returns MmapIoError::OutOfBounds if the offset + 4 exceeds file bounds.

Source

pub fn atomic_u64_slice( &self, offset: u64, count: usize, ) -> Result<&[AtomicU64]>

Get multiple atomic u64 views starting at the specified offset.

Returns a slice of atomic values. All values must be within bounds and the offset must be 8-byte aligned.

§Errors

Returns MmapIoError::Misaligned if the offset is not 8-byte aligned. Returns MmapIoError::OutOfBounds if the range exceeds file bounds.

Source

pub fn atomic_u32_slice( &self, offset: u64, count: usize, ) -> Result<&[AtomicU32]>

Get multiple atomic u32 views starting at the specified offset.

Returns a slice of atomic values. All values must be within bounds and the offset must be 4-byte aligned.

§Errors

Returns MmapIoError::Misaligned if the offset is not 4-byte aligned. Returns MmapIoError::OutOfBounds if the range exceeds file bounds.

Source§

impl MemoryMappedFile

Source

pub fn watch<F>(&self, callback: F) -> Result<WatchHandle>
where F: Fn(ChangeEvent) + Send + 'static,

Watch for changes to the mapped file.

The callback will be invoked whenever changes are detected. Returns a handle that stops watching when dropped.

§Platform-specific behavior
  • Linux: Uses inotify for efficient monitoring
  • macOS: Uses FSEvents or kqueue
  • Windows: Uses ReadDirectoryChangesW
  • Fallback: Polling-based implementation
§Examples
use mmap_io::{MemoryMappedFile, watch::ChangeEvent};
use std::sync::Arc;
use std::sync::atomic::{AtomicBool, Ordering};

let mmap = MemoryMappedFile::open_ro("data.bin")?;
let changed = Arc::new(AtomicBool::new(false));
let changed_clone = Arc::clone(&changed);

let handle = mmap.watch(move |event: ChangeEvent| {
    println!("File changed: {:?}", event);
    changed_clone.store(true, Ordering::SeqCst);
})?;

// File is being watched...
// Handle is dropped when out of scope, stopping the watch

Trait Implementations§

Source§

impl Clone for MemoryMappedFile

Source§

fn clone(&self) -> MemoryMappedFile

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for MemoryMappedFile

Source§

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

Formats the value using the given formatter. Read more

Auto Trait Implementations§

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> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. 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> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
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.