aboutsummaryrefslogtreecommitdiffstats
path: root/src/swarm/reach.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/swarm/reach.rs')
-rw-r--r--src/swarm/reach.rs49
1 files changed, 49 insertions, 0 deletions
diff --git a/src/swarm/reach.rs b/src/swarm/reach.rs
index e6cee2f..d4949ef 100644
--- a/src/swarm/reach.rs
+++ b/src/swarm/reach.rs
@@ -35,6 +35,35 @@ use crate::state::{AppState, HelloState, Standing};
use crate::swarm::peer::announce_heads;
use crate::swarm::{members, rtt, topology};
+/// The most members a node may say it reaches (`said`): more than it could hold
+/// sessions with.
+pub(crate) const MAX_REACHES: usize = 4096;
+
+/// A reach set as a neighbour said it (`Status::reaches`), with its round trips if
+/// they match it: sorted by pair key (as we send ours: `measure`), so it can be looked
+/// up by halving (`swarm::tree`); None if it is larger than `MAX_REACHES`.
+pub(crate) fn said(
+ reaches: &[u64],
+ round_trips: Option<&[u8]>,
+) -> Option<(Vec<u64>, Option<Vec<u8>>)> {
+ if reaches.len() > MAX_REACHES {
+ return None;
+ }
+ let round_trips = round_trips.filter(|round_trips| round_trips.len() == reaches.len());
+ if reaches.windows(2).all(|pair| pair[0] < pair[1]) {
+ return Some((reaches.to_vec(), round_trips.map(<[u8]>::to_vec)));
+ }
+ let mut pairs: Vec<(u64, Option<u8>)> = reaches
+ .iter()
+ .enumerate()
+ .map(|(i, key)| (*key, round_trips.map(|round_trips| round_trips[i])))
+ .collect();
+ pairs.sort_unstable_by_key(|(key, _)| *key);
+ pairs.dedup_by_key(|(key, _)| *key);
+ let round_trips = round_trips.map(|_| pairs.iter().filter_map(|(_, rtt)| *rtt).collect());
+ Some((pairs.into_iter().map(|(key, _)| key).collect(), round_trips))
+}
+
/// Whom we reach, as last told our neighbours, and our round trip to each.
#[derive(Default)]
pub(crate) struct Reach {
@@ -160,3 +189,23 @@ pub(crate) async fn names(state: &AppState) -> HashMap<u64, String> {
.map(|member| (topology::pair_key(&member), member))
.collect()
}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ /// A reach set comes sorted, its round trips along; too large, it is ignored.
+ #[test]
+ fn reach_sets_are_sorted_and_bounded() {
+ assert_eq!(
+ said(&[1, 3, 5], Some(&[9, 8, 7])),
+ Some((vec![1, 3, 5], Some(vec![9, 8, 7])))
+ );
+ assert_eq!(
+ said(&[5, 1, 3, 1], Some(&[7, 9, 8, 9])),
+ Some((vec![1, 3, 5], Some(vec![9, 8, 7])))
+ );
+ assert_eq!(said(&[2, 1], Some(&[1])), Some((vec![1, 2], None)));
+ assert_eq!(said(&vec![0; MAX_REACHES + 1], None), None);
+ }
+}