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