pub use crate::{
ContentAddr, content_addr,
hash::{CaHash, Hasher},
};
use petgraph::visit::{
Data, EdgeRef, IntoEdgeReferences, IntoNodeReferences, NodeIndexable, NodeRef,
};
use serde::{Deserialize, Serialize};
use std::{collections::HashMap, fmt, hash::Hash, ops};
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd, Deserialize, Serialize)]
pub struct GraphAddr(ContentAddr);
impl ops::Deref for GraphAddr {
type Target = ContentAddr;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl From<ContentAddr> for GraphAddr {
fn from(ca: ContentAddr) -> Self {
Self(ca)
}
}
impl From<GraphAddr> for ContentAddr {
fn from(addr: GraphAddr) -> Self {
addr.0
}
}
impl CaHash for GraphAddr {
fn hash(&self, hasher: &mut Hasher) {
CaHash::hash(&self.0, hasher);
}
}
impl fmt::Display for GraphAddr {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.0)
}
}
pub fn addr<G>(g: G) -> GraphAddr
where
G: Data + IntoEdgeReferences + IntoNodeReferences + NodeIndexable,
G::NodeId: Eq + Hash + Ord,
G::EdgeWeight: CaHash + Ord,
G::NodeWeight: CaHash,
{
let mut hasher = Hasher::new();
hash_graph(g, &mut hasher);
GraphAddr(ContentAddr(hasher.finalize().into()))
}
pub fn addr_with_nodes<G>(g: G, nodes: &HashMap<G::NodeId, ContentAddr>) -> GraphAddr
where
G: Data + IntoEdgeReferences + IntoNodeReferences + NodeIndexable,
G::NodeId: Hash + Ord,
G::EdgeWeight: CaHash + Ord,
{
let mut hasher = Hasher::new();
hash_graph_with_nodes(g, nodes, &mut hasher);
GraphAddr(ContentAddr(hasher.finalize().into()))
}
pub fn hash_graph<G>(g: G, hasher: &mut Hasher)
where
G: Data + IntoEdgeReferences + IntoNodeReferences + NodeIndexable,
G::NodeId: Eq + Hash + Ord,
G::EdgeWeight: CaHash + Ord,
G::NodeWeight: CaHash,
{
let nodes = node_addrs(g);
hash_graph_with_nodes(g, &nodes, hasher);
}
pub fn hash_graph_with_nodes<G>(g: G, nodes: &HashMap<G::NodeId, ContentAddr>, hasher: &mut Hasher)
where
G: Data + IntoEdgeReferences + IntoNodeReferences + NodeIndexable,
G::NodeId: Hash + Ord,
G::EdgeWeight: CaHash + Ord,
{
const OUT_OF_RANGE: &str = "graph node index exceeds u64::MAX";
for n_ref in g.node_references() {
let id = n_ref.id();
let node_ca = &nodes[&id];
let ix: u64 = g.to_index(id).try_into().expect(OUT_OF_RANGE);
CaHash::hash(&ix, hasher);
CaHash::hash(&**node_ca, hasher);
}
let mut edges = vec![];
for e_ref in g.edge_references() {
let src: u64 = g.to_index(e_ref.source()).try_into().expect(OUT_OF_RANGE);
let dst: u64 = g.to_index(e_ref.target()).try_into().expect(OUT_OF_RANGE);
edges.push((src, dst, e_ref));
}
edges.sort_by(|(sa, da, ea), (sb, db, eb)| (sa, da, ea.weight()).cmp(&(sb, db, eb.weight())));
for (src, dst, e_ref) in edges {
CaHash::hash(&src, hasher);
CaHash::hash(&dst, hasher);
CaHash::hash(e_ref.weight(), hasher);
}
}
pub fn node_addrs<G>(g: G) -> HashMap<G::NodeId, ContentAddr>
where
G: Data + IntoNodeReferences + NodeIndexable,
G::NodeId: Eq + std::hash::Hash,
G::NodeWeight: CaHash,
{
g.node_references()
.map(|n_ref| {
let id = n_ref.id();
let ca = content_addr(n_ref.weight());
(id, ca)
})
.collect()
}