use anyhow::{Context, Result, ensure};
use common_traits::UnsignedInt;
use core::fmt::Debug;
use mmap_rs::*;
use std::{mem::size_of, path::Path, sync::Arc};
#[derive(Clone)]
pub struct MmapHelper<W, M = Mmap> {
mmap: M,
len: usize,
_marker: core::marker::PhantomData<W>,
}
impl<W: Debug> Debug for MmapHelper<W, Mmap> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("MmapHelper")
.field("mmap", &self.mmap.as_ptr())
.field("len", &self.len)
.finish()
}
}
impl<W: Debug> Debug for MmapHelper<W, MmapMut> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("MmapHelper")
.field("mmap", &self.mmap.as_ptr())
.field("len", &self.len)
.finish()
}
}
impl<W> TryFrom<Mmap> for MmapHelper<W> {
type Error = anyhow::Error;
fn try_from(value: Mmap) -> std::result::Result<Self, Self::Error> {
#[cfg(windows)]
{
ensure!(
value.len() % size_of::<W>() == 0,
"The size of the mmap is not a multiple of the size of W"
);
}
let len = value.len().div_ceil(size_of::<W>());
Ok(Self {
len,
mmap: value,
_marker: core::marker::PhantomData,
})
}
}
impl<W> MmapHelper<W> {
#[inline(always)]
pub fn len(&self) -> usize {
self.len
}
#[inline(always)]
pub fn is_empty(&self) -> bool {
self.len == 0
}
pub fn mmap(path: impl AsRef<Path>, flags: MmapFlags) -> Result<Self> {
let file_len: usize = path
.as_ref()
.metadata()
.with_context(|| format!("Cannot stat {}", path.as_ref().display()))?
.len()
.try_into()
.with_context(|| "Cannot convert file length to usize")?;
let mmap_len = file_len.align_to(size_of::<W>());
#[cfg(windows)]
{
ensure!(
mmap_len == file_len,
"File has insufficient padding for word size {}. Use \"webgraph run pad BASENAME u{}\" to ensure sufficient padding.",
size_of::<W>() * 8,
size_of::<W>() * 8
);
}
let file = std::fs::File::open(path.as_ref())
.with_context(|| "Cannot open file for MmapHelper")?;
let mmap = unsafe {
mmap_rs::MmapOptions::new(mmap_len.max(size_of::<W>()))
.with_context(|| format!("Cannot initialize mmap of size {mmap_len}"))?
.with_flags(flags)
.with_file(&file, 0)
.map()
.with_context(|| {
format!(
"Cannot mmap {} (size {})",
path.as_ref().display(),
mmap_len
)
})?
};
Ok(Self {
len: mmap_len / core::mem::size_of::<W>(),
mmap,
_marker: core::marker::PhantomData,
})
}
}
impl<W> MmapHelper<W, MmapMut> {
pub fn mmap_mut(path: impl AsRef<Path>, flags: MmapFlags) -> Result<Self> {
let file_len: usize = path
.as_ref()
.metadata()
.with_context(|| format!("Cannot stat {}", path.as_ref().display()))?
.len()
.try_into()
.with_context(|| "Cannot convert file length to usize")?;
let file = std::fs::OpenOptions::new()
.read(true)
.write(true)
.open(path.as_ref())
.with_context(|| {
format!(
"Cannot open {} for mutable MmapHelper",
path.as_ref().display()
)
})?;
let mmap_len = file_len.align_to(size_of::<W>());
ensure!(
mmap_len == file_len,
"File has insufficient padding for word size {}. Use \"webgraph run pad BASENAME u{}\" to ensure sufficient padding.",
size_of::<W>() * 8,
size_of::<W>() * 8
);
let mmap = unsafe {
mmap_rs::MmapOptions::new(mmap_len.max(1))
.with_context(|| format!("Cannot initialize mmap of size {file_len}"))?
.with_flags(flags)
.with_file(&file, 0)
.map_mut()
.with_context(|| {
format!(
"Cannot mutably mmap {} (size {})",
path.as_ref().display(),
file_len
)
})?
};
Ok(Self {
len: mmap.len() / core::mem::size_of::<W>(),
mmap,
_marker: core::marker::PhantomData,
})
}
pub fn new(path: impl AsRef<Path>, flags: MmapFlags, len: usize) -> Result<Self> {
let file = std::fs::OpenOptions::new()
.read(true)
.write(true)
.create(true)
.truncate(true)
.open(path.as_ref())
.with_context(|| format!("Cannot create {} new MmapHelper", path.as_ref().display()))?;
let file_len = len * size_of::<W>();
#[cfg(windows)]
{
file.set_len(
file_len
.try_into()
.with_context(|| "Cannot convert usize to u64")?,
)
.with_context(|| "Cannot modify file size")?;
}
let mmap = unsafe {
mmap_rs::MmapOptions::new(file_len as _)
.with_context(|| format!("Cannot initialize mmap of size {file_len}"))?
.with_flags(flags)
.with_file(&file, 0)
.map_mut()
.with_context(|| format!("Cannot mutably mmap {}", path.as_ref().display()))?
};
Ok(Self {
len: mmap.len() / core::mem::size_of::<W>(),
mmap,
_marker: core::marker::PhantomData,
})
}
}
impl<W> AsRef<[W]> for MmapHelper<W> {
fn as_ref(&self) -> &[W] {
unsafe { std::slice::from_raw_parts(self.mmap.as_ptr() as *const W, self.len) }
}
}
impl<W> AsRef<[W]> for MmapHelper<W, MmapMut> {
fn as_ref(&self) -> &[W] {
unsafe { std::slice::from_raw_parts(self.mmap.as_ptr() as *const W, self.len) }
}
}
impl<W> AsMut<[W]> for MmapHelper<W, MmapMut> {
fn as_mut(&mut self) -> &mut [W] {
unsafe { std::slice::from_raw_parts_mut(self.mmap.as_mut_ptr() as *mut W, self.len) }
}
}
#[derive(Clone)]
pub struct ArcMmapHelper<W>(pub Arc<MmapHelper<W>>);
impl<W: Debug> Debug for ArcMmapHelper<W> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("ArcMmapHelper")
.field("mmap", &self.0.mmap.as_ptr())
.field("len", &self.0.len)
.finish()
}
}
impl<W> AsRef<[W]> for ArcMmapHelper<W> {
fn as_ref(&self) -> &[W] {
unsafe { std::slice::from_raw_parts(self.0.mmap.as_ptr() as *const W, self.0.len) }
}
}