Skip to content

Commit b4f37f9

Browse files
authored
Merge pull request #1
fix panic: runtime error: slice bounds out of range
2 parents 5b0b94c + 08f5669 commit b4f37f9

File tree

8 files changed

+101
-40
lines changed

8 files changed

+101
-40
lines changed

diffmatchpatch/diff.go

+18-17
Original file line numberDiff line numberDiff line change
@@ -388,30 +388,31 @@ func (dmp *DiffMatchPatch) diffBisectSplit(runes1, runes2 []rune, x, y int,
388388
}
389389

390390
// DiffLinesToChars splits two texts into a list of strings, and educes the texts to a string of hashes where each Unicode character represents one line.
391-
// It's slightly faster to call DiffLinesToRunes first, followed by DiffMainRunes.
392391
func (dmp *DiffMatchPatch) DiffLinesToChars(text1, text2 string) (string, string, []string) {
393-
chars1, chars2, lineArray := dmp.diffLinesToStrings(text1, text2)
394-
return chars1, chars2, lineArray
392+
chars1, chars2, lineArray := dmp.diffLinesToIndexes(text1, text2)
393+
return indexesToString(chars1), indexesToString(chars2), lineArray
395394
}
396395

397396
// DiffLinesToRunes splits two texts into a list of runes.
398397
func (dmp *DiffMatchPatch) DiffLinesToRunes(text1, text2 string) ([]rune, []rune, []string) {
398+
chars1, chars2, lineArray := dmp.diffLinesToIndexes(text1, text2)
399+
return []rune(indexesToString(chars1)), []rune(indexesToString(chars2)), lineArray
400+
}
401+
402+
func (dmp *DiffMatchPatch) diffLinesToIndexes(text1, text2 string) ([]index, []index, []string) {
399403
chars1, chars2, lineArray := dmp.diffLinesToStrings(text1, text2)
400-
return []rune(chars1), []rune(chars2), lineArray
404+
return chars1, chars2, lineArray
401405
}
402406

403407
// DiffCharsToLines rehydrates the text in a diff from a string of line hashes to real lines of text.
404408
func (dmp *DiffMatchPatch) DiffCharsToLines(diffs []Diff, lineArray []string) []Diff {
405409
hydrated := make([]Diff, 0, len(diffs))
406410
for _, aDiff := range diffs {
407-
runes := []rune(aDiff.Text)
408-
text := make([]string, len(runes))
409-
410-
for i, r := range runes {
411-
text[i] = lineArray[runeToInt(r)]
411+
var sb strings.Builder
412+
for _, i := range stringToIndex(aDiff.Text) {
413+
sb.WriteString(lineArray[i])
412414
}
413-
414-
aDiff.Text = strings.Join(text, "")
415+
aDiff.Text = sb.String()
415416
hydrated = append(hydrated, aDiff)
416417
}
417418
return hydrated
@@ -1304,7 +1305,7 @@ func (dmp *DiffMatchPatch) DiffFromDelta(text1 string, delta string) (diffs []Di
13041305
}
13051306

13061307
// diffLinesToStrings splits two texts into a list of strings. Each string represents one line.
1307-
func (dmp *DiffMatchPatch) diffLinesToStrings(text1, text2 string) (string, string, []string) {
1308+
func (dmp *DiffMatchPatch) diffLinesToStrings(text1, text2 string) ([]index, []index, []string) {
13081309
// '\x00' is a valid character, but various debuggers don't like it. So we'll insert a junk entry to avoid generating a null character.
13091310
lineArray := []string{""} // e.g. lineArray[4] == 'Hello\n'
13101311

@@ -1313,15 +1314,15 @@ func (dmp *DiffMatchPatch) diffLinesToStrings(text1, text2 string) (string, stri
13131314
strIndexArray1 := dmp.diffLinesToStringsMunge(text1, &lineArray, lineHash)
13141315
strIndexArray2 := dmp.diffLinesToStringsMunge(text2, &lineArray, lineHash)
13151316

1316-
return intArrayToString(strIndexArray1), intArrayToString(strIndexArray2), lineArray
1317+
return strIndexArray1, strIndexArray2, lineArray
13171318
}
13181319

13191320
// diffLinesToStringsMunge splits a text into an array of strings, and reduces the texts to a []string.
1320-
func (dmp *DiffMatchPatch) diffLinesToStringsMunge(text string, lineArray *[]string, lineHash map[string]int) []uint32 {
1321+
func (dmp *DiffMatchPatch) diffLinesToStringsMunge(text string, lineArray *[]string, lineHash map[string]int) []index {
13211322
// Walk the text, pulling out a substring for each line. text.split('\n') would would temporarily double our memory footprint. Modifying text would create many large strings to garbage collect.
13221323
lineStart := 0
13231324
lineEnd := -1
1324-
strs := []uint32{}
1325+
strs := []index{}
13251326

13261327
for lineEnd < len(text)-1 {
13271328
lineEnd = indexOf(text, "\n", lineStart)
@@ -1335,11 +1336,11 @@ func (dmp *DiffMatchPatch) diffLinesToStringsMunge(text string, lineArray *[]str
13351336
lineValue, ok := lineHash[line]
13361337

13371338
if ok {
1338-
strs = append(strs, uint32(lineValue))
1339+
strs = append(strs, index(lineValue))
13391340
} else {
13401341
*lineArray = append(*lineArray, line)
13411342
lineHash[line] = len(*lineArray) - 1
1342-
strs = append(strs, uint32(len(*lineArray)-1))
1343+
strs = append(strs, index(len(*lineArray)-1))
13431344
}
13441345
}
13451346

diffmatchpatch/diff_test.go

+8-6
Original file line numberDiff line numberDiff line change
@@ -332,13 +332,14 @@ func TestDiffLinesToChars(t *testing.T) {
332332
lineList := []string{
333333
"", // Account for the initial empty element of the lines array.
334334
}
335-
var charList []rune
335+
var charList []index
336336
for x := 1; x < n+1; x++ {
337337
lineList = append(lineList, strconv.Itoa(x)+"\n")
338-
charList = append(charList, rune(x))
338+
charList = append(charList, index(x))
339339
}
340340
lines := strings.Join(lineList, "")
341-
chars := string(charList)
341+
chars := indexesToString(charList)
342+
assert.Equal(t, n, len(charList))
342343

343344
actualChars1, actualChars2, actualLines := dmp.DiffLinesToChars(lines, "")
344345
assert.Equal(t, chars, actualChars1)
@@ -379,12 +380,13 @@ func TestDiffCharsToLines(t *testing.T) {
379380
lineList := []string{
380381
"", // Account for the initial empty element of the lines array.
381382
}
382-
charList := []rune{}
383+
charList := []index{}
383384
for x := 1; x <= n; x++ {
384385
lineList = append(lineList, strconv.Itoa(x)+"\n")
385-
charList = append(charList, rune(x))
386+
charList = append(charList, index(x))
386387
}
387-
chars := string(charList)
388+
assert.Equal(t, n, len(charList))
389+
chars := indexesToString(charList)
388390

389391
actual := dmp.DiffCharsToLines([]Diff{Diff{DiffDelete, chars}}, lineList)
390392
assert.Equal(t, []Diff{Diff{DiffDelete, strings.Join(lineList, "")}}, actual)

diffmatchpatch/index.go

+32
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
package diffmatchpatch
2+
3+
type index uint32
4+
5+
const runeSkipStart = 0xd800
6+
const runeSkipEnd = 0xdfff + 1
7+
const runeMax = 0x110000 // next invalid code point
8+
9+
func stringToIndex(text string) []index {
10+
runes := []rune(text)
11+
indexes := make([]index, len(runes))
12+
for i, r := range runes {
13+
if r < runeSkipEnd {
14+
indexes[i] = index(r)
15+
} else {
16+
indexes[i] = index(r) - (runeSkipEnd - runeSkipStart)
17+
}
18+
}
19+
return indexes
20+
}
21+
22+
func indexesToString(indexes []index) string {
23+
runes := make([]rune, len(indexes))
24+
for i, index := range indexes {
25+
if index < runeSkipStart {
26+
runes[i] = rune(index)
27+
} else {
28+
runes[i] = rune(index + (runeSkipEnd - runeSkipStart))
29+
}
30+
}
31+
return string(runes)
32+
}

diffmatchpatch/index_test.go

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
package diffmatchpatch
2+
3+
import (
4+
"testing"
5+
6+
"github.com/stretchr/testify/assert"
7+
)
8+
9+
func TestIndexConversion(t *testing.T) {
10+
n := runeMax - (runeSkipEnd - runeSkipStart)
11+
indexes := make([]index, n)
12+
for i := 0; i < n; i++ {
13+
indexes[i] = index(i)
14+
}
15+
indexes2 := stringToIndex(indexesToString(indexes))
16+
assert.EqualValues(t, indexes, indexes2)
17+
}

diffmatchpatch/patch_test.go

+25
Original file line numberDiff line numberDiff line change
@@ -337,3 +337,28 @@ func TestPatchApply(t *testing.T) {
337337
assert.Equal(t, tc.ExpectedApplies, actualApplies, fmt.Sprintf("Test case #%d, %s", i, tc.Name))
338338
}
339339
}
340+
341+
func TestPatchMakeOutOfRangePanic(t *testing.T) {
342+
text1 := `
343+
1111111111111 000000
344+
------------- ------
345+
xxxxxxxxxxxxx ------
346+
xxxxxxxxxxxxx ------
347+
xxxxxxxxxxxxx xxxxxx
348+
xxxxxxxxxxxxx ......
349+
xxxxxxxxxxxxx 111111
350+
xxxxxxxxxxxxx ??????
351+
xxxxxxxxxxxxx 333333
352+
xxxxxxxxxxxxx 555555
353+
xxxxxxxxxx xxxxx
354+
xxxxxxxxxx xxxxx
355+
xxxxxxxxxx xxxxx
356+
xxxxxxxxxx xxxxx
357+
`
358+
text2 := `
359+
2222222222222 000000
360+
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx`
361+
dmp := New()
362+
patches := dmp.PatchMake(text1, text2)
363+
assert.Equal(t, 6, len(patches), "TestPatchMakeOutOfRangePanic")
364+
}

diffmatchpatch/stringutil.go

-12
Original file line numberDiff line numberDiff line change
@@ -93,18 +93,6 @@ func runesIndex(r1, r2 []rune) int {
9393
return -1
9494
}
9595

96-
func intArrayToString(ns []uint32) string {
97-
if len(ns) == 0 {
98-
return ""
99-
}
100-
101-
b := []rune{}
102-
for _, n := range ns {
103-
b = append(b, intToRune(n))
104-
}
105-
return string(b)
106-
}
107-
10896
// These constants define the number of bits representable
10997
// in 1,2,3,4 byte utf8 sequences, respectively.
11098
const ONE_BYTE_BITS = 7

go.mod

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
module github.com/sergi/go-diff
1+
module github.com/goto/go-diff
22

33
require (
44
github.com/davecgh/go-spew v1.1.1 // indirect

go.sum

-4
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
21
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
32
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
43
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
@@ -15,9 +14,6 @@ github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81P
1514
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
1615
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
1716
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
18-
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
1917
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
20-
gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I=
21-
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
2218
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
2319
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=

0 commit comments

Comments
 (0)