use std::collections::HashMap;
use crate::graph::snapshot::Cyberlink;
pub type ParticleIdx = u32;
pub struct ParticleIndex {
map: HashMap<[u8; 32], ParticleIdx>,
hashes: Vec<[u8; 32]>,
}
impl ParticleIndex {
pub fn empty() -> Self {
Self { map: HashMap::new(), hashes: Vec::new() }
}
pub fn build<'a>(links: impl Iterator<Item = Cyberlink>) -> Self {
let mut first_seen: HashMap<[u8; 32], u64> = HashMap::new();
for link in links {
first_seen.entry(link.from).or_insert(link.block);
first_seen.entry(link.to).or_insert(link.block);
}
let mut ordered: Vec<([u8; 32], u64)> = first_seen.into_iter().collect();
ordered.sort_unstable_by(|a, b| a.1.cmp(&b.1).then_with(|| a.0.cmp(&b.0)));
let mut map = HashMap::with_capacity(ordered.len());
let mut hashes = Vec::with_capacity(ordered.len());
for (hash, _) in ordered {
let idx = hashes.len() as ParticleIdx;
map.insert(hash, idx);
hashes.push(hash);
}
Self { map, hashes }
}
pub fn len(&self) -> usize { self.hashes.len() }
pub fn is_empty(&self) -> bool { self.hashes.is_empty() }
pub fn get(&self, hash: &[u8; 32]) -> Option<ParticleIdx> {
self.map.get(hash).copied()
}
pub fn hash(&self, idx: ParticleIdx) -> &[u8; 32] {
&self.hashes[idx as usize]
}
pub fn anchor(&self) -> &[[u8; 32]] {
let n = self.hashes.len().min(1024);
&self.hashes[..n]
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::graph::snapshot::Cyberlink;
fn make_link(from: u8, to: u8, block: u64) -> Cyberlink {
let mut f = [0u8; 32]; f[0] = from;
let mut t = [0u8; 32]; t[0] = to;
Cyberlink { neuron: [0u8; 32], from: f, to: t, token: 0, amount: 0, valence: 0, block }
}
#[test]
fn insertion_order() {
let links = vec![
make_link(3, 4, 10),
make_link(1, 2, 5),
make_link(2, 3, 7),
];
let idx = ParticleIndex::build(links.into_iter());
let mut h1 = [0u8; 32]; h1[0] = 1;
assert_eq!(idx.get(&h1), Some(0));
let mut h4 = [0u8; 32]; h4[0] = 4;
assert_eq!(idx.get(&h4), Some(3));
assert_eq!(idx.len(), 4);
}
}