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]