This is an automated email from the ASF dual-hosted git repository.
fanningpj pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/pekko.git
The following commit(s) were added to refs/heads/main by this push:
new a02c62b1fd ByteString Indexof with from and to (#2271)
a02c62b1fd is described below
commit a02c62b1fdf0189f1909941e893c27b1b279f080
Author: PJ Fanning <[email protected]>
AuthorDate: Fri Sep 26 08:37:38 2025 +0100
ByteString Indexof with from and to (#2271)
* support ByteString indexOf from, to
tests
Update ByteStringSpec.scala
Create bytestring-indexOf-overload.excludes
* use explicit return in indexOf to reduce operations
---
.../org/apache/pekko/util/ByteStringSpec.scala | 55 +++++++++++++
.../bytestring-indexOf-overload.excludes | 19 +++++
.../scala/org/apache/pekko/util/ByteString.scala | 93 ++++++++++++++++++----
3 files changed, 151 insertions(+), 16 deletions(-)
diff --git
a/actor-tests/src/test/scala/org/apache/pekko/util/ByteStringSpec.scala
b/actor-tests/src/test/scala/org/apache/pekko/util/ByteStringSpec.scala
index edf9ea0849..020547cea5 100644
--- a/actor-tests/src/test/scala/org/apache/pekko/util/ByteStringSpec.scala
+++ b/actor-tests/src/test/scala/org/apache/pekko/util/ByteStringSpec.scala
@@ -919,6 +919,61 @@ class ByteStringSpec extends AnyWordSpec with Matchers
with Checkers {
byteStringLong.indexOf('z', 2) should ===(25)
byteStringLong.indexOf('a', 2) should ===(-1)
}
+ "indexOf from/to" in {
+ ByteString.empty.indexOf(5.toByte, -1, 10) should ===(-1)
+ ByteString.empty.indexOf(5.toByte, 0, 1) should ===(-1)
+ ByteString.empty.indexOf(5.toByte, 1, -1) should ===(-1)
+ val byteString1 = ByteString1.fromString("abc")
+ byteString1.indexOf('d'.toByte, -1, 1) should ===(-1)
+ byteString1.indexOf('a'.toByte, -1, 1) should ===(0)
+ byteString1.indexOf('a'.toByte, 0, 1) should ===(0)
+ byteString1.indexOf('a'.toByte, 0, 0) should ===(-1)
+ byteString1.indexOf('a'.toByte, 1, 2) should ===(-1)
+
+ val array = Array[Byte]('x', 'y', 'z', 'a', 'b', 'c')
+ val byteString2 = ByteString1(array, 3, 2)
+ byteString2.indexOf('x'.toByte, -1, 3) should ===(-1)
+ byteString2.indexOf('x'.toByte, 0, 3) should ===(-1)
+ byteString2.indexOf('a'.toByte, -1, 1) should ===(0)
+ byteString2.indexOf('a'.toByte, 0, 0) should ===(-1)
+ byteString2.indexOf('a'.toByte, 1, 2) should ===(-1)
+
+ val byteStrings = ByteStrings(ByteString1.fromString("abc"),
ByteString1.fromString("efg"))
+ byteStrings.indexOf('c'.toByte, -1, 6) should ===(2)
+ byteStrings.indexOf('c'.toByte, 0, 6) should ===(2)
+ byteStrings.indexOf('c'.toByte, 2, 3) should ===(2)
+ byteStrings.indexOf('c'.toByte, 2, 2) should ===(-1)
+ byteStrings.indexOf('c'.toByte, 3, 4) should ===(-1)
+
+ byteStrings.indexOf('e'.toByte, -1, 6) should ===(3)
+ byteStrings.indexOf('e'.toByte, 0, 4) should ===(3)
+ byteStrings.indexOf('e'.toByte, 1, 4) should ===(3)
+ byteStrings.indexOf('e'.toByte, 4, 5) should ===(-1)
+
+ byteStrings.indexOf('g'.toByte, -1, 6) should ===(5)
+ byteStrings.indexOf('g'.toByte, 0, 5) should ===(-1)
+
+ val compact = byteStrings.compact
+ compact.indexOf('c'.toByte, -1, 6) should ===(2)
+ compact.indexOf('c'.toByte, 0, 6) should ===(2)
+ compact.indexOf('c'.toByte, 2, 3) should ===(2)
+ compact.indexOf('c'.toByte, 2, 2) should ===(-1)
+ compact.indexOf('c'.toByte, 3, 4) should ===(-1)
+
+ compact.indexOf('e'.toByte, -1, 6) should ===(3)
+ compact.indexOf('e'.toByte, 0, 4) should ===(3)
+ compact.indexOf('e'.toByte, 1, 4) should ===(3)
+ compact.indexOf('e'.toByte, 4, 5) should ===(-1)
+
+ compact.indexOf('g'.toByte, -1, 6) should ===(5)
+ compact.indexOf('g'.toByte, 0, 5) should ===(-1)
+
+ val byteStringLong = ByteString1.fromString("abcdefghijklmnopqrstuvwxyz")
+ byteStringLong.indexOf('m', 2, 24) should ===(12)
+ byteStringLong.indexOf('z', 2, 26) should ===(25)
+ byteStringLong.indexOf('z', 2, 24) should ===(-1)
+ byteStringLong.indexOf('a', 2, 24) should ===(-1)
+ }
"copyToArray" in {
val byteString = ByteString(1, 2) ++ ByteString(3) ++ ByteString(4)
diff --git
a/actor/src/main/mima-filters/1.2.x.backwards.excludes/bytestring-indexOf-overload.excludes
b/actor/src/main/mima-filters/1.2.x.backwards.excludes/bytestring-indexOf-overload.excludes
new file mode 100644
index 0000000000..8cdb6b5679
--- /dev/null
+++
b/actor/src/main/mima-filters/1.2.x.backwards.excludes/bytestring-indexOf-overload.excludes
@@ -0,0 +1,19 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+# Add overload for ByteString.indexOf that handles from and to
+ProblemFilters.exclude[ReversedMissingMethodProblem]("org.apache.pekko.util.ByteString.indexOf")
diff --git a/actor/src/main/scala/org/apache/pekko/util/ByteString.scala
b/actor/src/main/scala/org/apache/pekko/util/ByteString.scala
index e07c6f5edb..2d9b94bf48 100644
--- a/actor/src/main/scala/org/apache/pekko/util/ByteString.scala
+++ b/actor/src/main/scala/org/apache/pekko/util/ByteString.scala
@@ -234,26 +234,37 @@ object ByteString {
override def indexOf[B >: Byte](elem: B, from: Int): Int = {
if (from >= length) -1
else {
- var found = -1
var i = math.max(from, 0)
- while (i < length && found == -1) {
- if (bytes(i) == elem) found = i
+ while (i < length) {
+ if (bytes(i) == elem) return i
i += 1
}
- found
+ -1
}
}
override def indexOf(elem: Byte, from: Int): Int = {
if (from >= length) -1
else {
- var found = -1
var i = math.max(from, 0)
- while (i < length && found == -1) {
- if (bytes(i) == elem) found = i
+ while (i < length) {
+ if (bytes(i) == elem) return i
i += 1
}
- found
+ -1
+ }
+ }
+
+ override def indexOf(elem: Byte, from: Int, to: Int): Int = {
+ if (from >= length || to <= from) -1
+ else {
+ val upto = math.min(to, length)
+ var i = math.max(from, 0)
+ while (i < upto) {
+ if (bytes(i) == elem) return i
+ i += 1
+ }
+ -1
}
}
@@ -438,26 +449,37 @@ object ByteString {
override def indexOf[B >: Byte](elem: B, from: Int): Int = {
if (from >= length) -1
else {
- var found = -1
var i = math.max(from, 0)
- while (i < length && found == -1) {
- if (bytes(startIndex + i) == elem) found = i
+ while (i < length) {
+ if (bytes(startIndex + i) == elem) return i
i += 1
}
- found
+ -1
}
}
override def indexOf(elem: Byte, from: Int): Int = {
if (from >= length) -1
else {
- var found = -1
var i = math.max(from, 0)
- while (i < length && found == -1) {
- if (bytes(startIndex + i) == elem) found = i
+ while (i < length) {
+ if (bytes(startIndex + i) == elem) return i
i += 1
}
- found
+ -1
+ }
+ }
+
+ override def indexOf(elem: Byte, from: Int, to: Int): Int = {
+ if (from >= length || to <= from) -1
+ else {
+ val upto = math.min(to, length)
+ var i = math.max(from, 0)
+ while (i < upto) {
+ if (bytes(startIndex + i) == elem) return i
+ i += 1
+ }
+ -1
}
}
@@ -755,6 +777,32 @@ object ByteString {
}
}
+ override def indexOf(elem: Byte, from: Int, to: Int): Int = {
+ if (from >= length || to <= from) -1
+ else {
+ val byteStringsSize = bytestrings.size
+
+ @tailrec
+ def find(bsIdx: Int, relativeIndex: Int, bytesPassed: Int): Int = {
+ if (bsIdx >= byteStringsSize) -1
+ else {
+ val bs = bytestrings(bsIdx)
+
+ if (bs.length <= relativeIndex) {
+ find(bsIdx + 1, relativeIndex - bs.length, bytesPassed +
bs.length)
+ } else {
+ val subIndexOf = bs.indexOf(elem, relativeIndex, to -
bytesPassed)
+ if (subIndexOf < 0) {
+ find(bsIdx + 1, relativeIndex - bs.length, bytesPassed +
bs.length)
+ } else subIndexOf + bytesPassed
+ }
+ }
+ }
+
+ find(0, math.max(from, 0), 0)
+ }
+ }
+
override def copyToArray[B >: Byte](dest: Array[B], start: Int, len: Int):
Int = {
if (bytestrings.size == 1) bytestrings.head.copyToArray(dest, start, len)
else {
@@ -877,6 +925,19 @@ sealed abstract class ByteString
// optimized in subclasses
override def indexOf[B >: Byte](elem: B, from: Int): Int =
super.indexOf(elem, from)
+ /**
+ * Finds index of first occurrence of some byte in this ByteString after or
at some start index.
+ * This overload allows you to specify an end index (exclusive).
+ *
+ * @param elem the element value to search for.
+ * @param from the start index
+ * @param to the end index (exclusive)
+ * @return the index `>= from` and `< to` of the first element of this
ByteString that is equal (as determined by `==`)
+ * to `elem`, or `-1`, if none exists.
+ * @since 1.3.0
+ */
+ def indexOf(elem: Byte, from: Int, to: Int): Int
+
// optimized version of indexOf for bytes, optimized in subclasses
/**
* Finds index of first occurrence of some byte in this ByteString after or
at some start index.
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]