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