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 MaybeTimestamps {}
pub struct MappedTimestamps {
author_timestamp: NumberMmap<BigEndian, i64, Mmap>,
author_timestamp_offset: NumberMmap<BigEndian, i16, Mmap>,
committer_timestamp: NumberMmap<BigEndian, i64, Mmap>,
committer_timestamp_offset: NumberMmap<BigEndian, i16, Mmap>,
}
impl<T: Timestamps> MaybeTimestamps for T {}
pub struct NoTimestamps;
impl MaybeTimestamps for NoTimestamps {}
pub trait Timestamps {
type Timestamps<'a>: GetIndex<Output = i64> + 'a
where
Self: 'a;
type Offsets<'a>: GetIndex<Output = i16> + 'a
where
Self: 'a;
fn author_timestamp(&self) -> Self::Timestamps<'_>;
fn author_timestamp_offset(&self) -> Self::Offsets<'_>;
fn committer_timestamp(&self) -> Self::Timestamps<'_>;
fn committer_timestamp_offset(&self) -> Self::Offsets<'_>;
}
impl Timestamps for MappedTimestamps {
type Timestamps<'a> = &'a NumberMmap<BigEndian, i64, Mmap>;
type Offsets<'a> = &'a NumberMmap<BigEndian, i16, Mmap>;
#[inline(always)]
fn author_timestamp(&self) -> Self::Timestamps<'_> {
&self.author_timestamp
}
#[inline(always)]
fn author_timestamp_offset(&self) -> Self::Offsets<'_> {
&self.author_timestamp_offset
}
#[inline(always)]
fn committer_timestamp(&self) -> Self::Timestamps<'_> {
&self.committer_timestamp
}
#[inline(always)]
fn committer_timestamp_offset(&self) -> Self::Offsets<'_> {
&self.committer_timestamp_offset
}
}
pub struct VecTimestamps {
author_timestamp: Vec<i64>,
author_timestamp_offset: Vec<i16>,
committer_timestamp: Vec<i64>,
committer_timestamp_offset: Vec<i16>,
}
impl VecTimestamps {
#[allow(clippy::type_complexity)]
pub fn new(
timestamps: Vec<(Option<i64>, Option<i16>, Option<i64>, Option<i16>)>,
) -> Result<Self> {
let mut author_timestamp = Vec::with_capacity(timestamps.len());
let mut author_timestamp_offset = Vec::with_capacity(timestamps.len());
let mut committer_timestamp = Vec::with_capacity(timestamps.len());
let mut committer_timestamp_offset = Vec::with_capacity(timestamps.len());
for (a_ts, a_ts_o, c_ts, c_ts_o) in timestamps {
ensure!(
a_ts != Some(i64::MIN),
"author timestamp may not be {}",
i64::MIN
);
ensure!(
a_ts_o != Some(i16::MIN),
"author timestamp offset may not be {}",
i16::MIN
);
ensure!(
c_ts != Some(i64::MIN),
"committer timestamp may not be {}",
i64::MIN
);
ensure!(
c_ts_o != Some(i16::MIN),
"committer timestamp offset may not be {}",
i16::MIN
);
author_timestamp.push(a_ts.unwrap_or(i64::MIN));
author_timestamp_offset.push(a_ts_o.unwrap_or(i16::MIN));
committer_timestamp.push(c_ts.unwrap_or(i64::MIN));
committer_timestamp_offset.push(c_ts_o.unwrap_or(i16::MIN));
}
Ok(VecTimestamps {
author_timestamp,
author_timestamp_offset,
committer_timestamp,
committer_timestamp_offset,
})
}
}
impl Timestamps for VecTimestamps {
type Timestamps<'a> = &'a [i64];
type Offsets<'a> = &'a [i16];
#[inline(always)]
fn author_timestamp(&self) -> Self::Timestamps<'_> {
self.author_timestamp.as_slice()
}
#[inline(always)]
fn author_timestamp_offset(&self) -> Self::Offsets<'_> {
self.author_timestamp_offset.as_slice()
}
#[inline(always)]
fn committer_timestamp(&self) -> Self::Timestamps<'_> {
self.committer_timestamp.as_slice()
}
#[inline(always)]
fn committer_timestamp_offset(&self) -> Self::Offsets<'_> {
self.committer_timestamp_offset.as_slice()
}
}
impl<
MAPS: MaybeMaps,
PERSONS: MaybePersons,
CONTENTS: MaybeContents,
STRINGS: MaybeStrings,
LABELNAMES: MaybeLabelNames,
> SwhGraphProperties<MAPS, NoTimestamps, PERSONS, CONTENTS, STRINGS, LABELNAMES>
{
pub fn load_timestamps(
self,
) -> Result<SwhGraphProperties<MAPS, MappedTimestamps, PERSONS, CONTENTS, STRINGS, LABELNAMES>>
{
let timestamps = MappedTimestamps {
author_timestamp: NumberMmap::new(
suffix_path(&self.path, AUTHOR_TIMESTAMP),
self.num_nodes,
)
.context("Could not load author_timestamp")?,
author_timestamp_offset: NumberMmap::new(
suffix_path(&self.path, AUTHOR_TIMESTAMP_OFFSET),
self.num_nodes,
)
.context("Could not load author_timestamp_offset")?,
committer_timestamp: NumberMmap::new(
suffix_path(&self.path, COMMITTER_TIMESTAMP),
self.num_nodes,
)
.context("Could not load committer_timestamp")?,
committer_timestamp_offset: NumberMmap::new(
suffix_path(&self.path, COMMITTER_TIMESTAMP_OFFSET),
self.num_nodes,
)
.context("Could not load committer_timestamp_offset")?,
};
self.with_timestamps(timestamps)
}
pub fn with_timestamps<TIMESTAMPS: Timestamps>(
self,
timestamps: TIMESTAMPS,
) -> Result<SwhGraphProperties<MAPS, TIMESTAMPS, PERSONS, CONTENTS, STRINGS, LABELNAMES>> {
Ok(SwhGraphProperties {
maps: self.maps,
timestamps,
persons: self.persons,
contents: self.contents,
strings: self.strings,
label_names: self.label_names,
path: self.path,
num_nodes: self.num_nodes,
})
}
}
impl<
MAPS: MaybeMaps,
TIMESTAMPS: Timestamps,
PERSONS: MaybePersons,
CONTENTS: MaybeContents,
STRINGS: MaybeStrings,
LABELNAMES: MaybeLabelNames,
> SwhGraphProperties<MAPS, TIMESTAMPS, PERSONS, CONTENTS, STRINGS, LABELNAMES>
{
#[inline]
pub fn author_timestamp(&self, node_id: NodeId) -> Option<i64> {
self.try_author_timestamp(node_id)
.unwrap_or_else(|e| panic!("Cannot get author timestamp: {}", e))
}
#[inline]
pub fn try_author_timestamp(&self, node_id: NodeId) -> Result<Option<i64>, OutOfBoundError> {
match self.timestamps.author_timestamp().get(node_id) {
None => Err(OutOfBoundError {
index: node_id,
len: self.timestamps.author_timestamp().len(),
}),
Some(i64::MIN) => Ok(None),
Some(ts) => Ok(Some(ts)),
}
}
#[inline]
pub fn author_timestamp_offset(&self, node_id: NodeId) -> Option<i16> {
self.try_author_timestamp_offset(node_id)
.unwrap_or_else(|e| panic!("Cannot get author timestamp offset: {}", e))
}
#[inline]
pub fn try_author_timestamp_offset(
&self,
node_id: NodeId,
) -> Result<Option<i16>, OutOfBoundError> {
match self.timestamps.author_timestamp_offset().get(node_id) {
None => Err(OutOfBoundError {
index: node_id,
len: self.timestamps.author_timestamp_offset().len(),
}),
Some(i16::MIN) => Ok(None),
Some(offset) => Ok(Some(offset)),
}
}
#[inline]
pub fn committer_timestamp(&self, node_id: NodeId) -> Option<i64> {
self.try_committer_timestamp(node_id)
.unwrap_or_else(|e| panic!("Cannot get committer timestamp: {}", e))
}
#[inline]
pub fn try_committer_timestamp(&self, node_id: NodeId) -> Result<Option<i64>, OutOfBoundError> {
match self.timestamps.committer_timestamp().get(node_id) {
None => Err(OutOfBoundError {
index: node_id,
len: self.timestamps.committer_timestamp().len(),
}),
Some(i64::MIN) => Ok(None),
Some(ts) => Ok(Some(ts)),
}
}
#[inline]
pub fn committer_timestamp_offset(&self, node_id: NodeId) -> Option<i16> {
self.try_committer_timestamp_offset(node_id)
.unwrap_or_else(|e| panic!("Cannot get committer timestamp: {}", e))
}
#[inline]
pub fn try_committer_timestamp_offset(
&self,
node_id: NodeId,
) -> Result<Option<i16>, OutOfBoundError> {
match self.timestamps.committer_timestamp_offset().get(node_id) {
None => Err(OutOfBoundError {
index: node_id,
len: self.timestamps.committer_timestamp_offset().len(),
}),
Some(i16::MIN) => Ok(None),
Some(offset) => Ok(Some(offset)),
}
}
}