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

pjfanning 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 819ec921bd perf: avoid redundant bounds checks on ByteString indexed 
reads (#3533)
819ec921bd is described below

commit 819ec921bdda9c106982bb86a5b796880637d3aa
Author: PJ Fanning <[email protected]>
AuthorDate: Tue Sep 8 08:21:44 2026 +0100

    perf: avoid redundant bounds checks on ByteString indexed reads (#3533)
    
    Motivation:
    ByteString.apply(idx) bounds-checks before delegating to byteAtUnchecked,
    and for the ByteStrings composite it also resolves the absolute offset to a
    fragment. Two call sites under src/main pay that check when the caller has
    already established the bound:
    
    - ByteStringParser.ByteReader.readByte guards with `off < input.length` and
      then calls input(off), so the bound is tested twice. Its siblings
      readShortLE/readIntLE/readLongLE already use the *Unchecked variants after
      their own guard, so readByte was the odd one out. ByteReader backs
      gzip/deflate header parsing, TLS and framing.
    - TcpDnsClient.decodeLength read the two length-prefix bytes with two apply
      calls: two bounds checks, and for a composite ByteString two fragment
      lookups.
    
    These are the only two places under src/main that index a ByteString outside
    ByteString.scala itself and bench-jmh.
    
    Modification:
    readByte now calls input.byteAtUnchecked(off). `off` starts at 0 and only 
ever
    moves forward, so the existing guard is sufficient.
    
    decodeLength now uses data.readShortBE(0) & 0xFFFF. readShortBE keeps the
    bounds check, so short input still throws IndexOutOfBoundsException as 
before,
    and it is SWAR-optimised for the single-array ByteString1C/ByteString1 that 
the
    common single-chunk read path produces.
    
    Result:
    One bounds check per byte instead of two in the stream parser, and one 
checked
    16-bit read instead of two indexed reads for the DNS length prefix. 
Behaviour
    is unchanged.
    
    Tests:
    - sbt "stream-tests/testOnly 
org.apache.pekko.stream.io.ByteStringParserSpec 
org.apache.pekko.stream.scaladsl.CompressionSpec 
org.apache.pekko.stream.io.compression.*" - 106 succeeded, 0 failed
    - sbt "actor-tests/testOnly 
org.apache.pekko.io.dns.internal.TcpDnsClientSpec" - 8 succeeded, 0 failed
    - sbt "actor/mimaReportBinaryIssues" "stream/mimaReportBinaryIssues" - 
success
    - Native scalafmt run on the two changed files.
    - No new tests: both changes are behaviour-preserving refactors of internal 
code, covered by the existing specs above.
    - Not benchmarked. The change is a strict reduction in work per read, not 
an algorithmic change.
    
    References:
    None - found by reviewing ByteString indexed access under src/main
---
 .../src/main/scala/org/apache/pekko/io/dns/internal/TcpDnsClient.scala | 2 +-
 .../main/scala/org/apache/pekko/stream/impl/io/ByteStringParser.scala  | 3 ++-
 2 files changed, 3 insertions(+), 2 deletions(-)

diff --git 
a/actor/src/main/scala/org/apache/pekko/io/dns/internal/TcpDnsClient.scala 
b/actor/src/main/scala/org/apache/pekko/io/dns/internal/TcpDnsClient.scala
index 4db1d1503d..cb8db381f4 100644
--- a/actor/src/main/scala/org/apache/pekko/io/dns/internal/TcpDnsClient.scala
+++ b/actor/src/main/scala/org/apache/pekko/io/dns/internal/TcpDnsClient.scala
@@ -107,7 +107,7 @@ private[internal] object TcpDnsClient {
   def encodeLength(length: Int): ByteString =
     ByteString(((length >> 8) & 0xFF).toByte, (length & 0xFF).toByte)
 
-  def decodeLength(data: ByteString): Int = ((data(0) & 0xFF) << 8) | (data(1) 
& 0xFF)
+  def decodeLength(data: ByteString): Int = data.readShortBE(0) & 0xFFFF
 
   def throwFailure(message: String, cause: Option[Throwable]): Unit =
     cause match {
diff --git 
a/stream/src/main/scala/org/apache/pekko/stream/impl/io/ByteStringParser.scala 
b/stream/src/main/scala/org/apache/pekko/stream/impl/io/ByteStringParser.scala
index bd81867026..e36f364e3b 100644
--- 
a/stream/src/main/scala/org/apache/pekko/stream/impl/io/ByteStringParser.scala
+++ 
b/stream/src/main/scala/org/apache/pekko/stream/impl/io/ByteStringParser.scala
@@ -215,7 +215,8 @@ import pekko.util.ByteString
 
     def readByte(): Int =
       if (off < input.length) {
-        val x = input(off)
+        // bounds already checked above, and off never goes backwards, so skip 
the check in apply
+        val x = input.byteAtUnchecked(off)
         off += 1
         x & 0xFF
       } else throw NeedMoreData


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to