From b6f1dcc166d88388be59479ea55c6d74294c2c05 Mon Sep 17 00:00:00 2001 From: Andrew Steinborn Date: Fri, 29 May 2020 20:52:04 -0400 Subject: [PATCH] Optimize varint frame decoding again Use ByteProcessor in a controlled matter in one specific case. Performance measurements with my Ryzen 5 3600 indicate a 25-35% improvement in time spent framing incoming packets. --- .../netty/MinecraftVarintFrameDecoder.java | 74 ++++++++++++------- 1 file changed, 48 insertions(+), 26 deletions(-) diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/netty/MinecraftVarintFrameDecoder.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/netty/MinecraftVarintFrameDecoder.java index 3adc8ac35..52779d797 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/netty/MinecraftVarintFrameDecoder.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/netty/MinecraftVarintFrameDecoder.java @@ -1,41 +1,63 @@ package com.velocitypowered.proxy.protocol.netty; -import com.velocitypowered.proxy.protocol.ProtocolUtils; +import com.velocitypowered.proxy.util.except.QuietException; import io.netty.buffer.ByteBuf; import io.netty.channel.ChannelHandlerContext; import io.netty.handler.codec.ByteToMessageDecoder; -import io.netty.handler.codec.CorruptedFrameException; +import io.netty.util.ByteProcessor; import java.util.List; public class MinecraftVarintFrameDecoder extends ByteToMessageDecoder { + private static final QuietException BAD_LENGTH_CACHED = new QuietException("Bad packet length"); + private final VarintByteDecoder reader = new VarintByteDecoder(); + @Override - protected void decode(ChannelHandlerContext ctx, ByteBuf in, List out) throws Exception { - read_lens: while (in.isReadable()) { - int origReaderIndex = in.readerIndex(); - for (int i = 0; i < 3; i++) { - if (!in.isReadable()) { - in.readerIndex(origReaderIndex); - return; - } - - byte read = in.readByte(); - if (read >= 0) { - // Make sure reader index of length buffer is returned to the beginning - in.readerIndex(origReaderIndex); - int packetLength = ProtocolUtils.readVarInt(in); - - if (in.readableBytes() >= packetLength) { - out.add(in.readBytes(packetLength)); - continue read_lens; - } else { - in.readerIndex(origReaderIndex); - return; - } - } + protected void decode(ChannelHandlerContext ctx, ByteBuf in, List out) { + while (in.isReadable()) { + int varintEnd = in.forEachByte(reader); + if (varintEnd == -1) { + return; } - throw new CorruptedFrameException("VarInt too big"); + if (!reader.successfulDecode) { + throw BAD_LENGTH_CACHED; + } + + int minimumRead = reader.bytesRead + reader.readVarint; + if (in.isReadable(minimumRead)) { + out.add(in.retainedSlice(varintEnd + 1, reader.readVarint)); + in.skipBytes(minimumRead); + reader.reset(); + } else { + reader.reset(); + return; + } + } + } + + private static class VarintByteDecoder implements ByteProcessor { + private int readVarint; + private int bytesRead; + private boolean successfulDecode; + + @Override + public boolean process(byte k) { + readVarint |= (k & 0x7F) << bytesRead++ * 7; + if (bytesRead > 3) { + return false; + } + if ((k & 0x80) != 128) { + successfulDecode = true; + return false; + } + return true; + } + + void reset() { + readVarint = 0; + bytesRead = 0; + successfulDecode = false; } } }