use super::format::{read_u32_le_at, read_u64_le_at, ColumnRange};
use super::MappedFrozen;
use crate::edge::{ColdStamps, HotNbr, Nbr};
use linkrs_core::types::{EdgeId, Timestamp};
impl MappedFrozen {
#[inline]
pub(crate) fn degree_at(&self, row: usize) -> u32 {
read_u32_le_at(&self.map, self.columns.degrees.start + row * 4)
.expect("snapshot degree column validated at open")
}
#[inline]
pub(crate) fn column_bytes(&self, range: ColumnRange) -> &[u8] {
self.map
.get(range.start..range.end())
.expect("snapshot column range validated at open")
}
#[inline]
pub(crate) fn row_column_bytes(
&self,
range: ColumnRange,
width: usize,
start: usize,
end: usize,
) -> &[u8] {
self.column_bytes(range)
.get(start * width..end * width)
.expect("snapshot row window validated against column ranges")
}
pub(crate) fn row_hot_slots(
&self,
start: usize,
end: usize,
) -> impl Iterator<Item = HotNbr> + '_ {
let endpoints = self
.row_column_bytes(self.columns.endpoints, 4, start, end)
.as_chunks::<4>()
.0;
let ranks = self
.row_column_bytes(self.columns.ranks, 8, start, end)
.as_chunks::<8>()
.0;
let edge_ids = self
.row_column_bytes(self.columns.edge_ids, 8, start, end)
.as_chunks::<8>()
.0;
endpoints
.iter()
.zip(ranks)
.zip(edge_ids)
.map(|((e, r), id)| HotNbr {
endpoint: u32::from_le_bytes(*e),
rank: i64::from_le_bytes(*r),
edge_id: EdgeId(u64::from_le_bytes(*id)),
})
}
pub(crate) fn row_slots(&self, start: usize, end: usize) -> impl Iterator<Item = Nbr> + '_ {
let deletes = self
.row_column_bytes(self.columns.deletes, 8, start, end)
.as_chunks::<8>()
.0;
self.row_hot_slots(start, end)
.zip(deletes)
.map(|(hot, d)| Nbr {
endpoint: hot.endpoint,
rank: hot.rank,
edge_id: hot.edge_id,
delete_ts: u64::from_le_bytes(*d),
})
}
#[inline]
pub(crate) fn endpoint_at(&self, idx: usize) -> u32 {
read_u32_le_at(&self.map, self.columns.endpoints.start + idx * 4)
.expect("snapshot endpoint column validated at open")
}
#[inline]
pub(crate) fn rank_at(&self, idx: usize) -> i64 {
read_u64_le_at(&self.map, self.columns.ranks.start + idx * 8)
.expect("snapshot rank column validated at open") as i64
}
#[inline]
pub(crate) fn edge_id_at(&self, idx: usize) -> EdgeId {
EdgeId(
read_u64_le_at(&self.map, self.columns.edge_ids.start + idx * 8)
.expect("snapshot edge-id column validated at open"),
)
}
#[inline]
pub(crate) fn delete_at(&self, idx: usize) -> Timestamp {
read_u64_le_at(&self.map, self.columns.deletes.start + idx * 8)
.expect("snapshot delete column validated at open")
}
pub fn has_valued_entries(&self) -> bool {
self.columns.values.len > 0
}
#[inline]
pub(crate) fn value_at(&self, idx: usize) -> u64 {
if self.columns.values.len == 0 {
return 0;
}
read_u64_le_at(&self.map, self.columns.values.start + idx * 8)
.expect("snapshot value column validated at open")
}
#[inline]
pub(crate) fn valid_at(&self, idx: usize) -> bool {
if self.columns.validity.len == 0 {
return false;
}
let at = self.columns.validity.start + idx / 8;
let byte = self
.map
.get(at..at + 1)
.expect("snapshot validity column validated at open")[0];
byte & (1 << (idx % 8)) != 0
}
#[inline]
pub fn hot_at(&self, idx: usize) -> Option<HotNbr> {
if idx >= self.entries {
return None;
}
Some(HotNbr {
endpoint: self.endpoint_at(idx),
rank: self.rank_at(idx),
edge_id: self.edge_id_at(idx),
})
}
#[inline]
pub fn cold_at(&self, idx: usize) -> Option<ColdStamps> {
if idx >= self.entries {
return None;
}
Some(ColdStamps {
delete_ts: self.delete_at(idx),
})
}
#[inline]
pub fn slot_at(&self, idx: usize) -> Option<Nbr> {
Some(Nbr::from_parts(self.hot_at(idx)?, self.cold_at(idx)?))
}
pub(crate) fn row_window(&self, src_vid: u32) -> Option<(usize, usize)> {
let idx = src_vid as usize;
if idx >= self.rows {
return None;
}
let start = self.offsets[idx] as usize;
let degree = self.degree_at(idx) as usize;
if start.saturating_add(degree) > self.entries {
return None;
}
Some((start, start + degree))
}
pub(crate) fn key_range(
&self,
start: usize,
end: usize,
endpoint: u32,
rank: i64,
) -> (usize, usize) {
let key_at = |idx: usize| (self.endpoint_at(idx), self.rank_at(idx));
let mut lo = start;
let mut hi = end;
while lo < hi {
let mid = lo + (hi - lo) / 2;
if key_at(mid) < (endpoint, rank) {
lo = mid + 1;
} else {
hi = mid;
}
}
let lower = lo;
hi = end;
while lo < hi {
let mid = lo + (hi - lo) / 2;
if key_at(mid) <= (endpoint, rank) {
lo = mid + 1;
} else {
hi = mid;
}
}
(lower, lo)
}
}