use anyhow::{ensure, Context, Result};
use mmap_rs::Mmap;
use super::suffixes::*;
use super::*;
use crate::graph::NodeId;
use crate::utils::suffix_path;
pub trait MaybeContents {}
pub struct MappedContents {
is_skipped_content: NumberMmap<BigEndian, u64, Mmap>,
content_length: NumberMmap<BigEndian, u64, Mmap>,
}
impl<C: Contents> MaybeContents for C {}
pub struct NoContents;
impl MaybeContents for NoContents {}
pub trait Contents {
type Data<'a>: GetIndex<Output = u64> + 'a
where
Self: 'a;
fn is_skipped_content(&self) -> Self::Data<'_>;
fn content_length(&self) -> Self::Data<'_>;
}
impl Contents for MappedContents {
type Data<'a> = &'a NumberMmap<BigEndian, u64, Mmap> where Self: 'a;
#[inline(always)]
fn is_skipped_content(&self) -> Self::Data<'_> {
&self.is_skipped_content
}
#[inline(always)]
fn content_length(&self) -> Self::Data<'_> {
&self.content_length
}
}
pub struct VecContents {
is_skipped_content: Vec<u64>,
content_length: Vec<u64>,
}
impl VecContents {
pub fn new(data: Vec<(bool, Option<u64>)>) -> Result<Self> {
let bit_vec_len = data.len().div_ceil(64);
let mut is_skipped_content = vec![0; bit_vec_len];
let mut content_length = Vec::with_capacity(data.len());
for (node_id, (is_skipped, length)) in data.into_iter().enumerate() {
ensure!(
length != Some(u64::MAX),
"content length may not be {}",
u64::MAX
);
content_length.push(length.unwrap_or(u64::MAX));
if is_skipped {
let cell_id = node_id / (u64::BITS as usize);
let mask = 1 << (node_id % (u64::BITS as usize));
is_skipped_content[cell_id] |= mask;
}
}
Ok(VecContents {
is_skipped_content,
content_length,
})
}
}
impl Contents for VecContents {
type Data<'a> = &'a [u64] where Self: 'a;
#[inline(always)]
fn is_skipped_content(&self) -> Self::Data<'_> {
self.is_skipped_content.as_slice()
}
#[inline(always)]
fn content_length(&self) -> Self::Data<'_> {
self.content_length.as_slice()
}
}
impl<
MAPS: MaybeMaps,
TIMESTAMPS: MaybeTimestamps,
PERSONS: MaybePersons,
STRINGS: MaybeStrings,
LABELNAMES: MaybeLabelNames,
> SwhGraphProperties<MAPS, TIMESTAMPS, PERSONS, NoContents, STRINGS, LABELNAMES>
{
pub fn load_contents(
self,
) -> Result<SwhGraphProperties<MAPS, TIMESTAMPS, PERSONS, MappedContents, STRINGS, LABELNAMES>>
{
let contents = MappedContents {
is_skipped_content: NumberMmap::new(
suffix_path(&self.path, CONTENT_IS_SKIPPED),
self.num_nodes.div_ceil(u64::BITS.try_into().unwrap()),
)
.context("Could not load is_skipped_content")?,
content_length: NumberMmap::new(
suffix_path(&self.path, CONTENT_LENGTH),
self.num_nodes,
)
.context("Could not load content_length")?,
};
self.with_contents(contents)
}
pub fn with_contents<CONTENTS: Contents>(
self,
contents: CONTENTS,
) -> Result<SwhGraphProperties<MAPS, TIMESTAMPS, PERSONS, CONTENTS, STRINGS, LABELNAMES>> {
Ok(SwhGraphProperties {
maps: self.maps,
timestamps: self.timestamps,
persons: self.persons,
contents,
strings: self.strings,
label_names: self.label_names,
path: self.path,
num_nodes: self.num_nodes,
})
}
}
impl<
MAPS: MaybeMaps,
TIMESTAMPS: MaybeTimestamps,
PERSONS: MaybePersons,
CONTENTS: Contents,
STRINGS: MaybeStrings,
LABELNAMES: MaybeLabelNames,
> SwhGraphProperties<MAPS, TIMESTAMPS, PERSONS, CONTENTS, STRINGS, LABELNAMES>
{
#[inline]
pub fn is_skipped_content(&self, node_id: NodeId) -> bool {
self.try_is_skipped_content(node_id)
.unwrap_or_else(|e| panic!("Cannot get is_skipped_content bit of node: {}", e))
}
#[inline]
pub fn try_is_skipped_content(&self, node_id: NodeId) -> Result<bool, OutOfBoundError> {
if node_id >= self.num_nodes {
return Err(OutOfBoundError {
index: node_id,
len: self.num_nodes,
});
}
let cell_id = node_id / (u64::BITS as usize);
let mask = 1 << (node_id % (u64::BITS as usize));
let cell = unsafe { self.contents.is_skipped_content().get_unchecked(cell_id) };
Ok((cell & mask) != 0)
}
#[inline]
pub fn content_length(&self, node_id: NodeId) -> Option<u64> {
self.try_content_length(node_id)
.unwrap_or_else(|e| panic!("Cannot get content length: {}", e))
}
#[inline]
pub fn try_content_length(&self, node_id: NodeId) -> Result<Option<u64>, OutOfBoundError> {
match self.contents.content_length().get(node_id) {
None => Err(OutOfBoundError {
index: node_id,
len: self.contents.content_length().len(),
}),
Some(u64::MAX) => Ok(None), Some(length) => Ok(Some(length)),
}
}
}