diff --git a/veilid-core/src/routing_table/route_spec_store.rs b/veilid-core/src/routing_table/route_spec_store.rs index 215b9056..46368ef3 100644 --- a/veilid-core/src/routing_table/route_spec_store.rs +++ b/veilid-core/src/routing_table/route_spec_store.rs @@ -32,6 +32,8 @@ struct RouteSpecDetail { last_checked_ts: Option, /// Directions this route is guaranteed to work in directions: DirectionSet, + /// Reliability + reliable: bool, } /// The core representation of the RouteSpecStore that can be serialized @@ -60,10 +62,19 @@ pub struct RouteSpecStore { cache: RouteSpecStoreCache, } -fn route_spec_to_hop_cache(spec: Arc) -> Vec { - let mut cache: Vec = Vec::with_capacity(spec.hops.len() * DHT_KEY_LENGTH); - for hop in spec.hops { - cache.extend_from_slice(&hop.dial_info.node_id.key.bytes); +fn route_hops_to_hop_cache(hops: &[DHTKey]) -> Vec { + let mut cache: Vec = Vec::with_capacity(hops.len() * DHT_KEY_LENGTH); + for hop in hops { + cache.extend_from_slice(&hop.bytes); + } + cache +} + +/// get the hop cache key for a particular route permutation +fn route_permutation_to_hop_cache(nodes: &[(DHTKey, NodeInfo)], perm: &[usize]) -> Vec { + let mut cache: Vec = Vec::with_capacity(perm.len() * DHT_KEY_LENGTH); + for n in perm { + cache.extend_from_slice(&nodes[*n].0.bytes) } cache } @@ -104,7 +115,7 @@ where return f(&permutation); } - // heaps algorithm + // heaps algorithm, but skipping the first element fn heaps_permutation(permutation: &mut [usize], size: usize, f: F) -> bool where F: FnMut(&[usize]) -> bool, @@ -133,15 +144,6 @@ where heaps_permutation(&mut permutation, hop_count - 1, f) } -/// get the hop cache key for a particular route permutation -fn route_permutation_to_hop_cache(nodes: &[(DHTKey, NodeInfo)], perm: &[usize]) -> Vec { - let mut cache: Vec = Vec::with_capacity(perm.len() * DHT_KEY_LENGTH); - for n in perm { - cache.extend_from_slice(&nodes[*n].0.bytes) - } - cache -} - impl RouteSpecStore { pub fn new() -> Self { Self { @@ -329,15 +331,16 @@ impl RouteSpecStore { } // Now go through nodes and try to build a route we haven't seen yet - let mut route_nodes: Vec = Vec::with_capacity(hop_count); + let mut route_nodes: Vec = Vec::new(); + let mut cache_key: Vec = Vec::new(); for start in 0..(nodes.len() - hop_count) { // Try the permutations available starting with 'start' let done = with_route_permutations(hop_count, start, |permutation: &[usize]| { // Get the route cache key - let key = route_permutation_to_hop_cache(&nodes, permutation); + cache_key = route_permutation_to_hop_cache(&nodes, permutation); // Skip routes we have already seen - if self.cache.hop_cache.contains(&key) { + if self.cache.hop_cache.contains(&cache_key) { return false; } @@ -417,18 +420,89 @@ impl RouteSpecStore { created_ts: cur_ts, last_checked_ts: None, directions, + reliable, }; + // Keep route in spec store self.content.details.insert(public_key, rsd); - - // xxx insert into cache too + if !self.cache.hop_cache.insert(cache_key) { + panic!("route should never be inserted twice"); + } + for h in &hops { + self.cache + .used_nodes + .entry(*h) + .and_modify(|e| *e += 1) + .or_insert(1); + } + self.cache + .used_end_nodes + .entry(*hops.last().unwrap()) + .and_modify(|e| *e += 1) + .or_insert(1); Some(public_key) } - pub fn release_route(&mut self, spec: Arc) {} + pub fn release_route(&mut self, public_key: DHTKey) { + if let Some(detail) = self.content.details.remove(&public_key) { + // Remove from hop cache + let cache_key = route_hops_to_hop_cache(&detail.hops); + if !self.cache.hop_cache.remove(&cache_key) { + panic!("hop cache should have contained cache key"); + } + // Remove from used nodes cache + for h in &detail.hops { + match self.cache.used_nodes.entry(*h) { + std::collections::hash_map::Entry::Occupied(o) => { + *o.get_mut() -= 1; + if *o.get() == 0 { + o.remove(); + } + } + std::collections::hash_map::Entry::Vacant(_) => { + panic!("used_nodes cache should have contained hop"); + } + } + } + // Remove from end nodes cache + match self.cache.used_nodes.entry(*detail.hops.last().unwrap()) { + std::collections::hash_map::Entry::Occupied(o) => { + *o.get_mut() -= 1; + if *o.get() == 0 { + o.remove(); + } + } + std::collections::hash_map::Entry::Vacant(_) => { + panic!("used_nodes cache should have contained hop"); + } + } + } else { + panic!("can't release route that was never allocated"); + } + } - pub fn best_route(&mut self, reliable: bool) -> Arc {} + pub fn best_route( + &mut self, + reliable: bool, + min_hop_count: usize, + max_hop_count: usize, + directions: DirectionSet, + ) -> Option { + for detail in &self.content.details { + if detail.1.reliable == reliable + && detail.1.hops.len() >= min_hop_count + && detail.1.hops.len() <= max_hop_count + && detail.1.directions.is_subset(directions) + { + return Some(*detail.0); + } + } + None + } + + /// xxx add route compiler here + /// /// Mark route as published /// When first deserialized, routes must be re-published in order to ensure they remain