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-http.git


The following commit(s) were added to refs/heads/1.3.x by this push:
     new 30f6d5a17 use byteAt instead of byteChar where it is easy to avoid the 
char conversion (#801) (#827)
30f6d5a17 is described below

commit 30f6d5a172eb1dead92d0fc5d13a68258c2014d0
Author: PJ Fanning <[email protected]>
AuthorDate: Sun Oct 5 18:27:41 2025 +0100

    use byteAt instead of byteChar where it is easy to avoid the char 
conversion (#801) (#827)
    
    * more performant boundary check
    
    * try to fix tests
    
    * Update BodyPartParser.scala
    
    * Update BodyPartParser.scala
    
    * Update BodyPartParser.scala
    
    * Update BodyPartParser.scala
    
    * add HttpConstants
    
    * use ByteAt to avoid some char conversions
    
    more changes
    
    more changes
    
    * add dash constant
    
    revert LineParser changes
---
 .../http/impl/engine/parsing/BodyPartParser.scala  | 15 +++++-----
 .../impl/engine/parsing/HttpHeaderParser.scala     | 13 +++++----
 .../impl/engine/parsing/HttpMessageParser.scala    | 32 ++++++++++----------
 .../impl/engine/parsing/HttpRequestParser.scala    | 25 ++++++++--------
 .../impl/engine/parsing/HttpResponseParser.scala   | 12 ++++----
 .../parsing/SpecializedHeaderValueParsers.scala    |  3 +-
 .../http/impl/model/parser/CharacterClasses.scala  | 10 +++----
 .../pekko/http/impl/util/HttpConstants.scala       | 34 ++++++++++++++++++++++
 .../scaladsl/unmarshalling/sse/LineParser.scala    |  5 ++--
 9 files changed, 96 insertions(+), 53 deletions(-)

diff --git 
a/http-core/src/main/scala/org/apache/pekko/http/impl/engine/parsing/BodyPartParser.scala
 
b/http-core/src/main/scala/org/apache/pekko/http/impl/engine/parsing/BodyPartParser.scala
index 28a69e872..0be9fc946 100644
--- 
a/http-core/src/main/scala/org/apache/pekko/http/impl/engine/parsing/BodyPartParser.scala
+++ 
b/http-core/src/main/scala/org/apache/pekko/http/impl/engine/parsing/BodyPartParser.scala
@@ -16,18 +16,19 @@ package org.apache.pekko.http.impl.engine.parsing
 import org.apache.pekko
 import pekko.NotUsed
 import pekko.annotation.InternalApi
-
-import scala.annotation.tailrec
 import pekko.event.LoggingAdapter
-import org.parboiled2.CharPredicate
+import pekko.http.impl.util._
+import pekko.http.impl.util.HttpConstants._
+import pekko.http.scaladsl.model._
+import pekko.http.scaladsl.model.headers._
+import pekko.stream.{ Attributes, FlowShape, Inlet, Outlet }
 import pekko.stream.scaladsl.Source
 import pekko.stream.stage._
 import pekko.util.ByteString
-import pekko.http.scaladsl.model._
-import pekko.http.impl.util._
-import pekko.stream.{ Attributes, FlowShape, Inlet, Outlet }
 import headers._
+import org.parboiled2.CharPredicate
 
+import scala.annotation.tailrec
 import scala.collection.mutable.ListBuffer
 
 /**
@@ -282,7 +283,7 @@ private[http] final class BodyPartParser(
       def done(): StateResult = null // StateResult is a phantom type
 
       def doubleDash(input: ByteString, offset: Int): Boolean =
-        byteChar(input, offset) == '-' && byteChar(input, offset + 1) == '-'
+        byteAt(input, offset) == DASH_BYTE && byteAt(input, offset + 1) == 
DASH_BYTE
     }
 }
 
diff --git 
a/http-core/src/main/scala/org/apache/pekko/http/impl/engine/parsing/HttpHeaderParser.scala
 
b/http-core/src/main/scala/org/apache/pekko/http/impl/engine/parsing/HttpHeaderParser.scala
index 8e00dd545..c7bdbda34 100644
--- 
a/http-core/src/main/scala/org/apache/pekko/http/impl/engine/parsing/HttpHeaderParser.scala
+++ 
b/http-core/src/main/scala/org/apache/pekko/http/impl/engine/parsing/HttpHeaderParser.scala
@@ -20,21 +20,22 @@ import java.lang.{ StringBuilder => JStringBuilder }
 import org.apache.pekko
 import pekko.annotation.InternalApi
 import pekko.event.LoggingAdapter
+import pekko.http.scaladsl.settings.ParserSettings
 import pekko.http.scaladsl.settings.ParserSettings.{
+  ErrorLoggingVerbosity,
   IllegalResponseHeaderNameProcessingMode,
   IllegalResponseHeaderValueProcessingMode
 }
-import pekko.http.scaladsl.settings.ParserSettings.ErrorLoggingVerbosity
-import pekko.http.scaladsl.settings.ParserSettings
-
-import scala.annotation.tailrec
-import pekko.util.ByteString
 import pekko.http.ccompat._
 import pekko.http.impl.util._
+import pekko.http.impl.util.HttpConstants._
 import pekko.http.scaladsl.model.{ ErrorInfo, HttpHeader, MediaTypes, 
StatusCode, StatusCodes }
 import pekko.http.scaladsl.model.headers.{ EmptyHeader, RawHeader }
 import pekko.http.impl.model.parser.HeaderParser
 import pekko.http.impl.model.parser.CharacterClasses._
+import pekko.util.ByteString
+
+import scala.annotation.tailrec
 
 /**
  * INTERNAL API
@@ -600,7 +601,7 @@ private[http] object HttpHeaderParser {
     if (ix < limit)
       byteChar(input, ix) match {
         case '\t' => scanHeaderValue(hhp, input, start, limit, log, 
mode)(appended(' '), ix + 1)
-        case '\r' if byteChar(input, ix + 1) == '\n' =>
+        case '\r' if byteAt(input, ix + 1) == LF_BYTE =>
           if (WSP(byteChar(input, ix + 2))) scanHeaderValue(hhp, input, start, 
limit, log, mode)(appended(' '), ix + 3)
           else (if (sb != null) sb.toString else asciiString(input, start, 
ix), ix + 2)
         case '\n' =>
diff --git 
a/http-core/src/main/scala/org/apache/pekko/http/impl/engine/parsing/HttpMessageParser.scala
 
b/http-core/src/main/scala/org/apache/pekko/http/impl/engine/parsing/HttpMessageParser.scala
index 374f3c3e2..40803632d 100644
--- 
a/http-core/src/main/scala/org/apache/pekko/http/impl/engine/parsing/HttpMessageParser.scala
+++ 
b/http-core/src/main/scala/org/apache/pekko/http/impl/engine/parsing/HttpMessageParser.scala
@@ -15,21 +15,23 @@ package org.apache.pekko.http.impl.engine.parsing
 
 import javax.net.ssl.SSLSession
 
-import org.apache.pekko
-import pekko.stream.TLSProtocol._
-
 import scala.annotation.tailrec
 import scala.collection.mutable.ListBuffer
+
 import org.parboiled2.CharUtils
-import pekko.util.ByteString
+
+import org.apache.pekko
+import pekko.annotation.InternalApi
 import pekko.http.impl.model.parser.CharacterClasses
-import pekko.http.scaladsl.settings.ParserSettings
+import pekko.http.impl.util.HttpConstants._
 import pekko.http.scaladsl.model.{ ParsingException => _, _ }
 import headers._
 import HttpProtocols._
 import ParserOutput._
-import pekko.annotation.InternalApi
+import pekko.http.scaladsl.settings.ParserSettings
 import 
pekko.http.scaladsl.settings.ParserSettings.ConflictingContentTypeHeaderProcessingMode
+import pekko.stream.TLSProtocol._
+import pekko.util.ByteString
 
 /**
  * INTERNAL API
@@ -288,20 +290,20 @@ private[http] trait HttpMessageParser[Output >: 
MessageOutput <: ParserOutput] {
           emit(EntityChunk(HttpEntity.Chunk(input.slice(cursor, 
chunkBodyEnd).compact, extension)))
           Trampoline(_ => parseChunk(input, chunkBodyEnd + terminatorLen, 
isLastMessage, totalBytesRead + chunkSize))
         }
-        byteChar(input, chunkBodyEnd) match {
-          case '\r' if byteChar(input, chunkBodyEnd + 1) == '\n' => result(2)
-          case '\n'                                              => result(1)
-          case x                                                 => 
failEntityStream("Illegal chunk termination")
+        byteAt(input, chunkBodyEnd) match {
+          case CR_BYTE if byteAt(input, chunkBodyEnd + 1) == LF_BYTE => 
result(2)
+          case LF_BYTE                                               => 
result(1)
+          case x                                                     => 
failEntityStream("Illegal chunk termination")
         }
       } else parseTrailer(extension, cursor)
 
     @tailrec def parseChunkExtensions(chunkSize: Int, cursor: Int)(startIx: 
Int = cursor): StateResult =
       if (cursor - startIx <= settings.maxChunkExtLength) {
         def extension = asciiString(input, startIx, cursor)
-        byteChar(input, cursor) match {
-          case '\r' if byteChar(input, cursor + 1) == '\n' => 
parseChunkBody(chunkSize, extension, cursor + 2)
-          case '\n'                                        => 
parseChunkBody(chunkSize, extension, cursor + 1)
-          case _                                           => 
parseChunkExtensions(chunkSize, cursor + 1)(startIx)
+        byteAt(input, cursor) match {
+          case CR_BYTE if byteAt(input, cursor + 1) == LF_BYTE => 
parseChunkBody(chunkSize, extension, cursor + 2)
+          case LF_BYTE                                         => 
parseChunkBody(chunkSize, extension, cursor + 1)
+          case _                                               => 
parseChunkExtensions(chunkSize, cursor + 1)(startIx)
         }
       } else failEntityStream(
         s"HTTP chunk extension length exceeds configured limit of 
${settings.maxChunkExtLength} characters")
@@ -314,7 +316,7 @@ private[http] trait HttpMessageParser[Output >: 
MessageOutput <: ParserOutput] {
             failEntityStream(
               s"HTTP chunk of $size bytes exceeds the configured limit of 
${settings.maxChunkSize} bytes")
           case ';' if cursor > offset => parseChunkExtensions(size.toInt, 
cursor + 1)()
-          case '\r' if cursor > offset && byteChar(input, cursor + 1) == '\n' 
=>
+          case '\r' if cursor > offset && byteAt(input, cursor + 1) == LF_BYTE 
=>
             parseChunkBody(size.toInt, "", cursor + 2)
           case '\n' if cursor > offset      => parseChunkBody(size.toInt, "", 
cursor + 1)
           case c if CharacterClasses.WSP(c) => parseSize(cursor + 1, size) // 
illegal according to the spec but can happen, see issue #1812
diff --git 
a/http-core/src/main/scala/org/apache/pekko/http/impl/engine/parsing/HttpRequestParser.scala
 
b/http-core/src/main/scala/org/apache/pekko/http/impl/engine/parsing/HttpRequestParser.scala
index eda2d7705..4ff31c0bd 100644
--- 
a/http-core/src/main/scala/org/apache/pekko/http/impl/engine/parsing/HttpRequestParser.scala
+++ 
b/http-core/src/main/scala/org/apache/pekko/http/impl/engine/parsing/HttpRequestParser.scala
@@ -18,22 +18,23 @@ import javax.net.ssl.SSLSession
 
 import scala.annotation.{ switch, tailrec }
 import org.apache.pekko
-import pekko.http.scaladsl.settings.{ ParserSettings, WebSocketSettings }
-import pekko.util.ByteString
-import pekko.util.OptionVal
+import pekko.annotation.InternalApi
+import pekko.http.impl.engine.server.HttpAttributes
+import pekko.http.impl.util.ByteStringParserInput
+import pekko.http.impl.util.HttpConstants._
 import pekko.http.impl.engine.ws.Handshake
 import pekko.http.impl.model.parser.{ CharacterClasses, UriParser }
 import pekko.http.scaladsl.model.{ ParsingException => _, _ }
-import headers._
-import StatusCodes._
+import pekko.http.scaladsl.model.headers._
+import pekko.http.scaladsl.model.StatusCodes._
+import pekko.http.scaladsl.settings.{ ParserSettings, WebSocketSettings }
 import ParserOutput._
-import pekko.annotation.InternalApi
-import pekko.http.impl.engine.server.HttpAttributes
-import pekko.http.impl.util.ByteStringParserInput
-import org.parboiled2.ParserInput
 import pekko.stream.{ Attributes, FlowShape, Inlet, Outlet }
 import pekko.stream.TLSProtocol.SessionBytes
 import pekko.stream.stage.{ GraphStage, GraphStageLogic, InHandler, OutHandler 
}
+import pekko.util.ByteString
+import pekko.util.OptionVal
+import org.parboiled2.ParserInput
 
 /**
  * INTERNAL API
@@ -90,9 +91,9 @@ private[http] final class HttpRequestParser(
           var cursor = parseMethod(input, offset)
           cursor = parseRequestTarget(input, cursor)
           cursor = parseProtocol(input, cursor)
-          if (byteChar(input, cursor) == '\r' && byteChar(input, cursor + 1) 
== '\n')
+          if (byteAt(input, cursor) == CR_BYTE && byteAt(input, cursor + 1) == 
LF_BYTE)
             parseHeaderLines(input, cursor + 2)
-          else if (byteChar(input, cursor) == '\n')
+          else if (byteAt(input, cursor) == LF_BYTE)
             parseHeaderLines(input, cursor + 1)
           else onBadProtocol(input.drop(cursor))
         } else
@@ -124,7 +125,7 @@ private[http] final class HttpRequestParser(
 
         @tailrec def parseMethod(meth: HttpMethod, ix: Int = 1): Int =
           if (ix == meth.value.length)
-            if (byteChar(input, cursor + ix) == ' ') {
+            if (byteAt(input, cursor + ix) == SPACE_BYTE) {
               method = meth
               cursor + ix + 1
             } else parseCustomMethod()
diff --git 
a/http-core/src/main/scala/org/apache/pekko/http/impl/engine/parsing/HttpResponseParser.scala
 
b/http-core/src/main/scala/org/apache/pekko/http/impl/engine/parsing/HttpResponseParser.scala
index 95a893432..b022dcc4f 100644
--- 
a/http-core/src/main/scala/org/apache/pekko/http/impl/engine/parsing/HttpResponseParser.scala
+++ 
b/http-core/src/main/scala/org/apache/pekko/http/impl/engine/parsing/HttpResponseParser.scala
@@ -17,14 +17,16 @@ import javax.net.ssl.SSLSession
 import scala.annotation.tailrec
 import scala.concurrent.Promise
 import scala.util.control.{ NoStackTrace, NonFatal }
+
 import org.apache.pekko
+import pekko.annotation.InternalApi
 import pekko.http.scaladsl.settings.ParserSettings
 import pekko.http.impl.model.parser.CharacterClasses
+import pekko.http.impl.util.HttpConstants._
 import pekko.util.ByteString
 import pekko.http.scaladsl.model.{ ParsingException => _, _ }
 import pekko.http.scaladsl.model.headers._
 import ParserOutput._
-import pekko.annotation.InternalApi
 import pekko.http.impl.util.LogByteStringTools
 import pekko.stream.scaladsl.Source
 
@@ -61,7 +63,7 @@ private[http] class HttpResponseParser(protected val 
settings: ParserSettings,
   override protected def parseMessage(input: ByteString, offset: Int): 
StateResult =
     if (contextForCurrentResponse.isDefined) {
       var cursor = parseProtocol(input, offset)
-      if (byteChar(input, cursor) == ' ') {
+      if (byteAt(input, cursor) == SPACE_BYTE) {
         cursor = parseStatus(input, cursor + 1)
         parseHeaderLines(input, cursor)
       } else onBadProtocol(input.drop(cursor))
@@ -104,8 +106,8 @@ private[http] class HttpResponseParser(protected val 
settings: ParserSettings,
       }
     }
 
-    def isLF(idx: Int) = byteChar(input, idx) == '\n'
-    def isCRLF(idx: Int) = byteChar(input, idx) == '\r' && isLF(idx + 1)
+    def isLF(idx: Int) = byteAt(input, idx) == LF_BYTE
+    def isCRLF(idx: Int) = byteAt(input, idx) == CR_BYTE && isLF(idx + 1)
     def isNewLine(idx: Int) = isLF(idx) || isCRLF(idx)
 
     def skipNewLine(idx: Int) = {
@@ -114,7 +116,7 @@ private[http] class HttpResponseParser(protected val 
settings: ParserSettings,
       else idx
     }
 
-    if (byteChar(input, cursor + 3) == ' ') {
+    if (byteAt(input, cursor + 3) == SPACE_BYTE) {
       val startIdx = cursor + 4
       @tailrec def scanNewLineIdx(idx: Int): Int =
         if (idx - startIdx <= maxResponseReasonLength)
diff --git 
a/http-core/src/main/scala/org/apache/pekko/http/impl/engine/parsing/SpecializedHeaderValueParsers.scala
 
b/http-core/src/main/scala/org/apache/pekko/http/impl/engine/parsing/SpecializedHeaderValueParsers.scala
index 2cde6dd8a..3b2977217 100644
--- 
a/http-core/src/main/scala/org/apache/pekko/http/impl/engine/parsing/SpecializedHeaderValueParsers.scala
+++ 
b/http-core/src/main/scala/org/apache/pekko/http/impl/engine/parsing/SpecializedHeaderValueParsers.scala
@@ -19,6 +19,7 @@ import pekko.annotation.InternalApi
 import scala.annotation.tailrec
 import pekko.util.ByteString
 import pekko.http.impl.model.parser.CharacterClasses._
+import pekko.http.impl.util.HttpConstants._
 import pekko.http.scaladsl.model.{ ErrorInfo, HttpHeader }
 import pekko.http.scaladsl.model.headers.`Content-Length`
 
@@ -39,7 +40,7 @@ private[parsing] object SpecializedHeaderValueParsers {
         if (result < 0) fail("`Content-Length` header value must not exceed 
63-bit integer range")
         else if (DIGIT(c)) recurse(ix + 1, result * 10 + c - '0')
         else if (WSP(c)) recurse(ix + 1, result)
-        else if (c == '\r' && byteChar(input, ix + 1) == '\n') 
(`Content-Length`(result), ix + 2)
+        else if (c == '\r' && byteAt(input, ix + 1) == LF_BYTE) 
(`Content-Length`(result), ix + 2)
         else if (c == '\n') (`Content-Length`(result), ix + 1)
         else fail("Illegal `Content-Length` header value")
       }
diff --git 
a/http-core/src/main/scala/org/apache/pekko/http/impl/model/parser/CharacterClasses.scala
 
b/http-core/src/main/scala/org/apache/pekko/http/impl/model/parser/CharacterClasses.scala
index 35bbde9ae..f7f1ca542 100644
--- 
a/http-core/src/main/scala/org/apache/pekko/http/impl/model/parser/CharacterClasses.scala
+++ 
b/http-core/src/main/scala/org/apache/pekko/http/impl/model/parser/CharacterClasses.scala
@@ -23,15 +23,15 @@ private[http] object CharacterClasses {
   def ALPHA = CharPredicate.Alpha
   def LOWER_ALPHA = CharPredicate.LowerAlpha
   def UPPER_ALPHA = CharPredicate.UpperAlpha
-  def CR = '\r'
+  final val CR = '\r'
   val CTL = CharPredicate('\u0000' to '\u001F', '\u007F')
   def DIGIT = CharPredicate.Digit
   def ALPHANUM = CharPredicate.AlphaNum
-  def DQUOTE = '"'
+  final val DQUOTE = '"'
   def HEXDIG = CharPredicate.HexDigit
-  def HTAB = '\t'
-  def LF = '\n'
-  def SP = ' '
+  final val HTAB = '\t'
+  final val LF = '\n'
+  final val SP = ' '
   def VCHAR = CharPredicate.Visible
   val WSP = CharPredicate(SP, HTAB)
   val WSPCRLF = WSP ++ CR ++ LF
diff --git 
a/http-core/src/main/scala/org/apache/pekko/http/impl/util/HttpConstants.scala 
b/http-core/src/main/scala/org/apache/pekko/http/impl/util/HttpConstants.scala
new file mode 100644
index 000000000..0252474fd
--- /dev/null
+++ 
b/http-core/src/main/scala/org/apache/pekko/http/impl/util/HttpConstants.scala
@@ -0,0 +1,34 @@
+/*
+ * 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.
+ */
+
+package org.apache.pekko.http.impl.util
+
+import org.apache.pekko.annotation.InternalApi
+
+/**
+ * INTERNAL API
+ *
+ * This object contains HTTP related constants that are used in various places.
+ * It is not intended to be used outside of the HTTP implementation.
+ */
+@InternalApi
+private[http] object HttpConstants {
+  final val CR_BYTE: Byte = 13
+  final val LF_BYTE: Byte = 10
+  final val SPACE_BYTE: Byte = 32
+  final val DASH_BYTE: Byte = 45 // '-' (minus, dash, hyphen)
+}
diff --git 
a/http/src/main/scala/org/apache/pekko/http/scaladsl/unmarshalling/sse/LineParser.scala
 
b/http/src/main/scala/org/apache/pekko/http/scaladsl/unmarshalling/sse/LineParser.scala
index 71bcb9ae3..17680994f 100644
--- 
a/http/src/main/scala/org/apache/pekko/http/scaladsl/unmarshalling/sse/LineParser.scala
+++ 
b/http/src/main/scala/org/apache/pekko/http/scaladsl/unmarshalling/sse/LineParser.scala
@@ -18,6 +18,7 @@ package sse
 
 import org.apache.pekko
 import pekko.annotation.InternalApi
+import pekko.http.impl.util.HttpConstants
 import pekko.stream.stage.{ GraphStage, GraphStageLogic, InHandler, OutHandler 
}
 import pekko.stream.{ Attributes, FlowShape, Inlet, Outlet }
 import pekko.util.ByteString
@@ -26,8 +27,8 @@ import scala.annotation.tailrec
 /** INTERNAL API */
 @InternalApi
 private object LineParser {
-  val CR = '\r'.toByte
-  val LF = '\n'.toByte
+  val CR = HttpConstants.CR_BYTE
+  val LF = HttpConstants.LF_BYTE
 }
 
 /** INTERNAL API */


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

Reply via email to