Merge branch 'master' into master
diff --git a/diffmatchpatch/diff.go b/diffmatchpatch/diff.go
index 4f7b424..17ad990 100644
--- a/diffmatchpatch/diff.go
+++ b/diffmatchpatch/diff.go
@@ -34,8 +34,6 @@
DiffInsert Operation = 1
// DiffEqual item represents an equal diff.
DiffEqual Operation = 0
- //IndexSeparator is used to seperate the array indexes in an index string
- IndexSeparator = ","
)
// Diff represents one diff operation
@@ -390,33 +388,32 @@
}
// 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.
-// It's slightly faster to call DiffLinesToRunes first, followed by DiffMainRunes.
func (dmp *DiffMatchPatch) DiffLinesToChars(text1, text2 string) (string, string, []string) {
- chars1, chars2, lineArray := dmp.diffLinesToStrings(text1, text2)
- return chars1, chars2, lineArray
+ chars1, chars2, lineArray := dmp.diffLinesToIndexes(text1, text2)
+ return indexesToString(chars1), indexesToString(chars2), lineArray
}
// DiffLinesToRunes splits two texts into a list of runes.
func (dmp *DiffMatchPatch) DiffLinesToRunes(text1, text2 string) ([]rune, []rune, []string) {
+ chars1, chars2, lineArray := dmp.diffLinesToIndexes(text1, text2)
+ return []rune(indexesToString(chars1)), []rune(indexesToString(chars2)), lineArray
+}
+
+// diffLinesToIndexes splits two texts into a list of indexes
+func (dmp *DiffMatchPatch) diffLinesToIndexes(text1, text2 string) ([]index, []index, []string) {
chars1, chars2, lineArray := dmp.diffLinesToStrings(text1, text2)
- return []rune(chars1), []rune(chars2), lineArray
+ return chars1, chars2, lineArray
}
// DiffCharsToLines rehydrates the text in a diff from a string of line hashes to real lines of text.
func (dmp *DiffMatchPatch) DiffCharsToLines(diffs []Diff, lineArray []string) []Diff {
hydrated := make([]Diff, 0, len(diffs))
for _, aDiff := range diffs {
- chars := strings.Split(aDiff.Text, IndexSeparator)
- text := make([]string, len(chars))
-
- for i, r := range chars {
- i1, err := strconv.Atoi(r)
- if err == nil {
- text[i] = lineArray[i1]
- }
+ var sb strings.Builder
+ for _, i := range stringToIndex(aDiff.Text) {
+ sb.WriteString(lineArray[i])
}
-
- aDiff.Text = strings.Join(text, "")
+ aDiff.Text = sb.String()
hydrated = append(hydrated, aDiff)
}
return hydrated
@@ -1309,7 +1306,7 @@
}
// diffLinesToStrings splits two texts into a list of strings. Each string represents one line.
-func (dmp *DiffMatchPatch) diffLinesToStrings(text1, text2 string) (string, string, []string) {
+func (dmp *DiffMatchPatch) diffLinesToStrings(text1, text2 string) ([]index, []index, []string) {
// '\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.
lineArray := []string{""} // e.g. lineArray[4] == 'Hello\n'
@@ -1318,7 +1315,7 @@
strIndexArray1 := dmp.diffLinesToStringsMunge(text1, &lineArray, lineHash)
strIndexArray2 := dmp.diffLinesToStringsMunge(text2, &lineArray, lineHash)
- return intArrayToString(strIndexArray1), intArrayToString(strIndexArray2), lineArray
+ return strIndexArray1, strIndexArray2, lineArray
}
// diffLinesToStringsMunge splits a text into an array of strings, and reduces the texts to a []string.
@@ -1326,7 +1323,7 @@
// 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.
lineStart := 0
lineEnd := -1
- strs := []uint32{}
+ strs := []index{}
for lineEnd < len(text)-1 {
lineEnd = indexOf(text, "\n", lineStart)
@@ -1340,11 +1337,11 @@
lineValue, ok := lineHash[line]
if ok {
- strs = append(strs, uint32(lineValue))
+ strs = append(strs, index(lineValue))
} else {
*lineArray = append(*lineArray, line)
lineHash[line] = len(*lineArray) - 1
- strs = append(strs, uint32(len(*lineArray)-1))
+ strs = append(strs, index(len(*lineArray)-1))
}
}
diff --git a/diffmatchpatch/diff_test.go b/diffmatchpatch/diff_test.go
index d6fed50..d1611da 100644
--- a/diffmatchpatch/diff_test.go
+++ b/diffmatchpatch/diff_test.go
@@ -314,8 +314,8 @@
dmp := New()
for i, tc := range []TestCase{
- {"", "alpha\r\nbeta\r\n\r\n\r\n", "", "1,2,3,3", []string{"", "alpha\r\n", "beta\r\n", "\r\n"}},
- {"a", "b", "1", "2", []string{"", "a", "b"}},
+ {"", "alpha\r\nbeta\r\n\r\n\r\n", "", "\x01\x02\x03\x03", []string{"", "alpha\r\n", "beta\r\n", "\r\n"}},
+ {"a", "b", "\x01", "\x02", []string{"", "a", "b"}},
// Omit final newline.
{"alpha\nbeta\nalpha", "", "1,2,3", "", []string{"", "alpha\n", "beta\n", "alpha"}},
// Same lines in Text1 and Text2
@@ -332,14 +332,14 @@
lineList := []string{
"", // Account for the initial empty element of the lines array.
}
- var charList []string
+ var charList []index
for x := 1; x < n+1; x++ {
lineList = append(lineList, strconv.Itoa(x)+"\n")
- charList = append(charList, strconv.Itoa(x))
+ charList = append(charList, index(x))
}
lines := strings.Join(lineList, "")
- chars := strings.Join(charList[:], ",")
- assert.Equal(t, n, len(strings.Split(chars, ",")))
+ chars := indexesToString(charList)
+ assert.Equal(t, n, len(charList))
actualChars1, actualChars2, actualLines := dmp.DiffLinesToChars(lines, "")
assert.Equal(t, chars, actualChars1)
@@ -360,8 +360,8 @@
for i, tc := range []TestCase{
{
Diffs: []Diff{
- {DiffEqual, "1,2,1"},
- {DiffInsert, "2,1,2"},
+ {DiffEqual, "\x01\x02\x01"},
+ {DiffInsert, "\x02\x01\x02"},
},
Lines: []string{"", "alpha\n", "beta\n"},
@@ -380,13 +380,13 @@
lineList := []string{
"", // Account for the initial empty element of the lines array.
}
- charList := []string{}
+ charList := []index{}
for x := 1; x <= n; x++ {
lineList = append(lineList, strconv.Itoa(x)+"\n")
- charList = append(charList, strconv.Itoa(x))
+ charList = append(charList, index(x))
}
assert.Equal(t, n, len(charList))
- chars := strings.Join(charList[:], ",")
+ chars := indexesToString(charList)
actual := dmp.DiffCharsToLines([]Diff{Diff{DiffDelete, chars}}, lineList)
assert.Equal(t, []Diff{Diff{DiffDelete, strings.Join(lineList, "")}}, actual)
diff --git a/diffmatchpatch/index.go b/diffmatchpatch/index.go
new file mode 100644
index 0000000..965a1c6
--- /dev/null
+++ b/diffmatchpatch/index.go
@@ -0,0 +1,32 @@
+package diffmatchpatch
+
+type index uint32
+
+const runeSkipStart = 0xd800
+const runeSkipEnd = 0xdfff + 1
+const runeMax = 0x110000 // next invalid code point
+
+func stringToIndex(text string) []index {
+ runes := []rune(text)
+ indexes := make([]index, len(runes))
+ for i, r := range runes {
+ if r < runeSkipEnd {
+ indexes[i] = index(r)
+ } else {
+ indexes[i] = index(r) - (runeSkipEnd - runeSkipStart)
+ }
+ }
+ return indexes
+}
+
+func indexesToString(indexes []index) string {
+ runes := make([]rune, len(indexes))
+ for i, index := range indexes {
+ if index < runeSkipStart {
+ runes[i] = rune(index)
+ } else {
+ runes[i] = rune(index + (runeSkipEnd - runeSkipStart))
+ }
+ }
+ return string(runes)
+}
diff --git a/diffmatchpatch/index_test.go b/diffmatchpatch/index_test.go
new file mode 100644
index 0000000..6f1d982
--- /dev/null
+++ b/diffmatchpatch/index_test.go
@@ -0,0 +1,16 @@
+package diffmatchpatch
+
+import (
+ "github.com/stretchr/testify/assert"
+ "testing"
+)
+
+func TestIndexConversion(t *testing.T) {
+ n := runeMax - (runeSkipEnd - runeSkipStart)
+ indexes := make([]index, n)
+ for i := 0; i < n; i++ {
+ indexes[i] = index(i)
+ }
+ indexes2 := stringToIndex(indexesToString(indexes))
+ assert.EqualValues(t, indexes, indexes2)
+}
diff --git a/diffmatchpatch/patch_test.go b/diffmatchpatch/patch_test.go
index b019f88..c564f8c 100644
--- a/diffmatchpatch/patch_test.go
+++ b/diffmatchpatch/patch_test.go
@@ -337,3 +337,28 @@
assert.Equal(t, tc.ExpectedApplies, actualApplies, fmt.Sprintf("Test case #%d, %s", i, tc.Name))
}
}
+
+func TestPatchMakeOutOfRangePanic(t *testing.T) {
+ text1 := `
+ 1111111111111 000000
+ ------------- ------
+ xxxxxxxxxxxxx ------
+ xxxxxxxxxxxxx ------
+ xxxxxxxxxxxxx xxxxxx
+ xxxxxxxxxxxxx ......
+ xxxxxxxxxxxxx 111111
+ xxxxxxxxxxxxx ??????
+ xxxxxxxxxxxxx 333333
+ xxxxxxxxxxxxx 555555
+ xxxxxxxxxx xxxxx
+ xxxxxxxxxx xxxxx
+ xxxxxxxxxx xxxxx
+ xxxxxxxxxx xxxxx
+`
+ text2 := `
+ 2222222222222 000000
+ xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx`
+ dmp := New()
+ patches := dmp.PatchMake(text1, text2)
+ assert.Equal(t, 6, len(patches), "TestPatchMakeOutOfRangePanic")
+}
diff --git a/diffmatchpatch/stringutil.go b/diffmatchpatch/stringutil.go
index 44c4359..265f29c 100644
--- a/diffmatchpatch/stringutil.go
+++ b/diffmatchpatch/stringutil.go
@@ -9,7 +9,6 @@
package diffmatchpatch
import (
- "strconv"
"strings"
"unicode/utf8"
)
@@ -87,20 +86,3 @@
}
return -1
}
-
-func intArrayToString(ns []uint32) string {
- if len(ns) == 0 {
- return ""
- }
-
- indexSeparator := IndexSeparator[0]
-
- // Appr. 3 chars per num plus the comma.
- b := []byte{}
- for _, n := range ns {
- b = strconv.AppendInt(b, int64(n), 10)
- b = append(b, indexSeparator)
- }
- b = b[:len(b)-1]
- return string(b)
-}