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