From 772eade8006c5a0b259594481ba077daadff6ada Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Wed, 27 Jan 2021 14:16:46 +0100 Subject: [PATCH] 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 --- .../codec/compression/Java11ZlibUtils.java | 79 +++++++++++++++++++ .../codec/compression/JdkZlibDecoder.java | 23 ++++-- 2 files changed, 97 insertions(+), 5 deletions(-) create mode 100644 codec/src/main/java/io/netty/handler/codec/compression/Java11ZlibUtils.java diff --git a/codec/src/main/java/io/netty/handler/codec/compression/Java11ZlibUtils.java b/codec/src/main/java/io/netty/handler/codec/compression/Java11ZlibUtils.java new file mode 100644 index 0000000000..462b801118 --- /dev/null +++ b/codec/src/main/java/io/netty/handler/codec/compression/Java11ZlibUtils.java @@ -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() { } +} diff --git a/codec/src/main/java/io/netty/handler/codec/compression/JdkZlibDecoder.java b/codec/src/main/java/io/netty/handler/codec/compression/JdkZlibDecoder.java index d11b87f261..2946b10d2b 100644 --- a/codec/src/main/java/io/netty/handler/codec/compression/JdkZlibDecoder.java +++ b/codec/src/main/java/io/netty/handler/codec/compression/JdkZlibDecoder.java @@ -19,6 +19,7 @@ import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBufAllocator; import io.netty.channel.ChannelHandlerContext; import io.netty.util.internal.ObjectUtil; +import io.netty.util.internal.PlatformDependent; import java.util.List; import java.util.zip.CRC32; @@ -30,6 +31,9 @@ import java.util.zip.Inflater; * Decompress a {@link ByteBuf} using the inflate algorithm. */ 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 FEXTRA = 0x04; private static final int FNAME = 0x08; @@ -220,7 +224,9 @@ public class JdkZlibDecoder extends ZlibDecoder { } 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); } else { byte[] array = new byte[readableBytes]; @@ -233,15 +239,13 @@ public class JdkZlibDecoder extends ZlibDecoder { try { boolean readFooter = false; while (!inflater.needsInput()) { - byte[] outArray = decompressed.array(); int writerIndex = decompressed.writerIndex(); - int outIndex = decompressed.arrayOffset() + writerIndex; int writable = decompressed.writableBytes(); - int outputLength = inflater.inflate(outArray, outIndex, writable); + int outputLength = inflate(decompressed, writerIndex, writable); if (outputLength > 0) { decompressed.writerIndex(writerIndex + outputLength); if (crc != null) { - crc.update(outArray, outIndex, outputLength); + crc.update(decompressed, writerIndex, outputLength); } } else if (inflater.needsDictionary()) { 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) { if (readGZIPFooter(in)) { finished = !decompressConcatenated;