From 590f18dbe076aa32104e570fd7de77152fd4b3db Mon Sep 17 00:00:00 2001 From: Andrew Steinborn Date: Sun, 23 May 2021 13:58:01 -0400 Subject: [PATCH] Convert to Java 11 compressor --- native/build.gradle | 4 +- .../compression/Java11VelocityCompressor.java | 162 ------------------ .../compression/JavaVelocityCompressor.java | 109 +++++------- .../velocitypowered/natives/util/Natives.java | 3 - .../compression/VelocityCompressorTest.java | 16 -- proxy/build.gradle | 5 - 6 files changed, 41 insertions(+), 258 deletions(-) delete mode 100644 native/src/main/java/com/velocitypowered/natives/compression/Java11VelocityCompressor.java diff --git a/native/build.gradle b/native/build.gradle index 03f7f2423..b58547459 100644 --- a/native/build.gradle +++ b/native/build.gradle @@ -9,8 +9,8 @@ apply from: '../gradle/checkstyle.gradle' apply from: '../gradle/publish.gradle' java { - sourceCompatibility = JavaVersion.VERSION_1_8 - targetCompatibility = JavaVersion.VERSION_1_8 + sourceCompatibility = JavaVersion.VERSION_11 + targetCompatibility = JavaVersion.VERSION_11 } license { diff --git a/native/src/main/java/com/velocitypowered/natives/compression/Java11VelocityCompressor.java b/native/src/main/java/com/velocitypowered/natives/compression/Java11VelocityCompressor.java deleted file mode 100644 index be3ed64e2..000000000 --- a/native/src/main/java/com/velocitypowered/natives/compression/Java11VelocityCompressor.java +++ /dev/null @@ -1,162 +0,0 @@ -/* - * Copyright (C) 2018 Velocity Contributors - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package com.velocitypowered.natives.compression; - -import static com.google.common.base.Preconditions.checkArgument; -import static com.google.common.base.Preconditions.checkState; -import static com.velocitypowered.natives.compression.CompressorUtils.ZLIB_BUFFER_SIZE; -import static com.velocitypowered.natives.compression.CompressorUtils.ensureMaxSize; - -import com.velocitypowered.natives.util.BufferPreference; -import io.netty.buffer.ByteBuf; -import java.lang.invoke.MethodHandle; -import java.lang.invoke.MethodHandles; -import java.lang.invoke.MethodType; -import java.nio.ByteBuffer; -import java.util.zip.DataFormatException; -import java.util.zip.Deflater; -import java.util.zip.Inflater; - -public class Java11VelocityCompressor implements VelocityCompressor { - - public static final VelocityCompressorFactory FACTORY = Java11VelocityCompressor::new; - - // The use of MethodHandle is intentional. Velocity targets Java 8, and these methods don't exist - // in Java 8. This was also the most performant solution I could find, only slightly slower than a - // direct method call without long warmup times, requiring bytecode generation through ASM, or - // other stuff. - private static final MethodHandle DEFLATE_SET_INPUT; - private static final MethodHandle INFLATE_SET_INPUT; - private static final MethodHandle DEFLATE_CALL; - private static final MethodHandle INFLATE_CALL; - - static { - try { - DEFLATE_SET_INPUT = MethodHandles.lookup().findVirtual(Deflater.class, "setInput", - MethodType.methodType(void.class, ByteBuffer.class)); - INFLATE_SET_INPUT = MethodHandles.lookup().findVirtual(Inflater.class, "setInput", - MethodType.methodType(void.class, ByteBuffer.class)); - - DEFLATE_CALL = MethodHandles.lookup().findVirtual(Deflater.class, "deflate", - MethodType.methodType(int.class, ByteBuffer.class)); - INFLATE_CALL = MethodHandles.lookup().findVirtual(Inflater.class, "inflate", - MethodType.methodType(int.class, ByteBuffer.class)); - } catch (NoSuchMethodException | IllegalAccessException e) { - throw new AssertionError("Can't use Java 11 compressor on your version of Java"); - } - } - - private final Deflater deflater; - private final Inflater inflater; - private boolean disposed = false; - - private Java11VelocityCompressor(int level) { - this.deflater = new Deflater(level); - this.inflater = new Inflater(); - } - - @Override - public void inflate(ByteBuf source, ByteBuf destination, int uncompressedSize) - throws DataFormatException { - ensureNotDisposed(); - - // We (probably) can't nicely deal with >=1 buffer nicely, so let's scream loudly. - checkArgument(source.nioBufferCount() == 1, "source has multiple backing buffers"); - checkArgument(destination.nioBufferCount() == 1, "destination has multiple backing buffers"); - - try { - final int origIdx = source.readerIndex(); - INFLATE_SET_INPUT.invokeExact(inflater, source.nioBuffer()); - - while (!inflater.finished() && inflater.getBytesWritten() < uncompressedSize) { - if (!destination.isWritable()) { - ensureMaxSize(destination, uncompressedSize); - destination.ensureWritable(ZLIB_BUFFER_SIZE); - } - - ByteBuffer destNioBuf = destination.nioBuffer(destination.writerIndex(), - destination.writableBytes()); - int produced = (int) INFLATE_CALL.invokeExact(inflater, destNioBuf); - destination.writerIndex(destination.writerIndex() + produced); - } - - if (!inflater.finished()) { - throw new DataFormatException("Received a deflate stream that was too large, wanted " - + uncompressedSize); - } - source.readerIndex(origIdx + inflater.getTotalIn()); - } catch (Throwable e) { - if (e instanceof DataFormatException) { - throw (DataFormatException) e; - } - throw new RuntimeException(e); - } finally { - inflater.reset(); - } - } - - @Override - public void deflate(ByteBuf source, ByteBuf destination) throws DataFormatException { - ensureNotDisposed(); - - // We (probably) can't nicely deal with >=1 buffer nicely, so let's scream loudly. - checkArgument(source.nioBufferCount() == 1, "source has multiple backing buffers"); - checkArgument(destination.nioBufferCount() == 1, "destination has multiple backing buffers"); - - try { - final int origIdx = source.readerIndex(); - DEFLATE_SET_INPUT.invokeExact(deflater, source.nioBuffer()); - deflater.finish(); - - while (!deflater.finished()) { - if (!destination.isWritable()) { - destination.ensureWritable(ZLIB_BUFFER_SIZE); - } - - ByteBuffer destNioBuf = destination.nioBuffer(destination.writerIndex(), - destination.writableBytes()); - int produced = (int) DEFLATE_CALL.invokeExact(deflater, destNioBuf); - destination.writerIndex(destination.writerIndex() + produced); - } - - source.readerIndex(origIdx + deflater.getTotalIn()); - deflater.reset(); - } catch (Throwable e) { - if (e instanceof DataFormatException) { - throw (DataFormatException) e; - } - throw new RuntimeException(e); - } - } - - @Override - public void close() { - disposed = true; - deflater.end(); - inflater.end(); - } - - private void ensureNotDisposed() { - checkState(!disposed, "Object already disposed"); - } - - @Override - public BufferPreference preferredBufferType() { - return BufferPreference.DIRECT_PREFERRED; - } -} diff --git a/native/src/main/java/com/velocitypowered/natives/compression/JavaVelocityCompressor.java b/native/src/main/java/com/velocitypowered/natives/compression/JavaVelocityCompressor.java index 97127d25d..e9809f2ac 100644 --- a/native/src/main/java/com/velocitypowered/natives/compression/JavaVelocityCompressor.java +++ b/native/src/main/java/com/velocitypowered/natives/compression/JavaVelocityCompressor.java @@ -17,12 +17,14 @@ package com.velocitypowered.natives.compression; +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkState; import static com.velocitypowered.natives.compression.CompressorUtils.ZLIB_BUFFER_SIZE; import static com.velocitypowered.natives.compression.CompressorUtils.ensureMaxSize; -import com.google.common.base.Preconditions; import com.velocitypowered.natives.util.BufferPreference; import io.netty.buffer.ByteBuf; +import java.nio.ByteBuffer; import java.util.zip.DataFormatException; import java.util.zip.Deflater; import java.util.zip.Inflater; @@ -33,7 +35,6 @@ public class JavaVelocityCompressor implements VelocityCompressor { private final Deflater deflater; private final Inflater inflater; - private byte[] buf = new byte[0]; private boolean disposed = false; private JavaVelocityCompressor(int level) { @@ -46,47 +47,32 @@ public class JavaVelocityCompressor implements VelocityCompressor { throws DataFormatException { ensureNotDisposed(); - final int available = source.readableBytes(); - this.setInflaterInput(source); + // We (probably) can't nicely deal with >=1 buffer nicely, so let's scream loudly. + checkArgument(source.nioBufferCount() == 1, "source has multiple backing buffers"); + checkArgument(destination.nioBufferCount() == 1, "destination has multiple backing buffers"); - if (destination.hasArray()) { - this.inflateDestinationIsHeap(destination, available, uncompressedSize); - } else { - if (buf.length == 0) { - buf = new byte[ZLIB_BUFFER_SIZE]; - } - while (!inflater.finished() && inflater.getBytesRead() < available) { - ensureMaxSize(destination, uncompressedSize); - int read = inflater.inflate(buf); - destination.writeBytes(buf, 0, read); - } - } - inflater.reset(); - } + final int origIdx = source.readerIndex(); + inflater.setInput(source.nioBuffer()); - private void setInflaterInput(ByteBuf source) { - final int available = source.readableBytes(); - if (source.hasArray()) { - inflater.setInput(source.array(), source.arrayOffset() + source.readerIndex(), available); - } else { - byte[] inData = new byte[available]; - source.readBytes(inData); - inflater.setInput(inData); - } - } + try { + while (!inflater.finished() && inflater.getBytesWritten() < uncompressedSize) { + if (!destination.isWritable()) { + destination.ensureWritable(ZLIB_BUFFER_SIZE); + } - private void inflateDestinationIsHeap(ByteBuf destination, int available, int max) - throws DataFormatException { - while (!inflater.finished() && inflater.getBytesRead() < available) { - if (!destination.isWritable()) { - ensureMaxSize(destination, max); - destination.ensureWritable(ZLIB_BUFFER_SIZE); + ByteBuffer destNioBuf = destination.nioBuffer(destination.writerIndex(), + destination.writableBytes()); + int produced = inflater.inflate(destNioBuf); + destination.writerIndex(destination.writerIndex() + produced); } - ensureMaxSize(destination, max); - int produced = inflater.inflate(destination.array(), destination.arrayOffset() - + destination.writerIndex(), destination.writableBytes()); - destination.writerIndex(destination.writerIndex() + produced); + if (!inflater.finished()) { + throw new DataFormatException("Received a deflate stream that was too large, wanted " + + uncompressedSize); + } + source.readerIndex(origIdx + inflater.getTotalIn()); + } finally { + inflater.reset(); } } @@ -94,44 +80,27 @@ public class JavaVelocityCompressor implements VelocityCompressor { public void deflate(ByteBuf source, ByteBuf destination) throws DataFormatException { ensureNotDisposed(); - this.setDeflaterInput(source); + // We (probably) can't nicely deal with >=1 buffer nicely, so let's scream loudly. + checkArgument(source.nioBufferCount() == 1, "source has multiple backing buffers"); + checkArgument(destination.nioBufferCount() == 1, "destination has multiple backing buffers"); + + final int origIdx = source.readerIndex(); + deflater.setInput(source.nioBuffer()); deflater.finish(); - if (destination.hasArray()) { - this.deflateDestinationIsHeap(destination); - } else { - if (buf.length == 0) { - buf = new byte[ZLIB_BUFFER_SIZE]; - } - while (!deflater.finished()) { - int bytes = deflater.deflate(buf); - destination.writeBytes(buf, 0, bytes); - } - } - deflater.reset(); - } - - private void setDeflaterInput(ByteBuf source) { - if (source.hasArray()) { - deflater.setInput(source.array(), source.arrayOffset() + source.readerIndex(), - source.readableBytes()); - } else { - byte[] inData = new byte[source.readableBytes()]; - source.readBytes(inData); - deflater.setInput(inData); - } - } - - private void deflateDestinationIsHeap(ByteBuf destination) { while (!deflater.finished()) { if (!destination.isWritable()) { destination.ensureWritable(ZLIB_BUFFER_SIZE); } - int produced = deflater.deflate(destination.array(), destination.arrayOffset() - + destination.writerIndex(), destination.writableBytes()); + ByteBuffer destNioBuf = destination.nioBuffer(destination.writerIndex(), + destination.writableBytes()); + int produced = deflater.deflate(destNioBuf); destination.writerIndex(destination.writerIndex() + produced); } + + source.readerIndex(origIdx + deflater.getTotalIn()); + deflater.reset(); } @Override @@ -142,11 +111,11 @@ public class JavaVelocityCompressor implements VelocityCompressor { } private void ensureNotDisposed() { - Preconditions.checkState(!disposed, "Object already disposed"); + checkState(!disposed, "Object already disposed"); } @Override public BufferPreference preferredBufferType() { - return BufferPreference.HEAP_PREFERRED; + return BufferPreference.DIRECT_PREFERRED; } -} +} \ No newline at end of file diff --git a/native/src/main/java/com/velocitypowered/natives/util/Natives.java b/native/src/main/java/com/velocitypowered/natives/util/Natives.java index b99e24b47..28110cacd 100644 --- a/native/src/main/java/com/velocitypowered/natives/util/Natives.java +++ b/native/src/main/java/com/velocitypowered/natives/util/Natives.java @@ -19,7 +19,6 @@ package com.velocitypowered.natives.util; import com.google.common.collect.ImmutableList; import com.velocitypowered.natives.NativeSetupException; -import com.velocitypowered.natives.compression.Java11VelocityCompressor; import com.velocitypowered.natives.compression.JavaVelocityCompressor; import com.velocitypowered.natives.compression.LibdeflateVelocityCompressor; import com.velocitypowered.natives.compression.VelocityCompressorFactory; @@ -87,8 +86,6 @@ public class Natives { copyAndLoadNative("/linux_aarch64/velocity-compress.so"), "libdeflate (Linux aarch64)", LibdeflateVelocityCompressor.FACTORY), - new NativeCodeLoader.Variant<>(NativeConstraints.JAVA_11, () -> { - }, "Java 11", () -> Java11VelocityCompressor.FACTORY), new NativeCodeLoader.Variant<>(NativeCodeLoader.ALWAYS, () -> { }, "Java", JavaVelocityCompressor.FACTORY) ) diff --git a/native/src/test/java/com/velocitypowered/natives/compression/VelocityCompressorTest.java b/native/src/test/java/com/velocitypowered/natives/compression/VelocityCompressorTest.java index af4d5abbc..c07d96688 100644 --- a/native/src/test/java/com/velocitypowered/natives/compression/VelocityCompressorTest.java +++ b/native/src/test/java/com/velocitypowered/natives/compression/VelocityCompressorTest.java @@ -79,22 +79,6 @@ class VelocityCompressorTest { check(compressor, () -> Unpooled.buffer(TEST_DATA.length + 32)); } - @Test - @EnabledOnJre(JRE.JAVA_11) - void java11IntegrityCheckDirect() throws DataFormatException { - VelocityCompressor compressor = Java11VelocityCompressor.FACTORY - .create(Deflater.DEFAULT_COMPRESSION); - check(compressor, () -> Unpooled.directBuffer(TEST_DATA.length + 32)); - } - - @Test - @EnabledOnJre(JRE.JAVA_11) - void java11IntegrityCheckHeap() throws DataFormatException { - VelocityCompressor compressor = Java11VelocityCompressor.FACTORY - .create(Deflater.DEFAULT_COMPRESSION); - check(compressor, () -> Unpooled.buffer(TEST_DATA.length + 32)); - } - private void check(VelocityCompressor compressor, Supplier bufSupplier) throws DataFormatException { ByteBuf source = bufSupplier.get(); diff --git a/proxy/build.gradle b/proxy/build.gradle index 33fa5bb41..49f9bc19f 100644 --- a/proxy/build.gradle +++ b/proxy/build.gradle @@ -13,11 +13,6 @@ license { header = project.rootProject.file('HEADER.txt') } -java { - sourceCompatibility = JavaVersion.VERSION_1_8 - targetCompatibility = JavaVersion.VERSION_1_8 -} - jar { manifest { def buildNumber = System.getenv("BUILD_NUMBER") ?: "unknown"