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;
01a47cd7 Andy McCall 1 month ago ↶
9
use crate :: lines:: { LineRef, Lines} ;
ff0af1ee unknown 1 month ago
10
use crate :: objects:: AtomRange;
0bdc3f91 Andy McCall 1 month ago ↶
11
use std:: collections:: { BTreeMap, BTreeSet} ;
ff0af1ee unknown 1 month ago
12
13
14
pub type AtomKey = ( ChangeHash, u32 ) ;
15
16
# [ derive ( Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Debug ) ]
17
pub enum Vertex {
18
Start,
19
Atom( AtomKey) ,
20
End,
21
}
22
23
24
pub trait DepOracle {
25
fn depends_on ( & self , a : & ChangeHash, b : & ChangeHash) -> bool ;
26
}
27
28
29
pub struct NoDeps ;
30
impl DepOracle for NoDeps {
31
fn depends_on ( & self , a : & ChangeHash, b : & ChangeHash) -> bool {
32
a = = b
33
}
34
}
35
36
# [ derive ( Default, Clone, Debug ) ]
37
struct AtomInfo {
01a47cd7 Andy McCall 1 month ago ↶
38
39
40
content : Option< LineRef> ,
ff0af1ee unknown 1 month ago
41
deleted_by : BTreeSet< ChangeHash> ,
42
}
43
44
# [ derive ( Clone, Debug ) ]
45
pub struct FileGraph {
46
atoms : HashMap< AtomKey, AtomInfo> ,
0bdc3f91 Andy McCall 1 month ago ↶
47
48
succ : HashMap< Vertex, Edges> ,
49
}
50
51
52
53
# [ derive ( Clone, Debug, Default, PartialEq, Eq ) ]
54
enum Edges {
55
# [ default ]
56
None ,
57
One( Vertex) ,
58
Many( Vec< Vertex> ) ,
ff0af1ee unknown 1 month ago
59
}
60
0bdc3f91 Andy McCall 1 month ago ↶
61
impl Edges {
62
fn insert ( & mut self , v : Vertex) {
63
match self {
64
Edges:: None => * self = Edges:: One( v) ,
65
Edges:: One( a) if * a = = v => { }
66
Edges:: One( a) => {
67
let a = * a;
68
* self = Edges:: Many( if a < v { vec! [ a, v] } else { vec! [ v, a] } ) ;
69
}
70
Edges:: Many( list) => {
71
if let Err ( i) = list.binary_search ( & v) {
72
list.insert ( i, v) ;
73
}
74
}
75
}
76
}
77
78
fn as_slice ( & self ) -> & [ Vertex] {
79
match self {
80
Edges:: None => & [ ] ,
81
Edges:: One( v) => std:: slice:: from_ref( v) ,
82
Edges:: Many( list) => list,
83
}
84
}
85
86
fn iter ( & self ) -> std:: slice:: Iter< '_ , Vertex> {
87
self .as_slice ( ) .iter ( )
88
}
89
}
90
ff0af1ee unknown 1 month ago
91
impl Default for FileGraph {
92
fn default ( ) -> Self {
93
Self :: new( )
94
}
95
}
96
97
98
# [ derive ( Clone, Debug, PartialEq, Eq ) ]
99
pub enum Segment {
100
Lines( Vec< AtomKey> ) ,
101
102
Conflict( Vec< Vec< AtomKey> > ) ,
103
104
75167672 Andy McCall 1 month ago
105
Zombie {
106
deleted: Vec< AtomKey> ,
107
inserted: Vec< AtomKey> ,
108
} ,
ff0af1ee unknown 1 month ago
109
}
110
111
# [ derive ( Clone, Debug, PartialEq, Eq, Default ) ]
112
pub struct Materialized {
113
pub segments : Vec< Segment> ,
114
}
115
116
impl Materialized {
117
pub fn is_conflicted ( & self ) -> bool {
75167672 Andy McCall 1 month ago
118
self .segments
119
.iter ( )
120
.any ( | s | ! matches! ( s, Segment:: Lines( _ ) ) )
ff0af1ee unknown 1 month ago
121
}
122
pub fn conflict_count ( & self ) -> usize {
75167672 Andy McCall 1 month ago
123
self .segments
124
.iter ( )
125
.filter ( | s | ! matches! ( s, Segment:: Lines( _ ) ) )
126
.count ( )
ff0af1ee unknown 1 month ago
127
}
128
129
pub fn live_atoms ( & self ) -> Vec< AtomKey> {
130
let mut out = Vec :: new( ) ;
131
for s in & self .segments {
132
match s {
133
Segment:: Lines( a) => out.extend ( a) ,
134
Segment:: Conflict( sides) => sides.iter ( ) .for_each ( | x | out.extend ( x) ) ,
135
Segment:: Zombie { inserted, .. } => out.extend ( inserted) ,
136
}
137
}
138
out
139
}
140
}
141
142
impl FileGraph {
143
pub fn new ( ) -> Self {
75167672 Andy McCall 1 month ago
144
let mut g = FileGraph {
0bdc3f91 Andy McCall 1 month ago ↶
145
atoms: HashMap:: default( ) ,
146
succ: HashMap:: default( ) ,
75167672 Andy McCall 1 month ago
147
} ;
ff0af1ee unknown 1 month ago
148
g.edge ( Vertex:: Start, Vertex:: End) ;
149
g
150
}
151
152
fn edge ( & mut self , a : Vertex, b : Vertex) {
153
self .succ.entry ( a) .or_default ( ) .insert ( b) ;
154
self .succ.entry ( b) .or_default ( ) ;
155
}
156
157
75167672 Andy McCall 1 month ago
158
pub fn insert (
159
& mut self ,
160
change : ChangeHash,
161
first_index : u32 ,
162
after : Vertex,
163
before : Vertex,
01a47cd7 Andy McCall 1 month ago ↶
164
lines : & Lines,
0bdc3f91 Andy McCall 1 month ago ↶
165
) {
01a47cd7 Andy McCall 1 month ago ↶
166
self .insert_owned ( change, first_index, after, before, lines.clone ( ) ) ;
0bdc3f91 Andy McCall 1 month ago ↶
167
}
168
169
170
171
172
pub fn insert_owned (
173
& mut self ,
174
change : ChangeHash,
175
first_index : u32 ,
176
after : Vertex,
177
before : Vertex,
01a47cd7 Andy McCall 1 month ago ↶
178
lines : Lines,
75167672 Andy McCall 1 month ago
179
) {
0bdc3f91 Andy McCall 1 month ago ↶
180
self .atoms.reserve ( lines.len ( ) ) ;
181
self .succ.reserve ( lines.len ( ) + 1 ) ;
ff0af1ee unknown 1 month ago
182
let mut prev = after;
01a47cd7 Andy McCall 1 month ago ↶
183
for i in 0 .. lines.len ( ) {
ff0af1ee unknown 1 month ago
184
let key = ( change, first_index + i as u32 ) ;
01a47cd7 Andy McCall 1 month ago ↶
185
self .atoms.entry ( key) .or_default ( ) .content =
186
Some ( LineRef:: new( lines.clone ( ) , i as u32 ) ) ;
ff0af1ee unknown 1 month ago
187
let v = Vertex:: Atom( key) ;
0bdc3f91 Andy McCall 1 month ago ↶
188
self .succ.entry ( prev) .or_default ( ) .insert ( v) ;
ff0af1ee unknown 1 month ago
189
prev = v;
190
}
0bdc3f91 Andy McCall 1 month ago ↶
191
self .succ.entry ( prev) .or_default ( ) .insert ( before) ;
192
self .succ.entry ( before) .or_default ( ) ;
ff0af1ee unknown 1 month ago
193
}
194
195
196
pub fn delete ( & mut self , by : ChangeHash, range : & AtomRange) {
197
for i in range.from.. range.to {
75167672 Andy McCall 1 month ago
198
self .atoms
199
.entry ( ( range.change, i) )
200
.or_default ( )
201
.deleted_by
202
.insert ( by) ;
ff0af1ee unknown 1 month ago
203
}
204
}
205
206
pub fn is_live ( & self , k : & AtomKey) -> bool {
75167672 Andy McCall 1 month ago
207
self .atoms
208
.get ( k)
209
.is_some_and ( | a | a.content.is_some ( ) & & a.deleted_by.is_empty ( ) )
ff0af1ee unknown 1 month ago
210
}
211
212
pub fn content ( & self , k : & AtomKey) -> Option< & [ u8 ] > {
01a47cd7 Andy McCall 1 month ago ↶
213
self .atoms
214
.get ( k)
215
.and_then ( | a | a.content.as_ref ( ) .map ( LineRef:: bytes) )
ff0af1ee unknown 1 month ago
216
}
217
218
pub fn deleters ( & self , k : & AtomKey) -> impl Iterator< Item = & ChangeHash> {
75167672 Andy McCall 1 month ago
219
self .atoms
220
.get ( k)
221
.into_iter ( )
222
.flat_map ( | a | a.deleted_by.iter ( ) )
ff0af1ee unknown 1 month ago
223
}
224
225
226
pub fn atom_count ( & self ) -> usize {
227
self .atoms.len ( )
228
}
229
62794ecc Andy McCall 1 month ago ↶
230
231
232
83d61e80 Andy McCall 1 month ago ↶
233
234
235
236
237
pub fn between ( & self , first : & AtomKey, last : & AtomKey) -> Vec< AtomKey> {
238
let ( from, to) = ( Vertex:: Atom( * first) , Vertex:: Atom( * last) ) ;
239
let mut ahead: HashSet< Vertex> = HashSet:: default( ) ;
240
let mut stack = vec! [ from] ;
241
while let Some ( v) = stack.pop ( ) {
242
if ahead.insert ( v) {
243
stack.extend (
244
self .succ
245
.get ( & v)
246
.map ( | e | e.as_slice ( ) )
247
.unwrap_or ( & [ ] )
248
.iter ( )
249
.copied ( ) ,
250
) ;
251
}
252
}
253
if ! ahead.contains ( & to) {
254
return vec! [ ] ;
255
}
256
let mut pred: HashMap< Vertex, Vec< Vertex> > = HashMap:: default( ) ;
257
for ( u, es) in & self .succ {
258
if ahead.contains ( u) {
259
for w in es.iter ( ) {
260
pred.entry ( * w) .or_default ( ) .push ( * u) ;
261
}
262
}
263
}
264
let mut behind: HashSet< Vertex> = HashSet:: default( ) ;
265
let mut stack = vec! [ to] ;
266
while let Some ( v) = stack.pop ( ) {
267
if behind.insert ( v) {
268
stack.extend ( pred.get ( & v) .into_iter ( ) .flatten ( ) .copied ( ) ) ;
269
}
270
}
271
let mut out: Vec< AtomKey> = ahead
272
.intersection ( & behind)
273
.filter_map ( | v | match v {
274
Vertex:: Atom( k) if self .atoms.contains_key ( k) => Some ( * k) ,
275
_ => None ,
276
} )
277
.collect ( ) ;
278
out.sort ( ) ;
279
out
280
}
281
282
283
pub fn touching ( & self , atoms : & [AtomKey]) -> BTreeSet< ChangeHash> {
284
let mut out = BTreeSet:: new( ) ;
285
for k in atoms {
286
out.insert ( k.0 ) ;
287
if let Some ( info) = self .atoms.get ( k) {
288
out.extend ( info.deleted_by.iter ( ) .copied ( ) ) ;
289
}
290
}
291
out
292
}
293
62794ecc Andy McCall 1 month ago ↶
294
pub fn live_successor_changes ( & self , k : & AtomKey) -> Vec< ChangeHash> {
295
self .succ
296
.get ( & Vertex:: Atom( * k) )
297
.map ( | ss | {
298
ss.iter ( )
299
.filter_map ( | s | match s {
300
Vertex:: Atom( y) if self .is_live ( y) => Some ( y.0 ) ,
301
_ => None ,
302
} )
303
.collect ( )
304
} )
305
.unwrap_or_default ( )
306
}
307
ff0af1ee unknown 1 month ago
308
fn is_ghost ( & self , k : & AtomKey, info : & AtomInfo, deps : & dyn DepOracle) -> bool {
23b217d7 Andy McCall 1 month ago ↶
309
310
311
312
ff0af1ee unknown 1 month ago
313
self .succ.get ( & Vertex:: Atom( * k) ) .is_some_and ( | ss | {
314
ss.iter ( ) .any ( | s | match s {
75167672 Andy McCall 1 month ago
315
Vertex:: Atom( y) if self .is_live ( y) => ! info
316
.deleted_by
317
.iter ( )
318
.any ( | d | deps.depends_on ( d, & y.0 ) | | deps.depends_on ( & y.0 , d) ) ,
ff0af1ee unknown 1 month ago
319
_ => false ,
320
} )
321
} )
322
}
323
324
75167672 Andy McCall 1 month ago
325
fn reduced_succ (
326
& self ,
327
v : Vertex,
328
kept : & HashSet< Vertex> ,
329
memo : & mut HashMap< Vertex, BTreeSet< Vertex> > ,
330
) -> BTreeSet< Vertex> {
598009a7 Andy McCall 1 month ago ↶
331
let direct = self .succ.get ( & v) .map ( Edges:: as_slice) .unwrap_or_default ( ) ;
332
333
334
if direct.iter ( ) .all ( | s | kept.contains ( s) ) {
335
return direct.iter ( ) .copied ( ) .collect ( ) ;
336
}
ff0af1ee unknown 1 month ago
337
let mut out = BTreeSet:: new( ) ;
598009a7 Andy McCall 1 month ago ↶
338
let mut stack: Vec< Vertex> = direct.to_vec ( ) ;
0bdc3f91 Andy McCall 1 month ago ↶
339
let mut seen = HashSet:: default( ) ;
ff0af1ee unknown 1 month ago
340
while let Some ( s) = stack.pop ( ) {
341
if kept.contains ( & s) {
342
out.insert ( s) ;
343
} else if seen.insert ( s) {
344
if let Some ( m) = memo.get ( & s) {
345
out.extend ( m.iter ( ) .copied ( ) ) ;
346
} else if let Some ( next) = self .succ.get ( & s) {
347
stack.extend ( next.iter ( ) .copied ( ) ) ;
348
}
349
}
350
}
351
out
598009a7 Andy McCall 1 month ago ↶
352
}
353
354
355
356
357
358
359
fn as_chain ( & self ) -> Option< Vec< AtomKey> > {
360
if self
361
.atoms
362
.values ( )
363
.any ( | a | a.content.is_none ( ) | | ! a.deleted_by.is_empty ( ) )
364
{
365
return None ;
366
}
367
let mut order = Vec :: with_capacity( self .atoms.len ( ) ) ;
368
let mut v = Vertex:: Start;
369
loop {
370
let succ = self .succ.get ( & v) ? .as_slice ( ) ;
371
let mut atoms = succ.iter ( ) .filter ( | s | * * s ! = Vertex:: End) ;
372
match ( atoms.next ( ) , atoms.next ( ) ) {
373
( None , _ ) if succ.contains ( & Vertex:: End) => break ,
374
( Some ( Vertex:: Atom( k) ) , None ) if order.len ( ) < self .atoms.len ( ) => {
375
order.push ( * k) ;
376
v = Vertex:: Atom( * k) ;
377
}
378
_ => return None ,
379
}
380
}
381
( order.len ( ) = = self .atoms.len ( ) ) .then_some ( order)
ff0af1ee unknown 1 month ago
382
}
383
384
385
pub fn materialize ( & self , deps : & dyn DepOracle) -> Materialized {
e83354cd Andy McCall 1 month ago ↶
386
if let Some ( order) = self .as_chain ( ) .or_else ( | | self .as_order ( deps) ) {
598009a7 Andy McCall 1 month ago ↶
387
return Materialized {
388
segments: if order.is_empty ( ) {
389
vec! [ ]
390
} else {
391
vec! [ Segment:: Lines( order) ]
392
} ,
393
} ;
394
}
395
self .materialize_general ( deps)
396
}
397
e83354cd Andy McCall 1 month ago ↶
398
399
400
401
402
403
404
fn as_order ( & self , deps : & dyn DepOracle) -> Option< Vec< AtomKey> > {
405
406
let mut index: HashMap< Vertex, u32 > =
407
HashMap:: with_capacity_and_hasher( self .atoms.len ( ) + 2 , Default :: default( ) ) ;
408
let mut verts: Vec< Vertex> = Vec :: with_capacity( self .atoms.len ( ) + 2 ) ;
409
verts.extend ( [ Vertex:: Start, Vertex:: End] ) ;
410
index.insert ( Vertex:: Start, 0 ) ;
411
index.insert ( Vertex:: End, 1 ) ;
412
for ( k, info) in & self .atoms {
413
if info.content.is_none ( ) {
414
continue ;
415
}
416
if info.deleted_by.is_empty ( ) {
417
index.insert ( Vertex:: Atom( * k) , verts.len ( ) as u32 ) ;
418
verts.push ( Vertex:: Atom( * k) ) ;
419
} else if self .is_ghost ( k, info, deps) {
420
return None ;
421
}
422
}
423
424
425
let mut reach: HashMap< Vertex, Vec< u32 > > = HashMap:: default( ) ;
426
let contracted = | v : Vertex, reach : & mut HashMap< Vertex, Vec< u32 > > | {
427
let mut stack: Vec< ( Vertex, bool ) > = vec! [ ( v, false ) ] ;
428
while let Some ( ( x, done) ) = stack.pop ( ) {
429
if reach.contains_key ( & x) {
430
continue ;
431
}
432
let next = self .succ.get ( & x) .map ( Edges:: as_slice) .unwrap_or_default ( ) ;
433
if ! done {
434
stack.push ( ( x, true ) ) ;
435
for s in next {
436
if ! index.contains_key ( s) & & ! reach.contains_key ( s) {
437
stack.push ( ( * s, false ) ) ;
438
}
439
}
440
continue ;
441
}
442
let mut out: Vec< u32 > = Vec :: new( ) ;
443
for s in next {
444
match index.get ( s) {
445
Some ( i) => out.push ( * i) ,
446
None => out.extend ( reach.get ( s) .into_iter ( ) .flatten ( ) ) ,
447
}
448
}
449
out.sort_unstable ( ) ;
450
out.dedup ( ) ;
451
reach.insert ( x, out) ;
452
}
453
} ;
454
455
let mut succ: Vec< u32 > = Vec :: with_capacity( verts.len ( ) * 2 ) ;
456
let mut starts: Vec< usize > = Vec :: with_capacity( verts.len ( ) + 1 ) ;
457
let mut indeg: Vec< u32 > = vec! [ 0 ; verts.len ( ) ] ;
458
for v in & verts {
459
let from = succ.len ( ) ;
460
starts.push ( from) ;
461
for s in self .succ.get ( v) .map ( Edges:: as_slice) .unwrap_or_default ( ) {
462
match index.get ( s) {
463
Some ( i) => succ.push ( * i) ,
464
None => {
465
contracted ( * s, & mut reach) ;
466
succ.extend ( reach.get ( s) .into_iter ( ) .flatten ( ) ) ;
467
}
468
}
469
}
470
if succ.len ( ) - from > 1 {
471
succ[ from.. ] .sort_unstable ( ) ;
472
let mut kept = from;
473
for i in from.. succ.len ( ) {
474
if i = = from | | succ[ i] ! = succ[ kept - 1 ] {
475
succ[ kept] = succ[ i] ;
476
kept + = 1 ;
477
}
478
}
479
succ.truncate ( kept) ;
480
}
481
for & w in & succ[ from.. ] {
482
indeg[ w as usize ] + = 1 ;
483
}
484
}
485
starts.push ( succ.len ( ) ) ;
486
487
let mut ready: Vec< u32 > = ( 0 .. verts.len ( ) as u32 )
488
.filter ( | & i | indeg[ i as usize ] = = 0 )
489
.collect ( ) ;
490
let mut order: Vec< AtomKey> = Vec :: with_capacity( verts.len ( ) ) ;
491
let mut seen = 0 ;
492
while let Some ( v) = ready.pop ( ) {
493
if ! ready.is_empty ( ) {
494
return None ;
495
}
496
seen + = 1 ;
497
if let Vertex:: Atom( k) = verts[ v as usize ] {
498
order.push ( k) ;
499
}
500
for & w in & succ[ starts[ v as usize ] .. starts[ v as usize + 1 ] ] {
501
indeg[ w as usize ] - = 1 ;
502
if indeg[ w as usize ] = = 0 {
503
ready.push ( w) ;
504
}
505
}
506
if ready.len ( ) > 1 {
507
return None ;
508
}
509
}
510
( seen = = verts.len ( ) ) .then_some ( order)
511
}
512
598009a7 Andy McCall 1 month ago ↶
513
514
515
fn materialize_general ( & self , deps : & dyn DepOracle) -> Materialized {
ff0af1ee unknown 1 month ago
516
0bdc3f91 Andy McCall 1 month ago ↶
517
let mut kept: HashSet< Vertex> = HashSet:: default( ) ;
518
let mut ghosts: HashSet< AtomKey> = HashSet:: default( ) ;
ff0af1ee unknown 1 month ago
519
kept.insert ( Vertex:: Start) ;
520
kept.insert ( Vertex:: End) ;
521
for ( k, info) in & self .atoms {
522
if info.content.is_none ( ) {
523
continue ;
524
}
525
if info.deleted_by.is_empty ( ) {
526
kept.insert ( Vertex:: Atom( * k) ) ;
527
} else if self .is_ghost ( k, info, deps) {
528
kept.insert ( Vertex:: Atom( * k) ) ;
529
ghosts.insert ( * k) ;
530
}
531
}
532
533
0bdc3f91 Andy McCall 1 month ago ↶
534
let mut memo = HashMap:: default( ) ;
535
let mut rsucc: HashMap< Vertex, BTreeSet< Vertex> > = HashMap:: default( ) ;
536
let mut indeg: HashMap< Vertex, usize > = HashMap:: default( ) ;
ff0af1ee unknown 1 month ago
537
for & u in & kept {
538
indeg.entry ( u) .or_insert ( 0 ) ;
539
let rs = self .reduced_succ ( u, & kept, & mut memo) ;
540
for & w in & rs {
541
* indeg.entry ( w) .or_insert ( 0 ) + = 1 ;
542
}
543
rsucc.insert ( u, rs) ;
544
}
545
546
75167672 Andy McCall 1 month ago
547
let mut ready: BTreeSet< Vertex> = indeg
548
.iter ( )
549
.filter ( | ( _, &d ) | d = = 0 )
550
.map ( | ( v , _) | * v)
551
.collect ( ) ;
0bdc3f91 Andy McCall 1 month ago ↶
552
let mut side: HashMap< Vertex, u32 > = HashMap:: default( ) ;
553
let mut joins: HashSet< Vertex> = HashSet:: default( ) ;
ff0af1ee unknown 1 month ago
554
let mut next_side = 0 u32 ;
555
let mut plain: Vec< Vertex> = Vec :: new( ) ;
556
let mut conflict: Option< BTreeMap< u32 , Vec< Vertex> > > = None ;
557
let mut segments: Vec< Segment> = Vec :: new( ) ;
558
559
let flush_plain = | plain : & mut Vec< Vertex> , segments : & mut Vec< Segment> | {
75167672 Andy McCall 1 month ago
560
let atoms: Vec< AtomKey> = plain
561
.iter ( )
562
.filter_map ( | v | {
563
if let Vertex:: Atom( k) = v {
564
Some ( * k)
565
} else {
566
None
567
}
568
} )
569
.collect ( ) ;
ff0af1ee unknown 1 month ago
570
plain.clear ( ) ;
571
if ! atoms.is_empty ( ) {
572
segments.extend ( split_zombies ( atoms, & ghosts) ) ;
573
}
574
} ;
575
let finish_conflict = | c : BTreeMap< u32 , Vec< Vertex> > , segments : & mut Vec< Segment> | {
576
let sides: Vec< Vec< AtomKey> > = c
577
.into_values ( )
578
.map ( | vs | {
579
vs.into_iter ( )
580
.filter_map ( | v | match v {
581
Vertex:: Atom( k) if ! ghosts.contains ( & k) => Some ( k) ,
582
_ => None ,
583
} )
584
.collect:: < Vec< _ > > ( )
585
} )
586
.filter ( | s | ! s.is_empty ( ) )
587
.collect ( ) ;
588
match sides.len ( ) {
589
0 => { }
590
1 => segments.push ( Segment:: Lines( sides.into_iter ( ) .next ( ) .unwrap ( ) ) ) ,
591
_ => segments.push ( Segment:: Conflict( sides) ) ,
592
}
593
} ;
594
595
while ! ready.is_empty ( ) {
596
if ready.len ( ) > = 2 {
597
if conflict.is_none ( ) {
598
flush_plain ( & mut plain, & mut segments) ;
599
conflict = Some ( BTreeMap:: new( ) ) ;
600
}
601
for r in & ready {
602
if ! side.contains_key ( r) {
603
side.insert ( * r, next_side) ;
604
next_side + = 1 ;
605
}
606
}
607
}
608
let v = ready.pop_first ( ) .unwrap ( ) ;
609
let my_side = side.get ( & v) .copied ( ) ;
610
match ( & mut conflict, my_side) {
611
( Some ( c) , Some ( s) ) => c.entry ( s) .or_default ( ) .push ( v) ,
612
( Some ( _ ) , None ) => {
613
let c = conflict.take ( ) .unwrap ( ) ;
614
finish_conflict ( c, & mut segments) ;
615
plain.push ( v) ;
616
}
617
( None , _ ) => plain.push ( v) ,
618
}
619
for & w in & rsucc[ & v] {
620
let d = indeg.get_mut ( & w) .unwrap ( ) ;
621
* d - = 1 ;
622
if conflict.is_some ( ) & & ! joins.contains ( & w) {
623
match ( my_side, side.get ( & w) .copied ( ) ) {
624
( Some ( s) , None ) => {
625
side.insert ( w, s) ;
626
}
627
( Some ( s) , Some ( t) ) if s ! = t => {
628
side.remove ( & w) ;
629
joins.insert ( w) ;
630
}
631
( None , Some ( _ ) ) => {
632
side.remove ( & w) ;
633
joins.insert ( w) ;
634
}
635
_ => { }
636
}
637
}
638
if * d = = 0 {
639
ready.insert ( w) ;
640
}
641
}
642
}
643
if let Some ( c) = conflict.take ( ) {
644
finish_conflict ( c, & mut segments) ;
645
}
646
flush_plain ( & mut plain, & mut segments) ;
647
Materialized { segments }
648
}
649
650
651
652
pub fn render ( & self , m : & Materialized, label : & dyn Fn( &ChangeHash ) -> String) -> Vec< u8 > {
653
let total = m.conflict_count ( ) ;
654
let mut n = 0 ;
655
let mut out: Vec< u8 > = Vec :: new( ) ;
656
let push_atoms = | out : & mut Vec< u8 > , atoms : & [AtomKey]| {
657
for a in atoms {
658
if let Some ( c) = self .content ( a) {
659
out.extend_from_slice ( c) ;
660
}
661
}
662
} ;
663
let newline_if_needed = | out : & mut Vec< u8 > | {
664
if ! out.is_empty ( ) & & * out.last ( ) .unwrap ( ) ! = b ' \n ' {
665
out.push ( b ' \n ' ) ;
666
}
667
} ;
668
for seg in & m.segments {
669
match seg {
670
Segment:: Lines( a) => push_atoms ( & mut out, a) ,
671
Segment:: Conflict( sides) => {
672
n + = 1 ;
673
let k = sides.len ( ) ;
674
for ( i, s) in sides.iter ( ) .enumerate ( ) {
675
newline_if_needed ( & mut out) ;
676
let who = s.first ( ) .map ( | a | label ( & a.0 ) ) .unwrap_or_default ( ) ;
677
let marker = if i = = 0 { " <<<<<<<" } else { " =======" } ;
678
out.extend_from_slice (
679
format! ( " {marker} oxy conflict {n} of {total} , side {} of {k} : change {who} \n " , i + 1 ) .as_bytes ( ) ,
680
) ;
681
push_atoms ( & mut out, s) ;
682
}
683
newline_if_needed ( & mut out) ;
75167672 Andy McCall 1 month ago
684
out.extend_from_slice (
685
format! ( " >>>>>>> oxy conflict {n} of {total} end\n " ) .as_bytes ( ) ,
686
) ;
ff0af1ee unknown 1 month ago
687
}
688
Segment:: Zombie { deleted, inserted } => {
689
n + = 1 ;
690
newline_if_needed ( & mut out) ;
691
let deleter = deleted
692
.first ( )
693
.and_then ( | a | self .deleters ( a) .next ( ) .copied ( ) )
694
.map ( | d | label ( & d) )
695
.unwrap_or_default ( ) ;
696
let inserter = inserted.first ( ) .map ( | a | label ( & a.0 ) ) .unwrap_or_default ( ) ;
697
out.extend_from_slice (
698
format! ( " <<<<<<< oxy conflict {n} of {total} , side 1 of 2: deleted by change {deleter} \n -------\n " ) .as_bytes ( ) ,
699
) ;
700
push_atoms ( & mut out, deleted) ;
701
newline_if_needed ( & mut out) ;
75167672 Andy McCall 1 month ago
702
out.extend_from_slice (
703
format! (
704
" ======= oxy conflict {n} of {total} , side 2 of 2: change {inserter} \n "
705
)
706
.as_bytes ( ) ,
707
) ;
ff0af1ee unknown 1 month ago
708
push_atoms ( & mut out, inserted) ;
709
newline_if_needed ( & mut out) ;
75167672 Andy McCall 1 month ago
710
out.extend_from_slice (
711
format! ( " >>>>>>> oxy conflict {n} of {total} end\n " ) .as_bytes ( ) ,
712
) ;
ff0af1ee unknown 1 month ago
713
}
714
}
715
}
716
out
717
}
718
}
719
252e8cb2 Andy McCall 1 month ago ↶
720
fn vertex_value ( v : & Vertex) -> crate:: cbor:: Value {
721
use crate :: cbor:: Value;
722
match v {
723
Vertex:: Start => Value:: Uint( 0 ) ,
724
Vertex:: End => Value:: Uint( 1 ) ,
75167672 Andy McCall 1 month ago
725
Vertex:: Atom( ( c, i) ) => {
726
Value:: Array( vec! [ Value:: Bytes( c.0. to_vec ( ) ) , Value:: Uint( * i as u64 ) ] )
727
}
252e8cb2 Andy McCall 1 month ago ↶
728
}
729
}
730
731
fn vertex_from ( v : & crate:: cbor:: Value) -> Result< Vertex, crate:: objects:: ObjectError> {
732
use crate :: cbor:: Value;
733
use crate :: objects:: ObjectError;
734
match v {
735
Value:: Uint( 0 ) => Ok ( Vertex:: Start) ,
736
Value:: Uint( 1 ) => Ok ( Vertex:: End) ,
75167672 Andy McCall 1 month ago
737
Value:: Array( a) if a.len ( ) = = 2 => Ok ( Vertex:: Atom( (
738
crate :: objects:: oref( & a[ 0 ] , " vertex" ) ? ,
739
crate :: objects:: u32_( & a[ 1 ] , " vertex" ) ? ,
740
) ) ) ,
741
_ => Err ( ObjectError:: Schema(
742
" vertex: expected 0, 1, or [hash, index]" .into ( ) ,
743
) ) ,
252e8cb2 Andy McCall 1 month ago ↶
744
}
745
}
746
747
impl FileGraph {
748
749
pub fn to_value ( & self ) -> crate:: cbor:: Value {
750
use crate :: cbor:: Value;
751
let mut atoms: Vec< ( & AtomKey, & AtomInfo) > = self .atoms.iter ( ) .collect ( ) ;
752
atoms.sort_by_key ( | ( k , _) | * * k) ;
75167672 Andy McCall 1 month ago
753
let mut edges: Vec< ( Vertex, Vertex) > = self
754
.succ
755
.iter ( )
756
.flat_map ( | ( a , bs ) | bs.iter ( ) .map ( move | b| ( * a, * b) ) )
757
.collect ( ) ;
252e8cb2 Andy McCall 1 month ago ↶
758
edges.sort ( ) ;
759
Value:: map( [
760
(
761
0 ,
762
Value:: Array(
763
atoms
764
.into_iter ( )
765
.map ( | ( (c , i ) , info) | {
766
Value:: Array( vec! [
767
Value:: Bytes( c.0. to_vec ( ) ) ,
768
Value:: Uint( * i as u64 ) ,
75167672 Andy McCall 1 month ago
769
info.content
770
.as_ref ( )
01a47cd7 Andy McCall 1 month ago ↶
771
.map ( | b | Value:: Bytes( b.bytes ( ) .to_vec ( ) ) )
75167672 Andy McCall 1 month ago
772
.unwrap_or ( Value:: Null) ,
773
Value:: Array(
774
info.deleted_by
775
.iter ( )
776
.map ( | d | Value:: Bytes( d.0. to_vec ( ) ) )
777
.collect ( ) ,
778
) ,
252e8cb2 Andy McCall 1 month ago ↶
779
] )
780
} )
781
.collect ( ) ,
782
) ,
783
),
75167672 Andy McCall 1 month ago
784
(
785
1 ,
786
Value:: Array(
787
edges
788
.iter ( )
789
.map ( | ( a , b ) | Value:: Array( vec! [ vertex_value ( a) , vertex_value ( b) ] ) )
790
.collect ( ) ,
791
) ,
792
) ,
252e8cb2 Andy McCall 1 month ago ↶
793
] )
794
}
795
796
pub fn from_value ( v : & crate:: cbor:: Value) -> Result< FileGraph, crate:: objects:: ObjectError> {
797
use crate :: objects:: { array, bytes, field, map, oref, u32_, ObjectError} ;
798
let m = map ( v, " file graph" ) ? ;
75167672 Andy McCall 1 month ago
799
let mut g = FileGraph {
0bdc3f91 Andy McCall 1 month ago ↶
800
atoms: HashMap:: default( ) ,
801
succ: HashMap:: default( ) ,
75167672 Andy McCall 1 month ago
802
} ;
01a47cd7 Andy McCall 1 month ago ↶
803
let atoms = array ( field ( m, 0 , " atoms" ) ? , " atoms" ) ? ;
804
805
let mut texts: Vec< & [ u8 ] > = Vec :: new( ) ;
806
for a in atoms {
807
let e = array ( a, " atom" ) ? ;
808
if e.len ( ) = = 4 & & ! e[ 2 ] .is_null ( ) {
809
texts.push ( bytes ( & e[ 2 ] , " atom content" ) ? ) ;
810
}
811
}
812
let all: Lines = texts.into_iter ( ) .collect ( ) ;
813
let mut next = 0 u32 ;
814
for a in atoms {
252e8cb2 Andy McCall 1 month ago ↶
815
let e = array ( a, " atom" ) ? ;
816
if e.len ( ) ! = 4 {
817
return Err ( ObjectError:: Schema( " atom: expected 4 fields" .into ( ) ) ) ;
818
}
819
let key = ( oref ( & e[ 0 ] , " atom change" ) ? , u32_ ( & e[ 1 ] , " atom index" ) ? ) ;
75167672 Andy McCall 1 month ago
820
let content = if e[ 2 ] .is_null ( ) {
821
None
822
} else {
01a47cd7 Andy McCall 1 month ago ↶
823
next + = 1 ;
824
Some ( LineRef:: new( all.clone ( ) , next - 1 ) )
75167672 Andy McCall 1 month ago
825
} ;
826
let deleted_by = array ( & e[ 3 ] , " deleters" ) ?
827
.iter ( )
828
.map ( | d | oref ( d, " deleter" ) )
829
.collect:: < Result< BTreeSet< _ > , _ > > ( ) ? ;
830
g.atoms.insert (
831
key,
832
AtomInfo {
833
content,
834
deleted_by,
835
} ,
836
) ;
252e8cb2 Andy McCall 1 month ago ↶
837
}
838
for e in array ( field ( m, 1 , " edges" ) ? , " edges" ) ? {
839
let pair = array ( e, " edge" ) ? ;
840
if pair.len ( ) ! = 2 {
841
return Err ( ObjectError:: Schema( " edge: expected [from, to]" .into ( ) ) ) ;
842
}
843
g.edge ( vertex_from ( & pair[ 0 ] ) ? , vertex_from ( & pair[ 1 ] ) ? ) ;
844
}
845
Ok ( g)
846
}
847
}
848
ff0af1ee unknown 1 month ago
849
850
fn split_zombies ( atoms : Vec< AtomKey> , ghosts : & HashSet< AtomKey> ) -> Vec< Segment> {
851
let mut out = Vec :: new( ) ;
852
let mut lines: Vec< AtomKey> = Vec :: new( ) ;
853
let mut i = 0 ;
854
while i < atoms.len ( ) {
855
if ! ghosts.contains ( & atoms[ i] ) {
856
lines.push ( atoms[ i] ) ;
857
i + = 1 ;
858
continue ;
859
}
860
if ! lines.is_empty ( ) {
861
out.push ( Segment:: Lines( std:: mem:: take( & mut lines) ) ) ;
862
}
863
let mut deleted = Vec :: new( ) ;
864
while i < atoms.len ( ) & & ghosts.contains ( & atoms[ i] ) {
865
deleted.push ( atoms[ i] ) ;
866
i + = 1 ;
867
}
868
let mut inserted = Vec :: new( ) ;
869
if i < atoms.len ( ) {
870
let ch = atoms[ i] .0 ;
871
while i < atoms.len ( ) & & atoms[ i] .0 = = ch & & ! ghosts.contains ( & atoms[ i] ) {
872
inserted.push ( atoms[ i] ) ;
873
i + = 1 ;
874
}
875
}
876
out.push ( Segment:: Zombie { deleted, inserted } ) ;
877
}
878
if ! lines.is_empty ( ) {
879
out.push ( Segment:: Lines( lines) ) ;
880
}
881
out
882
}
883
884
# [ cfg ( test ) ]
885
mod tests {
886
use super :: * ;
887
use crate :: hash:: ObjectRef;
888
889
fn h ( n : u8 ) -> ChangeHash {
890
ObjectRef( [ n; 32 ] )
891
}
01a47cd7 Andy McCall 1 month ago ↶
892
fn lines ( v : & [& str ]) -> Lines {
893
v.iter ( ) .map ( | s | s.as_bytes ( ) ) .collect ( )
ff0af1ee unknown 1 month ago
894
}
895
fn text ( g : & FileGraph, m : & Materialized) -> String {
896
String :: from_utf8( g.render ( m, & | c| c.short ( 4 ) ) ) .unwrap ( )
897
}
898
899
struct Deps ( HashMap< ChangeHash, Vec< ChangeHash> > ) ;
900
impl DepOracle for Deps {
901
fn depends_on ( & self , a : & ChangeHash, b : & ChangeHash) -> bool {
902
if a = = b {
903
return true ;
904
}
905
let mut stack = vec! [ * a] ;
0bdc3f91 Andy McCall 1 month ago ↶
906
let mut seen = HashSet:: default( ) ;
ff0af1ee unknown 1 month ago
907
while let Some ( x) = stack.pop ( ) {
908
if let Some ( ds) = self .0. get ( & x) {
909
for d in ds {
910
if d = = b {
911
return true ;
912
}
913
if seen.insert ( * d) {
914
stack.push ( * d) ;
915
}
916
}
917
}
918
}
919
false
e83354cd Andy McCall 1 month ago ↶
920
}
921
}
922
923
924
struct Linear ;
925
impl DepOracle for Linear {
926
fn depends_on ( & self , a : & ChangeHash, b : & ChangeHash) -> bool {
927
a.0 [ .. 2 ] > = b.0 [ .. 2 ]
928
}
929
}
930
931
932
933
fn edited_file ( n : usize , revs : usize , per : usize ) -> FileGraph {
934
let hn = | i : usize | {
935
let mut b = [ 0 u8 ; 32 ] ;
936
b[ .. 2 ] .copy_from_slice ( & ( i as u16 ) .to_be_bytes ( ) ) ;
937
ObjectRef( b)
938
} ;
939
let mut g = FileGraph:: new( ) ;
940
let first: Vec< String> = ( 0 .. n) .map ( | i | format! ( " line {i} \n " ) ) .collect ( ) ;
941
let first: Vec< & str > = first.iter ( ) .map ( String :: as_str) .collect ( ) ;
942
g.insert ( hn ( 1 ) , 0 , Vertex:: Start, Vertex:: End, & lines ( & first) ) ;
943
for r in 2 .. revs + 2 {
944
let order = g.materialize ( & Linear) .live_atoms ( ) ;
945
let step = order.len ( ) / per;
946
for j in 0 .. per {
947
let at = j * step + ( r % step.max ( 1 ) ) ;
948
let k = order[ at] ;
949
g.delete (
950
hn ( r) ,
951
& AtomRange {
952
change: k.0 ,
953
from: k.1 ,
954
to: k.1 + 1 ,
955
} ,
956
) ;
957
let after = if at = = 0 {
958
Vertex:: Start
959
} else {
960
Vertex:: Atom( order[ at - 1 ] )
961
} ;
962
let before = order
963
.get ( at + 1 )
964
.map ( | k | Vertex:: Atom( * k) )
965
.unwrap_or ( Vertex:: End) ;
966
g.insert ( hn ( r) , j as u32 , after, before, & lines ( & [ " new\n " ] ) ) ;
967
}
ff0af1ee unknown 1 month ago
968
}
e83354cd Andy McCall 1 month ago ↶
969
g
970
}
971
972
973
# [ test ]
974
fn the_order_shortcut_agrees_with_the_general_order ( ) {
975
let g = edited_file ( 500 , 10 , 20 ) ;
976
assert! ( g.as_chain ( ) .is_none ( ) ) ;
977
let fast = g.as_order ( & Linear) .expect ( " a single order" ) ;
978
assert_eq! (
979
Materialized {
980
segments: vec! [ Segment:: Lines( fast) ]
981
} ,
982
g.materialize_general ( & Linear)
983
) ;
984
985
986
let mut g = FileGraph:: new( ) ;
987
g.insert ( h ( 1 ) , 0 , Vertex:: Start, Vertex:: End, & lines ( & [ " a\n " ] ) ) ;
988
let a = Vertex:: Atom( ( h ( 1 ) , 0 ) ) ;
989
g.insert ( h ( 2 ) , 0 , a, Vertex:: End, & lines ( & [ " b\n " ] ) ) ;
990
g.insert ( h ( 3 ) , 0 , a, Vertex:: End, & lines ( & [ " c\n " ] ) ) ;
991
assert! ( g.as_order ( & NoDeps) .is_none ( ) ) ;
992
assert! ( g.materialize ( & NoDeps) .is_conflicted ( ) ) ;
993
}
994
995
996
# [ test ]
997
# [ ignore ]
998
fn bench_materialize_edited_file ( ) {
999
let g = edited_file ( 20_000 , 50 , 100 ) ;
1000
let t = std:: time:: Instant:: now( ) ;
1001
let mut n = 0 ;
1002
for _ in 0 .. 20 {
1003
n + = g.materialize ( & Linear) .live_atoms ( ) .len ( ) ;
1004
}
1005
eprintln! (
1006
" materialize: {:.2} ms each ({n} atoms)" ,
1007
t.elapsed ( ) .as_secs_f64 ( ) * 1000. 0 / 20. 0
1008
) ;
598009a7 Andy McCall 1 month ago ↶
1009
}
1010
1011
1012
1013
# [ test ]
1014
fn the_chain_shortcut_agrees_with_the_general_order ( ) {
1015
let mut g = FileGraph:: new( ) ;
1016
g.insert (
1017
h ( 1 ) ,
1018
0 ,
1019
Vertex:: Start,
1020
Vertex:: End,
1021
& lines ( & [ " a\n " , " b\n " , " c\n " ] ) ,
1022
) ;
1023
assert! ( g.as_chain ( ) .is_some ( ) ) ;
1024
assert_eq! ( g.materialize ( & NoDeps) , g.materialize_general ( & NoDeps) ) ;
1025
1026
g.insert (
1027
h ( 2 ) ,
1028
0 ,
1029
Vertex:: Atom( ( h ( 1 ) , 2 ) ) ,
1030
Vertex:: End,
1031
& lines ( & [ " d\n " ] ) ,
1032
) ;
1033
assert! ( g.as_chain ( ) .is_some ( ) ) ;
1034
assert_eq! ( g.materialize ( & NoDeps) , g.materialize_general ( & NoDeps) ) ;
1035
1036
g.insert (
1037
h ( 4 ) ,
1038
0 ,
1039
Vertex:: Atom( ( h ( 1 ) , 0 ) ) ,
1040
Vertex:: Atom( ( h ( 1 ) , 1 ) ) ,
1041
& lines ( & [ " x\n " ] ) ,
1042
) ;
1043
assert! ( g.as_chain ( ) .is_none ( ) ) ;
1044
assert_eq! ( text ( & g, & g.materialize ( & NoDeps) ) , " a\n x\n b\n c\n d\n " ) ;
1045
1046
let mut c = FileGraph:: new( ) ;
1047
c.insert ( h ( 1 ) , 0 , Vertex:: Start, Vertex:: End, & lines ( & [ " a\n " ] ) ) ;
1048
c.insert ( h ( 2 ) , 0 , Vertex:: Start, Vertex:: End, & lines ( & [ " b\n " ] ) ) ;
1049
assert! ( c.as_chain ( ) .is_none ( ) ) ;
1050
assert! ( c.materialize ( & NoDeps) .is_conflicted ( ) ) ;
1051
1052
let mut d = FileGraph:: new( ) ;
1053
d.insert ( h ( 1 ) , 0 , Vertex:: Start, Vertex:: End, & lines ( & [ " a\n " , " b\n " ] ) ) ;
1054
d.delete (
1055
h ( 3 ) ,
1056
& AtomRange {
1057
change: h ( 1 ) ,
1058
from: 0 ,
1059
to: 1 ,
1060
} ,
1061
) ;
1062
assert! ( d.as_chain ( ) .is_none ( ) ) ;
1063
assert_eq! ( d.materialize ( & NoDeps) , d.materialize_general ( & NoDeps) ) ;
1064
1065
let e = FileGraph:: new( ) ;
1066
assert_eq! ( e.materialize ( & NoDeps) , e.materialize_general ( & NoDeps) ) ;
ff0af1ee unknown 1 month ago
1067
}
1068
1069
# [ test ]
1070
fn sequential_inserts_and_deletes ( ) {
1071
let mut g = FileGraph:: new( ) ;
75167672 Andy McCall 1 month ago
1072
g.insert (
1073
h ( 1 ) ,
1074
0 ,
1075
Vertex:: Start,
1076
Vertex:: End,
1077
& lines ( & [ " a\n " , " b\n " , " c\n " ] ) ,
1078
) ;
ff0af1ee unknown 1 month ago
1079
let m = g.materialize ( & NoDeps) ;
1080
assert! ( ! m.is_conflicted ( ) ) ;
1081
assert_eq! ( text ( & g, & m) , " a\n b\n c\n " ) ;
1082
1083
75167672 Andy McCall 1 month ago
1084
g.insert (
1085
h ( 2 ) ,
1086
0 ,
1087
Vertex:: Atom( ( h ( 1 ) , 1 ) ) ,
1088
Vertex:: Atom( ( h ( 1 ) , 2 ) ) ,
1089
& lines ( & [ " x\n " ] ) ,
1090
) ;
1091
g.delete (
1092
h ( 2 ) ,
1093
& AtomRange {
1094
change: h ( 1 ) ,
1095
from: 0 ,
1096
to: 1 ,
1097
} ,
1098
) ;
ff0af1ee unknown 1 month ago
1099
let deps = Deps( [ ( h ( 2 ) , vec! [ h ( 1 ) ] ) ] .into_iter ( ) .collect ( ) ) ;
1100
let m = g.materialize ( & deps) ;
1101
assert! ( ! m.is_conflicted ( ) ) ;
1102
assert_eq! ( text ( & g, & m) , " b\n x\n c\n " ) ;
1103
assert_eq! ( m.live_atoms ( ) , vec! [ ( h ( 1 ) , 1 ) , ( h ( 2 ) , 0 ) , ( h ( 1 ) , 2 ) ] ) ;
1104
}
1105
1106
# [ test ]
1107
fn concurrent_inserts_at_same_anchor_conflict ( ) {
1108
let mut g = FileGraph:: new( ) ;
1109
g.insert ( h ( 1 ) , 0 , Vertex:: Start, Vertex:: End, & lines ( & [ " a\n " , " b\n " ] ) ) ;
75167672 Andy McCall 1 month ago
1110
g.insert (
1111
h ( 2 ) ,
1112
0 ,
1113
Vertex:: Atom( ( h ( 1 ) , 0 ) ) ,
1114
Vertex:: Atom( ( h ( 1 ) , 1 ) ) ,
1115
& lines ( & [ " from-2\n " ] ) ,
1116
) ;
1117
g.insert (
1118
h ( 3 ) ,
1119
0 ,
1120
Vertex:: Atom( ( h ( 1 ) , 0 ) ) ,
1121
Vertex:: Atom( ( h ( 1 ) , 1 ) ) ,
1122
& lines ( & [ " from-3\n " , " more-3\n " ] ) ,
1123
) ;
1124
let deps = Deps(
1125
[ ( h ( 2 ) , vec! [ h ( 1 ) ] ) , ( h ( 3 ) , vec! [ h ( 1 ) ] ) ]
1126
.into_iter ( )
1127
.collect ( ) ,
1128
) ;
ff0af1ee unknown 1 month ago
1129
let m = g.materialize ( & deps) ;
1130
assert! ( m.is_conflicted ( ) ) ;
1131
assert_eq! ( m.conflict_count ( ) , 1 ) ;
1132
assert_eq! (
1133
m.segments,
1134
vec! [
1135
Segment:: Lines( vec! [ ( h ( 1 ) , 0 ) ] ) ,
1136
Segment:: Conflict( vec! [ vec! [ ( h ( 2 ) , 0 ) ] , vec! [ ( h ( 3 ) , 0 ) , ( h ( 3 ) , 1 ) ] ] ) ,
1137
Segment:: Lines( vec! [ ( h ( 1 ) , 1 ) ] ) ,
1138
]
1139
) ;
1140
let t = text ( & g, & m) ;
1141
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}" ) ;
1142
}
1143
1144
# [ test ]
1145
fn concurrent_inserts_at_different_anchors_do_not_conflict ( ) {
1146
let mut g = FileGraph:: new( ) ;
75167672 Andy McCall 1 month ago
1147
g.insert (
1148
h ( 1 ) ,
1149
0 ,
1150
Vertex:: Start,
1151
Vertex:: End,
1152
& lines ( & [ " a\n " , " b\n " , " c\n " ] ) ,
1153
) ;
1154
g.insert (
1155
h ( 2 ) ,
1156
0 ,
1157
Vertex:: Start,
1158
Vertex:: Atom( ( h ( 1 ) , 0 ) ) ,
1159
& lines ( & [ " top\n " ] ) ,
1160
) ;
1161
g.insert (
1162
h ( 3 ) ,
1163
0 ,
1164
Vertex:: Atom( ( h ( 1 ) , 2 ) ) ,
1165
Vertex:: End,
1166
& lines ( & [ " bottom\n " ] ) ,
1167
) ;
1168
let deps = Deps(
1169
[ ( h ( 2 ) , vec! [ h ( 1 ) ] ) , ( h ( 3 ) , vec! [ h ( 1 ) ] ) ]
1170
.into_iter ( )
1171
.collect ( ) ,
1172
) ;
ff0af1ee unknown 1 month ago
1173
let m = g.materialize ( & deps) ;
1174
assert! ( ! m.is_conflicted ( ) ) ;
1175
assert_eq! ( text ( & g, & m) , " top\n a\n b\n c\n bottom\n " ) ;
1176
}
1177
1178
# [ test ]
1179
fn concurrent_deletes_of_the_same_line_agree ( ) {
1180
let mut g = FileGraph:: new( ) ;
1181
g.insert ( h ( 1 ) , 0 , Vertex:: Start, Vertex:: End, & lines ( & [ " a\n " , " b\n " ] ) ) ;
75167672 Andy McCall 1 month ago
1182
g.delete (
1183
h ( 2 ) ,
1184
& AtomRange {
1185
change: h ( 1 ) ,
1186
from: 0 ,
1187
to: 1 ,
1188
} ,
1189
) ;
1190
g.delete (
1191
h ( 3 ) ,
1192
& AtomRange {
1193
change: h ( 1 ) ,
1194
from: 0 ,
1195
to: 1 ,
1196
} ,
1197
) ;
1198
let deps = Deps(
1199
[ ( h ( 2 ) , vec! [ h ( 1 ) ] ) , ( h ( 3 ) , vec! [ h ( 1 ) ] ) ]
1200
.into_iter ( )
1201
.collect ( ) ,
1202
) ;
ff0af1ee unknown 1 month ago
1203
let m = g.materialize ( & deps) ;
1204
assert! ( ! m.is_conflicted ( ) ) ;
1205
assert_eq! ( text ( & g, & m) , " b\n " ) ;
1206
}
1207
1208
# [ test ]
1209
fn delete_insert_conflict_is_a_zombie ( ) {
1210
let mut g = FileGraph:: new( ) ;
1211
g.insert ( h ( 1 ) , 0 , Vertex:: Start, Vertex:: End, & lines ( & [ " a\n " , " b\n " ] ) ) ;
1212
75167672 Andy McCall 1 month ago
1213
g.delete (
1214
h ( 2 ) ,
1215
& AtomRange {
1216
change: h ( 1 ) ,
1217
from: 0 ,
1218
to: 1 ,
1219
} ,
1220
) ;
1221
g.insert (
1222
h ( 3 ) ,
1223
0 ,
1224
Vertex:: Atom( ( h ( 1 ) , 0 ) ) ,
1225
Vertex:: Atom( ( h ( 1 ) , 1 ) ) ,
1226
& lines ( & [ " after-a\n " ] ) ,
1227
) ;
1228
let deps = Deps(
1229
[ ( h ( 2 ) , vec! [ h ( 1 ) ] ) , ( h ( 3 ) , vec! [ h ( 1 ) ] ) ]
1230
.into_iter ( )
1231
.collect ( ) ,
1232
) ;
ff0af1ee unknown 1 month ago
1233
let m = g.materialize ( & deps) ;
1234
assert! ( m.is_conflicted ( ) ) ;
1235
assert_eq! (
1236
m.segments,
1237
vec! [
75167672 Andy McCall 1 month ago
1238
Segment:: Zombie {
1239
deleted: vec! [ ( h ( 1 ) , 0 ) ] ,
1240
inserted: vec! [ ( h ( 3 ) , 0 ) ]
1241
} ,
ff0af1ee unknown 1 month ago
1242
Segment:: Lines( vec! [ ( h ( 1 ) , 1 ) ] ) ,
1243
]
1244
) ;
1245
let t = text ( & g, & m) ;
1246
assert! ( t.contains ( " -------\n a\n =======" ) , " {t}" ) ;
1247
assert! ( t.contains ( " after-a\n >>>>>>>" ) , " {t}" ) ;
1248
1249
75167672 Andy McCall 1 month ago
1250
let deps = Deps(
1251
[ ( h ( 2 ) , vec! [ h ( 1 ) , h ( 3 ) ] ) , ( h ( 3 ) , vec! [ h ( 1 ) ] ) ]
1252
.into_iter ( )
1253
.collect ( ) ,
1254
) ;
ff0af1ee unknown 1 month ago
1255
let m = g.materialize ( & deps) ;
1256
assert! ( ! m.is_conflicted ( ) ) ;
1257
assert_eq! ( text ( & g, & m) , " after-a\n b\n " ) ;
1258
}
1259
1260
# [ test ]
23b217d7 Andy McCall 1 month ago ↶
1261
fn insert_after_a_line_deleted_by_a_dependency_is_not_a_conflict ( ) {
1262
1263
1264
let mut g = FileGraph:: new( ) ;
1265
g.insert ( h ( 1 ) , 0 , Vertex:: Start, Vertex:: End, & lines ( & [ " a\n " , " b\n " ] ) ) ;
75167672 Andy McCall 1 month ago
1266
g.delete (
1267
h ( 2 ) ,
1268
& AtomRange {
1269
change: h ( 1 ) ,
1270
from: 1 ,
1271
to: 2 ,
1272
} ,
1273
) ;
1274
g.insert (
1275
h ( 3 ) ,
1276
0 ,
1277
Vertex:: Atom( ( h ( 1 ) , 1 ) ) ,
1278
Vertex:: End,
1279
& lines ( & [ " c\n " ] ) ,
1280
) ;
1281
let deps = Deps(
1282
[ ( h ( 2 ) , vec! [ h ( 1 ) ] ) , ( h ( 3 ) , vec! [ h ( 2 ) ] ) ]
1283
.into_iter ( )
1284
.collect ( ) ,
1285
) ;
23b217d7 Andy McCall 1 month ago ↶
1286
let m = g.materialize ( & deps) ;
1287
assert! ( ! m.is_conflicted ( ) , " {:?}" , m.segments) ;
1288
assert_eq! ( text ( & g, & m) , " a\n c\n " ) ;
1289
1290
1291
let mut g = FileGraph:: new( ) ;
1292
g.insert ( h ( 1 ) , 0 , Vertex:: Start, Vertex:: End, & lines ( & [ " x\n " ] ) ) ;
75167672 Andy McCall 1 month ago
1293
g.delete (
1294
h ( 1 ) ,
1295
& AtomRange {
1296
change: h ( 1 ) ,
1297
from: 0 ,
1298
to: 1 ,
1299
} ,
1300
) ;
1301
g.insert (
1302
h ( 2 ) ,
1303
0 ,
1304
Vertex:: Atom( ( h ( 1 ) , 0 ) ) ,
1305
Vertex:: End,
1306
& lines ( & [ " y\n " ] ) ,
1307
) ;
23b217d7 Andy McCall 1 month ago ↶
1308
let deps = Deps( [ ( h ( 2 ) , vec! [ h ( 1 ) ] ) ] .into_iter ( ) .collect ( ) ) ;
1309
let m = g.materialize ( & deps) ;
1310
assert! ( ! m.is_conflicted ( ) ) ;
1311
assert_eq! ( text ( & g, & m) , " y\n " ) ;
1312
}
1313
1314
# [ test ]
ff0af1ee unknown 1 month ago
1315
fn resolution_collapses_a_conflict ( ) {
1316
let mut g = FileGraph:: new( ) ;
1317
g.insert ( h ( 1 ) , 0 , Vertex:: Start, Vertex:: End, & lines ( & [ " a\n " ] ) ) ;
75167672 Andy McCall 1 month ago
1318
g.insert (
1319
h ( 2 ) ,
1320
0 ,
1321
Vertex:: Start,
1322
Vertex:: Atom( ( h ( 1 ) , 0 ) ) ,
1323
& lines ( & [ " x\n " ] ) ,
1324
) ;
1325
g.insert (
1326
h ( 3 ) ,
1327
0 ,
1328
Vertex:: Start,
1329
Vertex:: Atom( ( h ( 1 ) , 0 ) ) ,
1330
& lines ( & [ " y\n " ] ) ,
1331
) ;
1332
let mut deps: HashMap< _ , _ > = [ ( h ( 2 ) , vec! [ h ( 1 ) ] ) , ( h ( 3 ) , vec! [ h ( 1 ) ] ) ]
1333
.into_iter ( )
1334
.collect ( ) ;
ff0af1ee unknown 1 month ago
1335
assert! ( g.materialize ( & Deps( deps.clone ( ) ) ) .is_conflicted ( ) ) ;
1336
75167672 Andy McCall 1 month ago
1337
g.delete (
1338
h ( 4 ) ,
1339
& AtomRange {
1340
change: h ( 2 ) ,
1341
from: 0 ,
1342
to: 1 ,
1343
} ,
1344
) ;
ff0af1ee unknown 1 month ago
1345
deps.insert ( h ( 4 ) , vec! [ h ( 2 ) , h ( 3 ) ] ) ;
1346
let m = g.materialize ( & Deps( deps) ) ;
1347
assert! ( ! m.is_conflicted ( ) ) ;
1348
assert_eq! ( text ( & g, & m) , " y\n a\n " ) ;
1349
}
1350
1351
# [ test ]
1352
fn empty_and_unterminated_files ( ) {
1353
let g = FileGraph:: new( ) ;
1354
let m = g.materialize ( & NoDeps) ;
1355
assert_eq! ( m.segments, vec! [ ] ) ;
1356
assert_eq! ( text ( & g, & m) , " " ) ;
1357
let mut g = FileGraph:: new( ) ;
1358
g.insert ( h ( 1 ) , 0 , Vertex:: Start, Vertex:: End, & lines ( & [ " no newline" ] ) ) ;
1359
assert_eq! ( text ( & g, & g.materialize ( & NoDeps) ) , " no newline" ) ;
1360
}
1361
252e8cb2 Andy McCall 1 month ago ↶
1362
# [ test ]
1363
fn serialization_round_trip ( ) {
1364
let mut g = FileGraph:: new( ) ;
1365
g.insert ( h ( 1 ) , 0 , Vertex:: Start, Vertex:: End, & lines ( & [ " a\n " , " b\n " ] ) ) ;
75167672 Andy McCall 1 month ago
1366
g.insert (
1367
h ( 2 ) ,
1368
0 ,
1369
Vertex:: Atom( ( h ( 1 ) , 0 ) ) ,
1370
Vertex:: Atom( ( h ( 1 ) , 1 ) ) ,
1371
& lines ( & [ " x\n " ] ) ,
1372
) ;
1373
g.delete (
1374
h ( 3 ) ,
1375
& AtomRange {
1376
change: h ( 1 ) ,
1377
from: 1 ,
1378
to: 2 ,
1379
} ,
1380
) ;
252e8cb2 Andy McCall 1 month ago ↶
1381
let v = g.to_value ( ) ;
1382
let bytes = crate :: cbor:: encode( & v) ;
1383
let back = FileGraph:: from_value( & crate :: cbor:: decode( & bytes) .unwrap ( ) ) .unwrap ( ) ;
75167672 Andy McCall 1 month ago
1384
let deps = Deps(
1385
[ ( h ( 2 ) , vec! [ h ( 1 ) ] ) , ( h ( 3 ) , vec! [ h ( 1 ) ] ) ]
1386
.into_iter ( )
1387
.collect ( ) ,
1388
) ;
252e8cb2 Andy McCall 1 month ago ↶
1389
assert_eq! ( back.materialize ( & deps) , g.materialize ( & deps) ) ;
1390
assert_eq! ( text ( & back, & back.materialize ( & deps) ) , " a\n x\n " ) ;
75167672 Andy McCall 1 month ago
1391
assert_eq! (
1392
crate :: cbor:: encode( & back.to_value ( ) ) ,
1393
bytes,
1394
" deterministic"
1395
) ;
252e8cb2 Andy McCall 1 month ago ↶
1396
}
1397
ff0af1ee unknown 1 month ago
1398
1399
# [ test ]
1400
fn application_order_is_irrelevant ( ) {
1401
use rand:: seq:: SliceRandom;
75167672 Andy McCall 1 month ago
1402
type Op = (
1403
ChangeHash,
01a47cd7 Andy McCall 1 month ago ↶
1404
Vec< ( u32 , Vertex, Vertex, Lines) > ,
75167672 Andy McCall 1 month ago
1405
Vec< AtomRange> ,
1406
) ;
ff0af1ee unknown 1 month ago
1407
let ops: Vec< Op> = vec! [
75167672 Andy McCall 1 month ago
1408
(
1409
h ( 1 ) ,
1410
vec! [ ( 0 , Vertex:: Start, Vertex:: End, lines ( & [ " a\n " , " b\n " , " c\n " ] ) ) ] ,
1411
vec! [ ] ,
1412
) ,
1413
(
1414
h ( 2 ) ,
1415
vec! [ (
1416
0 ,
1417
Vertex:: Atom( ( h ( 1 ) , 0 ) ) ,
1418
Vertex:: Atom( ( h ( 1 ) , 1 ) ) ,
1419
lines ( & [ " p\n " ] ) ,
1420
) ] ,
1421
vec! [ ] ,
1422
) ,
1423
(
1424
h ( 3 ) ,
1425
vec! [ (
1426
0 ,
1427
Vertex:: Atom( ( h ( 1 ) , 0 ) ) ,
1428
Vertex:: Atom( ( h ( 1 ) , 1 ) ) ,
1429
lines ( & [ " q\n " , " r\n " ] ) ,
1430
) ] ,
1431
vec! [ AtomRange {
1432
change: h ( 1 ) ,
1433
from: 2 ,
1434
to: 3 ,
1435
} ] ,
1436
) ,
1437
(
1438
h ( 4 ) ,
1439
vec! [ (
1440
0 ,
1441
Vertex:: Atom( ( h ( 3 ) , 1 ) ) ,
1442
Vertex:: Atom( ( h ( 1 ) , 1 ) ) ,
1443
lines ( & [ " s\n " ] ) ,
1444
) ] ,
1445
vec! [ AtomRange {
1446
change: h ( 1 ) ,
1447
from: 1 ,
1448
to: 2 ,
1449
} ] ,
1450
) ,
1451
(
1452
h ( 5 ) ,
1453
vec! [ ( 0 , Vertex:: Atom( ( h ( 1 ) , 2 ) ) , Vertex:: End, lines ( & [ " z\n " ] ) ) ] ,
1454
vec! [ ] ,
1455
) ,
ff0af1ee unknown 1 month ago
1456
] ;
1457
let deps = Deps(
75167672 Andy McCall 1 month ago
1458
[
1459
( h ( 2 ) , vec! [ h ( 1 ) ] ) ,
1460
( h ( 3 ) , vec! [ h ( 1 ) ] ) ,
1461
( h ( 4 ) , vec! [ h ( 3 ) , h ( 1 ) ] ) ,
1462
( h ( 5 ) , vec! [ h ( 1 ) ] ) ,
1463
]
1464
.into_iter ( )
1465
.collect ( ) ,
ff0af1ee unknown 1 month ago
1466
) ;
1467
let build = | order : & [usize ]| {
1468
let mut g = FileGraph:: new( ) ;
1469
for & i in order {
1470
let ( c, ins, dels) = & ops[ i] ;
1471
for ( idx, a, b, l) in ins {
1472
g.insert ( * c, * idx, * a, * b, l) ;
1473
}
1474
for d in dels {
1475
g.delete ( * c, d) ;
1476
}
1477
}
1478
let m = g.materialize ( & deps) ;
1479
( m.clone ( ) , text ( & g, & m) )
1480
} ;
1481
let reference = build ( & [ 0 , 1 , 2 , 3 , 4 ] ) ;
1482
assert! ( reference.0. is_conflicted ( ) ) ;
1483
let mut rng = rand:: thread_rng( ) ;
1484
let mut order: Vec< usize > = ( 0 .. ops.len ( ) ) .collect ( ) ;
1485
for _ in 0 .. 50 {
1486
order.shuffle ( & mut rng) ;
1487
assert_eq! ( build ( & order) , reference, " order {order:?}" ) ;
1488
}
1489
}
1490
}