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]

Reply via email to