Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -852,7 +852,6 @@ class ByteStringSpec extends AnyWordSpec with Matchers with Checkers {
compact.indexOf('e'.toByte) should ===(3)
compact.indexOf('f'.toByte) should ===(4)
compact.indexOf('g'.toByte) should ===(5)

}
"indexOf (specialized) from offset" in {
ByteString.empty.indexOf(5.toByte, -1) should ===(-1)
Expand Down Expand Up @@ -1001,6 +1000,56 @@ class ByteStringSpec extends AnyWordSpec with Matchers with Checkers {
byteStringLong.indexOf('z', 2, 24) should ===(-1)
byteStringLong.indexOf('a', 2, 24) should ===(-1)
}
"lastIndexOf (specialized)" in {
ByteString.empty.lastIndexOf(5.toByte, -1) should ===(-1)
ByteString.empty.lastIndexOf(5.toByte, 0) should ===(-1)
ByteString.empty.lastIndexOf(5.toByte, 1) should ===(-1)
ByteString.empty.lastIndexOf(5.toByte) should ===(-1)
val byteString1 = ByteString1.fromString("abb")
byteString1.lastIndexOf('d'.toByte) should ===(-1)
byteString1.lastIndexOf('d'.toByte, -1) should ===(-1)
byteString1.lastIndexOf('d'.toByte, 4) should ===(-1)
byteString1.lastIndexOf('d'.toByte, 1) should ===(-1)
byteString1.lastIndexOf('d'.toByte, 0) should ===(-1)
byteString1.lastIndexOf('a'.toByte, -1) should ===(-1)
byteString1.lastIndexOf('a'.toByte) should ===(0)
byteString1.lastIndexOf('a'.toByte, 0) should ===(0)
byteString1.lastIndexOf('a'.toByte, 1) should ===(0)
byteString1.lastIndexOf('b'.toByte) should ===(2)
byteString1.lastIndexOf('b'.toByte, 2) should ===(2)
byteString1.lastIndexOf('b'.toByte, 1) should ===(1)
byteString1.lastIndexOf('b'.toByte, 0) should ===(-1)

val byteStrings = ByteStrings(ByteString1.fromString("abb"), ByteString1.fromString("efg"))
byteStrings.lastIndexOf('e'.toByte) should ===(3)
byteStrings.lastIndexOf('e'.toByte, 6) should ===(3)
byteStrings.lastIndexOf('e'.toByte, 4) should ===(3)
byteStrings.lastIndexOf('e'.toByte, 1) should ===(-1)
byteStrings.lastIndexOf('e'.toByte, 0) should ===(-1)
byteStrings.lastIndexOf('e'.toByte, -1) should ===(-1)

byteStrings.lastIndexOf('b'.toByte) should ===(2)
byteStrings.lastIndexOf('b'.toByte, 6) should ===(2)
byteStrings.lastIndexOf('b'.toByte, 4) should ===(2)
byteStrings.lastIndexOf('b'.toByte, 1) should ===(1)
byteStrings.lastIndexOf('b'.toByte, 0) should ===(-1)
byteStrings.lastIndexOf('b'.toByte, -1) should ===(-1)

val compact = byteStrings.compact
compact.lastIndexOf('e'.toByte) should ===(3)
compact.lastIndexOf('e'.toByte, 6) should ===(3)
compact.lastIndexOf('e'.toByte, 4) should ===(3)
compact.lastIndexOf('e'.toByte, 1) should ===(-1)
compact.lastIndexOf('e'.toByte, 0) should ===(-1)
compact.lastIndexOf('e'.toByte, -1) should ===(-1)

compact.lastIndexOf('b'.toByte) should ===(2)
compact.lastIndexOf('b'.toByte, 6) should ===(2)
compact.lastIndexOf('b'.toByte, 4) should ===(2)
compact.lastIndexOf('b'.toByte, 1) should ===(1)
compact.lastIndexOf('b'.toByte, 0) should ===(-1)
compact.lastIndexOf('b'.toByte, -1) should ===(-1)
}
"copyToArray" in {
val byteString = ByteString(1, 2) ++ ByteString(3) ++ ByteString(4)

Expand Down
139 changes: 138 additions & 1 deletion actor/src/main/scala/org/apache/pekko/util/ByteString.scala
Original file line number Diff line number Diff line change
Expand Up @@ -314,6 +314,32 @@ object ByteString {
else -1
}

override def lastIndexOf[B >: Byte](elem: B, end: Int): Int = {
if (end < 0) -1
else {
var found = -1
var i = math.min(end, length - 1)
while (i >= 0 && found == -1) {
if (bytes(i) == elem) found = i
i -= 1
}
found
}
}

override def lastIndexOf(elem: Byte, end: Int): Int = {
if (end < 0) -1
else {
var found = -1
var i = math.min(end, length - 1)
while (i >= 0 && found == -1) {
if (bytes(i) == elem) found = i
i -= 1
}
found
}
}

override def slice(from: Int, until: Int): ByteString =
if (from <= 0 && until >= length) this
else if (from >= length || until <= 0 || from >= until) ByteString.empty
Expand Down Expand Up @@ -554,7 +580,6 @@ object ByteString {
i += 1
}
-1

}

// the calling code already adds the startIndex so this method does not need to
Expand All @@ -575,6 +600,32 @@ object ByteString {
else -1
}

override def lastIndexOf[B >: Byte](elem: B, end: Int): Int = {
if (end < 0) -1
else {
var found = -1
var i = math.min(end, length - 1)
while (i >= 0 && found == -1) {
if (bytes(startIndex + i) == elem) found = i
i -= 1
}
found
}
}

override def lastIndexOf(elem: Byte, end: Int): Int = {
if (end < 0) -1
else {
var found = -1
var i = math.min(end, length - 1)
while (i >= 0 && found == -1) {
if (bytes(startIndex + i) == elem) found = i
i -= 1
}
found
}
}

override def copyToArray[B >: Byte](dest: Array[B], start: Int, len: Int): Int = {
// min of the bytes available to copy, bytes there is room for in dest and the requested number of bytes
val toCopy = math.min(math.min(len, length), dest.length - start)
Expand Down Expand Up @@ -895,6 +946,67 @@ object ByteString {
}
}

override def lastIndexOf[B >: Byte](elem: B, end: Int): Int = {
if (end < 0) -1
else {
val byteStringsLast = bytestrings.size - 1

@tailrec
def find(bsIdx: Int, relativeIndex: Int, len: Int): Int = {
if (bsIdx < 0) -1
else {
val bs = bytestrings(bsIdx)
val bsStartIndex = len - bs.length

if (relativeIndex < bsStartIndex || bs.isEmpty) {
if (bsIdx == 0) -1
else find(bsIdx - 1, relativeIndex, bsStartIndex)
} else {
val subIndexOf = bs.lastIndexOf(elem, relativeIndex)
if (subIndexOf < 0) {
if (bsIdx == 0) -1
else find(bsIdx - 1, relativeIndex, bsStartIndex)
} else subIndexOf + bsStartIndex
}
}
}

find(byteStringsLast, math.min(end, length), length)
}
}

override def lastIndexOf(elem: Byte, end: Int): Int = {
if (end < 0) -1
else {
if (end < 0) -1
else {
val byteStringsLast = bytestrings.size - 1

@tailrec
def find(bsIdx: Int, relativeIndex: Int, len: Int): Int = {
if (bsIdx < 0) -1
else {
val bs = bytestrings(bsIdx)
val bsStartIndex = len - bs.length

if (relativeIndex < bsStartIndex || bs.isEmpty) {
if (bsIdx == 0) -1
else find(bsIdx - 1, relativeIndex, bsStartIndex)
} else {
val subIndexOf = bs.lastIndexOf(elem, relativeIndex)
if (subIndexOf < 0) {
if (bsIdx == 0) -1
else find(bsIdx - 1, relativeIndex, bsStartIndex)
} else subIndexOf + bsStartIndex
}
}
}

find(byteStringsLast, math.min(end, length), length)
}
}
}

override def copyToArray[B >: Byte](dest: Array[B], start: Int, len: Int): Int = {
if (bytestrings.size == 1) bytestrings.head.copyToArray(dest, start, len)
else {
Expand Down Expand Up @@ -1056,6 +1168,31 @@ sealed abstract class ByteString
*/
def indexOf(elem: Byte): Int = indexOf(elem, 0)

/**
* Finds index of last occurrence of some byte in this ByteString before or at some end index.
*
* Similar to lastIndexOf, but it avoids boxing if the value is already a byte.
*
* @param elem the element value to search for.
* @param end the end index
* @return the index `<= end` of the last element of this ByteString that is equal (as determined by `==`)
* to `elem`, or `-1`, if none exists.
* @since 2.0.0
*/
def lastIndexOf(elem: Byte, end: Int): Int = lastIndexOf[Byte](elem, end)

/**
* Finds index of last occurrence of some byte in this ByteString.
*
* Similar to lastIndexOf, but it avoids boxing if the value is already a byte.
*
* @param elem the element value to search for.
* @return the index of the last element of this ByteString that is equal (as determined by `==`)
* to `elem`, or `-1`, if none exists.
* @since 2.0.0
*/
def lastIndexOf(elem: Byte): Int = lastIndexOf(elem, length - 1)

override def contains[B >: Byte](elem: B): Boolean = indexOf(elem, 0) != -1

/**
Expand Down