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