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


The following commit(s) were added to refs/heads/main by this push:
     new 0d8bed1ad chore: replace deprecated String constructor in StringTools 
(#1230)
0d8bed1ad is described below

commit 0d8bed1ad2f6a58da342ada14ce85171ba160883
Author: PJ Fanning <[email protected]>
AuthorDate: Sun Aug 30 11:25:21 2026 +0100

    chore: replace deprecated String constructor in StringTools (#1230)
    
    Motivation:
    StringTools.asciiStringFromBytes used the deprecated `new String(byte[], 
int)`
    behind a @nowarn, justified by a comment saying it was "the fastest way to
    convert a ASCII encoded byte array into a String without extra copying". 
That
    was true on Java 8, where the alternative expanded the bytes into a char 
array.
    Since JDK 9 and compact strings it is not, and this branch requires JDK 17.
    
    The method is on the HTTP/2 HPACK decode path, so it is worth being sure the
    replacement is neither a behaviour nor a performance change.
    
    Modification:
    Decode with ISO-8859-1, which maps every byte to the character of the same
    value, exactly as the deprecated constructor did with hibyte 0. Not 
US-ASCII,
    which would turn bytes above 0x7F into replacement characters; HPACK string
    literals are opaque octets. Drops the @nowarn and the 
scala.annotation.nowarn
    import, and reuses the ISO88591 constant already in this package.
    
    Result:
    No deprecated JDK API and no misleading comment, with identical behaviour.
    
    Tests:
    - New StringToolsSpec pins the byte to character mapping across the whole
      0x00 to 0xFF range, which is what would break if the charset were changed 
to
      US-ASCII later. It is a regression guard, not a failing-before test: this
      change is deliberately behaviour preserving
    - Verified in jshell that the two forms produce equal Strings for all 256 
byte
      values
    - JMH, average time over sizes 12, 64 and 4096 bytes: 25.3 / 30.2 / 1260.2 
ns
      for the deprecated form against 28.2 / 43.1 / 952.7 ns for ISO-8859-1, 
error
      bars overlapping at every size, so no measurable difference either way
    - sbt "http-core / Test / testOnly 
org.apache.pekko.http.impl.util.StringToolsSpec" - 4 passed
    - sbt http-core/mimaReportBinaryIssues - clean
    - scalafmt --mode diff-ref=upstream/main - clean
    
    References:
    None - found while reviewing the code base against the JDK 17 baseline
---
 .../apache/pekko/http/impl/util/StringTools.scala  | 10 ++--
 .../pekko/http/impl/util/StringToolsSpec.scala     | 54 ++++++++++++++++++++++
 2 files changed, 58 insertions(+), 6 deletions(-)

diff --git 
a/http-core/src/main/scala/org/apache/pekko/http/impl/util/StringTools.scala 
b/http-core/src/main/scala/org/apache/pekko/http/impl/util/StringTools.scala
index 2c652c132..92823da04 100644
--- a/http-core/src/main/scala/org/apache/pekko/http/impl/util/StringTools.scala
+++ b/http-core/src/main/scala/org/apache/pekko/http/impl/util/StringTools.scala
@@ -16,18 +16,16 @@ package org.apache.pekko.http.impl.util
 import org.apache.pekko
 import pekko.annotation.InternalApi
 
-import scala.annotation.nowarn
-
 /**
  * INTERNAL API
  */
 @InternalApi
 private[http] object StringTools {
-  @nowarn("msg=deprecated")
   def asciiStringFromBytes(bytes: Array[Byte]): String =
-    // Deprecated constructor but also (unfortunately) the fastest way to 
convert a ASCII encoded byte array
-    // into a String without extra copying.
-    new String(bytes, 0)
+    // ISO-8859-1 rather than US-ASCII: this maps every byte to the character 
of the same value, which
+    // is what the deprecated `new String(bytes, 0)` this replaces did. Since 
JDK 9 (compact strings) it
+    // keeps the array as is with a LATIN1 coder, so it is the same single 
copy.
+    new String(bytes, ISO88591)
 
   def asciiStringBytes(string: String): Array[Byte] = {
     // this is as fast as Unsafe.copyUSAsciiStrToBytes for recent JDK versions
diff --git 
a/http-core/src/test/scala/org/apache/pekko/http/impl/util/StringToolsSpec.scala
 
b/http-core/src/test/scala/org/apache/pekko/http/impl/util/StringToolsSpec.scala
new file mode 100644
index 000000000..ec30db525
--- /dev/null
+++ 
b/http-core/src/test/scala/org/apache/pekko/http/impl/util/StringToolsSpec.scala
@@ -0,0 +1,54 @@
+/*
+ * 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 java.nio.charset.StandardCharsets
+
+import org.scalatest.matchers.should.Matchers
+import org.scalatest.wordspec.AnyWordSpec
+
+class StringToolsSpec extends AnyWordSpec with Matchers {
+
+  "StringTools.asciiStringFromBytes" should {
+    "map every byte to the character of the same value" in {
+      // HPACK string literals are opaque octets, so bytes above 0x7F do reach 
this method. They must
+      // keep mapping to the character of the same value rather than to a 
replacement character, which
+      // is what decoding as US-ASCII would do.
+      val allBytes = Array.tabulate(256)(_.toByte)
+      val decoded = StringTools.asciiStringFromBytes(allBytes)
+
+      decoded.length shouldEqual 256
+      decoded.toSeq.map(_.toInt) shouldEqual (0 until 256)
+    }
+
+    "round-trip an ASCII string through asciiStringBytes" in {
+      val original = "content-type: application/json"
+      StringTools.asciiStringFromBytes(StringTools.asciiStringBytes(original)) 
shouldEqual original
+    }
+
+    "decode an empty array to an empty string" in {
+      StringTools.asciiStringFromBytes(Array.emptyByteArray) shouldEqual ""
+    }
+  }
+
+  "StringTools.asciiStringBytes" should {
+    "encode an ASCII string to its US-ASCII bytes" in {
+      StringTools.asciiStringBytes("abc") shouldEqual 
"abc".getBytes(StandardCharsets.US_ASCII)
+    }
+  }
+}


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

Reply via email to