metrale_storage/snapshot_swap/
mmap_arena.rs

1// SPDX-License-Identifier: MIT OR Apache-2.0
2
3//! 2026-09-25: `MmapSlotArena`, the `SlotArena` over the paging peer's mapped arena.
4//!
5//! Owner: storage, SSM snapshot tier.
6//! Invariants:
7//! - `read_slot` and `write_slot` touch memory only after checking the slot index and
8//!   the buffer length; a mismatch is an error.
9
10use anyhow::{Result, bail};
11
12use super::SlotArena;
13
14/// 2026-09-25: `SlotArena` over the peer's mapped arena, from a raw base pointer. The
15/// peer copies between slots and its swap file; clients RDMA into and out of the same
16/// slots. The arena registers nothing; the peer registers the mapping per connection.
17pub struct MmapSlotArena {
18    base: *mut u8,
19    slot_bytes: usize,
20    num_slots: usize,
21}
22unsafe impl Send for MmapSlotArena {}
23
24impl MmapSlotArena {
25    /// 2026-09-25: Wrap the mapping at `base`.
26    ///
27    /// # Safety
28    /// `base` must be a valid, writable mapping of `>= num_slots*slot_bytes`
29    /// bytes that outlives this arena.
30    pub unsafe fn new(base: *mut u8, slot_bytes: usize, num_slots: usize) -> Self {
31        Self {
32            base,
33            slot_bytes,
34            num_slots,
35        }
36    }
37    fn slot_ptr(&self, slot: usize) -> *mut u8 {
38        // 2026-09-25: Both callers check `slot < num_slots` first.
39        unsafe { self.base.add(slot * self.slot_bytes) }
40    }
41}
42
43impl SlotArena for MmapSlotArena {
44    fn slot_bytes(&self) -> usize {
45        self.slot_bytes
46    }
47    fn num_slots(&self) -> usize {
48        self.num_slots
49    }
50    fn read_slot(&self, slot: usize, out: &mut [u8]) -> Result<()> {
51        if slot >= self.num_slots || out.len() != self.slot_bytes {
52            bail!("read_slot({slot}) out of range / size mismatch");
53        }
54        unsafe {
55            std::ptr::copy_nonoverlapping(self.slot_ptr(slot), out.as_mut_ptr(), self.slot_bytes)
56        };
57        Ok(())
58    }
59    fn write_slot(&mut self, slot: usize, bytes: &[u8]) -> Result<()> {
60        if slot >= self.num_slots || bytes.len() != self.slot_bytes {
61            bail!("write_slot({slot}) out of range / size mismatch");
62        }
63        unsafe {
64            std::ptr::copy_nonoverlapping(bytes.as_ptr(), self.slot_ptr(slot), self.slot_bytes)
65        };
66        Ok(())
67    }
68}
69
70#[cfg(test)]
71#[path = "mmap_arena_tests.rs"]
72mod tests;