1#![expect(clippy::doc_markdown)] use std::{num::NonZeroU32, ops::Range};
19
20fn wide(bytes: u32) -> usize {
23 usize::try_from(bytes).expect("usize is at least 32 bits")
24}
25
26#[derive(Clone)]
27pub struct Replica {
28 id: ReplicaId,
29 spans: Vec<Span>,
30 version: Version,
31 pending_insertions: Vec<Insertion>,
33 pending_deletions: Vec<Deletion>,
34}
35
36impl Replica {
38 #[must_use]
43 pub fn new(id: NonZeroU32, initial_length: usize) -> Self {
44 let mut replica = Self {
45 id: ReplicaId(id),
46 spans: Vec::new(),
47 version: Version::default(),
48 pending_insertions: Vec::new(),
49 pending_deletions: Vec::new(),
50 };
51 replica
52 .inserted(0, initial_length)
53 .expect("byte offset 0 is valid and the initial length fits the clock");
54 replica
55 }
56
57 #[must_use]
59 pub fn fork(&self, new_id: NonZeroU32) -> Self {
60 Self {
61 id: ReplicaId(new_id),
62 ..self.clone()
63 }
64 }
65
66 #[must_use]
68 pub fn len(&self) -> usize {
69 self.spans.iter().map(Span::visible_length).sum()
70 }
71
72 #[must_use]
73 pub fn is_empty(&self) -> bool {
74 self.len() == 0
75 }
76
77 pub fn inserted(&mut self, byte_offset: usize, length: usize) -> anyhow::Result<Insertion> {
81 let Ok(length) = u32::try_from(length) else {
82 anyhow::bail!("insertion length {length} exceeds the u32 byte clock");
83 };
84 let byte_index = self.version.count(self.id);
85 if byte_index.checked_add(length).is_none() {
86 anyhow::bail!("inserting {length} bytes would overflow the u32 byte clock");
87 }
88
89 let mut remaining = byte_offset;
93 let mut index = 0;
94 while remaining > 0 {
95 let Some(span) = self.spans.get(index) else {
96 anyhow::bail!("byte offset {byte_offset} is past the end of the document");
97 };
98 if !span.deleted {
99 if remaining < wide(span.length) {
100 let at = u32::try_from(remaining).expect("fits in a span");
101 let right = self.spans[index].split(at);
102 self.spans.insert(index + 1, right);
103 index += 1;
104 break;
105 }
106 remaining -= wide(span.length);
107 }
108 index += 1;
109 }
110
111 let op = Insertion {
112 start: ByteId {
113 replica: self.id,
114 byte_index,
115 },
116 length,
117 origin_left: index.checked_sub(1).map(|left| self.spans[left].last_id()),
118 origin_right: self.spans.get(index).map(|right| right.id),
119 };
120 self.version.record(op.start, op.length);
121 if length > 0 {
125 match index.checked_sub(1) {
126 Some(left) if self.spans[left].can_append(&op) => {
127 self.spans[left].length += length;
128 }
129 _ => self.spans.insert(index, Span::of(&op)),
130 }
131 }
132 Ok(op)
133 }
134
135 pub fn deleted(&mut self, byte_range: Range<usize>) -> anyhow::Result<Deletion> {
139 if byte_range.start > byte_range.end || byte_range.end > self.len() {
140 anyhow::bail!("invalid byte range {byte_range:?}");
141 }
142 let mut targets = Vec::<Run>::new();
143 if byte_range.is_empty() {
144 return Ok(Deletion { targets });
145 }
146 let mut visible = 0;
147 let mut index = 0;
148 while index < self.spans.len() && visible < byte_range.end {
149 let span = &self.spans[index];
150 if span.deleted {
151 index += 1;
152 continue;
153 }
154 if visible + wide(span.length) <= byte_range.start {
155 visible += wide(span.length);
156 index += 1;
157 continue;
158 }
159 let head = byte_range.start.saturating_sub(visible);
161 if head > 0 {
162 let at = u32::try_from(head).expect("fits in a span");
163 let right = self.spans[index].split(at);
164 self.spans.insert(index + 1, right);
165 visible += head;
166 index += 1;
167 continue;
168 }
169 let take = (byte_range.end - visible).min(wide(self.spans[index].length));
172 if take < wide(self.spans[index].length) {
173 let at = u32::try_from(take).expect("fits in a span");
174 let right = self.spans[index].split(at);
175 self.spans.insert(index + 1, right);
176 }
177 let span = &mut self.spans[index];
178 span.deleted = true;
179 match targets.last_mut() {
180 Some(run) if run.end() == span.id => run.length += span.length,
181 _ => targets.push(Run {
182 start: span.id,
183 length: span.length,
184 }),
185 }
186 visible += wide(span.length);
187 index += 1;
188 }
189 Ok(Deletion { targets })
190 }
191
192 #[must_use]
197 pub fn integrate_insertion(&mut self, op: &Insertion) -> Vec<BufferEdit> {
198 if op.length == 0 || self.has_applied(op) {
199 return Vec::new();
200 }
201 if !self.can_apply_insertion(op) {
202 self.pending_insertions.push(op.clone());
203 return Vec::new();
204 }
205 let mut edits = vec![self.apply_insertion(op)];
206 self.drain_pending(&mut edits);
207 edits
208 }
209
210 #[must_use]
214 pub fn integrate_deletion(&mut self, op: &Deletion) -> Vec<BufferEdit> {
215 if !self.can_apply_deletion(op) {
216 self.pending_deletions.push(op.clone());
217 return Vec::new();
218 }
219 match self.apply_deletion(op) {
220 Some(edit) => vec![edit],
221 None => Vec::new(),
222 }
223 }
224}
225
226impl Replica {
228 fn span_containing(&self, id: ByteId) -> usize {
229 self.spans
230 .iter()
231 .position(|span| span.contains(id))
232 .expect("byte exists")
233 }
234
235 fn position_of(&self, id: ByteId) -> usize {
238 let mut position = 0;
239 for span in &self.spans {
240 if span.contains(id) {
241 return position + wide(id.byte_index - span.id.byte_index);
242 }
243 position += wide(span.length);
244 }
245 unreachable!("byte exists")
246 }
247
248 fn expansion_len(&self) -> usize {
250 self.spans.iter().map(|span| wide(span.length)).sum()
251 }
252
253 fn visible_prefix(&self, index: usize) -> usize {
255 self.spans[..index].iter().map(Span::visible_length).sum()
256 }
257
258 fn split_after(&mut self, id: ByteId) -> usize {
261 let index = self.span_containing(id);
262 let keep = id.byte_index - self.spans[index].id.byte_index + 1;
263 if keep < self.spans[index].length {
264 let right = self.spans[index].split(keep);
265 self.spans.insert(index + 1, right);
266 }
267 index
268 }
269
270 fn split_before(&mut self, id: ByteId) -> usize {
273 let index = self.span_containing(id);
274 let keep = id.byte_index - self.spans[index].id.byte_index;
275 if keep == 0 {
276 index
277 } else {
278 let right = self.spans[index].split(keep);
279 self.spans.insert(index + 1, right);
280 index + 1
281 }
282 }
283
284 fn has_applied(&self, op: &Insertion) -> bool {
287 self.version.contains(op.start)
288 }
289
290 fn can_apply_insertion(&self, op: &Insertion) -> bool {
293 self.version.count(op.start.replica) == op.start.byte_index
294 && op.origin_left.is_none_or(|id| self.version.contains(id))
295 && op.origin_right.is_none_or(|id| self.version.contains(id))
296 }
297
298 fn can_apply_deletion(&self, op: &Deletion) -> bool {
300 op.targets
301 .iter()
302 .all(|run| self.version.contains(run.last()))
303 }
304
305 fn apply_insertion(&mut self, op: &Insertion) -> BufferEdit {
307 let left = op.origin_left.map(|id| self.split_after(id));
308 let right = op
309 .origin_right
310 .map_or(self.spans.len(), |id| self.split_before(id));
311
312 let left_position = op.origin_left.map(|id| self.position_of(id));
313 let right_position = op
314 .origin_right
315 .map_or(self.expansion_len(), |id| self.position_of(id));
316 let mut destination = left.map_or(0, |index| index + 1);
317 let mut scanning = false;
318 for index in destination.. {
319 if !scanning {
320 destination = index;
321 }
322 if index == self.spans.len() || index == right {
323 break;
324 }
325 let other = &self.spans[index];
326 let other_left = other.origin_left.map(|id| self.position_of(id));
327 let other_right = other
328 .origin_right
329 .map_or(self.expansion_len(), |id| self.position_of(id));
330 if other_left < left_position
331 || (other_left == left_position
332 && other_right == right_position
333 && op.start.replica < other.id.replica)
334 {
335 break;
336 }
337 if other_left == left_position {
338 scanning = other_right < right_position;
339 }
340 }
341
342 self.version.record(op.start, op.length);
343 let byte_offset = self.visible_prefix(destination);
344 match destination.checked_sub(1) {
345 Some(previous) if self.spans[previous].can_append(op) => {
346 self.spans[previous].length += op.length;
347 }
348 _ => self.spans.insert(destination, Span::of(op)),
349 }
350 BufferEdit::Insert {
351 byte_offset,
352 author: op.start.replica,
353 bytes: op.bytes(),
354 }
355 }
356
357 fn apply_deletion(&mut self, op: &Deletion) -> Option<BufferEdit> {
361 let mut byte_ranges = Vec::<Range<usize>>::new();
362 let mut visible = 0;
363 let mut index = 0;
364 while index < self.spans.len() {
365 let span = &self.spans[index];
366 if span.deleted {
367 index += 1;
368 continue;
369 }
370 let Some((from, to)) = op.earliest_overlap(span) else {
371 visible += wide(span.length);
372 index += 1;
373 continue;
374 };
375 if from > 0 {
377 let right = self.spans[index].split(from);
378 self.spans.insert(index + 1, right);
379 visible += wide(from);
380 index += 1;
381 continue;
382 }
383 if to < self.spans[index].length {
387 let right = self.spans[index].split(to);
388 self.spans.insert(index + 1, right);
389 }
390 let span = &mut self.spans[index];
391 span.deleted = true;
392 match byte_ranges.last_mut() {
393 Some(range) if range.end == visible => range.end += wide(span.length),
394 _ => byte_ranges.push(visible..visible + wide(span.length)),
395 }
396 visible += wide(span.length);
397 index += 1;
398 }
399 (!byte_ranges.is_empty()).then_some(BufferEdit::Delete { byte_ranges })
400 }
401
402 fn drain_pending(&mut self, edits: &mut Vec<BufferEdit>) {
404 loop {
405 let version = &self.version;
408 self.pending_insertions
409 .retain(|op| !version.contains(op.start));
410
411 if let Some(index) = self
412 .pending_insertions
413 .iter()
414 .position(|op| self.can_apply_insertion(op))
415 {
416 let op = self.pending_insertions.swap_remove(index);
417 edits.push(self.apply_insertion(&op));
418 } else if let Some(index) = self
419 .pending_deletions
420 .iter()
421 .position(|op| self.can_apply_deletion(op))
422 {
423 let op = self.pending_deletions.swap_remove(index);
424 edits.extend(self.apply_deletion(&op));
425 } else {
426 return;
427 }
428 }
429 }
430}
431
432#[derive(Clone)]
436pub struct Insertion {
437 start: ByteId,
439 length: u32,
440 origin_left: Option<ByteId>,
442 origin_right: Option<ByteId>,
444}
445
446impl Insertion {
447 #[must_use]
449 pub fn author(&self) -> ReplicaId {
450 self.start.replica
451 }
452
453 #[must_use]
457 pub fn bytes(&self) -> Range<usize> {
458 wide(self.start.byte_index)..wide(self.start.byte_index + self.length)
459 }
460}
461
462#[derive(Clone)]
466pub struct Deletion {
467 targets: Vec<Run>,
468}
469
470impl Deletion {
471 fn earliest_overlap(&self, span: &Span) -> Option<(u32, u32)> {
474 let mut earliest: Option<(u32, u32)> = None;
475 for run in &self.targets {
476 if run.start.replica != span.id.replica {
477 continue;
478 }
479 let from = run.start.byte_index.max(span.id.byte_index);
480 let to = (run.start.byte_index + run.length).min(span.id.byte_index + span.length);
481 if from < to {
482 let overlap = (from - span.id.byte_index, to - span.id.byte_index);
483 if earliest.is_none_or(|(start, _)| overlap.0 < start) {
484 earliest = Some(overlap);
485 }
486 }
487 }
488 earliest
489 }
490}
491
492#[derive(Clone, Copy)]
494struct Run {
495 start: ByteId,
496 length: u32,
497}
498
499impl Run {
500 fn end(&self) -> ByteId {
502 self.start.plus(self.length)
503 }
504
505 fn last(&self) -> ByteId {
506 self.start.plus(self.length - 1)
507 }
508}
509
510#[derive(Debug)]
513pub enum BufferEdit {
514 Insert {
517 byte_offset: usize,
518 author: ReplicaId,
519 bytes: Range<usize>,
520 },
521 Delete { byte_ranges: Vec<Range<usize>> },
524}
525
526#[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Debug)]
528pub struct ReplicaId(NonZeroU32);
529
530#[derive(Clone, Copy, PartialEq)]
531struct ByteId {
532 replica: ReplicaId,
533 byte_index: u32,
536}
537
538impl ByteId {
539 fn plus(self, bytes: u32) -> Self {
540 Self {
541 replica: self.replica,
542 byte_index: self.byte_index + bytes,
543 }
544 }
545}
546
547#[derive(Clone)]
550struct Span {
551 id: ByteId,
553 length: u32,
555 origin_left: Option<ByteId>,
558 origin_right: Option<ByteId>,
560 deleted: bool,
562}
563
564impl Span {
565 fn of(op: &Insertion) -> Self {
566 Self {
567 id: op.start,
568 length: op.length,
569 origin_left: op.origin_left,
570 origin_right: op.origin_right,
571 deleted: false,
572 }
573 }
574
575 fn contains(&self, id: ByteId) -> bool {
576 self.id.replica == id.replica
577 && (self.id.byte_index..self.id.byte_index + self.length).contains(&id.byte_index)
578 }
579
580 fn end_id(&self) -> ByteId {
583 self.id.plus(self.length)
584 }
585
586 fn last_id(&self) -> ByteId {
587 self.id.plus(self.length - 1)
588 }
589
590 fn visible_length(&self) -> usize {
591 if self.deleted { 0 } else { wide(self.length) }
592 }
593
594 fn split(&mut self, at: u32) -> Self {
598 debug_assert!(0 < at && at < self.length);
599 let right = Self {
600 id: self.id.plus(at),
601 length: self.length - at,
602 origin_left: Some(self.id.plus(at - 1)),
603 origin_right: self.origin_right,
604 deleted: self.deleted,
605 };
606 self.length = at;
607 right
608 }
609
610 fn can_append(&self, op: &Insertion) -> bool {
613 !self.deleted
614 && self.end_id() == op.start
615 && op.origin_left == Some(self.last_id())
616 && op.origin_right == self.origin_right
617 }
618}
619
620const _: () = assert!(size_of::<ByteId>() == 8);
622const _: () = assert!(size_of::<Option<ByteId>>() == 8);
623const _: () = assert!(size_of::<Span>() == 32);
624const _: () = assert!(size_of::<Insertion>() == 28);
625const _: () = assert!(size_of::<Run>() == 12);
626
627#[derive(Clone, Default)]
632struct Version(Vec<(ReplicaId, u32)>);
633
634impl Version {
635 fn count(&self, replica: ReplicaId) -> u32 {
636 self.0
637 .iter()
638 .find(|(counted, _)| *counted == replica)
639 .map_or(0, |(_, count)| *count)
640 }
641
642 fn contains(&self, id: ByteId) -> bool {
643 id.byte_index < self.count(id.replica)
644 }
645
646 fn record(&mut self, start: ByteId, length: u32) {
648 assert_eq!(
649 start.byte_index,
650 self.count(start.replica),
651 "bytes must be recorded in order",
652 );
653 let count = start
654 .byte_index
655 .checked_add(length)
656 .expect("the byte clock fits in u32");
657 match self
658 .0
659 .iter_mut()
660 .find(|(counted, _)| *counted == start.replica)
661 {
662 Some((_, existing)) => *existing = count,
663 None => self.0.push((start.replica, count)),
664 }
665 }
666}
667
668#[cfg(test)]
777mod tests {
778 use super::*;
779 use crate::IndigoBuildHasher;
780 use hegel::generators as gs;
781 use std::collections::{HashMap, HashSet};
782
783 fn rid(id: usize) -> NonZeroU32 {
784 NonZeroU32::new(u32::try_from(id).expect("test id fits")).expect("nonzero test id")
785 }
786
787 type OpId = (usize, usize);
791
792 #[derive(Clone)]
793 enum Op {
794 Insert(Insertion, String),
795 Delete(Deletion),
796 }
797
798 struct Doc {
801 id: usize,
802 buffer: String,
803 replica: Replica,
804 history: Vec<(OpId, Op)>,
807 known: HashSet<OpId, IndigoBuildHasher>,
808 texts: HashMap<(ReplicaId, usize), String, IndigoBuildHasher>,
810 authored: usize,
811 }
812
813 impl Doc {
814 fn new(id: usize) -> Self {
815 Self {
816 id,
817 buffer: String::new(),
818 replica: Replica::new(rid(id), 0),
819 history: Vec::new(),
820 known: HashSet::default(),
821 texts: HashMap::default(),
822 authored: 0,
823 }
824 }
825
826 fn insert(&mut self, byte_offset: usize, text: &str) {
827 self.buffer.insert_str(byte_offset, text);
828 let op = self.replica.inserted(byte_offset, text.len()).unwrap();
829 self.record(Op::Insert(op, text.to_owned()));
830 }
831
832 fn delete(&mut self, byte_range: Range<usize>) {
833 let op = self.replica.deleted(byte_range.clone()).unwrap();
834 self.buffer.replace_range(byte_range, "");
835 self.record(Op::Delete(op));
836 }
837
838 fn record(&mut self, op: Op) {
839 let op_id = (self.id, self.authored);
840 self.authored += 1;
841 self.known.insert(op_id);
842 self.stash_text(&op);
843 self.history.push((op_id, op));
844 }
845
846 fn stash_text(&mut self, op: &Op) {
850 if let Op::Insert(insertion, text) = op
851 && !text.is_empty()
852 {
853 self.texts
854 .insert((insertion.author(), insertion.bytes().start), text.clone());
855 }
856 }
857
858 fn receive(&mut self, op_id: OpId, op: &Op) {
859 if !self.known.insert(op_id) {
860 return;
861 }
862 self.stash_text(op);
863 let edits = match op {
864 Op::Insert(insertion, _) => self.replica.integrate_insertion(insertion),
865 Op::Delete(deletion) => self.replica.integrate_deletion(deletion),
866 };
867 self.history.push((op_id, op.clone()));
868 self.apply(edits);
869 }
870
871 fn apply(&mut self, edits: Vec<BufferEdit>) {
872 for edit in edits {
873 match edit {
874 BufferEdit::Insert {
875 byte_offset,
876 author,
877 bytes,
878 } => {
879 let text = &self.texts[&(author, bytes.start)];
880 self.buffer.insert_str(byte_offset, text);
881 }
882 BufferEdit::Delete { byte_ranges } => {
883 for byte_range in byte_ranges.into_iter().rev() {
884 self.buffer.replace_range(byte_range, "");
885 }
886 }
887 }
888 }
889 }
890
891 fn sync_from(&mut self, other: &Self) {
892 for (op_id, op) in &other.history {
893 self.receive(*op_id, op);
894 }
895 }
896 }
897
898 fn items(replica: &Replica) -> Vec<(u32, u32, bool)> {
902 replica
903 .spans
904 .iter()
905 .flat_map(|span| {
906 (span.id.byte_index..span.id.byte_index + span.length)
907 .map(move |byte_index| (span.id.replica.0.get(), byte_index, span.deleted))
908 })
909 .collect()
910 }
911
912 fn draw_boundary(tc: &hegel::TestCase, text: &str) -> usize {
914 let mut index = tc.draw(gs::integers::<usize>().max_value(text.len()));
915 while !text.is_char_boundary(index) {
916 index += 1;
917 }
918 index
919 }
920
921 fn draw_text(tc: &hegel::TestCase, ascii: bool) -> String {
922 if ascii {
923 tc.draw(gs::text().codec("ascii").max_size(8))
924 } else {
925 tc.draw(gs::text().max_size(8))
926 }
927 }
928
929 fn edit_randomly(tc: &hegel::TestCase, doc: &mut Doc) {
930 if doc.buffer.is_empty() || tc.draw(gs::booleans()) {
931 let byte_offset = draw_boundary(tc, &doc.buffer);
932 let text = draw_text(tc, false);
933 doc.insert(byte_offset, &text);
934 } else {
935 let one = draw_boundary(tc, &doc.buffer);
936 let two = draw_boundary(tc, &doc.buffer);
937 doc.delete(one.min(two)..one.max(two));
938 }
939 }
940
941 fn edit_many(tc: &hegel::TestCase, doc: &mut Doc, up_to: usize) {
942 let edits = tc.draw(gs::integers::<usize>().max_value(up_to));
943 for _ in 0..edits {
944 edit_randomly(tc, doc);
945 }
946 }
947
948 fn sync_pair(docs: &mut [Doc], into: usize, from: usize) {
951 if into < from {
952 let (left, right) = docs.split_at_mut(from);
953 left[into].sync_from(&right[0]);
954 } else {
955 let (left, right) = docs.split_at_mut(into);
956 right[0].sync_from(&left[from]);
957 }
958 }
959
960 #[test]
961 fn ties_broken_by_smaller_replica_id() {
962 let mut doc1 = Doc::new(1);
963 let mut doc2 = Doc::new(2);
964
965 doc1.insert(0, "A");
966 doc2.insert(0, "B");
967
968 doc1.sync_from(&doc2);
969 doc2.sync_from(&doc1);
970
971 assert_eq!(doc1.buffer, "AB");
972 assert_eq!(doc2.buffer, "AB");
973 }
974
975 #[test]
976 fn initial_content_and_fork() {
977 let mut replica1 = Replica::new(rid(1), 5);
978 let mut replica2 = replica1.fork(rid(2));
979 assert_eq!(replica2.len(), 5);
980
981 let op = replica2.inserted(5, 2).unwrap();
982 let edits = replica1.integrate_insertion(&op);
983 assert!(matches!(
984 edits.as_slice(),
985 [BufferEdit::Insert { byte_offset: 5, .. }]
986 ));
987 assert_eq!(replica1.len(), 7);
988 }
989
990 #[test]
991 fn invalid_positions_are_errors() {
992 let mut replica = Replica::new(rid(1), 3);
993 assert!(replica.inserted(4, 1).is_err());
994 assert!(replica.deleted(1..5).is_err());
995 let backwards = (2, 1);
996 assert!(replica.deleted(backwards.0..backwards.1).is_err());
997 assert_eq!(replica.len(), 3);
998 }
999
1000 #[test]
1004 fn typing_coalesces_into_one_span() {
1005 let mut doc1 = Doc::new(1);
1006 for (byte_offset, char) in "hello world".char_indices() {
1007 doc1.insert(byte_offset, &char.to_string());
1008 }
1009 assert_eq!(doc1.replica.spans.len(), 1);
1010
1011 let mut doc2 = Doc::new(2);
1012 doc2.sync_from(&doc1);
1013 assert_eq!(doc2.replica.spans.len(), 1);
1014 }
1015
1016 #[test]
1021 fn forward_typing_does_not_interleave() {
1022 let mut doc1 = Doc::new(1);
1023 let mut doc2 = Doc::new(2);
1024 for (byte_offset, char) in "Hello".char_indices() {
1025 doc1.insert(byte_offset, &char.to_string());
1026 }
1027 for (byte_offset, char) in "World".char_indices() {
1028 doc2.insert(byte_offset, &char.to_string());
1029 }
1030 doc1.sync_from(&doc2);
1031 doc2.sync_from(&doc1);
1032 assert_eq!(doc1.buffer, "HelloWorld");
1033 assert_eq!(doc2.buffer, "HelloWorld");
1034 }
1035
1036 #[test]
1037 fn backward_typing_does_not_interleave() {
1038 let mut doc1 = Doc::new(1);
1039 let mut doc2 = Doc::new(2);
1040 for char in "Hello".chars().rev() {
1041 doc1.insert(0, &char.to_string());
1042 }
1043 for char in "World".chars().rev() {
1044 doc2.insert(0, &char.to_string());
1045 }
1046 doc1.sync_from(&doc2);
1047 doc2.sync_from(&doc1);
1048 assert_eq!(doc1.buffer, doc2.buffer);
1049 assert!(doc1.buffer.contains("Hello"));
1050 assert!(doc1.buffer.contains("World"));
1051 }
1052
1053 #[test]
1054 fn mixed_typing_does_not_interleave() {
1055 let mut doc1 = Doc::new(1);
1056 let mut doc2 = Doc::new(2);
1057 for (byte_offset, char) in "Hello".char_indices() {
1058 doc1.insert(byte_offset, &char.to_string());
1059 }
1060 for char in "World".chars().rev() {
1061 doc2.insert(0, &char.to_string());
1062 }
1063 doc1.sync_from(&doc2);
1064 doc2.sync_from(&doc1);
1065 assert_eq!(doc1.buffer, doc2.buffer);
1066 assert!(doc1.buffer.contains("Hello"));
1067 assert!(doc1.buffer.contains("World"));
1068 }
1069
1070 fn type_at(tc: &hegel::TestCase, doc: &mut Doc, byte_offset: usize, text: &str) {
1073 if tc.draw(gs::booleans()) {
1074 let mut byte_offset = byte_offset;
1075 for char in text.chars() {
1076 doc.insert(byte_offset, &char.to_string());
1077 byte_offset += char.len_utf8();
1078 }
1079 } else {
1080 for char in text.chars().rev() {
1081 doc.insert(byte_offset, &char.to_string());
1082 }
1083 }
1084 }
1085
1086 #[hegel::test(test_cases = 2_000)]
1089 fn concurrent_typing_does_not_interleave(tc: hegel::TestCase) {
1090 let base: String = tc.draw(gs::text().alphabet("xyz"));
1091 let mut doc1 = Doc::new(1);
1092 doc1.insert(0, &base);
1093 let mut doc2 = Doc::new(2);
1094 doc2.sync_from(&doc1);
1095
1096 let byte_offset = draw_boundary(&tc, &base);
1097 let text1: String = tc.draw(gs::text().alphabet("ABC").min_size(1));
1098 let text2: String = tc.draw(gs::text().alphabet("123").min_size(1));
1099 type_at(&tc, &mut doc1, byte_offset, &text1);
1100 type_at(&tc, &mut doc2, byte_offset, &text2);
1101
1102 doc1.sync_from(&doc2);
1103 doc2.sync_from(&doc1);
1104 assert_eq!(doc1.buffer, doc2.buffer);
1105 assert!(doc1.buffer.contains(&text1));
1106 assert!(doc1.buffer.contains(&text2));
1107 }
1108
1109 #[hegel::test(test_cases = 2_000)]
1114 fn continued_typing_after_merge_does_not_interleave(tc: hegel::TestCase) {
1115 let mut doc1 = Doc::new(1);
1116 let first: String = tc.draw(gs::text().alphabet("ABC").min_size(1));
1117 type_at(&tc, &mut doc1, 0, &first);
1118 let mut doc2 = Doc::new(2);
1119 doc2.sync_from(&doc1);
1120
1121 let byte_offset = draw_boundary(&tc, &doc1.buffer);
1122 let more: String = tc.draw(gs::text().alphabet("abc").min_size(1));
1123 let rival: String = tc.draw(gs::text().alphabet("123").min_size(1));
1124 type_at(&tc, &mut doc1, byte_offset, &more);
1125 type_at(&tc, &mut doc2, byte_offset, &rival);
1126
1127 doc1.sync_from(&doc2);
1128 doc2.sync_from(&doc1);
1129 assert_eq!(doc1.buffer, doc2.buffer);
1130 assert_eq!(items(&doc1.replica), items(&doc2.replica));
1131 assert!(doc1.buffer.contains(&more));
1132 assert!(doc1.buffer.contains(&rival));
1133 }
1134
1135 #[hegel::test(test_cases = 2_000)]
1136 fn local_edits_match_string(tc: hegel::TestCase) {
1137 let mut doc = Doc::new(1);
1138 let edits = tc.draw(gs::integers::<usize>().max_value(20));
1139 for _ in 0..edits {
1140 edit_randomly(&tc, &mut doc);
1141 assert_eq!(doc.replica.len(), doc.buffer.len());
1142 }
1143
1144 let mut mirror = Doc::new(2);
1146 mirror.sync_from(&doc);
1147 assert_eq!(mirror.buffer, doc.buffer);
1148 }
1149
1150 #[hegel::test(test_cases = 2_000)]
1151 fn concurrent_edits_converge(tc: hegel::TestCase) {
1152 let count = tc.draw(gs::integers::<usize>().min_value(2).max_value(4));
1153 let mut docs = (1..=count).map(Doc::new).collect::<Vec<Doc>>();
1154
1155 let rounds = tc.draw(gs::integers::<usize>().min_value(1).max_value(3));
1156 for _ in 0..rounds {
1157 for doc in &mut docs {
1159 edit_many(&tc, doc, 3);
1160 }
1161 let into = tc.draw(gs::integers::<usize>().max_value(count - 1));
1163 let from = tc.draw(gs::integers::<usize>().max_value(count - 1));
1164 if into != from {
1165 sync_pair(&mut docs, into, from);
1166 }
1167 }
1168
1169 for into in 0..count {
1171 for from in 0..count {
1172 if into != from {
1173 sync_pair(&mut docs, into, from);
1174 }
1175 }
1176 }
1177 for pair in docs.windows(2) {
1178 assert_eq!(pair[0].buffer, pair[1].buffer);
1179 assert_eq!(items(&pair[0].replica), items(&pair[1].replica));
1180 }
1181 }
1182
1183 #[hegel::test(test_cases = 5_000)]
1184 fn out_of_order_delivery(tc: hegel::TestCase) {
1185 let mut doc1 = Doc::new(1);
1186 edit_many(&tc, &mut doc1, 6);
1187
1188 let mut doc2 = Doc::new(2);
1191 let mut remaining = (0..doc1.history.len()).collect::<Vec<usize>>();
1192 while !remaining.is_empty() {
1193 let pick = tc.draw(gs::integers::<usize>().max_value(remaining.len() - 1));
1194 let index = remaining.swap_remove(pick);
1195 let (op_id, op) = &doc1.history[index];
1196 doc2.receive(*op_id, op);
1197 }
1198
1199 assert_eq!(doc2.buffer, doc1.buffer);
1200 assert!(doc2.replica.pending_insertions.is_empty());
1201 assert!(doc2.replica.pending_deletions.is_empty());
1202 }
1203
1204 #[hegel::test(test_cases = 5_000)]
1205 fn integration_is_idempotent(tc: hegel::TestCase) {
1206 let mut doc1 = Doc::new(1);
1207 let mut doc2 = Doc::new(2);
1208 edit_many(&tc, &mut doc1, 4);
1209 edit_many(&tc, &mut doc2, 4);
1210
1211 doc1.sync_from(&doc2);
1214 doc2.sync_from(&doc1);
1215 edit_many(&tc, &mut doc1, 2);
1216 doc2.sync_from(&doc1);
1217
1218 let buffer = doc2.buffer.clone();
1219 let snapshot = items(&doc2.replica);
1220 for (_, op) in &doc1.history {
1221 let edits = match op {
1222 Op::Insert(insertion, _) => doc2.replica.integrate_insertion(insertion),
1223 Op::Delete(deletion) => doc2.replica.integrate_deletion(deletion),
1224 };
1225 assert!(edits.is_empty());
1226 }
1227 assert_eq!(doc2.buffer, buffer);
1228 assert_eq!(items(&doc2.replica), snapshot);
1229 }
1230
1231 #[hegel::test(test_cases = 1_500)]
1236 fn matches_char_based_implementation(tc: hegel::TestCase) {
1237 use crate::crdt_vibed_not_length;
1238
1239 fn merge_pair_old(docs: &mut [crdt_vibed_not_length::Document], into: usize, from: usize) {
1240 if into < from {
1241 let (left, right) = docs.split_at_mut(from);
1242 left[into].merge(&right[0]);
1243 } else {
1244 let (left, right) = docs.split_at_mut(into);
1245 right[0].merge(&left[from]);
1246 }
1247 }
1248
1249 let ascii = tc.draw(gs::booleans());
1250 let count = tc.draw(gs::integers::<usize>().min_value(2).max_value(3));
1251 let mut olds = (1..=count)
1252 .map(crdt_vibed_not_length::Document::new)
1253 .collect::<Vec<crdt_vibed_not_length::Document>>();
1254 let mut news = (1..=count).map(Doc::new).collect::<Vec<Doc>>();
1255
1256 let rounds = tc.draw(gs::integers::<usize>().min_value(1).max_value(3));
1257 for _ in 0..rounds {
1258 for index in 0..count {
1259 let edits = tc.draw(gs::integers::<usize>().max_value(3));
1260 for _ in 0..edits {
1261 let old = &mut olds[index];
1262 let new = &mut news[index];
1263 if new.buffer.is_empty() || tc.draw(gs::booleans()) {
1264 let byte_offset = draw_boundary(&tc, &new.buffer);
1265 let text = draw_text(&tc, ascii);
1266 old.insert(byte_offset, &text).unwrap();
1267 new.insert(byte_offset, &text);
1268 } else {
1269 let one = draw_boundary(&tc, &new.buffer);
1270 let two = draw_boundary(&tc, &new.buffer);
1271 let byte_range = one.min(two)..one.max(two);
1272 old.delete(byte_range.clone()).unwrap();
1273 new.delete(byte_range);
1274 }
1275 }
1276 }
1277 let into = tc.draw(gs::integers::<usize>().max_value(count - 1));
1278 let from = tc.draw(gs::integers::<usize>().max_value(count - 1));
1279 if into != from {
1280 merge_pair_old(&mut olds, into, from);
1281 sync_pair(&mut news, into, from);
1282 assert_eq!(olds[into].text(), news[into].buffer);
1283 }
1284 }
1285
1286 for into in 0..count {
1287 for from in 0..count {
1288 if into != from {
1289 merge_pair_old(&mut olds, into, from);
1290 sync_pair(&mut news, into, from);
1291 }
1292 }
1293 }
1294 for index in 0..count {
1295 assert_eq!(olds[index].text(), news[index].buffer);
1296 if ascii {
1297 let old_items = olds[index]
1298 .item_summaries()
1299 .into_iter()
1300 .map(|(replica, sequence, deleted)| {
1301 (
1302 u32::try_from(replica).unwrap(),
1303 u32::try_from(sequence).unwrap(),
1304 deleted,
1305 )
1306 })
1307 .collect::<Vec<(u32, u32, bool)>>();
1308 assert_eq!(old_items, items(&news[index].replica));
1309 }
1310 }
1311 }
1312}