Merge pull request #15 from dannyyoo/master
Correcting indexOf and adding lastIndexOf.
diff --git a/diffmatchpatch/dmp.go b/diffmatchpatch/dmp.go
index 0ec5f51..fb560f4 100644
--- a/diffmatchpatch/dmp.go
+++ b/diffmatchpatch/dmp.go
@@ -70,25 +70,31 @@
return append(slice[:index], append(elements, slice[index+amount:]...)...)
}
+// indexOf returns the first index of pattern in str, starting at str[i].
func indexOf(str string, pattern string, i int) int {
if i > len(str)-1 {
return -1
}
-
- if i == 0 {
+ if i <= 0 {
return strings.Index(str, pattern)
}
-
- str1 := str[0:i]
- str2 := str[i:]
-
- ind := strings.Index(str2, pattern)
+ ind := strings.Index(str[i:], pattern)
if ind == -1 {
return -1
}
+ return ind + i
+}
- return ind + len(str1)
-
+// lastIndexOf returns the last index of pattern in str, starting at str[i].
+func lastIndexOf(str string, pattern string, i int) int {
+ if i < 0 {
+ return -1
+ }
+ if i >= len(str) {
+ return strings.LastIndex(str, pattern)
+ }
+ _, size := utf8.DecodeRuneInString(str[i:])
+ return strings.LastIndex(str[:i+size], pattern)
}
// Return the index of pattern in target, starting at target[i].
@@ -96,11 +102,14 @@
if i > len(target)-1 {
return -1
}
+ if i <= 0 {
+ return runesIndex(target, pattern)
+ }
ind := runesIndex(target[i:], pattern)
if ind == -1 {
return -1
}
- return i + ind
+ return ind + i
}
func min(x, y int) int {
@@ -1583,12 +1592,12 @@
// Highest score beyond which we give up.
var score_threshold float64 = dmp.MatchThreshold
// Is there a nearby exact match? (speedup)
- best_loc := strings.Index(text, pattern)
+ best_loc := indexOf(text, pattern, loc)
if best_loc != -1 {
score_threshold = math.Min(dmp.matchBitapScore(0, best_loc, loc,
pattern), score_threshold)
// What about in the other direction? (speedup)
- best_loc = strings.LastIndex(text, pattern)
+ best_loc = lastIndexOf(text, pattern, loc+len(pattern))
if best_loc != -1 {
score_threshold = math.Min(dmp.matchBitapScore(0, best_loc, loc,
pattern), score_threshold)
diff --git a/diffmatchpatch/dmp_test.go b/diffmatchpatch/dmp_test.go
index 086e5df..34d696e 100644
--- a/diffmatchpatch/dmp_test.go
+++ b/diffmatchpatch/dmp_test.go
@@ -257,7 +257,6 @@
assert.True(t, utf8.ValidString(d.Text))
}
}
-
func Test_diffLinesToChars(t *testing.T) {
dmp := New()
@@ -1385,6 +1384,80 @@
assert.Equal(t, "x123\ttrue", resultStr, "patch_apply: Edge partial match.")
}
+func TestIndexOf(t *testing.T) {
+ type TestCase struct {
+ String string
+ Pattern string
+ Position int
+ Expected int
+ }
+ cases := []TestCase{
+ {"hi world", "world", -1, 3},
+ {"hi world", "world", 0, 3},
+ {"hi world", "world", 1, 3},
+ {"hi world", "world", 2, 3},
+ {"hi world", "world", 3, 3},
+ {"hi world", "world", 4, -1},
+ {"abbc", "b", -1, 1},
+ {"abbc", "b", 0, 1},
+ {"abbc", "b", 1, 1},
+ {"abbc", "b", 2, 2},
+ {"abbc", "b", 3, -1},
+ {"abbc", "b", 4, -1},
+ // The greek letter beta is the two-byte sequence of "\u03b2".
+ {"a\u03b2\u03b2c", "\u03b2", -1, 1},
+ {"a\u03b2\u03b2c", "\u03b2", 0, 1},
+ {"a\u03b2\u03b2c", "\u03b2", 1, 1},
+ {"a\u03b2\u03b2c", "\u03b2", 3, 3},
+ {"a\u03b2\u03b2c", "\u03b2", 5, -1},
+ {"a\u03b2\u03b2c", "\u03b2", 6, -1},
+ }
+ for i, c := range cases {
+ actual := indexOf(c.String, c.Pattern, c.Position)
+ assert.Equal(t, c.Expected, actual, fmt.Sprintf("TestIndex case %d", i))
+ }
+}
+
+func TestLastIndexOf(t *testing.T) {
+ type TestCase struct {
+ String string
+ Pattern string
+ Position int
+ Expected int
+ }
+ cases := []TestCase{
+ {"hi world", "world", -1, -1},
+ {"hi world", "world", 0, -1},
+ {"hi world", "world", 1, -1},
+ {"hi world", "world", 2, -1},
+ {"hi world", "world", 3, -1},
+ {"hi world", "world", 4, -1},
+ {"hi world", "world", 5, -1},
+ {"hi world", "world", 6, -1},
+ {"hi world", "world", 7, 3},
+ {"hi world", "world", 8, 3},
+ {"abbc", "b", -1, -1},
+ {"abbc", "b", 0, -1},
+ {"abbc", "b", 1, 1},
+ {"abbc", "b", 2, 2},
+ {"abbc", "b", 3, 2},
+ {"abbc", "b", 4, 2},
+ // The greek letter beta is the two-byte sequence of "\u03b2".
+ {"a\u03b2\u03b2c", "\u03b2", -1, -1},
+ {"a\u03b2\u03b2c", "\u03b2", 0, -1},
+ {"a\u03b2\u03b2c", "\u03b2", 1, 1},
+ {"a\u03b2\u03b2c", "\u03b2", 3, 3},
+ {"a\u03b2\u03b2c", "\u03b2", 5, 3},
+ {"a\u03b2\u03b2c", "\u03b2", 6, 3},
+ }
+
+ for i, c := range cases {
+ actual := lastIndexOf(c.String, c.Pattern, c.Position)
+ assert.Equal(t, c.Expected, actual,
+ fmt.Sprintf("TestLastIndex case %d", i))
+ }
+}
+
func Benchmark_DiffMain(bench *testing.B) {
dmp := New()
dmp.DiffTimeout = time.Second