Skip to main content

MmapBackend

Struct MmapBackend 

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

A storage backend backed by a memory-mapped file.

This struct holds a reference to the memory map. Accessing data is as efficient as a memory copy (memcpy), avoiding the explicit system call overhead of read or pread. The OS handles paging data in from disk as needed.

Implementations§

Source§

impl MmapBackend

Source

pub fn new(path: &Path) -> Result<Self>

Opens a snapshot file and maps it into the process address space.

This constructor performs three operations:

  1. Opens the file at path in read-only mode
  2. Queries the file size via fstat(2)
  3. Creates a read-only memory mapping (MAP_PRIVATE | PROT_READ)

The mapping is private (copy-on-write), but since no writes occur, it effectively shares the underlying page cache with other processes reading the same file.

§Parameters
  • path: Filesystem path to the snapshot file (absolute or relative)
§Returns
  • Ok(MmapBackend): Successfully mapped and initialized
  • Err(Error::Io): If the file cannot be opened or mapped
§Errors

Common error conditions:

  • File not found (ENOENT): Path does not exist
  • Permission denied (EACCES): Insufficient permissions to read file
  • Out of memory (ENOMEM): Insufficient virtual address space (rare on 64-bit)
  • Invalid file: Cannot map special files (e.g., /dev/null, pipes, sockets)
§Safety

This function uses unsafe { Mmap::map(&file) } internally. The safety invariant is that the mapped file must not be modified or truncated by any process while the mapping exists. Violating this invariant can cause:

  • SIGBUS: Accessing pages corresponding to truncated regions
  • Data races: Undefined behavior if file contents change during reads

This backend enforces immutable snapshot semantics: the caller must ensure the file is not modified after construction.

§Performance

The mapping operation is lazy. This constructor only reserves virtual address space; no disk I/O occurs until pages are accessed. For a 1GB file:

  • Constructor latency: ~100µs (no I/O, just syscall overhead)
  • First read latency: 5-50µs per 4KB page (demand paging)
§Examples
use hexz_core::store::local::MmapBackend;
use std::path::Path;

// Absolute path
let backend = MmapBackend::new(Path::new("/var/data/snapshot.hxz"))?;

// Relative path
let backend = MmapBackend::new(Path::new("./snapshots/test.hxz"))?;

// Error handling
match MmapBackend::new(Path::new("/nonexistent.hxz")) {
    Ok(_) => println!("Success"),
    Err(e) => eprintln!("Failed to map: {}", e),
}

Trait Implementations§

Source§

impl Debug for MmapBackend

Source§

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

Formats the value using the given formatter. Read more
Source§

impl StorageBackend for MmapBackend

Source§

fn read_exact(&self, offset: u64, len: usize) -> Result<Bytes>

Reads exactly len bytes starting at offset from the mapped file.

This method performs a memcpy from the mapped region into a Bytes buffer. If the requested pages are not resident in RAM, the CPU will trigger a page fault, and the kernel will transparently load the data from disk (demand paging).

§Parameters
  • offset: Absolute byte offset from the start of the file (0-indexed)
  • len: Number of bytes to read (must not cause offset + len to exceed file size)
§Returns
  • Ok(Bytes): A buffer containing exactly len bytes of data
  • Err(Error::Io): If the read exceeds file boundaries
§Errors

This method returns an error if:

  • Out of bounds (ErrorKind::UnexpectedEof): offset + len > file_size

Note: If the mapped file is modified externally (violating immutability), accessing those pages may trigger SIGBUS, which cannot be caught by this code.

§Performance
  • Time complexity: O(len) for memcpy, O(1) for bounds check
  • Syscalls: 0 if pages are resident, page fault handler if not resident
  • Allocations: 1 heap allocation of len bytes for the returned Bytes
  • CPU overhead: ~10-50 CPU cycles per byte for resident pages

Latency breakdown (4KB read):

  • Resident pages: ~1-5µs (pure memory copy)
  • Non-resident pages: ~5-50µs (page fault + disk I/O)
§Concurrency

This method is safe to call concurrently from multiple threads. The kernel handles concurrent page faults transparently. Reads never block each other unless they trigger page faults for the same pages simultaneously (rare and efficiently handled by the kernel).

§Examples
use hexz_core::store::local::MmapBackend;
use hexz_core::store::StorageBackend;
use std::path::Path;

let backend = MmapBackend::new(Path::new("/data/snapshot.hxz"))?;

// Read first 512 bytes (header)
let header = backend.read_exact(0, 512)?;
assert_eq!(header.len(), 512);

// Read 4KB block at offset 1MB
let block = backend.read_exact(1024 * 1024, 4096)?;
assert_eq!(block.len(), 4096);

// Error: reading beyond file boundary
let file_size = backend.len();
assert!(backend.read_exact(file_size, 1).is_err());
Source§

fn len(&self) -> u64

Returns the total mapped file size in bytes.

This value is cached during construction via File::metadata() and reflects the file size at the time the mapping was created. The file is assumed to be immutable; if the file is truncated or extended externally, this value will not be updated and accessing changed regions may cause undefined behavior.

§Returns

The file size in bytes as of the time MmapBackend::new() was called.

§Performance

This method is a simple field access with no system calls (O(1)).

§Examples
use hexz_core::store::local::MmapBackend;
use hexz_core::store::StorageBackend;
use std::path::Path;

let backend = MmapBackend::new(Path::new("/data/snapshot.hxz"))?;
let size = backend.len();
println!("Snapshot size: {} bytes ({} MB)", size, size / 1024 / 1024);
Source§

fn is_empty(&self) -> bool

Returns true if the underlying store has zero length. 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> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
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> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> if into_left is true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> if into_left(&self) returns true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

impl<T> Pointable for T

Source§

const ALIGN: usize

The alignment of pointer.
Source§

type Init = T

The type for initializers.
Source§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
Source§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
Source§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
Source§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
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.
Source§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

Source§

fn vzip(self) -> V

Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more