Fix varint writing (for real!)

Thanks to @Leymooo for reporting and providing test cases. (I also added one of my own tests which proved the most useful to debugging the issue.)
This commit is contained in:
Andrew Steinborn
2021-05-04 16:30:32 -04:00
parent 2713831f77
commit 3dc8e25ec7
2 changed files with 108 additions and 9 deletions

View File

@@ -62,16 +62,13 @@ public enum ProtocolUtils {
private static final int DEFAULT_MAX_STRING_SIZE = 65536; // 64KiB
private static final QuietDecoderException BAD_VARINT_CACHED =
new QuietDecoderException("Bad VarInt decoded");
private static final int[] VARINT_BYTE_LENGTHS = new int[33];
private static final int[] VARINT_EXACT_BYTE_LENGTHS = new int[33];
static {
// Inspired by https://richardstartin.github.io/posts/dont-use-protobuf-for-telemetry
// This has been slightly modified in that we reduce the length to 32-bit only, since Velocity
// doesn't look at any part of the Minecraft protocol that requires us to look at VarLongs.
for (int i = 0; i <= 32; ++i) {
VARINT_BYTE_LENGTHS[i] = (int) Math.ceil((31d - (i - 1)) / 7d);
VARINT_EXACT_BYTE_LENGTHS[i] = (int) Math.ceil((31d - (i - 1)) / 7d);
}
VARINT_BYTE_LENGTHS[32] = 1; // Special case for the number 0.
VARINT_EXACT_BYTE_LENGTHS[32] = 1; // Special case for the number 0.
}
/**
@@ -108,8 +105,13 @@ public enum ProtocolUtils {
return Integer.MIN_VALUE;
}
/**
* Returns the exact byte size of {@code value} if it were encoded as a VarInt.
* @param value the value to encode
* @return the byte size of {@code value} if encoded as a VarInt
*/
public static int varIntBytes(int value) {
return VARINT_BYTE_LENGTHS[Integer.numberOfLeadingZeros(value)];
return VARINT_EXACT_BYTE_LENGTHS[Integer.numberOfLeadingZeros(value)];
}
/**
@@ -118,8 +120,11 @@ public enum ProtocolUtils {
* @param value the integer to write
*/
public static void writeVarInt(ByteBuf buf, int value) {
int length = varIntBytes(value);
for (int i = 0; i < length; ++i) {
// Inspired by https://richardstartin.github.io/posts/dont-use-protobuf-for-telemetry
// This has been slightly modified in that we reduce the length to 32-bit only, since Velocity
// doesn't look at any part of the Minecraft protocol that requires us to look at VarLongs.
int continuationBytes = (31 - Integer.numberOfLeadingZeros(value)) / 7;
for (int i = 0; i < continuationBytes; ++i) {
buf.writeByte(((byte) ((value & 0x7F) | 0x80)));
value >>>= 7;
}