iroh_db_core/
causality.rs1use std::{cmp::Ordering, collections::BTreeMap};
2
3use crate::AuthorId;
4
5#[derive(
7 Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, minicbor::Encode, minicbor::Decode,
8)]
9#[cbor(array)]
10pub struct Dot {
11 #[n(0)]
12 author: AuthorId,
13 #[n(1)]
14 sequence: u64,
15 #[n(2)]
16 operation: u32,
17}
18
19impl Dot {
20 pub const fn new(author: AuthorId, sequence: u64, operation: u32) -> Self {
22 Self {
23 author,
24 sequence,
25 operation,
26 }
27 }
28
29 pub const fn author(self) -> AuthorId {
31 self.author
32 }
33
34 pub const fn sequence(self) -> u64 {
36 self.sequence
37 }
38
39 pub const fn operation(self) -> u32 {
41 self.operation
42 }
43}
44
45#[derive(Debug, Clone, Copy, PartialEq, Eq)]
47pub enum CausalRelation {
48 Equal,
50 Before,
52 After,
54 Concurrent,
56}
57
58#[derive(Debug, Clone, Default, PartialEq, Eq, minicbor::Encode, minicbor::Decode)]
60#[cbor(transparent)]
61pub struct VersionVector {
62 #[n(0)]
63 entries: BTreeMap<AuthorId, u64>,
64}
65
66impl VersionVector {
67 pub const fn new() -> Self {
69 Self {
70 entries: BTreeMap::new(),
71 }
72 }
73
74 pub fn observe(&mut self, dot: Dot) {
76 self.entries
77 .entry(dot.author())
78 .and_modify(|sequence| *sequence = (*sequence).max(dot.sequence()))
79 .or_insert(dot.sequence());
80 }
81
82 pub fn covers(&self, dot: &Dot) -> bool {
84 self.entries
85 .get(&dot.author())
86 .is_some_and(|sequence| *sequence >= dot.sequence())
87 }
88
89 pub fn merge(&mut self, other: &Self) {
91 for (&author, &other_sequence) in &other.entries {
92 self.entries
93 .entry(author)
94 .and_modify(|sequence| *sequence = (*sequence).max(other_sequence))
95 .or_insert(other_sequence);
96 }
97 }
98
99 pub fn relation(&self, other: &Self) -> CausalRelation {
101 let mut has_less = false;
102 let mut has_greater = false;
103
104 for author in self.entries.keys().chain(other.entries.keys()) {
105 match self.entries.get(author).cmp(&other.entries.get(author)) {
106 Ordering::Less => has_less = true,
107 Ordering::Equal => {}
108 Ordering::Greater => has_greater = true,
109 }
110
111 if has_less && has_greater {
112 return CausalRelation::Concurrent;
113 }
114 }
115
116 match (has_less, has_greater) {
117 (false, false) => CausalRelation::Equal,
118 (true, false) => CausalRelation::Before,
119 (false, true) => CausalRelation::After,
120 (true, true) => CausalRelation::Concurrent,
121 }
122 }
123
124 pub fn iter(&self) -> impl ExactSizeIterator<Item = (&AuthorId, &u64)> {
126 self.entries.iter()
127 }
128}