hexz_store/local/mmap.rs
1//! Memory-mapped file storage backend with zero-copy reads.
2//!
3//! `MmapBackend` maps the entire file into the process address space and returns
4//! `Bytes` slices backed by the mapping. Each `read_exact` call is O(1) — just
5//! an atomic refcount increment and pointer arithmetic, with no `memcpy`.
6
7use bytes::Bytes;
8use hexz_common::Result;
9use hexz_core::store::StorageBackend;
10use memmap2::Mmap;
11use std::fs::File;
12
13/// A read-only memory-mapped file backend with zero-copy reads.
14///
15/// Maps the entire file into the process address space with `MAP_PRIVATE | PROT_READ`.
16/// Reads return `Bytes` slices that reference the mapped region directly, avoiding
17/// any allocation or copy on the read path.
18#[derive(Debug)]
19pub struct MmapBackend {
20 bytes: Bytes,
21 len: u64,
22}
23
24impl MmapBackend {
25 /// Opens and maps `path` read-only. No I/O occurs until pages are accessed.
26 pub fn new(path: &std::path::Path) -> Result<Self> {
27 let file = File::open(path)?;
28 let len = file.metadata()?.len();
29 // SAFETY: The file is immutable for the lifetime of the mapping (archive semantics).
30 let map = unsafe { Mmap::map(&file)? };
31 let bytes = Bytes::from_owner(map);
32 Ok(Self { bytes, len })
33 }
34}
35
36impl StorageBackend for MmapBackend {
37 fn read_exact(&self, offset: u64, len: usize) -> Result<Bytes> {
38 let start = offset as usize;
39 let end = start + len;
40 if end > self.bytes.len() {
41 return Err(std::io::Error::new(
42 std::io::ErrorKind::UnexpectedEof,
43 "Read out of bounds",
44 )
45 .into());
46 }
47 Ok(self.bytes.slice(start..end))
48 }
49
50 fn len(&self) -> u64 {
51 self.len
52 }
53}