Use Inflater ByteBuffer methods when running on Java11

Motivation:

We should make use of the Inflater methods that allows us to use a ByteBuffer to be able to reduce memory copies when running on Java11+.

Modifications:

Add new class that uses MethodHandles to call the right methods when running on Java11

Result:

Replaces parts of https://github.com/netty/netty/pull/10228
This commit is contained in:
Norman Maurer 2021-01-27 14:16:46 +01:00
parent 147ccb6213
commit 772eade800
2 changed files with 97 additions and 5 deletions

View File

@ -0,0 +1,79 @@
/*
* Copyright 2021 The Netty Project
*
* The Netty Project 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:
*
* https://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 io.netty.handler.codec.compression;
import io.netty.util.internal.PlatformDependent;
import io.netty.util.internal.SuppressJava6Requirement;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.nio.ByteBuffer;
import java.util.zip.Inflater;
@SuppressJava6Requirement(reason = "Guarded by version check")
final class Java11ZlibUtils {
private static final MethodHandle INFLATER_SET_INPUT;
private static final MethodHandle INFLATER_INFLATE;
static {
MethodHandle inflaterSetInput = null;
MethodHandle inflaterInflate = null;
try {
inflaterSetInput = MethodHandles.lookup().findVirtual(Inflater.class, "setInput",
MethodType.methodType(void.class, ByteBuffer.class));
inflaterInflate = MethodHandles.lookup().findVirtual(Inflater.class, "inflate",
MethodType.methodType(int.class, ByteBuffer.class));
} catch (NoSuchMethodException e) {
e.printStackTrace();
inflaterSetInput = null;
inflaterInflate = null;
} catch (IllegalAccessException e) {
e.printStackTrace();
inflaterSetInput = null;
inflaterInflate = null;
}
INFLATER_SET_INPUT = inflaterSetInput;
INFLATER_INFLATE = inflaterInflate;
}
static boolean isSupported() {
return INFLATER_SET_INPUT != null && INFLATER_INFLATE != null;
}
static void setInput(Inflater inflater, ByteBuffer input) {
try {
// Use invokeWithArguments as we compile with -target 6
INFLATER_SET_INPUT.invokeWithArguments(inflater, input);
} catch (Throwable cause) {
PlatformDependent.throwException(cause);
}
}
static int inflate(Inflater inflater, ByteBuffer input) {
try {
// Use invokeWithArguments as we compile with -target 6
return (Integer) INFLATER_INFLATE.invokeWithArguments(inflater, input);
} catch (Throwable cause) {
PlatformDependent.throwException(cause);
}
return -1;
}
private Java11ZlibUtils() { }
}

View File

@ -19,6 +19,7 @@ import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufAllocator; import io.netty.buffer.ByteBufAllocator;
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelHandlerContext;
import io.netty.util.internal.ObjectUtil; import io.netty.util.internal.ObjectUtil;
import io.netty.util.internal.PlatformDependent;
import java.util.List; import java.util.List;
import java.util.zip.CRC32; import java.util.zip.CRC32;
@ -30,6 +31,9 @@ import java.util.zip.Inflater;
* Decompress a {@link ByteBuf} using the inflate algorithm. * Decompress a {@link ByteBuf} using the inflate algorithm.
*/ */
public class JdkZlibDecoder extends ZlibDecoder { public class JdkZlibDecoder extends ZlibDecoder {
private static final boolean IS_JAVA_11_INFLATER =
PlatformDependent.javaVersion() >= 11 && Java11ZlibUtils.isSupported();
private static final int FHCRC = 0x02; private static final int FHCRC = 0x02;
private static final int FEXTRA = 0x04; private static final int FEXTRA = 0x04;
private static final int FNAME = 0x08; private static final int FNAME = 0x08;
@ -220,7 +224,9 @@ public class JdkZlibDecoder extends ZlibDecoder {
} }
if (inflater.needsInput()) { if (inflater.needsInput()) {
if (in.hasArray()) { if (IS_JAVA_11_INFLATER) {
Java11ZlibUtils.setInput(inflater, in.internalNioBuffer(in.readerIndex(), in.readableBytes()));
} else if (in.hasArray()) {
inflater.setInput(in.array(), in.arrayOffset() + in.readerIndex(), readableBytes); inflater.setInput(in.array(), in.arrayOffset() + in.readerIndex(), readableBytes);
} else { } else {
byte[] array = new byte[readableBytes]; byte[] array = new byte[readableBytes];
@ -233,15 +239,13 @@ public class JdkZlibDecoder extends ZlibDecoder {
try { try {
boolean readFooter = false; boolean readFooter = false;
while (!inflater.needsInput()) { while (!inflater.needsInput()) {
byte[] outArray = decompressed.array();
int writerIndex = decompressed.writerIndex(); int writerIndex = decompressed.writerIndex();
int outIndex = decompressed.arrayOffset() + writerIndex;
int writable = decompressed.writableBytes(); int writable = decompressed.writableBytes();
int outputLength = inflater.inflate(outArray, outIndex, writable); int outputLength = inflate(decompressed, writerIndex, writable);
if (outputLength > 0) { if (outputLength > 0) {
decompressed.writerIndex(writerIndex + outputLength); decompressed.writerIndex(writerIndex + outputLength);
if (crc != null) { if (crc != null) {
crc.update(outArray, outIndex, outputLength); crc.update(decompressed, writerIndex, outputLength);
} }
} else if (inflater.needsDictionary()) { } else if (inflater.needsDictionary()) {
if (dictionary == null) { if (dictionary == null) {
@ -280,6 +284,15 @@ public class JdkZlibDecoder extends ZlibDecoder {
} }
} }
private int inflate(ByteBuf decompressed, int writerIndex, int writable) throws DataFormatException {
if (IS_JAVA_11_INFLATER) {
return Java11ZlibUtils.inflate(inflater, decompressed.internalNioBuffer(writerIndex, writable));
}
byte[] outArray = decompressed.array();
int outIndex = decompressed.arrayOffset() + writerIndex;
return inflater.inflate(outArray, outIndex, writable);
}
private boolean handleGzipFooter(ByteBuf in) { private boolean handleGzipFooter(ByteBuf in) {
if (readGZIPFooter(in)) { if (readGZIPFooter(in)) {
finished = !decompressConcatenated; finished = !decompressConcatenated;