This is an automated email from the ASF dual-hosted git repository.

fanningpj pushed a commit to branch 1.3.x
in repository https://gitbox.apache.org/repos/asf/pekko.git


The following commit(s) were added to refs/heads/1.3.x by this push:
     new 89b53c8427 Backport overload of indexOf from and to (#2272)
89b53c8427 is described below

commit 89b53c8427d0003e2b18ae22859da79f6c84f73a
Author: PJ Fanning <[email protected]>
AuthorDate: Fri Sep 26 14:19:47 2025 +0100

    Backport overload of indexOf from and to (#2272)
    
    * 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
    
    * remove var found
    
    * add changes to other Scala versions
    
    * Update ByteString.scala
---
 .../org/apache/pekko/util/ByteStringSpec.scala     | 55 +++++++++++++
 .../bytestring-indexOf-overload.excludes           | 19 +++++
 .../org/apache/pekko/util/ByteString.scala         | 93 ++++++++++++++++++----
 .../org/apache/pekko/util/ByteString.scala         | 88 +++++++++++++++++---
 .../scala-3/org/apache/pekko/util/ByteString.scala | 93 ++++++++++++++++++----
 5 files changed, 303 insertions(+), 45 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 d565174609..de1b3b7e59 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
@@ -793,6 +793,61 @@ class ByteStringSpec extends AnyWordSpec with Matchers 
with Checkers {
       compact.indexOf('g'.toByte, 5) should ===(5)
       compact.indexOf('g'.toByte, 6) 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-2.12/org/apache/pekko/util/ByteString.scala 
b/actor/src/main/scala-2.12/org/apache/pekko/util/ByteString.scala
index 9855a19fe3..da31b62699 100644
--- a/actor/src/main/scala-2.12/org/apache/pekko/util/ByteString.scala
+++ b/actor/src/main/scala-2.12/org/apache/pekko/util/ByteString.scala
@@ -236,13 +236,12 @@ 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
       }
     }
 
@@ -250,13 +249,25 @@ object ByteString {
     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
       }
     }
 
@@ -437,13 +448,12 @@ 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
       }
     }
 
@@ -451,13 +461,25 @@ object ByteString {
     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
       }
     }
 
@@ -747,6 +769,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)
+      }
+    }
+
     protected def writeReplace(): AnyRef = new SerializationProxy(this)
   }
 
@@ -844,6 +892,19 @@ sealed abstract class ByteString extends IndexedSeq[Byte] 
with IndexedSeqOptimiz
   // optimized in subclasses
   override def indexOf[B >: Byte](elem: B): Int = indexOf[B](elem, 0)
 
+  /**
+   * 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, implemented in subclasses
   /**
    * Finds index of first occurrence of some byte in this ByteString after or 
at some start index.
diff --git a/actor/src/main/scala-2.13/org/apache/pekko/util/ByteString.scala 
b/actor/src/main/scala-2.13/org/apache/pekko/util/ByteString.scala
index 10886bf26e..af8af0529e 100644
--- a/actor/src/main/scala-2.13/org/apache/pekko/util/ByteString.scala
+++ b/actor/src/main/scala-2.13/org/apache/pekko/util/ByteString.scala
@@ -233,26 +233,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
       }
     }
 
@@ -450,13 +461,25 @@ object ByteString {
     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
       }
     }
 
@@ -753,6 +776,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 {
@@ -872,6 +921,19 @@ sealed abstract class ByteString
 
   override def indexWhere(p: Byte => Boolean, from: Int): Int = 
iterator.indexWhere(p, 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.
@@ -1048,7 +1110,7 @@ sealed abstract class ByteString
    */
   def decodeString(charset: Charset): String
 
-  /*
+  /**
    * Returns a ByteString which is the binary representation of this ByteString
    * if this ByteString is Base64-encoded.
    */
diff --git a/actor/src/main/scala-3/org/apache/pekko/util/ByteString.scala 
b/actor/src/main/scala-3/org/apache/pekko/util/ByteString.scala
index 8aa06c5eec..3db15c8243 100644
--- a/actor/src/main/scala-3/org/apache/pekko/util/ByteString.scala
+++ b/actor/src/main/scala-3/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
       }
     }
 
@@ -754,6 +776,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 {
@@ -876,6 +924,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