plugins/native/znippy-plugin-git/src/archive_map.rs
//! **The verbatim blob file, mapped once and shared read-only.**
//!
//! # Why this exists
//!
//! [`crate::git_ops::Absorber::read_extent`] answers "give me the bytes at
//! `(offset, len)`" with a `pread` into a fresh `Vec::with_capacity(len)` — one
//! syscall, one heap allocation and one kernel→user copy, **per object**. On a
//! `linux.git` clone that is ~13.8 M of each, and every resulting `Vec` is held
//! at once because [`crate::pack_walk::EmitEntry::stored`] owns its bytes.
//!
//! MEASURED, oden 2026-08-14, 39 `perf record` captures of `gunnar serve`
//! during a kernel mirror clone:
//!
//! | symbol | share | what it is |
//! |---|---:|---|
//! | `rep_movs_alternative` | 11.0% | the copy |
//! | `filemap_read` + `filemap_get_read_batch` + `_copy_to_iter` + … | ~8% | the reads |
//! | `kernel_init_pages` | 1.4% | zeroing the pages each `Vec` demands |
//!
//! ~20% of cycles moving bytes that never needed to move, and a peak RSS of
//! **2314 MB** against gitea's 122 MB on the identical clone
//! (`gunnar/.nornir/forge-bakeoff-benchmarks.md`, `vs_forge_clone`).
//!
//! A mapping replaces all three columns with pointer arithmetic: the page cache
//! already holds these bytes, and `mmap` lets a reader address them where they
//! are instead of asking the kernel to copy them somewhere else first.
//!
//! # Why it is SAFE to map this particular file
//!
//! An mmap over a file another writer is changing underneath you is a way to
//! read torn bytes. This file cannot do that, and the reason is §14's storage
//! shape rather than luck — [`crate::git_ops::Absorber::adopt_journal`] states
//! it outright:
//!
//! > *"The blob file is append-only and `gc` truncates nothing, so a retired
//! > pack's bytes are still exactly where the journal says they are"*
//!
//! So every byte that has ever been acked is **immutable for the life of the
//! file**, including bytes belonging to packs a `gc` has since retired. Growth
//! happens only past the end. That gives two guarantees a reader needs:
//!
//! * a slice handed out stays valid and correct while it is held, and
//! * a mapping taken at length `N` is a correct view of `[0, N)` forever.
//!
//! It also makes the fan-out trivially thread-safe: N workers reading disjoint
//! (or overlapping) extents of an immutable mapping need no lock at all, which
//! is what lets the emit path obey the performance LAW's *"cpu wait by serial
//! but instead use gatlin"* without introducing a single mutex.
//!
//! # Growth, and why a snapshot rather than a live map
//!
//! The file grows as pushes land. A mapping does not grow with it, so a reader
//! must either remap or fall back. [`ArchiveMap::snapshot`] remaps **once per
//! operation** when the file has grown past the current mapping, and hands back
//! an [`Arc<Mapped>`](Mapped) that the caller keeps for the whole operation.
//! One remap per clone, not one per object — and a clone reads extents that were
//! durable before it started, so in the ordinary case it does not even remap.
//!
//! [`Mapped::get`] returns `None` rather than a short slice when an extent falls
//! outside the mapping. `None` means *"go and `pread` it"*, never *"there are no
//! bytes"* — the same shape as every other absent answer in this engine.
use std::fs::File;
use std::path::{Path, PathBuf};
use std::sync::{Arc, RwLock};
use anyhow::{Context, Result};
use memmap2::Mmap;
/// One mapping of the blob file, valid over `[0, len)`.
pub(crate) struct Mapped {
map: Mmap,
len: u64,
}
impl Mapped {
/// The bytes at `(offset, len)`, or `None` if that extent is not inside this
/// mapping.
///
/// `None` is *"fall back and `pread`"*. It is returned for an extent past
/// the mapped end (the file grew after this snapshot) and for one that
/// overflows — never for one that is simply absent, because this map knows
/// nothing about which extents are live.
pub(crate) fn get(&self, offset: u64, len: u64) -> Option<&[u8]> {
let end = offset.checked_add(len)?;
if end > self.len {
return None;
}
let (a, b) = (usize::try_from(offset).ok()?, usize::try_from(end).ok()?);
self.map.get(a..b)
}
/// Bytes this mapping covers.
pub(crate) fn len(&self) -> u64 {
self.len
}
}
/// The blob file, mapped and remapped as it grows.
pub(crate) struct ArchiveMap {
path: PathBuf,
current: RwLock<Option<Arc<Mapped>>>,
}
impl ArchiveMap {
pub(crate) fn new(path: &Path) -> Self {
Self {
path: path.to_path_buf(),
current: RwLock::new(None),
}
}
/// A mapping that covers at least the file's length as of this call.
///
/// Call this **once per operation** and slice the result N times; calling it
/// per object would trade one syscall per read for another and buy nothing.
///
/// An empty file maps to nothing on some platforms and to an error on
/// others, so a zero-length file yields a zero-length mapping rather than a
/// failure: a store with no packs yet is an ordinary state, not a fault.
pub(crate) fn snapshot(&self) -> Result<Arc<Mapped>> {
let on_disk = match std::fs::metadata(&self.path) {
Ok(m) => m.len(),
// No blob file yet — an empty archive, not an error.
Err(e) if e.kind() == std::io::ErrorKind::NotFound => 0,
Err(e) => {
return Err(e).with_context(|| format!("stat {}", self.path.display()));
}
};
{
let cur = self
.current
.read()
.map_err(|_| anyhow::anyhow!("archive map poisoned"))?;
if let Some(m) = cur.as_ref()
&& m.len >= on_disk
{
return Ok(Arc::clone(m));
}
}
let mut cur = self
.current
.write()
.map_err(|_| anyhow::anyhow!("archive map poisoned"))?;
// Another thread may have remapped while we waited for the write lock.
if let Some(m) = cur.as_ref()
&& m.len >= on_disk
{
return Ok(Arc::clone(m));
}
let mapped = if on_disk == 0 {
Arc::new(Mapped {
map: Self::empty_map()?,
len: 0,
})
} else {
let f = File::open(&self.path)
.with_context(|| format!("opening {} to map", self.path.display()))?;
// SAFETY: the file is append-only and `gc` truncates nothing (§14),
// so no byte inside `[0, on_disk)` is ever rewritten or removed
// while this mapping lives. Growth past the end is invisible here,
// which is what `get` returning `None` covers.
let map = unsafe { Mmap::map(&f) }
.with_context(|| format!("mapping {}", self.path.display()))?;
let len = map.len() as u64;
Arc::new(Mapped { map, len })
};
*cur = Some(Arc::clone(&mapped));
Ok(mapped)
}
/// A zero-length mapping. `Mmap::map` on an empty file is an error on Linux,
/// and an empty archive is not a fault, so this maps one page of an
/// anonymous region and reports zero length — `get` refuses every extent
/// against it, which is the correct answer for a store holding nothing.
fn empty_map() -> Result<Mmap> {
memmap2::MmapOptions::new()
.len(1)
.map_anon()
.map(memmap2::MmapMut::make_read_only)
.context("mapping an empty archive")?
.context("freezing the empty archive mapping")
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::io::Write;
fn tmp(name: &str) -> PathBuf {
let d = std::env::temp_dir().join(format!("archive-map-{}-{name}", std::process::id()));
let _ = std::fs::remove_dir_all(&d);
std::fs::create_dir_all(&d).unwrap();
d.join("objects.pack")
}
/// ★ The bytes come back without a copy, and they are the right ones.
#[test]
fn an_extent_reads_back_as_the_bytes_that_were_written() {
let p = tmp("basic");
std::fs::write(&p, b"0123456789abcdef").unwrap();
let m = ArchiveMap::new(&p);
let snap = m.snapshot().unwrap();
assert_eq!(snap.get(0, 4).unwrap(), b"0123");
assert_eq!(snap.get(10, 6).unwrap(), b"abcdef");
assert_eq!(snap.len(), 16);
}
/// An extent past the mapped end is `None` — *go and pread it* — and never a
/// short slice, which a caller would splice into a pack as truncated data.
#[test]
fn an_extent_past_the_end_is_none_not_a_short_slice() {
let p = tmp("past");
std::fs::write(&p, b"0123456789").unwrap();
let snap = ArchiveMap::new(&p).snapshot().unwrap();
assert!(snap.get(8, 4).is_none(), "8..12 of a 10-byte file must refuse");
assert!(snap.get(10, 1).is_none());
assert!(snap.get(u64::MAX, 1).is_none(), "an overflowing extent must refuse");
assert_eq!(snap.get(6, 4).unwrap(), b"6789", "the exact tail is fine");
}
/// The file grows; a NEW snapshot sees the growth, and the OLD one stays
/// valid over what it already covered. That pair is what makes it safe to
/// hold a snapshot for a whole clone while pushes keep landing.
#[test]
fn growth_is_seen_by_the_next_snapshot_and_the_old_one_still_works() {
let p = tmp("grow");
std::fs::write(&p, b"AAAA").unwrap();
let m = ArchiveMap::new(&p);
let first = m.snapshot().unwrap();
assert_eq!(first.len(), 4);
let mut f = std::fs::OpenOptions::new().append(true).open(&p).unwrap();
f.write_all(b"BBBB").unwrap();
f.sync_all().unwrap();
let second = m.snapshot().unwrap();
assert_eq!(second.len(), 8, "the remap did not pick up the append");
assert_eq!(second.get(4, 4).unwrap(), b"BBBB");
// The older snapshot is still correct over its own range.
assert_eq!(first.get(0, 4).unwrap(), b"AAAA");
assert!(first.get(4, 4).is_none(), "the old map must not see the growth");
}
/// An archive with no blob file yet is an ordinary state.
#[test]
fn an_absent_or_empty_archive_maps_to_nothing_rather_than_failing() {
let p = tmp("empty");
let snap = ArchiveMap::new(&p).snapshot().unwrap();
assert_eq!(snap.len(), 0);
assert!(snap.get(0, 1).is_none());
std::fs::write(&p, b"").unwrap();
let snap = ArchiveMap::new(&p).snapshot().unwrap();
assert_eq!(snap.len(), 0);
assert!(snap.get(0, 0).is_some(), "a zero-length extent at 0 is legal");
}
/// Two snapshots with no growth between them are the SAME mapping, not two.
/// One remap per operation is the whole point; one per call would trade a
/// pread for an mmap and buy nothing.
#[test]
fn a_second_snapshot_without_growth_reuses_the_mapping() {
let p = tmp("reuse");
std::fs::write(&p, b"0123456789").unwrap();
let m = ArchiveMap::new(&p);
let a = m.snapshot().unwrap();
let b = m.snapshot().unwrap();
assert!(Arc::ptr_eq(&a, &b), "the mapping was rebuilt for no reason");
}
}