#![expect(
unsafe_code,
reason = "the control block is a mapped file; every other module takes the reference this one hands out"
)]
use std::fs::File;
use std::io;
use memmap2::MmapMut;
use crate::sidecar::layout::ControlBlock;
#[derive(Debug)]
pub(crate) struct Mapping(MmapMut);
impl Mapping {
pub(crate) fn open(file: &File) -> io::Result<Self> {
let map = unsafe { MmapMut::map_mut(file) }?;
if map.len() != size_of::<ControlBlock>() {
return Err(io::Error::new(
io::ErrorKind::InvalidData,
"the mapping is not one control block long",
));
}
#[expect(
clippy::cast_ptr_alignment,
reason = "a mapping starts on a page, and the check below proves it"
)]
if !map.as_ptr().cast::<ControlBlock>().is_aligned() {
return Err(io::Error::new(
io::ErrorKind::InvalidData,
"the mapping is not page-aligned",
));
}
Ok(Self(map))
}
pub(crate) fn control_block(&self) -> &ControlBlock {
#[expect(
clippy::cast_ptr_alignment,
reason = "`open` refused a mapping that is not page-aligned"
)]
let pointer = self.0.as_ptr().cast::<ControlBlock>();
unsafe { &*pointer }
}
}
#[cfg(test)]
mod tests {
use std::fs::OpenOptions;
use std::path::PathBuf;
use super::*;
use crate::sidecar::layout::MAGIC;
use crate::sidecar::sync::Ordering;
fn scratch_file(name: &str, size: u64) -> (PathBuf, File) {
let path = PathBuf::from(format!(
"/dev/shm/rigless-mapping-test-{}-{name}",
std::process::id()
));
let file = OpenOptions::new()
.read(true)
.write(true)
.create_new(true)
.open(&path)
.unwrap();
file.set_len(size).unwrap();
(path, file)
}
#[test]
#[cfg_attr(miri, ignore = "Miri cannot map a file")]
fn a_fresh_page_reads_as_zeros_and_takes_stores() {
let (path, file) = scratch_file("fresh", size_of::<ControlBlock>() as u64);
std::fs::remove_file(&path).unwrap();
let mapping = Mapping::open(&file).unwrap();
let block = mapping.control_block();
assert_eq!(block.magic.load(Ordering::Relaxed), 0);
ControlBlock::initialise(block);
assert_eq!(block.magic.load(Ordering::Relaxed), MAGIC);
}
#[test]
#[cfg_attr(miri, ignore = "Miri cannot map a file")]
fn a_file_of_the_wrong_length_is_refused() {
let (path, file) = scratch_file("short", size_of::<ControlBlock>() as u64 / 2);
std::fs::remove_file(&path).unwrap();
assert_eq!(
Mapping::open(&file).unwrap_err().kind(),
io::ErrorKind::InvalidData
);
}
}