diff --git a/akka-actor-tests/src/test/scala/akka/util/ByteStringSpec.scala b/akka-actor-tests/src/test/scala/akka/util/ByteStringSpec.scala index 1e2fce7b4c..1514635c8a 100644 --- a/akka-actor-tests/src/test/scala/akka/util/ByteStringSpec.scala +++ b/akka-actor-tests/src/test/scala/akka/util/ByteStringSpec.scala @@ -21,11 +21,7 @@ import scala.collection.mutable.Builder class ByteStringSpec extends WordSpec with Matchers with Checkers { - // // uncomment when developing locally to get better coverage - // implicit override val generatorDrivenConfig = - // PropertyCheckConfig( - // minSuccessful = 1000, - // minSize = 0, maxSize = 100) + implicit val betterGeneratorDrivenConfig = PropertyCheckConfig().copy(minSuccessful = 1000) def genSimpleByteString(min: Int, max: Int) = for { n ← Gen.choose(min, max) @@ -365,13 +361,48 @@ class ByteStringSpec extends WordSpec with Matchers with Checkers { ByteStrings(ByteString1.fromString(""), ByteString1.fromString("ab")).dropRight(Int.MinValue) should ===(ByteString("ab")) } "slice" in { - ByteStrings(ByteString1.fromString("a"), ByteString1.fromString("")).slice(0, 1) should ===(ByteString("a")) ByteStrings(ByteString1.fromString(""), ByteString1.fromString("a")).slice(1, 1) should ===(ByteString("")) + // We explicitly test all edge cases to always test them, refs bug #21237 + ByteStrings(ByteString1.fromString("a"), ByteString1.fromString("")).slice(-10, 10) should ===(ByteString("a")) + ByteStrings(ByteString1.fromString("a"), ByteString1.fromString("")).slice(-10, 0) should ===(ByteString("")) + ByteStrings(ByteString1.fromString("a"), ByteString1.fromString("")).slice(-10, 1) should ===(ByteString("a")) + ByteStrings(ByteString1.fromString("a"), ByteString1.fromString("")).slice(0, 1) should ===(ByteString("a")) + ByteStrings(ByteString1.fromString("a"), ByteString1.fromString("")).slice(0, 10) should ===(ByteString("a")) + ByteStrings(ByteString1.fromString("a"), ByteString1.fromString("")).slice(1, 10) should ===(ByteString("")) + ByteStrings(ByteString1.fromString("a"), ByteString1.fromString("")).slice(1, -2) should ===(ByteString("")) + ByteStrings(ByteString1.fromString("a"), ByteString1.fromString("")).slice(-10, -100) should ===(ByteString("")) + ByteStrings(ByteString1.fromString("a"), ByteString1.fromString("")).slice(-100, -10) should ===(ByteString("")) + // Get an empty if `from` is greater then `until` + ByteStrings(ByteString1.fromString("a"), ByteString1.fromString("")).slice(1, 0) should ===(ByteString("")) + ByteStrings(ByteString1.fromString("ab"), ByteString1.fromString("cd")).slice(2, 2) should ===(ByteString("")) ByteStrings(ByteString1.fromString("ab"), ByteString1.fromString("cd")).slice(2, 3) should ===(ByteString("c")) ByteStrings(ByteString1.fromString("ab"), ByteString1.fromString("cd")).slice(2, 4) should ===(ByteString("cd")) ByteStrings(ByteString1.fromString("ab"), ByteString1.fromString("cd")).slice(3, 4) should ===(ByteString("d")) - ByteStrings(ByteString1.fromString("ab"), ByteString1.fromString("cd")).slice(10, 100) should ===(ByteString("")) + // Can obtain expected results from 6 basic patterns + ByteStrings(ByteString1.fromString("ab"), ByteString1.fromString("cd")).slice(-10, 10) should ===(ByteString("abcd")) + ByteStrings(ByteString1.fromString("ab"), ByteString1.fromString("cd")).slice(-10, 0) should ===(ByteString("")) + ByteStrings(ByteString1.fromString("ab"), ByteString1.fromString("cd")).slice(-10, 4) should ===(ByteString("abcd")) + ByteStrings(ByteString1.fromString("ab"), ByteString1.fromString("cd")).slice(0, 4) should ===(ByteString("abcd")) + ByteStrings(ByteString1.fromString("ab"), ByteString1.fromString("cd")).slice(1, -2) should ===(ByteString("")) + ByteStrings(ByteString1.fromString("ab"), ByteString1.fromString("cd")).slice(0, 10) should ===(ByteString("abcd")) + ByteStrings(ByteString1.fromString("ab"), ByteString1.fromString("cd")).slice(-10, -100) should ===(ByteString("")) + ByteStrings(ByteString1.fromString("ab"), ByteString1.fromString("cd")).slice(-100, -10) should ===(ByteString("")) + + ByteStrings(ByteString1.fromString("a"), ByteString1.fromString("")).slice(1, -2) should ===(ByteString("")) + ByteStrings(ByteString1.fromString("a"), ByteString1.fromString("")).slice(-10, -100) should ===(ByteString("")) + ByteStrings(ByteString1.fromString("a"), ByteString1.fromString("")).slice(-100, -10) should ===(ByteString("")) + + // various edge cases using raw ByteString1 + ByteString1.fromString("cd").slice(100, 10) should ===(ByteString("")) + ByteString1.fromString("cd").slice(100, 1000) should ===(ByteString("")) + ByteString1.fromString("cd").slice(-10, -5) should ===(ByteString("")) + ByteString1.fromString("cd").slice(-2, -5) should ===(ByteString("")) + ByteString1.fromString("cd").slice(-2, 1) should ===(ByteString("c")) + ByteString1.fromString("abcd").slice(1, -1) should ===(ByteString("")) + + // Get an empty if `from` is greater than `until` + ByteStrings(ByteString1.fromString("ab"), ByteString1.fromString("cd")).slice(4, 0) should ===(ByteString("")) } "dropRight" in { ByteStrings(ByteString1.fromString("a"), ByteString1.fromString("")).dropRight(0) should ===(ByteString("a")) diff --git a/akka-actor/src/main/scala/akka/util/ByteString.scala b/akka-actor/src/main/scala/akka/util/ByteString.scala index 6438d95c48..0b07bbdcc5 100644 --- a/akka-actor/src/main/scala/akka/util/ByteString.scala +++ b/akka-actor/src/main/scala/akka/util/ByteString.scala @@ -163,8 +163,8 @@ object ByteString { else toByteString1.drop(n) override def slice(from: Int, until: Int): ByteString = - if ((from == 0) && (until == length)) this - else if (from > length) ByteString.empty + if (from <= 0 && until >= length) this + else if (from >= length || until <= 0 || from >= until) ByteString.empty else toByteString1.slice(from, until) private[akka] override def writeToOutputStream(os: ObjectOutputStream): Unit = @@ -252,11 +252,8 @@ object ByteString { if (n <= 0) ByteString.empty else ByteString1(bytes, startIndex, Math.min(n, length)) - override def slice(from: Int, until: Int): ByteString = { - if (from <= 0 && until >= length) this // we can do < / > since we're Compact - else if (until <= from) ByteString1.empty - else ByteString1(bytes, startIndex + from, until - from) - } + override def slice(from: Int, until: Int): ByteString = + drop(from).take(until - Math.max(0, from)) override def copyToBuffer(buffer: ByteBuffer): Int = writeToBuffer(buffer) @@ -466,7 +463,7 @@ object ByteString { } override def slice(from: Int, until: Int): ByteString = - if ((from == 0) && (until == length)) this + if (from <= 0 && until >= length) this else if (from > length || until <= from) ByteString.empty else drop(from).dropRight(length - until)