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

Velocity's varint reading/writing optimizations #1070

Open
wants to merge 6 commits into
base: 2.0
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 5 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
Expand Up @@ -23,6 +23,7 @@
import java.nio.charset.Charset;

public class ByteBufHelper {

public static int capacity(Object buffer) {
return PacketEvents.getAPI().getNettyManager().getByteBufOperator().capacity(buffer);
}
Expand Down Expand Up @@ -239,29 +240,82 @@ public static Object resetWriterIndex(Object buffer) {
return PacketEvents.getAPI().getNettyManager().getByteBufOperator().resetWriterIndex(buffer);
}

public static int readVarInt(Object buffer) {
int value = 0;
int length = 0;
byte currentByte;
do {
currentByte = readByte(buffer);
value |= (currentByte & 0x7F) << (length * 7);
length++;
if (length > 5) {
throw new RuntimeException("VarInt is too large. Must be smaller than 5 bytes.");
}
} while ((currentByte & 0x80) == 0x80);
return value;
}

public static void writeVarInt(Object buffer, int value) {
while (true) {
if ((value & ~0x7F) == 0) {
writeByte(buffer, value);
return;
}
writeByte(buffer, (value & 0x7F) | 0x80);
value >>>= 7;
public static int getIntLE(Object buffer, int readerIndex) {
return PacketEvents.getAPI().getNettyManager().getByteBufOperator().getIntLE(buffer, readerIndex);
}

//Src: https://github.com/jonesdevelopment/sonar/blob/main/common/src/main/java/xyz/jonesdev/sonar/common/fallback/netty/FallbackVarInt21FrameDecoder.java#L69
booky10 marked this conversation as resolved.
Show resolved Hide resolved
public static int readVarInt(Object buf) {
if (readableBytes(buf) < 4) return readVarIntSmallBuffer(buf);

// take the last three bytes and check if any of them have the high bit set
int wholeOrMore = getIntLE(buf, readerIndex(buf));
int atStop = ~wholeOrMore & 0x808080;
if (atStop == 0) throw new RuntimeException("fucked up - VarInt too big");

final int bitsToKeep = Integer.numberOfTrailingZeros(atStop) + 1;
skipBytes(buf, bitsToKeep >> 3);

// https://github.com/netty/netty/pull/14050#issuecomment-2107750734
int preservedBytes = wholeOrMore & (atStop ^ (atStop - 1));

// https://github.com/netty/netty/pull/14050#discussion_r1597896639
preservedBytes = (preservedBytes & 0x007F007F) | ((preservedBytes & 0x00007F00) >> 1);
preservedBytes = (preservedBytes & 0x00003FFF) | ((preservedBytes & 0x3FFF0000) >> 2);
return preservedBytes;
}

//fallback to unrolled loop
//I'll try to optimize this in the future
private static int readVarIntSmallBuffer(Object buf) {
if (!isReadable(buf)) return 0;

int rIdx = readerIndex(buf);

byte tmp = readByte(buf);
if (tmp >= 0) return tmp;

int result = tmp & 0x7F;
if (!isReadable(buf)) {
readerIndex(buf, rIdx);
return 0;
}

if ((tmp = readByte(buf)) >= 0) return result | tmp << 7;

result |= (tmp & 0x7F) << 7;
if (!isReadable(buf)) {
readerIndex(buf, rIdx);
return 0;
}
if ((tmp = readByte(buf)) >= 0) return result | tmp << 14;

return result | (tmp & 0x7F) << 14;
}

//Src: https://steinborn.me/posts/performance/how-fast-can-you-write-a-varint/
public static void writeVarInt(Object buf, int i) {
if ((i & (0xFFFFFFFF << 7)) == 0) {
// 1 byte case
writeByte(buf, i);
} else if ((i & (0xFFFFFFFF << 14)) == 0) {
// 2 byte case
int w = (i & 0x7F | 0x80) << 8 | (i >>> 7);
writeShort(buf, w);
} else if ((i & (0xFFFFFFFF << 21)) == 0) {
// 3 byte case
int w = ((i & 0x7F | 0x80) << 16) | ((i >>> 7 & 0x7F | 0x80) << 8) | (i >>> 14);
writeMedium(buf, w);
} else if ((i & (0xFFFFFFFF << 28)) == 0) {
// 4 byte case
int w = ((i & 0x7F | 0x80) << 24) | ((i >>> 7 & 0x7F | 0x80) << 16) | ((i >>> 14 & 0x7F | 0x80) << 8) | (i >>> 21);
writeInt(buf, w);
} else {
// 5 byte case (the max size for a VarInt)
// Write the first 4 bytes as an int
int w = ((i & 0x7F | 0x80) << 24) | ((i >>> 7 & 0x7F | 0x80) << 16) | ((i >>> 14 & 0x7F | 0x80) << 8) | (i >>> 21 & 0x7F | 0x80);
writeInt(buf, w); // Write the first 4 bytes
writeByte(buf, i >>> 28); // Write the remaining 5th byte
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ public interface ByteBufOperator {
Object getBytes(Object buffer, int index, byte[] destination);
short getUnsignedByte(Object buffer, int index);

int getIntLE(Object buffer, int readerIndex);

boolean isReadable(Object buffer);
Object copy(Object buffer);
Object duplicate(Object buffer);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -387,18 +387,7 @@ public void writeMedium(int value) {
}

public int readVarInt() {
int value = 0;
int length = 0;
byte currentByte;
do {
currentByte = readByte();
value |= (currentByte & 0x7F) << (length * 7);
length++;
if (length > 5) {
throw new RuntimeException("VarInt is too large. Must be smaller than 5 bytes.");
}
} while ((currentByte & 0x80) == 0x80);
return value;
return ByteBufHelper.readVarInt(this.buffer);//delegate to ByteBufHelper
}

public void writeVarInt(int value) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,11 @@ public long readLong(Object buffer) {
return ((ByteBuf)buffer).readLong();
}

@Override
public int getIntLE(Object buffer, int readerIndex) {
return ((ByteBuf) buffer).getIntLE(readerIndex);
}

@Override
public void writeByte(Object buffer, int value) {
((ByteBuf)buffer).writeByte(value);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,11 @@ public long readLong(Object buffer) {
return ((ByteBuf)buffer).readLong();
}

@Override
public int getIntLE(Object buffer, int readerIndex) {
return ((ByteBuf) buffer).getIntLE(readerIndex);
}


@Override
public void writeByte(Object buffer, int value) {
Expand Down
Loading