From 13a63eff7672956871ea2f92a59533bc58df92b9 Mon Sep 17 00:00:00 2001 From: Andrew Steinborn Date: Thu, 5 Nov 2020 16:50:57 -0500 Subject: [PATCH] Replace old Java compressor with Java 11 compressor --- .../compression/Java11VelocityCompressor.java | 140 ------------------ .../compression/JavaVelocityCompressor.java | 97 ++++-------- .../velocitypowered/natives/util/Natives.java | 5 +- .../compression/VelocityCompressorTest.java | 6 +- 4 files changed, 34 insertions(+), 214 deletions(-) delete mode 100644 native/src/main/java/com/velocitypowered/natives/compression/Java11VelocityCompressor.java 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 18397fee7..000000000 --- a/native/src/main/java/com/velocitypowered/natives/compression/Java11VelocityCompressor.java +++ /dev/null @@ -1,140 +0,0 @@ -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 { - int origIdx = source.readerIndex(); - INFLATE_SET_INPUT.invokeExact(inflater, source.nioBuffer()); - - while (!inflater.finished() && inflater.getBytesRead() < source.readableBytes()) { - 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); - source.readerIndex(origIdx + inflater.getTotalIn()); - destination.writerIndex(destination.writerIndex() + produced); - } - - inflater.reset(); - } catch (Throwable e) { - if (e instanceof DataFormatException) { - throw (DataFormatException) e; - } - throw new RuntimeException(e); - } - } - - @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 { - 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); - source.readerIndex(origIdx + deflater.getTotalIn()); - destination.writerIndex(destination.writerIndex() + produced); - } - - 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 d5ad754b9..28b941a65 100644 --- a/native/src/main/java/com/velocitypowered/natives/compression/JavaVelocityCompressor.java +++ b/native/src/main/java/com/velocitypowered/natives/compression/JavaVelocityCompressor.java @@ -1,11 +1,13 @@ 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; @@ -16,7 +18,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) { @@ -29,92 +30,54 @@ 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(); - } + 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); - } - } - - private void inflateDestinationIsHeap(ByteBuf destination, int available, int max) - throws DataFormatException { - while (!inflater.finished() && inflater.getBytesRead() < available) { + while (!inflater.finished() && inflater.getBytesRead() < source.readableBytes()) { if (!destination.isWritable()) { - ensureMaxSize(destination, max); + ensureMaxSize(destination, uncompressedSize); destination.ensureWritable(ZLIB_BUFFER_SIZE); } - ensureMaxSize(destination, max); - int produced = inflater.inflate(destination.array(), destination.arrayOffset() - + destination.writerIndex(), destination.writableBytes()); + ByteBuffer destNioBuf = destination.nioBuffer(destination.writerIndex(), + destination.writableBytes()); + int produced = inflater.inflate(destNioBuf); + source.readerIndex(origIdx + inflater.getTotalIn()); destination.writerIndex(destination.writerIndex() + produced); } + + inflater.reset(); } @Override 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 @@ -125,11 +88,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; } } 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 24ff34472..fdc0bbdad 100644 --- a/native/src/main/java/com/velocitypowered/natives/util/Natives.java +++ b/native/src/main/java/com/velocitypowered/natives/util/Natives.java @@ -2,7 +2,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; @@ -71,9 +70,7 @@ public class Natives { "libdeflate (Linux aarch64)", LibdeflateVelocityCompressor.FACTORY), new NativeCodeLoader.Variant<>(NativeConstraints.JAVA_11, () -> { - }, "Java 11", () -> Java11VelocityCompressor.FACTORY), - new NativeCodeLoader.Variant<>(NativeCodeLoader.ALWAYS, () -> { - }, "Java", JavaVelocityCompressor.FACTORY) + }, "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 e8ff18f8a..9da119a48 100644 --- a/native/src/test/java/com/velocitypowered/natives/compression/VelocityCompressorTest.java +++ b/native/src/test/java/com/velocitypowered/natives/compression/VelocityCompressorTest.java @@ -65,7 +65,7 @@ class VelocityCompressorTest { @Test @EnabledOnJre(JRE.JAVA_11) void java11IntegrityCheckDirect() throws DataFormatException { - VelocityCompressor compressor = Java11VelocityCompressor.FACTORY + VelocityCompressor compressor = JavaVelocityCompressor.FACTORY .create(Deflater.DEFAULT_COMPRESSION); check(compressor, () -> Unpooled.directBuffer(TEST_DATA.length + 32)); } @@ -73,7 +73,7 @@ class VelocityCompressorTest { @Test @EnabledOnJre(JRE.JAVA_11) void java11IntegrityCheckHeap() throws DataFormatException { - VelocityCompressor compressor = Java11VelocityCompressor.FACTORY + VelocityCompressor compressor = JavaVelocityCompressor.FACTORY .create(Deflater.DEFAULT_COMPRESSION); check(compressor, () -> Unpooled.buffer(TEST_DATA.length + 32)); } @@ -82,7 +82,7 @@ class VelocityCompressorTest { throws DataFormatException { ByteBuf source = bufSupplier.get(); ByteBuf dest = bufSupplier.get(); - ByteBuf decompressed = bufSupplier.get(); + ByteBuf decompressed = Unpooled.directBuffer(16); source.writeBytes(TEST_DATA); int uncompressedData = source.readableBytes();