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)
-}