1use std::cmp::Reverse;
10use std::collections::{BTreeMap, BTreeSet, BinaryHeap};
11
12use crate::log::change::Change;
13use crate::log::id::{ActorId, ChangeHash, Lamport};
14
15#[derive(Clone, Debug, Default)]
18pub struct Dag {
19 changes: BTreeMap<ChangeHash, Change>,
20}
21
22impl Dag {
23 pub fn new() -> Self {
24 Self::default()
25 }
26
27 pub fn len(&self) -> usize {
28 self.changes.len()
29 }
30
31 pub fn is_empty(&self) -> bool {
32 self.changes.is_empty()
33 }
34
35 pub fn contains(&self, hash: ChangeHash) -> bool {
36 self.changes.contains_key(&hash)
37 }
38
39 pub fn get(&self, hash: ChangeHash) -> Option<&Change> {
40 self.changes.get(&hash)
41 }
42
43 pub fn insert(&mut self, change: Change) -> ChangeHash {
44 let hash = change.hash();
45 self.changes.entry(hash).or_insert(change);
46 hash
47 }
48
49 pub fn iter(&self) -> impl Iterator<Item = (ChangeHash, &Change)> {
50 self.changes.iter().map(|(hash, change)| (*hash, change))
51 }
52
53 pub fn missing_parents(&self) -> BTreeSet<ChangeHash> {
59 self.changes
60 .values()
61 .flat_map(Change::parents)
62 .copied()
63 .filter(|parent| !self.changes.contains_key(parent))
64 .collect()
65 }
66
67 pub fn heads(&self) -> Vec<ChangeHash> {
71 let claimed: BTreeSet<ChangeHash> = self
72 .changes
73 .values()
74 .flat_map(Change::parents)
75 .copied()
76 .collect();
77 self.changes
78 .keys()
79 .copied()
80 .filter(|hash| !claimed.contains(hash))
81 .collect()
82 }
83
84 pub fn linearize(&self) -> Vec<ChangeHash> {
92 let mut waiting_on: BTreeMap<ChangeHash, usize> = BTreeMap::new();
93 let mut dependents: BTreeMap<ChangeHash, Vec<ChangeHash>> = BTreeMap::new();
94
95 for (hash, change) in &self.changes {
96 let held_parents: Vec<ChangeHash> = change
97 .parents()
98 .iter()
99 .copied()
100 .filter(|p| self.changes.contains_key(p))
101 .collect();
102 waiting_on.insert(*hash, held_parents.len());
103 for parent in held_parents {
104 dependents.entry(parent).or_default().push(*hash);
105 }
106 }
107
108 let mut ready: BinaryHeap<Reverse<Key>> = waiting_on
109 .iter()
110 .filter(|(_, count)| **count == 0)
111 .map(|(hash, _)| Reverse(self.key(*hash)))
112 .collect();
113
114 let mut order = Vec::with_capacity(self.changes.len());
115 while let Some(Reverse(Key { hash, .. })) = ready.pop() {
116 order.push(hash);
117 for dependent in dependents.get(&hash).into_iter().flatten() {
118 let count = waiting_on.entry(*dependent).or_default();
119 *count = count.saturating_sub(1);
120 if *count == 0 {
121 ready.push(Reverse(self.key(*dependent)));
122 }
123 }
124 }
125 order
126 }
127
128 fn key(&self, hash: ChangeHash) -> Key {
129 let change = self.changes.get(&hash);
130 Key {
131 lamport: change.map_or(Lamport::ZERO, |c| c.lamport()),
132 actor: change.map_or_else(|| ActorId::from_uuid(uuid::Uuid::nil()), |c| c.actor()),
133 hash,
134 }
135 }
136}
137
138#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
141struct Key {
142 lamport: Lamport,
143 actor: ActorId,
144 hash: ChangeHash,
145}
146
147#[cfg(test)]
148mod tests {
149 use super::*;
150 use crate::log::change::SemanticLabel;
151 use crate::log::command::{Command, ElementKind, PropSet};
152 use crate::log::id::{ElementId, Stamp};
153
154 fn actor(byte: u8) -> ActorId {
155 ActorId::from_uuid(uuid::Uuid::from_bytes([byte; 16]))
156 }
157
158 fn change(
159 lamport: u64,
160 actor_byte: u8,
161 parents: impl IntoIterator<Item = ChangeHash>,
162 ) -> Change {
163 Change::new(
164 Stamp {
165 lamport: Lamport::new(lamport),
166 actor: actor(actor_byte),
167 },
168 parents,
169 SemanticLabel::new(format!("l{lamport}a{actor_byte}")),
170 vec![Command::Create {
171 id: ElementId::new(),
172 kind: ElementKind::Block,
173 parent: ElementId::DOCUMENT,
174 init: PropSet::new(),
175 }],
176 )
177 }
178
179 fn position(order: &[ChangeHash], hash: ChangeHash) -> usize {
180 order
181 .iter()
182 .position(|h| *h == hash)
183 .expect("every held change is linearized")
184 }
185
186 #[test]
187 fn inserting_a_change_twice_holds_it_once() {
188 let mut dag = Dag::new();
189 let c = change(1, 1, []);
190 let first = dag.insert(c.clone());
191 let second = dag.insert(c);
192 assert_eq!(first, second);
193 assert_eq!(dag.len(), 1);
194 }
195
196 #[test]
197 fn a_chain_has_one_head_and_a_fork_has_two() {
198 let mut dag = Dag::new();
199 let root = dag.insert(change(1, 1, []));
200 let next = dag.insert(change(2, 1, [root]));
201 assert_eq!(dag.heads(), vec![next]);
202
203 let branch = dag.insert(change(2, 2, [root]));
204 let mut heads = dag.heads();
205 heads.sort_unstable();
206 let mut expected = vec![next, branch];
207 expected.sort_unstable();
208 assert_eq!(heads, expected);
209 }
210
211 #[test]
212 fn a_merge_change_collapses_the_heads() {
213 let mut dag = Dag::new();
214 let root = dag.insert(change(1, 1, []));
215 let left = dag.insert(change(2, 1, [root]));
216 let right = dag.insert(change(2, 2, [root]));
217 assert_eq!(dag.heads().len(), 2);
218
219 let merged = change(3, 1, [left, right]);
220 assert!(merged.is_merge());
221 let merge = dag.insert(merged);
222 assert_eq!(dag.heads(), vec![merge]);
223 }
224
225 #[test]
226 fn linearization_puts_parents_first() {
227 let mut dag = Dag::new();
228 let root = dag.insert(change(1, 1, []));
229 let left = dag.insert(change(2, 1, [root]));
230 let right = dag.insert(change(2, 2, [root]));
231 let merge = dag.insert(change(3, 1, [left, right]));
232
233 let order = dag.linearize();
234 assert_eq!(order.len(), 4);
235 assert!(position(&order, root) < position(&order, left));
236 assert!(position(&order, root) < position(&order, right));
237 assert!(position(&order, left) < position(&order, merge));
238 assert!(position(&order, right) < position(&order, merge));
239 }
240
241 #[test]
244 fn linearization_does_not_depend_on_insertion_order() {
245 let root = change(1, 1, []);
246 let root_hash = root.hash();
247 let left = change(2, 1, [root_hash]);
248 let right = change(2, 2, [root_hash]);
249 let merge = change(3, 1, [left.hash(), right.hash()]);
250
251 let mut forwards = Dag::new();
252 for c in [&root, &left, &right, &merge] {
253 forwards.insert(c.clone());
254 }
255 let mut backwards = Dag::new();
256 for c in [&merge, &right, &left, &root] {
257 backwards.insert(c.clone());
258 }
259
260 assert_eq!(forwards.linearize(), backwards.linearize());
261 }
262
263 #[test]
266 fn concurrent_changes_are_ordered_by_stamp() {
267 let root = change(1, 1, []);
268 let root_hash = root.hash();
269 let earlier = change(2, 9, [root_hash]);
270 let later = change(5, 1, [root_hash]);
271
272 let mut dag = Dag::new();
273 for c in [&root, &later, &earlier] {
274 dag.insert(c.clone());
275 }
276 let order = dag.linearize();
277 assert!(position(&order, earlier.hash()) < position(&order, later.hash()));
278 }
279
280 #[test]
281 fn a_parent_that_is_not_held_is_reported_and_does_not_stall_replay() {
282 let absent = ChangeHash::from_bytes([0xEE; 32]);
283 let mut dag = Dag::new();
284 let orphan = dag.insert(change(4, 1, [absent]));
285
286 assert_eq!(
287 dag.missing_parents(),
288 [absent].into_iter().collect::<BTreeSet<_>>()
289 );
290 assert_eq!(dag.linearize(), vec![orphan]);
291 }
292
293 #[test]
294 fn a_complete_history_reports_no_missing_parents() {
295 let mut dag = Dag::new();
296 let root = dag.insert(change(1, 1, []));
297 dag.insert(change(2, 1, [root]));
298 assert!(dag.missing_parents().is_empty());
299 }
300}