summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/library/scala/collection/mutable/StringBuilder.scala134
-rw-r--r--test/files/run/stringbuilder.scala32
2 files changed, 97 insertions, 69 deletions
diff --git a/src/library/scala/collection/mutable/StringBuilder.scala b/src/library/scala/collection/mutable/StringBuilder.scala
index bcb85fe97f..c8ddb065ea 100644
--- a/src/library/scala/collection/mutable/StringBuilder.scala
+++ b/src/library/scala/collection/mutable/StringBuilder.scala
@@ -879,79 +879,75 @@ object StringBuilder {
dest
}
- private def indexOf(source: Array[Char], sourceOffset: Int, sourceCount: Int,
- target: Array[Char], targetOffset: Int, targetCount: Int,
- fromIndex: Int): Int =
- // todo: There are faster string search algorithms than this!
- // we should use at least KMP here.
- if (fromIndex >= sourceCount)
- if (targetCount == 0) sourceCount else -1
- else {
- val inx = if (fromIndex < 0) 0 else fromIndex
- if (targetCount == 0)
- inx
- else {
- val first = target(targetOffset)
- val max = sourceOffset + (sourceCount - targetCount)
-
- var i = sourceOffset + inx
- while (i <= max) {
- /* Look for first character. */
- if (source(i) != first) {
- i += 1
- while (i <= max && source(i) != first) i += 1
- }
- /* Found first character, now look at the rest of v2 */
- if (i <= max) {
- var j = i + 1
- val end = j + targetCount - 1
- var k = targetOffset + 1
- while (j < end && source(j) == target(k)) {
- j += 1
- k += 1
- }
- if (j == end) {
- /* Found whole string. */
- return i - sourceOffset
- }
- } // if
- i += 1
- } // while
- -1
- }
+ // KMP implementation by paulp, based on the undoubtedly reliable wikipedia entry
+ private def KMP(S: Array[Char], W: Array[Char]): Option[Int] = {
+ // trivial cases
+ if (W.length == 0) return Some(0)
+ else if (W.length == 1) return S.indexOf(W(0)) match {
+ case -1 => None
+ case x => Some(x)
}
- private def lastIndexOf(source: Array[Char], sourceOffset: Int, sourceCount: Int,
- target: Array[Char], targetOffset: Int, targetCount: Int,
- fromIndex: Int): Int = {
- val rightIndex = sourceCount - targetCount
- if (fromIndex < 0) return -1
- val inx = if (fromIndex > rightIndex) rightIndex else fromIndex
- // Empty string always matches
- if (targetCount == 0) return inx
-
- val strLastIndex = targetOffset + targetCount - 1
- val strLastChar = target(strLastIndex)
- val min = sourceOffset + targetCount - 1
- var i = min + fromIndex
-
- while (true) {
- while (i >= min && source(i) != strLastChar) i -= 1
- if (i < min) return -1
- var j = i - 1
- val start = j - (targetCount - 1)
- var k = strLastIndex - 1
- var outerWhile = false
- while (j > start && !outerWhile) {
- if (source(j) != target(k)) {
- j -= 1
- k -= 1
- i -= 1
- outerWhile = true
+ val T: Array[Int] = {
+ val arr = new Array[Int](W.length)
+ var pos = 2
+ var cnd = 0
+ arr(0) = -1
+ arr(1) = 0
+ while (pos < W.length) {
+ if (W(pos - 1) == W(cnd)) {
+ arr(pos) = cnd + 1
+ pos += 1
+ cnd += 1
+ }
+ else if (cnd > 0) {
+ cnd = arr(cnd)
+ }
+ else {
+ arr(pos) = 0
+ pos += 1
}
}
- if (!outerWhile) return start - sourceOffset + 1
+ arr
}
- -1
+
+ var m, i = 0
+ def mi = m + i
+
+ while (mi < S.length) {
+ if (W(i) == S(mi)) {
+ i += 1
+ if (i == W.length)
+ return Some(m)
+ }
+ else {
+ m = mi - T(i)
+ if (i > 0)
+ i = T(i)
+ }
+ }
+ None
}
+
+ private def indexOf(
+ source: Array[Char], sourceOffset: Int, sourceCount: Int,
+ target: Array[Char], targetOffset: Int, targetCount: Int,
+ fromIndex: Int): Int =
+ KMP(source.slice(sourceOffset, sourceCount) drop fromIndex, target.slice(targetOffset, targetCount)) match {
+ case None => -1
+ case Some(x) => x + fromIndex
+ }
+
+ private def lastIndexOf(
+ source: Array[Char], sourceOffset: Int, sourceCount: Int,
+ target: Array[Char], targetOffset: Int, targetCount: Int,
+ fromIndex: Int): Int = {
+ val src = (source.slice(sourceOffset, sourceCount) take fromIndex).reverse
+ val tgt = target.slice(targetOffset, targetCount).reverse
+
+ KMP(src, tgt) match {
+ case None => -1
+ case Some(x) => (src.length - tgt.length - x) + sourceOffset
+ }
+ }
}
diff --git a/test/files/run/stringbuilder.scala b/test/files/run/stringbuilder.scala
new file mode 100644
index 0000000000..eb0fccd067
--- /dev/null
+++ b/test/files/run/stringbuilder.scala
@@ -0,0 +1,32 @@
+object Test extends Application {
+ val str = "ABCDEFGHIJKLMABCDEFGHIJKLM"
+ type SB = {
+ def indexOf(str: String): Int
+ def indexOf(str: String, fromIndex: Int): Int
+ def lastIndexOf(str: String): Int
+ def lastIndexOf(str: String, fromIndex: Int): Int
+ }
+
+ import scala.collection.mutable.{ StringBuilder => ScalaStringBuilder }
+ import java.lang.{ StringBuilder => JavaStringBuilder }
+
+ val sbScala = new ScalaStringBuilder() append str
+ val sbJava = new JavaStringBuilder() append str
+ val sbs: List[SB] = List[SB](sbScala, sbJava)
+
+ def sameAnswers(f: (SB) => Int) = assert(f(sbScala) == f(sbJava))
+
+ sameAnswers(_.indexOf(""))
+ sameAnswers(_.indexOf("G"))
+ sameAnswers(_.indexOf("ABC"))
+ sameAnswers(_.indexOf("KLM"))
+ sameAnswers(_.indexOf("QZV"))
+ sameAnswers(_.indexOf("LMABC"))
+ sameAnswers(_.lastIndexOf(""))
+ sameAnswers(_.lastIndexOf("M"))
+ sameAnswers(_.lastIndexOf("ABCDEFG"))
+ sameAnswers(_.lastIndexOf("KLM"))
+ sameAnswers(_.lastIndexOf("QZV"))
+ sameAnswers(_.lastIndexOf("GHI", 22))
+ sameAnswers(_.lastIndexOf("KLM", 22))
+} \ No newline at end of file