@@ -40,8 +40,41 @@ type Diff struct {
40
40
Text string
41
41
}
42
42
43
+ // splice removes amount elements from slice at index index, replacing them with elements.
43
44
func splice (slice []Diff , index int , amount int , elements ... Diff ) []Diff {
44
- return append (slice [:index ], append (elements , slice [index + amount :]... )... )
45
+ if len (elements ) == amount {
46
+ // Easy case: overwrite the relevant items.
47
+ copy (slice [index :], elements )
48
+ return slice
49
+ }
50
+ if len (elements ) < amount {
51
+ // Fewer new items than old.
52
+ // Copy in the new items.
53
+ copy (slice [index :], elements )
54
+ // Shift the remaining items left.
55
+ copy (slice [index + len (elements ):], slice [index + amount :])
56
+ // Calculate the new end of the slice.
57
+ end := len (slice ) - amount + len (elements )
58
+ // Zero stranded elements at end so that they can be garbage collected.
59
+ tail := slice [end :]
60
+ for i := range tail {
61
+ tail [i ] = Diff {}
62
+ }
63
+ return slice [:end ]
64
+ }
65
+ // More new items than old.
66
+ // Make room in slice for new elements.
67
+ // There's probably an even more efficient way to do this,
68
+ // but this is simple and clear.
69
+ need := len (slice ) - amount + len (elements )
70
+ for len (slice ) < need {
71
+ slice = append (slice , Diff {})
72
+ }
73
+ // Shift slice elements right to make room for new elements.
74
+ copy (slice [index + len (elements ):], slice [index + amount :])
75
+ // Copy in new elements.
76
+ copy (slice [index :], elements )
77
+ return slice
45
78
}
46
79
47
80
// DiffMain finds the differences between two texts.
@@ -145,7 +178,10 @@ func (dmp *DiffMatchPatch) diffCompute(text1, text2 []rune, checklines bool, dea
145
178
diffsA := dmp .diffMainRunes (text1A , text2A , checklines , deadline )
146
179
diffsB := dmp .diffMainRunes (text1B , text2B , checklines , deadline )
147
180
// Merge the results.
148
- return append (diffsA , append ([]Diff {Diff {DiffEqual , string (midCommon )}}, diffsB ... )... )
181
+ diffs := diffsA
182
+ diffs = append (diffs , Diff {DiffEqual , string (midCommon )})
183
+ diffs = append (diffs , diffsB ... )
184
+ return diffs
149
185
} else if checklines && len (text1 ) > 100 && len (text2 ) > 100 {
150
186
return dmp .diffLineMode (text1 , text2 , deadline )
151
187
}
@@ -247,7 +283,7 @@ func (dmp *DiffMatchPatch) diffBisect(runes1, runes2 []rune, deadline time.Time)
247
283
k2end := 0
248
284
for d := 0 ; d < maxD ; d ++ {
249
285
// Bail out if deadline is reached.
250
- if ! deadline .IsZero () && time .Now ().After (deadline ) {
286
+ if ! deadline .IsZero () && d % 16 == 0 && time .Now ().After (deadline ) {
251
287
break
252
288
}
253
289
@@ -434,48 +470,29 @@ func (dmp *DiffMatchPatch) DiffCommonSuffix(text1, text2 string) int {
434
470
435
471
// commonPrefixLength returns the length of the common prefix of two rune slices.
436
472
func commonPrefixLength (text1 , text2 []rune ) int {
437
- short , long := text1 , text2
438
- if len (short ) > len (long ) {
439
- short , long = long , short
440
- }
441
- for i , r := range short {
442
- if r != long [i ] {
443
- return i
473
+ // Linear search. See comment in commonSuffixLength.
474
+ n := 0
475
+ for ; n < len (text1 ) && n < len (text2 ); n ++ {
476
+ if text1 [n ] != text2 [n ] {
477
+ return n
444
478
}
445
479
}
446
- return len ( short )
480
+ return n
447
481
}
448
482
449
483
// commonSuffixLength returns the length of the common suffix of two rune slices.
450
484
func commonSuffixLength (text1 , text2 []rune ) int {
451
- n := min (len (text1 ), len (text2 ))
452
- for i := 0 ; i < n ; i ++ {
453
- if text1 [len (text1 )- i - 1 ] != text2 [len (text2 )- i - 1 ] {
454
- return i
485
+ // Use linear search rather than the binary search discussed at https://neil.fraser.name/news/2007/10/09/.
486
+ // See discussion at https://github.com/sergi/go-diff/issues/54.
487
+ i1 := len (text1 )
488
+ i2 := len (text2 )
489
+ for n := 0 ; ; n ++ {
490
+ i1 --
491
+ i2 --
492
+ if i1 < 0 || i2 < 0 || text1 [i1 ] != text2 [i2 ] {
493
+ return n
455
494
}
456
495
}
457
- return n
458
-
459
- // TODO research and benchmark this, why is it not activated? https://github.com/sergi/go-diff/issues/54
460
- // Binary search.
461
- // Performance analysis: http://neil.fraser.name/news/2007/10/09/
462
- /*
463
- pointermin := 0
464
- pointermax := math.Min(len(text1), len(text2))
465
- pointermid := pointermax
466
- pointerend := 0
467
- for pointermin < pointermid {
468
- if text1[len(text1)-pointermid:len(text1)-pointerend] ==
469
- text2[len(text2)-pointermid:len(text2)-pointerend] {
470
- pointermin = pointermid
471
- pointerend = pointermin
472
- } else {
473
- pointermax = pointermid
474
- }
475
- pointermid = math.Floor((pointermax-pointermin)/2 + pointermin)
476
- }
477
- return pointermid
478
- */
479
496
}
480
497
481
498
// DiffCommonOverlap determines if the suffix of one string is the prefix of another.
@@ -628,11 +645,7 @@ func (dmp *DiffMatchPatch) diffHalfMatchI(l, s []rune, i int) [][]rune {
628
645
func (dmp * DiffMatchPatch ) DiffCleanupSemantic (diffs []Diff ) []Diff {
629
646
changes := false
630
647
// Stack of indices where equalities are found.
631
- type equality struct {
632
- data int
633
- next * equality
634
- }
635
- var equalities * equality
648
+ equalities := make ([]int , 0 , len (diffs ))
636
649
637
650
var lastequality string
638
651
// Always equal to diffs[equalities[equalitiesLength - 1]][1]
@@ -645,11 +658,7 @@ func (dmp *DiffMatchPatch) DiffCleanupSemantic(diffs []Diff) []Diff {
645
658
for pointer < len (diffs ) {
646
659
if diffs [pointer ].Type == DiffEqual {
647
660
// Equality found.
648
-
649
- equalities = & equality {
650
- data : pointer ,
651
- next : equalities ,
652
- }
661
+ equalities = append (equalities , pointer )
653
662
lengthInsertions1 = lengthInsertions2
654
663
lengthDeletions1 = lengthDeletions2
655
664
lengthInsertions2 = 0
@@ -670,23 +679,20 @@ func (dmp *DiffMatchPatch) DiffCleanupSemantic(diffs []Diff) []Diff {
670
679
(len (lastequality ) <= difference1 ) &&
671
680
(len (lastequality ) <= difference2 ) {
672
681
// Duplicate record.
673
- insPoint := equalities .data
674
- diffs = append (
675
- diffs [:insPoint ],
676
- append ([]Diff {Diff {DiffDelete , lastequality }}, diffs [insPoint :]... )... )
682
+ insPoint := equalities [len (equalities )- 1 ]
683
+ diffs = splice (diffs , insPoint , 0 , Diff {DiffDelete , lastequality })
677
684
678
685
// Change second copy to insert.
679
686
diffs [insPoint + 1 ].Type = DiffInsert
680
687
// Throw away the equality we just deleted.
681
- equalities = equalities . next
688
+ equalities = equalities [: len ( equalities ) - 1 ]
682
689
683
- if equalities != nil {
684
- equalities = equalities . next
690
+ if len ( equalities ) > 0 {
691
+ equalities = equalities [: len ( equalities ) - 1 ]
685
692
}
686
- if equalities != nil {
687
- pointer = equalities .data
688
- } else {
689
- pointer = - 1
693
+ pointer = - 1
694
+ if len (equalities ) > 0 {
695
+ pointer = equalities [len (equalities )- 1 ]
690
696
}
691
697
692
698
lengthInsertions1 = 0 // Reset the counters.
@@ -724,10 +730,7 @@ func (dmp *DiffMatchPatch) DiffCleanupSemantic(diffs []Diff) []Diff {
724
730
float64 (overlapLength1 ) >= float64 (len (insertion ))/ 2 {
725
731
726
732
// Overlap found. Insert an equality and trim the surrounding edits.
727
- diffs = append (
728
- diffs [:pointer ],
729
- append ([]Diff {Diff {DiffEqual , insertion [:overlapLength1 ]}}, diffs [pointer :]... )... )
730
-
733
+ diffs = splice (diffs , pointer , 0 , Diff {DiffEqual , insertion [:overlapLength1 ]})
731
734
diffs [pointer - 1 ].Text =
732
735
deletion [0 : len (deletion )- overlapLength1 ]
733
736
diffs [pointer + 1 ].Text = insertion [overlapLength1 :]
@@ -738,10 +741,7 @@ func (dmp *DiffMatchPatch) DiffCleanupSemantic(diffs []Diff) []Diff {
738
741
float64 (overlapLength2 ) >= float64 (len (insertion ))/ 2 {
739
742
// Reverse overlap found. Insert an equality and swap and trim the surrounding edits.
740
743
overlap := Diff {DiffEqual , deletion [:overlapLength2 ]}
741
- diffs = append (
742
- diffs [:pointer ],
743
- append ([]Diff {overlap }, diffs [pointer :]... )... )
744
-
744
+ diffs = splice (diffs , pointer , 0 , overlap )
745
745
diffs [pointer - 1 ].Type = DiffInsert
746
746
diffs [pointer - 1 ].Text = insertion [0 : len (insertion )- overlapLength2 ]
747
747
diffs [pointer + 1 ].Type = DiffDelete
@@ -954,8 +954,7 @@ func (dmp *DiffMatchPatch) DiffCleanupEfficiency(diffs []Diff) []Diff {
954
954
insPoint := equalities .data
955
955
956
956
// Duplicate record.
957
- diffs = append (diffs [:insPoint ],
958
- append ([]Diff {Diff {DiffDelete , lastequality }}, diffs [insPoint :]... )... )
957
+ diffs = splice (diffs , insPoint , 0 , Diff {DiffDelete , lastequality })
959
958
960
959
// Change second copy to insert.
961
960
diffs [insPoint + 1 ].Type = DiffInsert
0 commit comments