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