Safe, guarded memory-mapped file I/O for Rust. Wraps memmap2::Mmap::map() behind a safe API so downstream crates can use #![forbid(unsafe_code)] while still benefiting from zero-copy file access.
Projects that enforce #![forbid(unsafe_code)] cannot call memmap2::Mmap::map() directly because it is unsafe. The alternative — std::fs::read() — copies the entire file into heap memory, which is impractical for disk images and multi-gigabyte binaries.
mmap-guard bridges this gap by isolating the unsafe boundary in a single, focused crate. By centralizing it here, we can concentrate testing, fuzzing, and hardening efforts on that one point — so every consumer benefits from those protections without reasoning about mmap safety themselves.
- Safe mmap construction — wraps
memmap2::Mmap::map()with pre-flight checks (empty file detection, permission errors) - Advisory file locking — acquires a shared advisory lock (via
fs4) before mapping; returnsio::ErrorKind::WouldBlockon lock contention - Platform quirk mitigation — documents and (where possible) mitigates SIGBUS/access violations from file truncation
- Unified read API — returns
&[u8]whether backed by mmap or a heap buffer (for stdin/non-seekable inputs) - Zero unsafe for consumers — exactly one
unsafeblock, fully documented with a// SAFETY:comment - Minimal dependencies —
memmap2andfs4at runtime
Add to your Cargo.toml:
[dependencies]
mmap-guard = "0.1"use mmap_guard::map_file;
// Memory-map a file — returns FileData that derefs to &[u8]
let data = map_file("large-file.bin")?;
assert!(!data.is_empty());
// Use it like any byte slice
println!("first byte: {:#04x}", data[0]);For CLI tools that accept both file paths and stdin:
use mmap_guard::load;
// load() handles both file paths and stdin ("-" with a 1 GiB default cap).
let data = load("input.txt")?;
// For a custom stdin byte limit, call load_stdin directly:
// use mmap_guard::load_stdin;
// let data = load_stdin(Some(10 * 1024 * 1024))?; // 10 MiB cap
// let data = load_stdin(None)?; // unlimitedgraph TD
load["load(path)"] -->|path == "-"| load_stdin
load -->|other paths| map_file
load_stdin["load_stdin(max_bytes)"] --> FileData
map_file["map_file()"] --> unsafe["unsafe { Mmap::map() }"]
unsafe --> FileData["FileData<br/>Deref<Target=[u8]>"]
FileData --- Mapped["Mapped(Mmap, File)"]
FileData --- Loaded["Loaded(Vec<u8>)"]
| Module | Purpose |
|---|---|
src/lib.rs |
Crate-level docs, re-exports public API |
src/map.rs |
map_file() with pre-flight stat check; the single unsafe block |
src/load.rs |
load() routes "-" to load_stdin(), others to map_file() |
src/file_data.rs |
FileData enum (Mapped(Mmap, File) / Loaded), Deref, AsRef |
- Provide mutable/writable mappings
- Abstract over async I/O
- Implement its own mmap syscalls (delegates entirely to
memmap2)
This crate contains exactly one unsafe block — the call to memmap2::Mmap::map(). The safety contract is maintained through:
- File opened read-only (
File::open()) - Shared advisory lock acquired via
fs4before mapping — contention returnsio::ErrorKind::WouldBlock - File descriptor and lock held through ownership in
FileData::Mapped(Mmap, File)— released on drop - No mutable aliasing (read-only mappings only)
#![deny(clippy::undocumented_unsafe_blocks)]enforced
See the safety documentation for the full contract and known limitations (SIGBUS from concurrent file truncation).
- Strict linting — pedantic/nursery/cargo clippy groups,
unwrap_useddenied,panicdenied - Dependency auditing —
cargo auditandcargo denyin CI - Coverage threshold — 80%+ project coverage enforced
- Supply chain — OpenSSF Scorecard monitoring
- Developer Guide — mdBook documentation
- API Reference — docs.rs
- GitHub Issues
See CONTRIBUTING.md for development setup and guidelines.
Licensed under either of Apache License 2.0 or MIT License, at your option.
- memmap2 for the underlying memory-mapping implementation
- The Rust community for excellent tooling and ecosystem