ff0af1ee unknown 1 month ago
1
2
3
4
5
6
0bdc3f91 Andy McCall 1 month ago ↶
7
use crate :: fasthash:: { FastMap as HashMap, FastSet as HashSet} ;
ff0af1ee unknown 1 month ago
8
use crate :: hash:: ChangeHash;
9
use crate :: objects:: AtomRange;
0bdc3f91 Andy McCall 1 month ago ↶
10
use std:: collections:: { BTreeMap, BTreeSet} ;
ff0af1ee unknown 1 month ago
11
12
13
pub type AtomKey = ( ChangeHash, u32 ) ;
14
15
# [ derive ( Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Debug ) ]
16
pub enum Vertex {
17
Start,
18
Atom( AtomKey) ,
19
End,
20
}
21
22
23
pub trait DepOracle {
24
fn depends_on ( & self , a : & ChangeHash, b : & ChangeHash) -> bool ;
25
}
26
27
28
pub struct NoDeps ;
29
impl DepOracle for NoDeps {
30
fn depends_on ( & self , a : & ChangeHash, b : & ChangeHash) -> bool {
31
a = = b
32
}
33
}
34
35
# [ derive ( Default, Clone, Debug ) ]
36
struct AtomInfo {
37
38
content : Option< Vec< u8 > > ,
39
deleted_by : BTreeSet< ChangeHash> ,
40
}
41
42
# [ derive ( Clone, Debug ) ]
43
pub struct FileGraph {
44
atoms : HashMap< AtomKey, AtomInfo> ,
0bdc3f91 Andy McCall 1 month ago ↶
45
46
succ : HashMap< Vertex, Edges> ,
47
}
48
49
50
51
# [ derive ( Clone, Debug, Default, PartialEq, Eq ) ]
52
enum Edges {
53
# [ default ]
54
None ,
55
One( Vertex) ,
56
Many( Vec< Vertex> ) ,
ff0af1ee unknown 1 month ago
57
}
58
0bdc3f91 Andy McCall 1 month ago ↶
59
impl Edges {
60
fn insert ( & mut self , v : Vertex) {
61
match self {
62
Edges:: None => * self = Edges:: One( v) ,
63
Edges:: One( a) if * a = = v => { }
64
Edges:: One( a) => {
65
let a = * a;
66
* self = Edges:: Many( if a < v { vec! [ a, v] } else { vec! [ v, a] } ) ;
67
}
68
Edges:: Many( list) => {
69
if let Err ( i) = list.binary_search ( & v) {
70
list.insert ( i, v) ;
71
}
72
}
73
}
74
}
75
76
fn as_slice ( & self ) -> & [ Vertex] {
77
match self {
78
Edges:: None => & [ ] ,
79
Edges:: One( v) => std:: slice:: from_ref( v) ,
80
Edges:: Many( list) => list,
81
}
82
}
83
84
fn iter ( & self ) -> std:: slice:: Iter< '_ , Vertex> {
85
self .as_slice ( ) .iter ( )
86
}
87
}
88
ff0af1ee unknown 1 month ago
89
impl Default for FileGraph {
90
fn default ( ) -> Self {
91
Self :: new( )
92
}
93
}
94
95
96
# [ derive ( Clone, Debug, PartialEq, Eq ) ]
97
pub enum Segment {
98
Lines( Vec< AtomKey> ) ,
99
100
Conflict( Vec< Vec< AtomKey> > ) ,
101
102
75167672 Andy McCall 1 month ago
103
Zombie {
104
deleted: Vec< AtomKey> ,
105
inserted: Vec< AtomKey> ,
106
} ,
ff0af1ee unknown 1 month ago
107
}
108
109
# [ derive ( Clone, Debug, PartialEq, Eq, Default ) ]
110
pub struct Materialized {
111
pub segments : Vec< Segment> ,
112
}
113
114
impl Materialized {
115
pub fn is_conflicted ( & self ) -> bool {
75167672 Andy McCall 1 month ago
116
self .segments
117
.iter ( )
118
.any ( | s | ! matches! ( s, Segment:: Lines( _ ) ) )
ff0af1ee unknown 1 month ago
119
}
120
pub fn conflict_count ( & self ) -> usize {
75167672 Andy McCall 1 month ago
121
self .segments
122
.iter ( )
123
.filter ( | s | ! matches! ( s, Segment:: Lines( _ ) ) )
124
.count ( )
ff0af1ee unknown 1 month ago
125
}
126
127
pub fn live_atoms ( & self ) -> Vec< AtomKey> {
128
let mut out = Vec :: new( ) ;
129
for s in & self .segments {
130
match s {
131
Segment:: Lines( a) => out.extend ( a) ,
132
Segment:: Conflict( sides) => sides.iter ( ) .for_each ( | x | out.extend ( x) ) ,
133
Segment:: Zombie { inserted, .. } => out.extend ( inserted) ,
134
}
135
}
136
out
137
}
138
}
139
140
impl FileGraph {
141
pub fn new ( ) -> Self {
75167672 Andy McCall 1 month ago
142
let mut g = FileGraph {
0bdc3f91 Andy McCall 1 month ago ↶
143
atoms: HashMap:: default( ) ,
144
succ: HashMap:: default( ) ,
75167672 Andy McCall 1 month ago
145
} ;
ff0af1ee unknown 1 month ago
146
g.edge ( Vertex:: Start, Vertex:: End) ;
147
g
148
}
149
150
fn edge ( & mut self , a : Vertex, b : Vertex) {
151
self .succ.entry ( a) .or_default ( ) .insert ( b) ;
152
self .succ.entry ( b) .or_default ( ) ;
153
}
154
155
75167672 Andy McCall 1 month ago
156
pub fn insert (
157
& mut self ,
158
change : ChangeHash,
159
first_index : u32 ,
160
after : Vertex,
161
before : Vertex,
162
lines : & [Vec< u8 > ],
0bdc3f91 Andy McCall 1 month ago ↶
163
) {
164
self .insert_owned ( change, first_index, after, before, lines.to_vec ( ) ) ;
165
}
166
167
168
169
170
pub fn insert_owned (
171
& mut self ,
172
change : ChangeHash,
173
first_index : u32 ,
174
after : Vertex,
175
before : Vertex,
176
lines : Vec< Vec< u8 > > ,
75167672 Andy McCall 1 month ago
177
) {
0bdc3f91 Andy McCall 1 month ago ↶
178
self .atoms.reserve ( lines.len ( ) ) ;
179
self .succ.reserve ( lines.len ( ) + 1 ) ;
ff0af1ee unknown 1 month ago
180
let mut prev = after;
0bdc3f91 Andy McCall 1 month ago ↶
181
for ( i, line) in lines.into_iter ( ) .enumerate ( ) {
ff0af1ee unknown 1 month ago
182
let key = ( change, first_index + i as u32 ) ;
0bdc3f91 Andy McCall 1 month ago ↶
183
self .atoms.entry ( key) .or_default ( ) .content = Some ( line) ;
ff0af1ee unknown 1 month ago
184
let v = Vertex:: Atom( key) ;
0bdc3f91 Andy McCall 1 month ago ↶
185
self .succ.entry ( prev) .or_default ( ) .insert ( v) ;
ff0af1ee unknown 1 month ago
186
prev = v;
187
}
0bdc3f91 Andy McCall 1 month ago ↶
188
self .succ.entry ( prev) .or_default ( ) .insert ( before) ;
189
self .succ.entry ( before) .or_default ( ) ;
ff0af1ee unknown 1 month ago
190
}
191
192
193
pub fn delete ( & mut self , by : ChangeHash, range : & AtomRange) {
194
for i in range.from.. range.to {
75167672 Andy McCall 1 month ago
195
self .atoms
196
.entry ( ( range.change, i) )
197
.or_default ( )
198
.deleted_by
199
.insert ( by) ;
ff0af1ee unknown 1 month ago
200
}
201
}
202
203
pub fn is_live ( & self , k : & AtomKey) -> bool {
75167672 Andy McCall 1 month ago
204
self .atoms
205
.get ( k)
206
.is_some_and ( | a | a.content.is_some ( ) & & a.deleted_by.is_empty ( ) )
ff0af1ee unknown 1 month ago
207
}
208
209
pub fn content ( & self , k : & AtomKey) -> Option< & [ u8 ] > {
210
self .atoms.get ( k) .and_then ( | a | a.content.as_deref ( ) )
211
}
212
213
pub fn deleters ( & self , k : & AtomKey) -> impl Iterator< Item = & ChangeHash> {
75167672 Andy McCall 1 month ago
214
self .atoms
215
.get ( k)
216
.into_iter ( )
217
.flat_map ( | a | a.deleted_by.iter ( ) )
ff0af1ee unknown 1 month ago
218
}
219
220
221
pub fn atom_count ( & self ) -> usize {
222
self .atoms.len ( )
223
}
224
62794ecc Andy McCall 1 month ago ↶
225
226
227
228
pub fn live_successor_changes ( & self , k : & AtomKey) -> Vec< ChangeHash> {
229
self .succ
230
.get ( & Vertex:: Atom( * k) )
231
.map ( | ss | {
232
ss.iter ( )
233
.filter_map ( | s | match s {
234
Vertex:: Atom( y) if self .is_live ( y) => Some ( y.0 ) ,
235
_ => None ,
236
} )
237
.collect ( )
238
} )
239
.unwrap_or_default ( )
240
}
241
ff0af1ee unknown 1 month ago
242
fn is_ghost ( & self , k : & AtomKey, info : & AtomInfo, deps : & dyn DepOracle) -> bool {
23b217d7 Andy McCall 1 month ago ↶
243
244
245
246
ff0af1ee unknown 1 month ago
247
self .succ.get ( & Vertex:: Atom( * k) ) .is_some_and ( | ss | {
248
ss.iter ( ) .any ( | s | match s {
75167672 Andy McCall 1 month ago
249
Vertex:: Atom( y) if self .is_live ( y) => ! info
250
.deleted_by
251
.iter ( )
252
.any ( | d | deps.depends_on ( d, & y.0 ) | | deps.depends_on ( & y.0 , d) ) ,
ff0af1ee unknown 1 month ago
253
_ => false ,
254
} )
255
} )
256
}
257
258
75167672 Andy McCall 1 month ago
259
fn reduced_succ (
260
& self ,
261
v : Vertex,
262
kept : & HashSet< Vertex> ,
263
memo : & mut HashMap< Vertex, BTreeSet< Vertex> > ,
264
) -> BTreeSet< Vertex> {
ff0af1ee unknown 1 month ago
265
let mut out = BTreeSet:: new( ) ;
75167672 Andy McCall 1 month ago
266
let mut stack: Vec< Vertex> = self
267
.succ
268
.get ( & v)
269
.map ( | s | s.iter ( ) .copied ( ) .collect ( ) )
270
.unwrap_or_default ( ) ;
0bdc3f91 Andy McCall 1 month ago ↶
271
let mut seen = HashSet:: default( ) ;
ff0af1ee unknown 1 month ago
272
while let Some ( s) = stack.pop ( ) {
273
if kept.contains ( & s) {
274
out.insert ( s) ;
275
} else if seen.insert ( s) {
276
if let Some ( m) = memo.get ( & s) {
277
out.extend ( m.iter ( ) .copied ( ) ) ;
278
} else if let Some ( next) = self .succ.get ( & s) {
279
stack.extend ( next.iter ( ) .copied ( ) ) ;
280
}
281
}
282
}
283
out
284
}
285
286
287
pub fn materialize ( & self , deps : & dyn DepOracle) -> Materialized {
288
0bdc3f91 Andy McCall 1 month ago ↶
289
let mut kept: HashSet< Vertex> = HashSet:: default( ) ;
290
let mut ghosts: HashSet< AtomKey> = HashSet:: default( ) ;
ff0af1ee unknown 1 month ago
291
kept.insert ( Vertex:: Start) ;
292
kept.insert ( Vertex:: End) ;
293
for ( k, info) in & self .atoms {
294
if info.content.is_none ( ) {
295
continue ;
296
}
297
if info.deleted_by.is_empty ( ) {
298
kept.insert ( Vertex:: Atom( * k) ) ;
299
} else if self .is_ghost ( k, info, deps) {
300
kept.insert ( Vertex:: Atom( * k) ) ;
301
ghosts.insert ( * k) ;
302
}
303
}
304
305
0bdc3f91 Andy McCall 1 month ago ↶
306
let mut memo = HashMap:: default( ) ;
307
let mut rsucc: HashMap< Vertex, BTreeSet< Vertex> > = HashMap:: default( ) ;
308
let mut indeg: HashMap< Vertex, usize > = HashMap:: default( ) ;
ff0af1ee unknown 1 month ago
309
for & u in & kept {
310
indeg.entry ( u) .or_insert ( 0 ) ;
311
let rs = self .reduced_succ ( u, & kept, & mut memo) ;
312
for & w in & rs {
313
* indeg.entry ( w) .or_insert ( 0 ) + = 1 ;
314
}
315
rsucc.insert ( u, rs) ;
316
}
317
318
75167672 Andy McCall 1 month ago
319
let mut ready: BTreeSet< Vertex> = indeg
320
.iter ( )
321
.filter ( | ( _, &d ) | d = = 0 )
322
.map ( | ( v , _) | * v)
323
.collect ( ) ;
0bdc3f91 Andy McCall 1 month ago ↶
324
let mut side: HashMap< Vertex, u32 > = HashMap:: default( ) ;
325
let mut joins: HashSet< Vertex> = HashSet:: default( ) ;
ff0af1ee unknown 1 month ago
326
let mut next_side = 0 u32 ;
327
let mut plain: Vec< Vertex> = Vec :: new( ) ;
328
let mut conflict: Option< BTreeMap< u32 , Vec< Vertex> > > = None ;
329
let mut segments: Vec< Segment> = Vec :: new( ) ;
330
331
let flush_plain = | plain : & mut Vec< Vertex> , segments : & mut Vec< Segment> | {
75167672 Andy McCall 1 month ago
332
let atoms: Vec< AtomKey> = plain
333
.iter ( )
334
.filter_map ( | v | {
335
if let Vertex:: Atom( k) = v {
336
Some ( * k)
337
} else {
338
None
339
}
340
} )
341
.collect ( ) ;
ff0af1ee unknown 1 month ago
342
plain.clear ( ) ;
343
if ! atoms.is_empty ( ) {
344
segments.extend ( split_zombies ( atoms, & ghosts) ) ;
345
}
346
} ;
347
let finish_conflict = | c : BTreeMap< u32 , Vec< Vertex> > , segments : & mut Vec< Segment> | {
348
let sides: Vec< Vec< AtomKey> > = c
349
.into_values ( )
350
.map ( | vs | {
351
vs.into_iter ( )
352
.filter_map ( | v | match v {
353
Vertex:: Atom( k) if ! ghosts.contains ( & k) => Some ( k) ,
354
_ => None ,
355
} )
356
.collect:: < Vec< _ > > ( )
357
} )
358
.filter ( | s | ! s.is_empty ( ) )
359
.collect ( ) ;
360
match sides.len ( ) {
361
0 => { }
362
1 => segments.push ( Segment:: Lines( sides.into_iter ( ) .next ( ) .unwrap ( ) ) ) ,
363
_ => segments.push ( Segment:: Conflict( sides) ) ,
364
}
365
} ;
366
367
while ! ready.is_empty ( ) {
368
if ready.len ( ) > = 2 {
369
if conflict.is_none ( ) {
370
flush_plain ( & mut plain, & mut segments) ;
371
conflict = Some ( BTreeMap:: new( ) ) ;
372
}
373
for r in & ready {
374
if ! side.contains_key ( r) {
375
side.insert ( * r, next_side) ;
376
next_side + = 1 ;
377
}
378
}
379
}
380
let v = ready.pop_first ( ) .unwrap ( ) ;
381
let my_side = side.get ( & v) .copied ( ) ;
382
match ( & mut conflict, my_side) {
383
( Some ( c) , Some ( s) ) => c.entry ( s) .or_default ( ) .push ( v) ,
384
( Some ( _ ) , None ) => {
385
let c = conflict.take ( ) .unwrap ( ) ;
386
finish_conflict ( c, & mut segments) ;
387
plain.push ( v) ;
388
}
389
( None , _ ) => plain.push ( v) ,
390
}
391
for & w in & rsucc[ & v] {
392
let d = indeg.get_mut ( & w) .unwrap ( ) ;
393
* d - = 1 ;
394
if conflict.is_some ( ) & & ! joins.contains ( & w) {
395
match ( my_side, side.get ( & w) .copied ( ) ) {
396
( Some ( s) , None ) => {
397
side.insert ( w, s) ;
398
}
399
( Some ( s) , Some ( t) ) if s ! = t => {
400
side.remove ( & w) ;
401
joins.insert ( w) ;
402
}
403
( None , Some ( _ ) ) => {
404
side.remove ( & w) ;
405
joins.insert ( w) ;
406
}
407
_ => { }
408
}
409
}
410
if * d = = 0 {
411
ready.insert ( w) ;
412
}
413
}
414
}
415
if let Some ( c) = conflict.take ( ) {
416
finish_conflict ( c, & mut segments) ;
417
}
418
flush_plain ( & mut plain, & mut segments) ;
419
Materialized { segments }
420
}
421
422
423
424
pub fn render ( & self , m : & Materialized, label : & dyn Fn( &ChangeHash ) -> String) -> Vec< u8 > {
425
let total = m.conflict_count ( ) ;
426
let mut n = 0 ;
427
let mut out: Vec< u8 > = Vec :: new( ) ;
428
let push_atoms = | out : & mut Vec< u8 > , atoms : & [AtomKey]| {
429
for a in atoms {
430
if let Some ( c) = self .content ( a) {
431
out.extend_from_slice ( c) ;
432
}
433
}
434
} ;
435
let newline_if_needed = | out : & mut Vec< u8 > | {
436
if ! out.is_empty ( ) & & * out.last ( ) .unwrap ( ) ! = b ' \n ' {
437
out.push ( b ' \n ' ) ;
438
}
439
} ;
440
for seg in & m.segments {
441
match seg {
442
Segment:: Lines( a) => push_atoms ( & mut out, a) ,
443
Segment:: Conflict( sides) => {
444
n + = 1 ;
445
let k = sides.len ( ) ;
446
for ( i, s) in sides.iter ( ) .enumerate ( ) {
447
newline_if_needed ( & mut out) ;
448
let who = s.first ( ) .map ( | a | label ( & a.0 ) ) .unwrap_or_default ( ) ;
449
let marker = if i = = 0 { " <<<<<<<" } else { " =======" } ;
450
out.extend_from_slice (
451
format! ( " {marker} oxy conflict {n} of {total} , side {} of {k} : change {who} \n " , i + 1 ) .as_bytes ( ) ,
452
) ;
453
push_atoms ( & mut out, s) ;
454
}
455
newline_if_needed ( & mut out) ;
75167672 Andy McCall 1 month ago
456
out.extend_from_slice (
457
format! ( " >>>>>>> oxy conflict {n} of {total} end\n " ) .as_bytes ( ) ,
458
) ;
ff0af1ee unknown 1 month ago
459
}
460
Segment:: Zombie { deleted, inserted } => {
461
n + = 1 ;
462
newline_if_needed ( & mut out) ;
463
let deleter = deleted
464
.first ( )
465
.and_then ( | a | self .deleters ( a) .next ( ) .copied ( ) )
466
.map ( | d | label ( & d) )
467
.unwrap_or_default ( ) ;
468
let inserter = inserted.first ( ) .map ( | a | label ( & a.0 ) ) .unwrap_or_default ( ) ;
469
out.extend_from_slice (
470
format! ( " <<<<<<< oxy conflict {n} of {total} , side 1 of 2: deleted by change {deleter} \n -------\n " ) .as_bytes ( ) ,
471
) ;
472
push_atoms ( & mut out, deleted) ;
473
newline_if_needed ( & mut out) ;
75167672 Andy McCall 1 month ago
474
out.extend_from_slice (
475
format! (
476
" ======= oxy conflict {n} of {total} , side 2 of 2: change {inserter} \n "
477
)
478
.as_bytes ( ) ,
479
) ;
ff0af1ee unknown 1 month ago
480
push_atoms ( & mut out, inserted) ;
481
newline_if_needed ( & mut out) ;
75167672 Andy McCall 1 month ago
482
out.extend_from_slice (
483
format! ( " >>>>>>> oxy conflict {n} of {total} end\n " ) .as_bytes ( ) ,
484
) ;
ff0af1ee unknown 1 month ago
485
}
486
}
487
}
488
out
489
}
490
}
491
252e8cb2 Andy McCall 1 month ago ↶
492
fn vertex_value ( v : & Vertex) -> crate:: cbor:: Value {
493
use crate :: cbor:: Value;
494
match v {
495
Vertex:: Start => Value:: Uint( 0 ) ,
496
Vertex:: End => Value:: Uint( 1 ) ,
75167672 Andy McCall 1 month ago
497
Vertex:: Atom( ( c, i) ) => {
498
Value:: Array( vec! [ Value:: Bytes( c.0. to_vec ( ) ) , Value:: Uint( * i as u64 ) ] )
499
}
252e8cb2 Andy McCall 1 month ago ↶
500
}
501
}
502
503
fn vertex_from ( v : & crate:: cbor:: Value) -> Result< Vertex, crate:: objects:: ObjectError> {
504
use crate :: cbor:: Value;
505
use crate :: objects:: ObjectError;
506
match v {
507
Value:: Uint( 0 ) => Ok ( Vertex:: Start) ,
508
Value:: Uint( 1 ) => Ok ( Vertex:: End) ,
75167672 Andy McCall 1 month ago
509
Value:: Array( a) if a.len ( ) = = 2 => Ok ( Vertex:: Atom( (
510
crate :: objects:: oref( & a[ 0 ] , " vertex" ) ? ,
511
crate :: objects:: u32_( & a[ 1 ] , " vertex" ) ? ,
512
) ) ) ,
513
_ => Err ( ObjectError:: Schema(
514
" vertex: expected 0, 1, or [hash, index]" .into ( ) ,
515
) ) ,
252e8cb2 Andy McCall 1 month ago ↶
516
}
517
}
518
519
impl FileGraph {
520
521
pub fn to_value ( & self ) -> crate:: cbor:: Value {
522
use crate :: cbor:: Value;
523
let mut atoms: Vec< ( & AtomKey, & AtomInfo) > = self .atoms.iter ( ) .collect ( ) ;
524
atoms.sort_by_key ( | ( k , _) | * * k) ;
75167672 Andy McCall 1 month ago
525
let mut edges: Vec< ( Vertex, Vertex) > = self
526
.succ
527
.iter ( )
528
.flat_map ( | ( a , bs ) | bs.iter ( ) .map ( move | b| ( * a, * b) ) )
529
.collect ( ) ;
252e8cb2 Andy McCall 1 month ago ↶
530
edges.sort ( ) ;
531
Value:: map( [
532
(
533
0 ,
534
Value:: Array(
535
atoms
536
.into_iter ( )
537
.map ( | ( (c , i ) , info) | {
538
Value:: Array( vec! [
539
Value:: Bytes( c.0. to_vec ( ) ) ,
540
Value:: Uint( * i as u64 ) ,
75167672 Andy McCall 1 month ago
541
info.content
542
.as_ref ( )
543
.map ( | b | Value:: Bytes( b.clone ( ) ) )
544
.unwrap_or ( Value:: Null) ,
545
Value:: Array(
546
info.deleted_by
547
.iter ( )
548
.map ( | d | Value:: Bytes( d.0. to_vec ( ) ) )
549
.collect ( ) ,
550
) ,
252e8cb2 Andy McCall 1 month ago ↶
551
] )
552
} )
553
.collect ( ) ,
554
) ,
555
),
75167672 Andy McCall 1 month ago
556
(
557
1 ,
558
Value:: Array(
559
edges
560
.iter ( )
561
.map ( | ( a , b ) | Value:: Array( vec! [ vertex_value ( a) , vertex_value ( b) ] ) )
562
.collect ( ) ,
563
) ,
564
) ,
252e8cb2 Andy McCall 1 month ago ↶
565
] )
566
}
567
568
pub fn from_value ( v : & crate:: cbor:: Value) -> Result< FileGraph, crate:: objects:: ObjectError> {
569
use crate :: objects:: { array, bytes, field, map, oref, u32_, ObjectError} ;
570
let m = map ( v, " file graph" ) ? ;
75167672 Andy McCall 1 month ago
571
let mut g = FileGraph {
0bdc3f91 Andy McCall 1 month ago ↶
572
atoms: HashMap:: default( ) ,
573
succ: HashMap:: default( ) ,
75167672 Andy McCall 1 month ago
574
} ;
252e8cb2 Andy McCall 1 month ago ↶
575
for a in array ( field ( m, 0 , " atoms" ) ? , " atoms" ) ? {
576
let e = array ( a, " atom" ) ? ;
577
if e.len ( ) ! = 4 {
578
return Err ( ObjectError:: Schema( " atom: expected 4 fields" .into ( ) ) ) ;
579
}
580
let key = ( oref ( & e[ 0 ] , " atom change" ) ? , u32_ ( & e[ 1 ] , " atom index" ) ? ) ;
75167672 Andy McCall 1 month ago
581
let content = if e[ 2 ] .is_null ( ) {
582
None
583
} else {
584
Some ( bytes ( & e[ 2 ] , " atom content" ) ? .to_vec ( ) )
585
} ;
586
let deleted_by = array ( & e[ 3 ] , " deleters" ) ?
587
.iter ( )
588
.map ( | d | oref ( d, " deleter" ) )
589
.collect:: < Result< BTreeSet< _ > , _ > > ( ) ? ;
590
g.atoms.insert (
591
key,
592
AtomInfo {
593
content,
594
deleted_by,
595
} ,
596
) ;
252e8cb2 Andy McCall 1 month ago ↶
597
}
598
for e in array ( field ( m, 1 , " edges" ) ? , " edges" ) ? {
599
let pair = array ( e, " edge" ) ? ;
600
if pair.len ( ) ! = 2 {
601
return Err ( ObjectError:: Schema( " edge: expected [from, to]" .into ( ) ) ) ;
602
}
603
g.edge ( vertex_from ( & pair[ 0 ] ) ? , vertex_from ( & pair[ 1 ] ) ? ) ;
604
}
605
Ok ( g)
606
}
607
}
608
ff0af1ee unknown 1 month ago
609
610
fn split_zombies ( atoms : Vec< AtomKey> , ghosts : & HashSet< AtomKey> ) -> Vec< Segment> {
611
let mut out = Vec :: new( ) ;
612
let mut lines: Vec< AtomKey> = Vec :: new( ) ;
613
let mut i = 0 ;
614
while i < atoms.len ( ) {
615
if ! ghosts.contains ( & atoms[ i] ) {
616
lines.push ( atoms[ i] ) ;
617
i + = 1 ;
618
continue ;
619
}
620
if ! lines.is_empty ( ) {
621
out.push ( Segment:: Lines( std:: mem:: take( & mut lines) ) ) ;
622
}
623
let mut deleted = Vec :: new( ) ;
624
while i < atoms.len ( ) & & ghosts.contains ( & atoms[ i] ) {
625
deleted.push ( atoms[ i] ) ;
626
i + = 1 ;
627
}
628
let mut inserted = Vec :: new( ) ;
629
if i < atoms.len ( ) {
630
let ch = atoms[ i] .0 ;
631
while i < atoms.len ( ) & & atoms[ i] .0 = = ch & & ! ghosts.contains ( & atoms[ i] ) {
632
inserted.push ( atoms[ i] ) ;
633
i + = 1 ;
634
}
635
}
636
out.push ( Segment:: Zombie { deleted, inserted } ) ;
637
}
638
if ! lines.is_empty ( ) {
639
out.push ( Segment:: Lines( lines) ) ;
640
}
641
out
642
}
643
644
# [ cfg ( test ) ]
645
mod tests {
646
use super :: * ;
647
use crate :: hash:: ObjectRef;
648
649
fn h ( n : u8 ) -> ChangeHash {
650
ObjectRef( [ n; 32 ] )
651
}
652
fn lines ( v : & [& str ]) -> Vec< Vec< u8 > > {
653
v.iter ( ) .map ( | s | s.as_bytes ( ) .to_vec ( ) ) .collect ( )
654
}
655
fn text ( g : & FileGraph, m : & Materialized) -> String {
656
String :: from_utf8( g.render ( m, & | c| c.short ( 4 ) ) ) .unwrap ( )
657
}
658
659
struct Deps ( HashMap< ChangeHash, Vec< ChangeHash> > ) ;
660
impl DepOracle for Deps {
661
fn depends_on ( & self , a : & ChangeHash, b : & ChangeHash) -> bool {
662
if a = = b {
663
return true ;
664
}
665
let mut stack = vec! [ * a] ;
0bdc3f91 Andy McCall 1 month ago ↶
666
let mut seen = HashSet:: default( ) ;
ff0af1ee unknown 1 month ago
667
while let Some ( x) = stack.pop ( ) {
668
if let Some ( ds) = self .0. get ( & x) {
669
for d in ds {
670
if d = = b {
671
return true ;
672
}
673
if seen.insert ( * d) {
674
stack.push ( * d) ;
675
}
676
}
677
}
678
}
679
false
680
}
681
}
682
683
# [ test ]
684
fn sequential_inserts_and_deletes ( ) {
685
let mut g = FileGraph:: new( ) ;
75167672 Andy McCall 1 month ago
686
g.insert (
687
h ( 1 ) ,
688
0 ,
689
Vertex:: Start,
690
Vertex:: End,
691
& lines ( & [ " a\n " , " b\n " , " c\n " ] ) ,
692
) ;
ff0af1ee unknown 1 month ago
693
let m = g.materialize ( & NoDeps) ;
694
assert! ( ! m.is_conflicted ( ) ) ;
695
assert_eq! ( text ( & g, & m) , " a\n b\n c\n " ) ;
696
697
75167672 Andy McCall 1 month ago
698
g.insert (
699
h ( 2 ) ,
700
0 ,
701
Vertex:: Atom( ( h ( 1 ) , 1 ) ) ,
702
Vertex:: Atom( ( h ( 1 ) , 2 ) ) ,
703
& lines ( & [ " x\n " ] ) ,
704
) ;
705
g.delete (
706
h ( 2 ) ,
707
& AtomRange {
708
change: h ( 1 ) ,
709
from: 0 ,
710
to: 1 ,
711
} ,
712
) ;
ff0af1ee unknown 1 month ago
713
let deps = Deps( [ ( h ( 2 ) , vec! [ h ( 1 ) ] ) ] .into_iter ( ) .collect ( ) ) ;
714
let m = g.materialize ( & deps) ;
715
assert! ( ! m.is_conflicted ( ) ) ;
716
assert_eq! ( text ( & g, & m) , " b\n x\n c\n " ) ;
717
assert_eq! ( m.live_atoms ( ) , vec! [ ( h ( 1 ) , 1 ) , ( h ( 2 ) , 0 ) , ( h ( 1 ) , 2 ) ] ) ;
718
}
719
720
# [ test ]
721
fn concurrent_inserts_at_same_anchor_conflict ( ) {
722
let mut g = FileGraph:: new( ) ;
723
g.insert ( h ( 1 ) , 0 , Vertex:: Start, Vertex:: End, & lines ( & [ " a\n " , " b\n " ] ) ) ;
75167672 Andy McCall 1 month ago
724
g.insert (
725
h ( 2 ) ,
726
0 ,
727
Vertex:: Atom( ( h ( 1 ) , 0 ) ) ,
728
Vertex:: Atom( ( h ( 1 ) , 1 ) ) ,
729
& lines ( & [ " from-2\n " ] ) ,
730
) ;
731
g.insert (
732
h ( 3 ) ,
733
0 ,
734
Vertex:: Atom( ( h ( 1 ) , 0 ) ) ,
735
Vertex:: Atom( ( h ( 1 ) , 1 ) ) ,
736
& lines ( & [ " from-3\n " , " more-3\n " ] ) ,
737
) ;
738
let deps = Deps(
739
[ ( h ( 2 ) , vec! [ h ( 1 ) ] ) , ( h ( 3 ) , vec! [ h ( 1 ) ] ) ]
740
.into_iter ( )
741
.collect ( ) ,
742
) ;
ff0af1ee unknown 1 month ago
743
let m = g.materialize ( & deps) ;
744
assert! ( m.is_conflicted ( ) ) ;
745
assert_eq! ( m.conflict_count ( ) , 1 ) ;
746
assert_eq! (
747
m.segments,
748
vec! [
749
Segment:: Lines( vec! [ ( h ( 1 ) , 0 ) ] ) ,
750
Segment:: Conflict( vec! [ vec! [ ( h ( 2 ) , 0 ) ] , vec! [ ( h ( 3 ) , 0 ) , ( h ( 3 ) , 1 ) ] ] ) ,
751
Segment:: Lines( vec! [ ( h ( 1 ) , 1 ) ] ) ,
752
]
753
) ;
754
let t = text ( & g, & m) ;
755
assert! ( t.starts_with ( " a\n <<<<<<< oxy conflict 1 of 1, side 1 of 2: change 0202\n from-2\n ======= oxy conflict 1 of 1, side 2 of 2: change 0303\n from-3\n more-3\n >>>>>>> oxy conflict 1 of 1 end\n b\n " ) , " {t}" ) ;
756
}
757
758
# [ test ]
759
fn concurrent_inserts_at_different_anchors_do_not_conflict ( ) {
760
let mut g = FileGraph:: new( ) ;
75167672 Andy McCall 1 month ago
761
g.insert (
762
h ( 1 ) ,
763
0 ,
764
Vertex:: Start,
765
Vertex:: End,
766
& lines ( & [ " a\n " , " b\n " , " c\n " ] ) ,
767
) ;
768
g.insert (
769
h ( 2 ) ,
770
0 ,
771
Vertex:: Start,
772
Vertex:: Atom( ( h ( 1 ) , 0 ) ) ,
773
& lines ( & [ " top\n " ] ) ,
774
) ;
775
g.insert (
776
h ( 3 ) ,
777
0 ,
778
Vertex:: Atom( ( h ( 1 ) , 2 ) ) ,
779
Vertex:: End,
780
& lines ( & [ " bottom\n " ] ) ,
781
) ;
782
let deps = Deps(
783
[ ( h ( 2 ) , vec! [ h ( 1 ) ] ) , ( h ( 3 ) , vec! [ h ( 1 ) ] ) ]
784
.into_iter ( )
785
.collect ( ) ,
786
) ;
ff0af1ee unknown 1 month ago
787
let m = g.materialize ( & deps) ;
788
assert! ( ! m.is_conflicted ( ) ) ;
789
assert_eq! ( text ( & g, & m) , " top\n a\n b\n c\n bottom\n " ) ;
790
}
791
792
# [ test ]
793
fn concurrent_deletes_of_the_same_line_agree ( ) {
794
let mut g = FileGraph:: new( ) ;
795
g.insert ( h ( 1 ) , 0 , Vertex:: Start, Vertex:: End, & lines ( & [ " a\n " , " b\n " ] ) ) ;
75167672 Andy McCall 1 month ago
796
g.delete (
797
h ( 2 ) ,
798
& AtomRange {
799
change: h ( 1 ) ,
800
from: 0 ,
801
to: 1 ,
802
} ,
803
) ;
804
g.delete (
805
h ( 3 ) ,
806
& AtomRange {
807
change: h ( 1 ) ,
808
from: 0 ,
809
to: 1 ,
810
} ,
811
) ;
812
let deps = Deps(
813
[ ( h ( 2 ) , vec! [ h ( 1 ) ] ) , ( h ( 3 ) , vec! [ h ( 1 ) ] ) ]
814
.into_iter ( )
815
.collect ( ) ,
816
) ;
ff0af1ee unknown 1 month ago
817
let m = g.materialize ( & deps) ;
818
assert! ( ! m.is_conflicted ( ) ) ;
819
assert_eq! ( text ( & g, & m) , " b\n " ) ;
820
}
821
822
# [ test ]
823
fn delete_insert_conflict_is_a_zombie ( ) {
824
let mut g = FileGraph:: new( ) ;
825
g.insert ( h ( 1 ) , 0 , Vertex:: Start, Vertex:: End, & lines ( & [ " a\n " , " b\n " ] ) ) ;
826
75167672 Andy McCall 1 month ago
827
g.delete (
828
h ( 2 ) ,
829
& AtomRange {
830
change: h ( 1 ) ,
831
from: 0 ,
832
to: 1 ,
833
} ,
834
) ;
835
g.insert (
836
h ( 3 ) ,
837
0 ,
838
Vertex:: Atom( ( h ( 1 ) , 0 ) ) ,
839
Vertex:: Atom( ( h ( 1 ) , 1 ) ) ,
840
& lines ( & [ " after-a\n " ] ) ,
841
) ;
842
let deps = Deps(
843
[ ( h ( 2 ) , vec! [ h ( 1 ) ] ) , ( h ( 3 ) , vec! [ h ( 1 ) ] ) ]
844
.into_iter ( )
845
.collect ( ) ,
846
) ;
ff0af1ee unknown 1 month ago
847
let m = g.materialize ( & deps) ;
848
assert! ( m.is_conflicted ( ) ) ;
849
assert_eq! (
850
m.segments,
851
vec! [
75167672 Andy McCall 1 month ago
852
Segment:: Zombie {
853
deleted: vec! [ ( h ( 1 ) , 0 ) ] ,
854
inserted: vec! [ ( h ( 3 ) , 0 ) ]
855
} ,
ff0af1ee unknown 1 month ago
856
Segment:: Lines( vec! [ ( h ( 1 ) , 1 ) ] ) ,
857
]
858
) ;
859
let t = text ( & g, & m) ;
860
assert! ( t.contains ( " -------\n a\n =======" ) , " {t}" ) ;
861
assert! ( t.contains ( " after-a\n >>>>>>>" ) , " {t}" ) ;
862
863
75167672 Andy McCall 1 month ago
864
let deps = Deps(
865
[ ( h ( 2 ) , vec! [ h ( 1 ) , h ( 3 ) ] ) , ( h ( 3 ) , vec! [ h ( 1 ) ] ) ]
866
.into_iter ( )
867
.collect ( ) ,
868
) ;
ff0af1ee unknown 1 month ago
869
let m = g.materialize ( & deps) ;
870
assert! ( ! m.is_conflicted ( ) ) ;
871
assert_eq! ( text ( & g, & m) , " after-a\n b\n " ) ;
872
}
873
874
# [ test ]
23b217d7 Andy McCall 1 month ago ↶
875
fn insert_after_a_line_deleted_by_a_dependency_is_not_a_conflict ( ) {
876
877
878
let mut g = FileGraph:: new( ) ;
879
g.insert ( h ( 1 ) , 0 , Vertex:: Start, Vertex:: End, & lines ( & [ " a\n " , " b\n " ] ) ) ;
75167672 Andy McCall 1 month ago
880
g.delete (
881
h ( 2 ) ,
882
& AtomRange {
883
change: h ( 1 ) ,
884
from: 1 ,
885
to: 2 ,
886
} ,
887
) ;
888
g.insert (
889
h ( 3 ) ,
890
0 ,
891
Vertex:: Atom( ( h ( 1 ) , 1 ) ) ,
892
Vertex:: End,
893
& lines ( & [ " c\n " ] ) ,
894
) ;
895
let deps = Deps(
896
[ ( h ( 2 ) , vec! [ h ( 1 ) ] ) , ( h ( 3 ) , vec! [ h ( 2 ) ] ) ]
897
.into_iter ( )
898
.collect ( ) ,
899
) ;
23b217d7 Andy McCall 1 month ago ↶
900
let m = g.materialize ( & deps) ;
901
assert! ( ! m.is_conflicted ( ) , " {:?}" , m.segments) ;
902
assert_eq! ( text ( & g, & m) , " a\n c\n " ) ;
903
904
905
let mut g = FileGraph:: new( ) ;
906
g.insert ( h ( 1 ) , 0 , Vertex:: Start, Vertex:: End, & lines ( & [ " x\n " ] ) ) ;
75167672 Andy McCall 1 month ago
907
g.delete (
908
h ( 1 ) ,
909
& AtomRange {
910
change: h ( 1 ) ,
911
from: 0 ,
912
to: 1 ,
913
} ,
914
) ;
915
g.insert (
916
h ( 2 ) ,
917
0 ,
918
Vertex:: Atom( ( h ( 1 ) , 0 ) ) ,
919
Vertex:: End,
920
& lines ( & [ " y\n " ] ) ,
921
) ;
23b217d7 Andy McCall 1 month ago ↶
922
let deps = Deps( [ ( h ( 2 ) , vec! [ h ( 1 ) ] ) ] .into_iter ( ) .collect ( ) ) ;
923
let m = g.materialize ( & deps) ;
924
assert! ( ! m.is_conflicted ( ) ) ;
925
assert_eq! ( text ( & g, & m) , " y\n " ) ;
926
}
927
928
# [ test ]
ff0af1ee unknown 1 month ago
929
fn resolution_collapses_a_conflict ( ) {
930
let mut g = FileGraph:: new( ) ;
931
g.insert ( h ( 1 ) , 0 , Vertex:: Start, Vertex:: End, & lines ( & [ " a\n " ] ) ) ;
75167672 Andy McCall 1 month ago
932
g.insert (
933
h ( 2 ) ,
934
0 ,
935
Vertex:: Start,
936
Vertex:: Atom( ( h ( 1 ) , 0 ) ) ,
937
& lines ( & [ " x\n " ] ) ,
938
) ;
939
g.insert (
940
h ( 3 ) ,
941
0 ,
942
Vertex:: Start,
943
Vertex:: Atom( ( h ( 1 ) , 0 ) ) ,
944
& lines ( & [ " y\n " ] ) ,
945
) ;
946
let mut deps: HashMap< _ , _ > = [ ( h ( 2 ) , vec! [ h ( 1 ) ] ) , ( h ( 3 ) , vec! [ h ( 1 ) ] ) ]
947
.into_iter ( )
948
.collect ( ) ;
ff0af1ee unknown 1 month ago
949
assert! ( g.materialize ( & Deps( deps.clone ( ) ) ) .is_conflicted ( ) ) ;
950
75167672 Andy McCall 1 month ago
951
g.delete (
952
h ( 4 ) ,
953
& AtomRange {
954
change: h ( 2 ) ,
955
from: 0 ,
956
to: 1 ,
957
} ,
958
) ;
ff0af1ee unknown 1 month ago
959
deps.insert ( h ( 4 ) , vec! [ h ( 2 ) , h ( 3 ) ] ) ;
960
let m = g.materialize ( & Deps( deps) ) ;
961
assert! ( ! m.is_conflicted ( ) ) ;
962
assert_eq! ( text ( & g, & m) , " y\n a\n " ) ;
963
}
964
965
# [ test ]
966
fn empty_and_unterminated_files ( ) {
967
let g = FileGraph:: new( ) ;
968
let m = g.materialize ( & NoDeps) ;
969
assert_eq! ( m.segments, vec! [ ] ) ;
970
assert_eq! ( text ( & g, & m) , " " ) ;
971
let mut g = FileGraph:: new( ) ;
972
g.insert ( h ( 1 ) , 0 , Vertex:: Start, Vertex:: End, & lines ( & [ " no newline" ] ) ) ;
973
assert_eq! ( text ( & g, & g.materialize ( & NoDeps) ) , " no newline" ) ;
974
}
975
252e8cb2 Andy McCall 1 month ago ↶
976
# [ test ]
977
fn serialization_round_trip ( ) {
978
let mut g = FileGraph:: new( ) ;
979
g.insert ( h ( 1 ) , 0 , Vertex:: Start, Vertex:: End, & lines ( & [ " a\n " , " b\n " ] ) ) ;
75167672 Andy McCall 1 month ago
980
g.insert (
981
h ( 2 ) ,
982
0 ,
983
Vertex:: Atom( ( h ( 1 ) , 0 ) ) ,
984
Vertex:: Atom( ( h ( 1 ) , 1 ) ) ,
985
& lines ( & [ " x\n " ] ) ,
986
) ;
987
g.delete (
988
h ( 3 ) ,
989
& AtomRange {
990
change: h ( 1 ) ,
991
from: 1 ,
992
to: 2 ,
993
} ,
994
) ;
252e8cb2 Andy McCall 1 month ago ↶
995
let v = g.to_value ( ) ;
996
let bytes = crate :: cbor:: encode( & v) ;
997
let back = FileGraph:: from_value( & crate :: cbor:: decode( & bytes) .unwrap ( ) ) .unwrap ( ) ;
75167672 Andy McCall 1 month ago
998
let deps = Deps(
999
[ ( h ( 2 ) , vec! [ h ( 1 ) ] ) , ( h ( 3 ) , vec! [ h ( 1 ) ] ) ]
1000
.into_iter ( )
1001
.collect ( ) ,
1002
) ;
252e8cb2 Andy McCall 1 month ago ↶
1003
assert_eq! ( back.materialize ( & deps) , g.materialize ( & deps) ) ;
1004
assert_eq! ( text ( & back, & back.materialize ( & deps) ) , " a\n x\n " ) ;
75167672 Andy McCall 1 month ago
1005
assert_eq! (
1006
crate :: cbor:: encode( & back.to_value ( ) ) ,
1007
bytes,
1008
" deterministic"
1009
) ;
252e8cb2 Andy McCall 1 month ago ↶
1010
}
1011
ff0af1ee unknown 1 month ago
1012
1013
# [ test ]
1014
fn application_order_is_irrelevant ( ) {
1015
use rand:: seq:: SliceRandom;
75167672 Andy McCall 1 month ago
1016
type Op = (
1017
ChangeHash,
1018
Vec< ( u32 , Vertex, Vertex, Vec< Vec< u8 > > ) > ,
1019
Vec< AtomRange> ,
1020
) ;
ff0af1ee unknown 1 month ago
1021
let ops: Vec< Op> = vec! [
75167672 Andy McCall 1 month ago
1022
(
1023
h ( 1 ) ,
1024
vec! [ ( 0 , Vertex:: Start, Vertex:: End, lines ( & [ " a\n " , " b\n " , " c\n " ] ) ) ] ,
1025
vec! [ ] ,
1026
) ,
1027
(
1028
h ( 2 ) ,
1029
vec! [ (
1030
0 ,
1031
Vertex:: Atom( ( h ( 1 ) , 0 ) ) ,
1032
Vertex:: Atom( ( h ( 1 ) , 1 ) ) ,
1033
lines ( & [ " p\n " ] ) ,
1034
) ] ,
1035
vec! [ ] ,
1036
) ,
1037
(
1038
h ( 3 ) ,
1039
vec! [ (
1040
0 ,
1041
Vertex:: Atom( ( h ( 1 ) , 0 ) ) ,
1042
Vertex:: Atom( ( h ( 1 ) , 1 ) ) ,
1043
lines ( & [ " q\n " , " r\n " ] ) ,
1044
) ] ,
1045
vec! [ AtomRange {
1046
change: h ( 1 ) ,
1047
from: 2 ,
1048
to: 3 ,
1049
} ] ,
1050
) ,
1051
(
1052
h ( 4 ) ,
1053
vec! [ (
1054
0 ,
1055
Vertex:: Atom( ( h ( 3 ) , 1 ) ) ,
1056
Vertex:: Atom( ( h ( 1 ) , 1 ) ) ,
1057
lines ( & [ " s\n " ] ) ,
1058
) ] ,
1059
vec! [ AtomRange {
1060
change: h ( 1 ) ,
1061
from: 1 ,
1062
to: 2 ,
1063
} ] ,
1064
) ,
1065
(
1066
h ( 5 ) ,
1067
vec! [ ( 0 , Vertex:: Atom( ( h ( 1 ) , 2 ) ) , Vertex:: End, lines ( & [ " z\n " ] ) ) ] ,
1068
vec! [ ] ,
1069
) ,
ff0af1ee unknown 1 month ago
1070
] ;
1071
let deps = Deps(
75167672 Andy McCall 1 month ago
1072
[
1073
( h ( 2 ) , vec! [ h ( 1 ) ] ) ,
1074
( h ( 3 ) , vec! [ h ( 1 ) ] ) ,
1075
( h ( 4 ) , vec! [ h ( 3 ) , h ( 1 ) ] ) ,
1076
( h ( 5 ) , vec! [ h ( 1 ) ] ) ,
1077
]
1078
.into_iter ( )
1079
.collect ( ) ,
ff0af1ee unknown 1 month ago
1080
) ;
1081
let build = | order : & [usize ]| {
1082
let mut g = FileGraph:: new( ) ;
1083
for & i in order {
1084
let ( c, ins, dels) = & ops[ i] ;
1085
for ( idx, a, b, l) in ins {
1086
g.insert ( * c, * idx, * a, * b, l) ;
1087
}
1088
for d in dels {
1089
g.delete ( * c, d) ;
1090
}
1091
}
1092
let m = g.materialize ( & deps) ;
1093
( m.clone ( ) , text ( & g, & m) )
1094
} ;
1095
let reference = build ( & [ 0 , 1 , 2 , 3 , 4 ] ) ;
1096
assert! ( reference.0. is_conflicted ( ) ) ;
1097
let mut rng = rand:: thread_rng( ) ;
1098
let mut order: Vec< usize > = ( 0 .. ops.len ( ) ) .collect ( ) ;
1099
for _ in 0 .. 50 {
1100
order.shuffle ( & mut rng) ;
1101
assert_eq! ( build ( & order) , reference, " order {order:?}" ) ;
1102
}
1103
}
1104
}