This is an automated email from the ASF dual-hosted git repository.
gianm pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/druid.git
The following commit(s) were added to refs/heads/master by this push:
new 3c3eec5b1cb perf: Use Charset in StringUtils to/fromUtf8. (#20310)
3c3eec5b1cb is described below
commit 3c3eec5b1cb4c16a263bff79393d486f0381a3df
Author: Gian Merlino <[email protected]>
AuthorDate: Tue Sep 29 01:39:20 2026 -0700
perf: Use Charset in StringUtils to/fromUtf8. (#20310)
There is an old optimization in StringUtils#fromUtf8 and toUtf8 that
has outlived its usefulness. Apparently it used to be faster to pass the
String charset name rather than a Charset object, but that is no longer
the case.
---
.../seekablestream/StreamChunkReaderTest.java | 38 +++++++++++-----------
.../org/apache/druid/msq/exec/MSQExportTest.java | 7 ++--
.../apache/druid/java/util/common/StringUtils.java | 24 ++------------
.../druid/java/util/common/StringUtilsTest.java | 6 ++--
.../InputStreamFullResponseHandlerTest.java | 5 ++-
.../response/ObjectOrErrorResponseHandlerTest.java | 9 +++--
6 files changed, 35 insertions(+), 54 deletions(-)
diff --git
a/indexing-service/src/test/java/org/apache/druid/indexing/seekablestream/StreamChunkReaderTest.java
b/indexing-service/src/test/java/org/apache/druid/indexing/seekablestream/StreamChunkReaderTest.java
index 9acee174b98..5ac9d83da40 100644
---
a/indexing-service/src/test/java/org/apache/druid/indexing/seekablestream/StreamChunkReaderTest.java
+++
b/indexing-service/src/test/java/org/apache/druid/indexing/seekablestream/StreamChunkReaderTest.java
@@ -34,7 +34,6 @@ import org.apache.druid.error.DruidException;
import org.apache.druid.indexing.common.task.InputRowFilter;
import org.apache.druid.java.util.common.DateTimes;
import org.apache.druid.java.util.common.RE;
-import org.apache.druid.java.util.common.StringUtils;
import org.apache.druid.java.util.common.parsers.JSONPathSpec;
import org.apache.druid.java.util.common.parsers.ParseException;
import org.apache.druid.query.filter.AndDimFilter;
@@ -58,6 +57,7 @@ import org.mockito.MockitoAnnotations;
import javax.annotation.Nullable;
import java.io.File;
import java.io.IOException;
+import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
@@ -203,11 +203,11 @@ public class StreamChunkReaderTest
Arrays.asList(
new ByteEntity(
"{\"timestamp\": \"2020-01-01\", \"column_a\": \"y\",
\"column_b\": \"other\"}"
- .getBytes(StringUtils.UTF8_STRING)
+ .getBytes(StandardCharsets.UTF_8)
),
new ByteEntity(
"{\"timestamp\": \"2020-01-01\", \"column_a\": \"y\",
\"column_b\": \"title1\"}"
- .getBytes(StringUtils.UTF8_STRING)
+ .getBytes(StandardCharsets.UTF_8)
)
),
false
@@ -264,19 +264,19 @@ public class StreamChunkReaderTest
Arrays.asList(
new ByteEntity(
"{\"timestamp\": \"2020-01-01\", \"column_a\": \"y\",
\"column_b\": \"other\"}"
- .getBytes(StringUtils.UTF8_STRING)
+ .getBytes(StandardCharsets.UTF_8)
),
new ByteEntity(
"{\"timestamp\": \"2020-01-01\", \"column_a\": \"y\",
\"column_b\": \"late\"}"
- .getBytes(StringUtils.UTF8_STRING)
+ .getBytes(StandardCharsets.UTF_8)
),
new ByteEntity(
"{\"timestamp\": \"2020-01-01\", \"column_a\": \"y\",
\"column_b\": \"early\"}"
- .getBytes(StringUtils.UTF8_STRING)
+ .getBytes(StandardCharsets.UTF_8)
),
new ByteEntity(
"{\"timestamp\": \"2020-01-01\", \"column_a\": \"y\",
\"column_b\": \"title1\"}"
- .getBytes(StringUtils.UTF8_STRING)
+ .getBytes(StandardCharsets.UTF_8)
)
),
false
@@ -314,7 +314,7 @@ public class StreamChunkReaderTest
List<InputRow> parsedRows = chunkParser.parse(
Collections.singletonList(
- new ByteEntity(json.getBytes(StringUtils.UTF8_STRING))), false
+ new ByteEntity(json.getBytes(StandardCharsets.UTF_8))), false
);
// no exception and no parsed rows
Assertions.assertEquals(0, parsedRows.size());
@@ -334,11 +334,11 @@ public class StreamChunkReaderTest
Mockito.when(mockedByteEntityReader.read()).thenThrow(new
ParseException(null, "error parsing malformed data"));
final String json = "malformedJson";
List<ByteEntity> byteEntities = Arrays.asList(
- new ByteEntity(json.getBytes(StringUtils.UTF8_STRING)),
- new ByteEntity(json.getBytes(StringUtils.UTF8_STRING)),
- new ByteEntity(json.getBytes(StringUtils.UTF8_STRING)),
- new ByteEntity(json.getBytes(StringUtils.UTF8_STRING)),
- new ByteEntity(json.getBytes(StringUtils.UTF8_STRING))
+ new ByteEntity(json.getBytes(StandardCharsets.UTF_8)),
+ new ByteEntity(json.getBytes(StandardCharsets.UTF_8)),
+ new ByteEntity(json.getBytes(StandardCharsets.UTF_8)),
+ new ByteEntity(json.getBytes(StandardCharsets.UTF_8)),
+ new ByteEntity(json.getBytes(StandardCharsets.UTF_8))
);
Assertions.assertThrows(
RE.class,
@@ -367,11 +367,11 @@ public class StreamChunkReaderTest
final String json = "malformedJson";
List<ByteEntity> byteEntities = Arrays.asList(
- new ByteEntity(json.getBytes(StringUtils.UTF8_STRING)),
- new ByteEntity(json.getBytes(StringUtils.UTF8_STRING)),
- new ByteEntity(json.getBytes(StringUtils.UTF8_STRING)),
- new ByteEntity(json.getBytes(StringUtils.UTF8_STRING)),
- new ByteEntity(json.getBytes(StringUtils.UTF8_STRING))
+ new ByteEntity(json.getBytes(StandardCharsets.UTF_8)),
+ new ByteEntity(json.getBytes(StandardCharsets.UTF_8)),
+ new ByteEntity(json.getBytes(StandardCharsets.UTF_8)),
+ new ByteEntity(json.getBytes(StandardCharsets.UTF_8)),
+ new ByteEntity(json.getBytes(StandardCharsets.UTF_8))
);
List<InputRow> parsedRows = chunkParser.parse(byteEntities, false);
@@ -398,7 +398,7 @@ public class StreamChunkReaderTest
private void parseAndAssertResult(StreamChunkReader<ByteEntity> chunkParser)
throws IOException
{
final String json = "{\"timestamp\": \"2020-01-01\", \"dim\": \"val\",
\"met\": \"val2\"}";
- List<InputRow> parsedRows =
chunkParser.parse(Collections.singletonList(new
ByteEntity(json.getBytes(StringUtils.UTF8_STRING))), false);
+ List<InputRow> parsedRows =
chunkParser.parse(Collections.singletonList(new
ByteEntity(json.getBytes(StandardCharsets.UTF_8))), false);
Assertions.assertEquals(1, parsedRows.size());
InputRow row = parsedRows.get(0);
Assertions.assertEquals(DateTimes.of("2020-01-01"), row.getTimestamp());
diff --git
a/multi-stage-query/src/test/java/org/apache/druid/msq/exec/MSQExportTest.java
b/multi-stage-query/src/test/java/org/apache/druid/msq/exec/MSQExportTest.java
index 158484462e7..402d0ac8a34 100644
---
a/multi-stage-query/src/test/java/org/apache/druid/msq/exec/MSQExportTest.java
+++
b/multi-stage-query/src/test/java/org/apache/druid/msq/exec/MSQExportTest.java
@@ -36,6 +36,7 @@ import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
+import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.util.ArrayList;
import java.util.Arrays;
@@ -368,7 +369,7 @@ public class MSQExportTest extends MSQTestBase
private List<String> readResultsFromFile(File resultFile) throws IOException
{
List<String> results = new ArrayList<>();
- try (BufferedReader br = new BufferedReader(new
InputStreamReader(Files.newInputStream(resultFile.toPath()),
StringUtils.UTF8_STRING))) {
+ try (BufferedReader br = new BufferedReader(new
InputStreamReader(Files.newInputStream(resultFile.toPath()),
StandardCharsets.UTF_8))) {
String line;
while (!(line = br.readLine()).isEmpty()) {
results.add(line);
@@ -430,7 +431,7 @@ public class MSQExportTest extends MSQTestBase
final File manifestFile = new File(exportDir,
ExportMetadataManager.MANIFEST_FILE);
try (
BufferedReader bufferedReader = new BufferedReader(
- new InputStreamReader(Files.newInputStream(manifestFile.toPath()),
StringUtils.UTF8_STRING)
+ new InputStreamReader(Files.newInputStream(manifestFile.toPath()),
StandardCharsets.UTF_8)
)
) {
for (File file : resultFiles) {
@@ -445,7 +446,7 @@ public class MSQExportTest extends MSQTestBase
final File metaFile = new File(exportDir, ExportMetadataManager.META_FILE);
try (
BufferedReader bufferedReader = new BufferedReader(
- new InputStreamReader(Files.newInputStream(metaFile.toPath()),
StringUtils.UTF8_STRING)
+ new InputStreamReader(Files.newInputStream(metaFile.toPath()),
StandardCharsets.UTF_8)
)
) {
Assertions.assertEquals(
diff --git
a/processing/src/main/java/org/apache/druid/java/util/common/StringUtils.java
b/processing/src/main/java/org/apache/druid/java/util/common/StringUtils.java
index f351abd0cd8..84915633f70 100644
---
a/processing/src/main/java/org/apache/druid/java/util/common/StringUtils.java
+++
b/processing/src/main/java/org/apache/druid/java/util/common/StringUtils.java
@@ -28,12 +28,10 @@ import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.io.IOException;
import java.io.InputStream;
-import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
-import java.nio.charset.Charset;
import java.nio.charset.CharsetEncoder;
import java.nio.charset.CodingErrorAction;
import java.nio.charset.StandardCharsets;
@@ -43,15 +41,11 @@ import java.util.IllegalFormatException;
import java.util.Locale;
/**
- * As of OpenJDK / Oracle JDK 8, the JVM is optimized around String charset
variable instead of Charset passing, that
- * is exploited in {@link #toUtf8(String)} and {@link #fromUtf8(byte[])}.
+ * Utilities for working with strings, including UTF-8 encoding and decoding.
*/
public class StringUtils
{
public static final byte[] EMPTY_BYTES = new byte[0];
- @Deprecated // Charset parameters to String are currently slower than the
charset's string name
- public static final Charset UTF8_CHARSET = StandardCharsets.UTF_8;
- public static final String UTF8_STRING = StandardCharsets.UTF_8.toString();
private static final Base64.Encoder BASE64_ENCODER = Base64.getEncoder();
private static final Base64.Decoder BASE64_DECODER = Base64.getDecoder();
@@ -231,13 +225,7 @@ public class StringUtils
public static String fromUtf8(final byte[] bytes, int offset, int length)
{
- try {
- return new String(bytes, offset, length, UTF8_STRING);
- }
- catch (UnsupportedEncodingException e) {
- // Should never happen
- throw new RuntimeException(e);
- }
+ return new String(bytes, offset, length, StandardCharsets.UTF_8);
}
/**
@@ -299,13 +287,7 @@ public class StringUtils
*/
public static byte[] toUtf8(final String string)
{
- try {
- return string.getBytes(UTF8_STRING);
- }
- catch (UnsupportedEncodingException e) {
- // Should never happen
- throw new RuntimeException(e);
- }
+ return string.getBytes(StandardCharsets.UTF_8);
}
/**
diff --git
a/processing/src/test/java/org/apache/druid/java/util/common/StringUtilsTest.java
b/processing/src/test/java/org/apache/druid/java/util/common/StringUtilsTest.java
index 8a7c69fd46c..97bf05c74ac 100644
---
a/processing/src/test/java/org/apache/druid/java/util/common/StringUtilsTest.java
+++
b/processing/src/test/java/org/apache/druid/java/util/common/StringUtilsTest.java
@@ -24,9 +24,9 @@ import org.apache.druid.collections.ResourceHolder;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
-import java.io.UnsupportedEncodingException;
import java.nio.BufferUnderflowException;
import java.nio.ByteBuffer;
+import java.nio.charset.StandardCharsets;
import java.util.List;
/**
@@ -53,13 +53,13 @@ public class StringUtilsTest
);
@Test
- public void fromUtf8ConversionTest() throws UnsupportedEncodingException
+ public void fromUtf8ConversionTest()
{
byte[] bytes = new byte[]{'a', 'b', 'c', 'd'};
Assertions.assertEquals("abcd", StringUtils.fromUtf8(bytes));
String abcd = "abcd";
- Assertions.assertEquals(abcd,
StringUtils.fromUtf8(abcd.getBytes(StringUtils.UTF8_STRING)));
+ Assertions.assertEquals(abcd,
StringUtils.fromUtf8(abcd.getBytes(StandardCharsets.UTF_8)));
}
@Test
diff --git
a/processing/src/test/java/org/apache/druid/java/util/http/client/response/InputStreamFullResponseHandlerTest.java
b/processing/src/test/java/org/apache/druid/java/util/http/client/response/InputStreamFullResponseHandlerTest.java
index 38d4c1ac5df..48d57f79ce8 100644
---
a/processing/src/test/java/org/apache/druid/java/util/http/client/response/InputStreamFullResponseHandlerTest.java
+++
b/processing/src/test/java/org/apache/druid/java/util/http/client/response/InputStreamFullResponseHandlerTest.java
@@ -26,7 +26,6 @@ import io.netty.handler.codec.http.HttpResponse;
import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.handler.codec.http.HttpVersion;
import org.apache.commons.io.IOUtils;
-import org.apache.druid.java.util.common.StringUtils;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
@@ -43,10 +42,10 @@ public class InputStreamFullResponseHandlerTest
ClientResponse<InputStreamFullResponseHolder> clientResp =
responseHandler.handleResponse(response, null);
// In Netty 4 the body arrives via HttpContent chunks after the initial
HttpResponse.
- DefaultHttpContent firstChunk = new
DefaultHttpContent(Unpooled.wrappedBuffer("abcd".getBytes(StringUtils.UTF8_STRING)));
+ DefaultHttpContent firstChunk = new
DefaultHttpContent(Unpooled.wrappedBuffer("abcd".getBytes(StandardCharsets.UTF_8)));
clientResp = responseHandler.handleChunk(clientResp, firstChunk, 1);
- DefaultHttpContent secondChunk = new
DefaultHttpContent(Unpooled.wrappedBuffer("efg".getBytes(StringUtils.UTF8_STRING)));
+ DefaultHttpContent secondChunk = new
DefaultHttpContent(Unpooled.wrappedBuffer("efg".getBytes(StandardCharsets.UTF_8)));
clientResp = responseHandler.handleChunk(clientResp, secondChunk, 2);
clientResp = responseHandler.done(clientResp);
diff --git
a/processing/src/test/java/org/apache/druid/java/util/http/client/response/ObjectOrErrorResponseHandlerTest.java
b/processing/src/test/java/org/apache/druid/java/util/http/client/response/ObjectOrErrorResponseHandlerTest.java
index d49d3f2c674..f7dd0d421b6 100644
---
a/processing/src/test/java/org/apache/druid/java/util/http/client/response/ObjectOrErrorResponseHandlerTest.java
+++
b/processing/src/test/java/org/apache/druid/java/util/http/client/response/ObjectOrErrorResponseHandlerTest.java
@@ -27,7 +27,6 @@ import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.handler.codec.http.HttpVersion;
import org.apache.commons.io.IOUtils;
import org.apache.druid.java.util.common.Either;
-import org.apache.druid.java.util.common.StringUtils;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
@@ -50,10 +49,10 @@ public class ObjectOrErrorResponseHandlerTest
// In Netty 4 the body arrives via HttpContent chunks after the initial
HttpResponse.
DefaultHttpContent firstChunk =
- new
DefaultHttpContent(Unpooled.wrappedBuffer("abcd".getBytes(StringUtils.UTF8_STRING)));
+ new
DefaultHttpContent(Unpooled.wrappedBuffer("abcd".getBytes(StandardCharsets.UTF_8)));
clientResp = responseHandler.handleChunk(clientResp, firstChunk, 1);
DefaultHttpContent secondChunk =
- new
DefaultHttpContent(Unpooled.wrappedBuffer("efg".getBytes(StringUtils.UTF8_STRING)));
+ new
DefaultHttpContent(Unpooled.wrappedBuffer("efg".getBytes(StandardCharsets.UTF_8)));
clientResp = responseHandler.handleChunk(clientResp, secondChunk, 2);
clientResp = responseHandler.done(clientResp);
@@ -104,10 +103,10 @@ public class ObjectOrErrorResponseHandlerTest
// Body chunks for an error response.
DefaultHttpContent firstChunk =
- new
DefaultHttpContent(Unpooled.wrappedBuffer("abcd".getBytes(StringUtils.UTF8_STRING)));
+ new
DefaultHttpContent(Unpooled.wrappedBuffer("abcd".getBytes(StandardCharsets.UTF_8)));
clientResp = responseHandler.handleChunk(clientResp, firstChunk, 1);
DefaultHttpContent secondChunk =
- new
DefaultHttpContent(Unpooled.wrappedBuffer("efg".getBytes(StringUtils.UTF8_STRING)));
+ new
DefaultHttpContent(Unpooled.wrappedBuffer("efg".getBytes(StandardCharsets.UTF_8)));
clientResp = responseHandler.handleChunk(clientResp, secondChunk, 2);
clientResp = responseHandler.done(clientResp);
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]