use std::collections::HashMap;
use std::hash::Hash;
use super::PeerId;
pub const fn seq_diff(a: u16, b: u16) -> i16 {
a.wrapping_sub(b) as i16
}
pub const fn seq_newer(a: u16, b: u16) -> bool {
seq_diff(a, b) > 0
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
pub struct UpdateStamp {
pub epoch: u16,
pub seq: u16,
}
impl UpdateStamp {
pub const fn newer_than(self, other: UpdateStamp) -> bool {
if self.epoch != other.epoch {
seq_newer(self.epoch, other.epoch)
} else {
seq_newer(self.seq, other.seq)
}
}
}
#[derive(Clone, Debug)]
pub struct LatestWins<K: Hash + Eq> {
latest: HashMap<K, UpdateStamp>,
}
impl<K: Hash + Eq> Default for LatestWins<K> {
fn default() -> Self {
Self::new()
}
}
impl<K: Hash + Eq> LatestWins<K> {
pub fn new() -> Self {
Self { latest: HashMap::new() }
}
pub fn accept(&mut self, key: K, stamp: UpdateStamp) -> bool {
match self.latest.get_mut(&key) {
Some(last) if stamp.newer_than(*last) => {
*last = stamp;
true
}
Some(_) => false,
None => {
self.latest.insert(key, stamp);
true
}
}
}
pub fn accept_seq(&mut self, key: K, seq: u16) -> bool {
self.accept(key, UpdateStamp { epoch: 0, seq })
}
pub fn latest(&self, key: &K) -> Option<UpdateStamp> {
self.latest.get(key).copied()
}
pub fn forget(&mut self, key: &K) {
self.latest.remove(key);
}
pub fn clear(&mut self) {
self.latest.clear();
}
pub fn retain(&mut self, mut keep: impl FnMut(&K) -> bool) {
self.latest.retain(|k, _| keep(k));
}
pub fn len(&self) -> usize {
self.latest.len()
}
pub fn is_empty(&self) -> bool {
self.latest.is_empty()
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum AuthorityCheck {
Accept,
Stale,
WrongOwner,
Ahead,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct Authority {
owner: PeerId,
epoch: u16,
}
impl Authority {
pub const fn new(owner: PeerId) -> Self {
Self { owner, epoch: 0 }
}
pub const fn with_epoch(owner: PeerId, epoch: u16) -> Self {
Self { owner, epoch }
}
pub const fn owner(&self) -> PeerId {
self.owner
}
pub const fn epoch(&self) -> u16 {
self.epoch
}
pub fn handoff(&mut self, owner: PeerId) -> u16 {
self.epoch = self.epoch.wrapping_add(1);
self.owner = owner;
self.epoch
}
pub fn adopt(&mut self, owner: PeerId, epoch: u16) -> bool {
let take = if epoch == self.epoch {
owner < self.owner
} else {
seq_newer(epoch, self.epoch)
};
if take {
self.owner = owner;
self.epoch = epoch;
}
take
}
pub fn check(&self, from: PeerId, epoch: u16) -> AuthorityCheck {
if epoch == self.epoch {
if from == self.owner {
AuthorityCheck::Accept
} else {
AuthorityCheck::WrongOwner
}
} else if seq_newer(epoch, self.epoch) {
AuthorityCheck::Ahead
} else {
AuthorityCheck::Stale
}
}
}
#[cfg(test)]
mod tests {
use super::*;
fn peer(n: u64) -> PeerId {
PeerId::from_bits(n)
}
#[test]
fn sequence_comparison_wraps_around() {
assert!(seq_newer(1, 0));
assert!(seq_newer(0, 65535));
assert!(seq_newer(100, 65500)); assert!(!seq_newer(65500, 100));
assert!(!seq_newer(5, 5));
assert_eq!(seq_diff(100, 65500), 136);
assert_eq!(seq_diff(65500, 100), -136);
}
#[test]
fn sequence_comparison_is_antisymmetric_except_at_half_range() {
for a in (0..=u16::MAX).step_by(251) {
for b in (0..=u16::MAX).step_by(257) {
let (ab, ba) = (seq_newer(a, b), seq_newer(b, a));
assert!(!(ab && ba), "{a} {b}");
if a != b && a.wrapping_sub(b) != 0x8000 {
assert!(ab || ba, "{a} {b}");
}
}
}
assert!(!seq_newer(0, 0x8000) && !seq_newer(0x8000, 0));
}
#[test]
fn newer_than_prefers_epoch_then_sequence() {
let s = |epoch, seq| UpdateStamp { epoch, seq };
assert!(s(1, 0).newer_than(s(0, 60000)));
assert!(!s(0, 60000).newer_than(s(1, 0)));
assert!(s(0, 0).newer_than(s(65535, 65535))); assert!(!s(3, 4).newer_than(s(3, 4)));
}
#[test]
fn latest_wins_drops_old_and_duplicate_updates() {
let mut f = LatestWins::new();
let accepted: Vec<bool> = [1u16, 3, 2, 3, 4, 1]
.into_iter()
.map(|seq| f.accept_seq(9u64, seq))
.collect();
assert_eq!(accepted, [true, true, false, false, true, false]);
assert_eq!(f.latest(&9).unwrap().seq, 4);
}
#[test]
fn latest_wins_survives_sequence_wrap() {
let mut f = LatestWins::new();
for seq in 65530..=u16::MAX {
assert!(f.accept_seq(1u8, seq));
}
for seq in 0..10 {
assert!(f.accept_seq(1, seq), "{seq}");
}
assert!(!f.accept_seq(1, 65535), "pre-wrap packet arriving late");
}
#[test]
fn latest_wins_new_epoch_resets_sequence() {
let mut f = LatestWins::new();
assert!(f.accept(1u8, UpdateStamp { epoch: 0, seq: 5000 }));
assert!(f.accept(1, UpdateStamp { epoch: 1, seq: 0 }));
assert!(
!f.accept(1, UpdateStamp { epoch: 0, seq: 5001 }),
"old owner's late packet"
);
assert!(f.accept(1, UpdateStamp { epoch: 1, seq: 1 }));
}
#[test]
fn latest_wins_forget_and_retain() {
let mut f = LatestWins::new();
for k in 0..5u32 {
f.accept_seq(k, 10);
}
f.retain(|k| k % 2 == 0);
assert_eq!(f.len(), 3);
assert!(f.accept_seq(1, 3), "forgotten keys start fresh");
f.forget(&1);
assert_eq!(f.latest(&1), None);
f.clear();
assert!(f.is_empty());
}
#[test]
fn handoff_rejects_updates_from_the_previous_owner() {
let mut a = Authority::new(peer(0));
assert_eq!(a.check(peer(0), 0), AuthorityCheck::Accept);
assert_eq!(a.handoff(peer(1)), 1);
assert_eq!(a.check(peer(0), 0), AuthorityCheck::Stale);
assert_eq!(a.check(peer(1), 0), AuthorityCheck::Stale);
assert_eq!(a.check(peer(1), 1), AuthorityCheck::Accept);
assert_eq!(a.check(peer(0), 1), AuthorityCheck::WrongOwner);
assert_eq!(a.check(peer(1), 2), AuthorityCheck::Ahead);
}
#[test]
fn adopt_ignores_late_announcements_and_applies_newer_ones() {
let mut a = Authority::new(peer(0));
assert!(a.adopt(peer(1), 1));
assert!(a.adopt(peer(2), 2));
assert!(!a.adopt(peer(1), 1));
assert_eq!((a.owner(), a.epoch()), (peer(2), 2));
assert!(a.adopt(peer(3), 7));
assert_eq!(a.epoch(), 7);
}
#[test]
fn simultaneous_handoffs_converge_on_the_lower_owner() {
let mut one = Authority::new(peer(0));
let mut two = Authority::new(peer(0));
let ea = one.handoff(peer(1));
let eb = two.handoff(peer(2));
assert_eq!(ea, eb);
one.adopt(peer(2), eb);
two.adopt(peer(1), ea);
assert_eq!(one, two);
assert_eq!(one.owner(), peer(1));
assert_eq!(one.check(peer(2), 1), AuthorityCheck::WrongOwner);
assert_eq!(one.check(peer(1), 1), AuthorityCheck::Accept);
}
#[test]
fn an_update_can_race_ahead_of_its_handoff_announcement() {
let mut a = Authority::new(peer(0));
assert_eq!(a.check(peer(1), 1), AuthorityCheck::Ahead);
a.adopt(peer(1), 1);
assert_eq!(a.check(peer(1), 1), AuthorityCheck::Accept);
}
#[test]
fn epoch_wraps_around() {
let mut a = Authority::with_epoch(peer(0), u16::MAX);
assert_eq!(a.handoff(peer(1)), 0);
assert_eq!(a.check(peer(0), u16::MAX), AuthorityCheck::Stale);
let mut b = Authority::with_epoch(peer(0), u16::MAX);
assert!(b.adopt(peer(1), 0));
}
}