use anyhow::{Context, Error};
use epserde::deser::{Deserialize, Flags, MemCase};
use mmap_rs::Mmap;
use std::path::{Path, PathBuf};
use sux::{
bits::{BitFieldVec, BitVec},
dict::{elias_fano::EfSeq, EliasFano},
rank_sel::SelectAdaptConst,
traits::IndexedSeq,
};
type FullnamesOffsets = MemCase<
EliasFano<
SelectAdaptConst<BitVec<Box<[usize]>>, Box<[usize]>>,
BitFieldVec<usize, Box<[usize]>>,
>,
>;
pub struct FullnameMap {
fullnames: Mmap,
offsets: FullnamesOffsets,
}
impl FullnameMap {
pub fn new(graph_path: PathBuf) -> Result<FullnameMap, Error> {
let fullnames_path = suffix_path(&graph_path, ".persons");
let offsets_path = suffix_path(&graph_path, ".persons.ef");
let fullnames = mmap(&fullnames_path)
.with_context(|| format!("Could not mmap {}", fullnames_path.display()))?;
let offsets = unsafe { <EfSeq>::mmap(&offsets_path, Flags::RANDOM_ACCESS) }
.with_context(|| format!("Could not mmap {}", offsets_path.display()))?;
Ok(FullnameMap { fullnames, offsets })
}
pub fn map_id(&self, id: usize) -> Result<&[u8], Error> {
let offsets = self.offsets.uncase();
Ok(self
.fullnames
.get(offsets.get(id)..offsets.get(id + 1))
.unwrap())
}
}
fn suffix_path<P: AsRef<Path>, S: AsRef<std::ffi::OsStr>>(path: P, suffix: S) -> PathBuf {
let mut path = path.as_ref().as_os_str().to_owned();
path.push(suffix);
path.into()
}
fn mmap(path: &Path) -> Result<Mmap, Error> {
let file_len = path
.metadata()
.with_context(|| format!("Could not stat {}", path.display()))?
.len();
let file =
std::fs::File::open(path).with_context(|| format!("Could not open {}", path.display()))?;
let data = unsafe {
mmap_rs::MmapOptions::new(file_len as _)
.with_context(|| format!("Could not initialize mmap of size {file_len}"))?
.with_flags(
mmap_rs::MmapFlags::TRANSPARENT_HUGE_PAGES | mmap_rs::MmapFlags::RANDOM_ACCESS,
)
.with_file(&file, 0)
.map()
.with_context(|| format!("Could not mmap {}", path.display()))?
};
Ok(data)
}