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
impl MmapBackend
Sourcepub fn new(path: &Path) -> Result<Self>
pub fn new(path: &Path) -> Result<Self>
Opens a snapshot file and maps it into the process address space.
This constructor performs three operations:
- Opens the file at
pathin read-only mode - Queries the file size via
fstat(2) - 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 initializedErr(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
impl Debug for MmapBackend
Source§impl StorageBackend for MmapBackend
impl StorageBackend for MmapBackend
Source§fn read_exact(&self, offset: u64, len: usize) -> Result<Bytes>
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 causeoffset + lento exceed file size)
§Returns
Ok(Bytes): A buffer containing exactlylenbytes of dataErr(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
lenbytes for the returnedBytes - 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
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);Auto Trait Implementations§
impl Freeze for MmapBackend
impl RefUnwindSafe for MmapBackend
impl Send for MmapBackend
impl Sync for MmapBackend
impl Unpin for MmapBackend
impl UnsafeUnpin for MmapBackend
impl UnwindSafe for MmapBackend
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Source§impl<T> Instrument for T
impl<T> Instrument for T
Source§fn instrument(self, span: Span) -> Instrumented<Self>
fn instrument(self, span: Span) -> Instrumented<Self>
Source§fn in_current_span(self) -> Instrumented<Self>
fn in_current_span(self) -> Instrumented<Self>
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self>
fn into_either(self, into_left: bool) -> Either<Self, Self>
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 moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
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