3
0
Mirror von https://github.com/PaperMC/Velocity.git synchronisiert 2024-11-17 05:20:14 +01:00

Replace old Java compressor with Java 11 compressor

Dieser Commit ist enthalten in:
Andrew Steinborn 2020-11-05 16:50:57 -05:00
Ursprung bcff8a04bc
Commit 13a63eff76
4 geänderte Dateien mit 34 neuen und 214 gelöschten Zeilen

Datei anzeigen

@ -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;
}
}

Datei anzeigen

@ -1,11 +1,13 @@
package com.velocitypowered.natives.compression; 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.ZLIB_BUFFER_SIZE;
import static com.velocitypowered.natives.compression.CompressorUtils.ensureMaxSize; import static com.velocitypowered.natives.compression.CompressorUtils.ensureMaxSize;
import com.google.common.base.Preconditions;
import com.velocitypowered.natives.util.BufferPreference; import com.velocitypowered.natives.util.BufferPreference;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import java.nio.ByteBuffer;
import java.util.zip.DataFormatException; import java.util.zip.DataFormatException;
import java.util.zip.Deflater; import java.util.zip.Deflater;
import java.util.zip.Inflater; import java.util.zip.Inflater;
@ -16,7 +18,6 @@ public class JavaVelocityCompressor implements VelocityCompressor {
private final Deflater deflater; private final Deflater deflater;
private final Inflater inflater; private final Inflater inflater;
private byte[] buf = new byte[0];
private boolean disposed = false; private boolean disposed = false;
private JavaVelocityCompressor(int level) { private JavaVelocityCompressor(int level) {
@ -29,92 +30,54 @@ public class JavaVelocityCompressor implements VelocityCompressor {
throws DataFormatException { throws DataFormatException {
ensureNotDisposed(); ensureNotDisposed();
final int available = source.readableBytes(); // We (probably) can't nicely deal with >=1 buffer nicely, so let's scream loudly.
this.setInflaterInput(source); checkArgument(source.nioBufferCount() == 1, "source has multiple backing buffers");
checkArgument(destination.nioBufferCount() == 1, "destination has multiple backing buffers");
if (destination.hasArray()) { int origIdx = source.readerIndex();
this.inflateDestinationIsHeap(destination, available, uncompressedSize); inflater.setInput(source.nioBuffer());
} 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();
}
private void setInflaterInput(ByteBuf source) { while (!inflater.finished() && inflater.getBytesRead() < source.readableBytes()) {
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) {
if (!destination.isWritable()) { if (!destination.isWritable()) {
ensureMaxSize(destination, max); ensureMaxSize(destination, uncompressedSize);
destination.ensureWritable(ZLIB_BUFFER_SIZE); destination.ensureWritable(ZLIB_BUFFER_SIZE);
} }
ensureMaxSize(destination, max); ByteBuffer destNioBuf = destination.nioBuffer(destination.writerIndex(),
int produced = inflater.inflate(destination.array(), destination.arrayOffset() destination.writableBytes());
+ destination.writerIndex(), destination.writableBytes()); int produced = inflater.inflate(destNioBuf);
source.readerIndex(origIdx + inflater.getTotalIn());
destination.writerIndex(destination.writerIndex() + produced); destination.writerIndex(destination.writerIndex() + produced);
} }
inflater.reset();
} }
@Override @Override
public void deflate(ByteBuf source, ByteBuf destination) throws DataFormatException { public void deflate(ByteBuf source, ByteBuf destination) throws DataFormatException {
ensureNotDisposed(); 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(); 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()) { while (!deflater.finished()) {
if (!destination.isWritable()) { if (!destination.isWritable()) {
destination.ensureWritable(ZLIB_BUFFER_SIZE); destination.ensureWritable(ZLIB_BUFFER_SIZE);
} }
int produced = deflater.deflate(destination.array(), destination.arrayOffset() ByteBuffer destNioBuf = destination.nioBuffer(destination.writerIndex(),
+ destination.writerIndex(), destination.writableBytes()); destination.writableBytes());
int produced = deflater.deflate(destNioBuf);
destination.writerIndex(destination.writerIndex() + produced); destination.writerIndex(destination.writerIndex() + produced);
} }
source.readerIndex(origIdx + deflater.getTotalIn());
deflater.reset();
} }
@Override @Override
@ -125,11 +88,11 @@ public class JavaVelocityCompressor implements VelocityCompressor {
} }
private void ensureNotDisposed() { private void ensureNotDisposed() {
Preconditions.checkState(!disposed, "Object already disposed"); checkState(!disposed, "Object already disposed");
} }
@Override @Override
public BufferPreference preferredBufferType() { public BufferPreference preferredBufferType() {
return BufferPreference.HEAP_PREFERRED; return BufferPreference.DIRECT_PREFERRED;
} }
} }

Datei anzeigen

@ -2,7 +2,6 @@ package com.velocitypowered.natives.util;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
import com.velocitypowered.natives.NativeSetupException; import com.velocitypowered.natives.NativeSetupException;
import com.velocitypowered.natives.compression.Java11VelocityCompressor;
import com.velocitypowered.natives.compression.JavaVelocityCompressor; import com.velocitypowered.natives.compression.JavaVelocityCompressor;
import com.velocitypowered.natives.compression.LibdeflateVelocityCompressor; import com.velocitypowered.natives.compression.LibdeflateVelocityCompressor;
import com.velocitypowered.natives.compression.VelocityCompressorFactory; import com.velocitypowered.natives.compression.VelocityCompressorFactory;
@ -71,9 +70,7 @@ public class Natives {
"libdeflate (Linux aarch64)", "libdeflate (Linux aarch64)",
LibdeflateVelocityCompressor.FACTORY), LibdeflateVelocityCompressor.FACTORY),
new NativeCodeLoader.Variant<>(NativeConstraints.JAVA_11, () -> { new NativeCodeLoader.Variant<>(NativeConstraints.JAVA_11, () -> {
}, "Java 11", () -> Java11VelocityCompressor.FACTORY), }, "Java", () -> JavaVelocityCompressor.FACTORY)
new NativeCodeLoader.Variant<>(NativeCodeLoader.ALWAYS, () -> {
}, "Java", JavaVelocityCompressor.FACTORY)
) )
); );

Datei anzeigen

@ -65,7 +65,7 @@ class VelocityCompressorTest {
@Test @Test
@EnabledOnJre(JRE.JAVA_11) @EnabledOnJre(JRE.JAVA_11)
void java11IntegrityCheckDirect() throws DataFormatException { void java11IntegrityCheckDirect() throws DataFormatException {
VelocityCompressor compressor = Java11VelocityCompressor.FACTORY VelocityCompressor compressor = JavaVelocityCompressor.FACTORY
.create(Deflater.DEFAULT_COMPRESSION); .create(Deflater.DEFAULT_COMPRESSION);
check(compressor, () -> Unpooled.directBuffer(TEST_DATA.length + 32)); check(compressor, () -> Unpooled.directBuffer(TEST_DATA.length + 32));
} }
@ -73,7 +73,7 @@ class VelocityCompressorTest {
@Test @Test
@EnabledOnJre(JRE.JAVA_11) @EnabledOnJre(JRE.JAVA_11)
void java11IntegrityCheckHeap() throws DataFormatException { void java11IntegrityCheckHeap() throws DataFormatException {
VelocityCompressor compressor = Java11VelocityCompressor.FACTORY VelocityCompressor compressor = JavaVelocityCompressor.FACTORY
.create(Deflater.DEFAULT_COMPRESSION); .create(Deflater.DEFAULT_COMPRESSION);
check(compressor, () -> Unpooled.buffer(TEST_DATA.length + 32)); check(compressor, () -> Unpooled.buffer(TEST_DATA.length + 32));
} }
@ -82,7 +82,7 @@ class VelocityCompressorTest {
throws DataFormatException { throws DataFormatException {
ByteBuf source = bufSupplier.get(); ByteBuf source = bufSupplier.get();
ByteBuf dest = bufSupplier.get(); ByteBuf dest = bufSupplier.get();
ByteBuf decompressed = bufSupplier.get(); ByteBuf decompressed = Unpooled.directBuffer(16);
source.writeBytes(TEST_DATA); source.writeBytes(TEST_DATA);
int uncompressedData = source.readableBytes(); int uncompressedData = source.readableBytes();