use crate::key::{EdgeType, NodeId, UrlKey};
pub trait LendingIterator {
type Item<'a>
where
Self: 'a;
fn next(&mut self) -> Option<Self::Item<'_>>;
}
#[derive(Clone, Copy, Debug)]
pub struct EdgeRef<'a> {
pub dst: Option<NodeId>,
pub dst_key: UrlKey,
pub dst_url: Option<&'a str>,
pub etype: EdgeType,
pub flags: u8,
pub weight: u16,
}
impl EdgeRef<'_> {
#[must_use]
pub fn is_inferred(&self) -> bool {
self.flags & crate::format::base::eflags::INFERRED != 0
}
}
#[derive(Debug)]
pub struct Neighbors<'s> {
pub(crate) inner: NeighborsInner<'s>,
}
#[derive(Debug)]
pub(crate) enum NeighborsInner<'s> {
Base { snap: &'s crate::snapshot::Snapshot<'s>, edges: &'s [u8], idx: usize, end: usize },
Overlay { snap: &'s crate::snapshot::Snapshot<'s>, edges: &'s [u8], idx: usize },
Empty,
}
impl LendingIterator for Neighbors<'_> {
type Item<'a>
= EdgeRef<'a>
where
Self: 'a;
fn next(&mut self) -> Option<EdgeRef<'_>> {
match &mut self.inner {
NeighborsInner::Base { snap, edges, idx, end } => {
if idx >= end {
return None;
}
let rec = &edges[*idx * crate::format::base::EDGE_LEN
..(*idx + 1) * crate::format::base::EDGE_LEN];
*idx += 1;
let dst = NodeId(crate::bytesio::get_u32(rec, 0));
let (dst_key, dst_url) = snap.node_identity(dst);
Some(EdgeRef {
dst: Some(dst),
dst_key,
dst_url,
etype: EdgeType::from_tag(rec[4]),
flags: rec[5],
weight: crate::bytesio::get_u16(rec, 6),
})
}
NeighborsInner::Overlay { snap, edges, idx } => {
if *idx >= crate::format::wal::edge_count(edges) {
return None;
}
let (dst_key, etype, flags, weight) = crate::format::wal::edge_at(edges, *idx);
*idx += 1;
let dst = snap.base_node_id(dst_key);
let dst_url = dst.and_then(|id| snap.node_identity(id).1);
Some(EdgeRef {
dst,
dst_key,
dst_url,
etype: EdgeType::from_tag(etype),
flags,
weight,
})
}
NeighborsInner::Empty => None,
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Direction {
Out,
In,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct Hop {
pub key: UrlKey,
pub from: UrlKey,
pub etype: EdgeType,
pub depth: u32,
pub exact_chain: bool,
}
#[derive(Debug, Default)]
pub struct Walk {
seen: std::collections::HashSet<u64>,
frontier: Vec<(UrlKey, bool)>,
next: Vec<(UrlKey, bool)>,
}
impl Walk {
#[must_use]
pub fn new() -> Self {
Self::default()
}
}
impl crate::Snapshot<'_> {
pub fn walk(
&self,
from: UrlKey,
dir: Direction,
max_depth: u32,
follow: impl Fn(EdgeType) -> bool,
w: &mut Walk,
visit: impl FnMut(Hop) -> bool,
) {
let out = dir == Direction::Out;
self.walk_along(from, out, !out, max_depth, follow, w, visit);
}
pub fn walk_both(
&self,
from: UrlKey,
max_depth: u32,
follow: impl Fn(EdgeType) -> bool,
w: &mut Walk,
visit: impl FnMut(Hop) -> bool,
) {
self.walk_along(from, true, true, max_depth, follow, w, visit);
}
#[allow(clippy::too_many_arguments, clippy::fn_params_excessive_bools)] fn walk_along(
&self,
from: UrlKey,
out: bool,
inbound: bool,
max_depth: u32,
follow: impl Fn(EdgeType) -> bool,
w: &mut Walk,
mut visit: impl FnMut(Hop) -> bool,
) {
w.seen.clear();
w.frontier.clear();
w.seen.insert(from.0);
w.frontier.push((from, true));
let exact = crate::format::base::eflags::EXACT;
for depth in 1..=max_depth {
w.next.clear();
for k in 0..w.frontier.len() {
let (node, chain) = w.frontier[k];
let mut step = |key: UrlKey, etype: EdgeType, flags: u8, w: &mut Walk| {
if !follow(etype) || !w.seen.insert(key.0) {
return;
}
let hop = Hop {
key,
from: node,
etype,
depth,
exact_chain: chain && flags & exact != 0,
};
if visit(hop) {
w.next.push((key, hop.exact_chain));
}
};
if out {
let mut it = self.neighbors(node);
while let Some(e) = it.next() {
step(e.dst_key, e.etype, e.flags, w);
}
}
if inbound {
self.in_edges(node, |src, etype, flags, _| step(src, etype, flags, w));
}
}
if w.next.is_empty() {
break;
}
std::mem::swap(&mut w.frontier, &mut w.next);
}
}
}