Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Highly optimize Varint21FrameDecoder #3792

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
package net.md_5.bungee.protocol;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
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 Varint21FrameDecoder extends ByteToMessageDecoder
{

private static final ByteProcessor CONTINUE_BIT_PROCESSOR = value -> value < 0;
private static boolean DIRECT_WARNING;

@Override
Expand All @@ -18,60 +18,66 @@ protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) t
// If we decode an invalid packet and an exception is thrown (thus triggering a close of the connection),
// the Netty ByteToMessageDecoder will continue to frame more packets and potentially call fireChannelRead()
// on them, likely with more invalid packets. Therefore, check if the connection is no longer active and if so
// sliently discard the packet.
// silently discard the packet.
if ( !ctx.channel().isActive() )
{
in.skipBytes( in.readableBytes() );
return;
}

in.markReaderIndex();

final byte[] buf = new byte[ 3 ];
for ( int i = 0; i < buf.length; i++ )
int maxVarIntBytes = Math.min( in.readableBytes(), 3 );
int endIdx = in.forEachByte( in.readerIndex(), maxVarIntBytes, CONTINUE_BIT_PROCESSOR );
if ( endIdx == -1 )
{
if ( !in.isReadable() )
if ( in.readableBytes() < 3 ) // Not enough bytes for the packet length yet
{
in.resetReaderIndex();
return;
}
throw new CorruptedFrameException( "length wider than 21-bit" );
}

buf[i] = in.readByte();
if ( buf[i] >= 0 )
int varIntOffset = ( endIdx - in.readerIndex() );
int length = getVarInt( in, varIntOffset );
if ( length == 0 )
{
throw new CorruptedFrameException( "Empty Packet!" );
}

int varIntLen = varIntOffset + 1;
if ( in.readableBytes() - varIntLen >= length )
{
in.skipBytes( varIntLen );
if ( in.hasMemoryAddress() )
{
int length = DefinedPacket.readVarInt( Unpooled.wrappedBuffer( buf ) );
if ( length == 0 )
out.add( in.readRetainedSlice( length ) );
} else
{
if ( !DIRECT_WARNING )
{
throw new CorruptedFrameException( "Empty Packet!" );
DIRECT_WARNING = true;
System.out.println( "Netty is not using direct IO buffers." );
}

if ( in.readableBytes() < length )
{
in.resetReaderIndex();
return;
} else
{
if ( in.hasMemoryAddress() )
{
out.add( in.readRetainedSlice( length ) );
} else
{
if ( !DIRECT_WARNING )
{
DIRECT_WARNING = true;
System.out.println( "Netty is not using direct IO buffers." );
}

// See https://github.com/SpigotMC/BungeeCord/issues/1717
ByteBuf dst = ctx.alloc().directBuffer( length );
in.readBytes( dst );
out.add( dst );
}
return;
}
// See https://github.com/SpigotMC/BungeeCord/issues/1717
ByteBuf dst = ctx.alloc().directBuffer( length );
in.readBytes( dst );
out.add( dst );
}
}
}

throw new CorruptedFrameException( "length wider than 21-bit" );
private static int getVarInt(ByteBuf byteBuf, int varIntOffset)
{
int readerIndex = byteBuf.readerIndex();
if ( varIntOffset == 0 )
{
return byteBuf.getByte( readerIndex );
} else if ( varIntOffset == 1 )
{
return byteBuf.getByte( readerIndex ) & 0x7F | byteBuf.getByte( readerIndex + 1 ) << 7;
} else
{
return byteBuf.getByte( readerIndex ) & 0x7F | ( byteBuf.getByte( readerIndex + 1 ) & 0x7F ) << 7 | byteBuf.getByte( readerIndex + 2 ) << 14;
}
}
}