From 3db2fe8d63162c716b8ac57ae0898027ecd3a7bb Mon Sep 17 00:00:00 2001 From: Andrew Steinborn Date: Mon, 3 May 2021 17:31:32 -0400 Subject: [PATCH] Optimize varint writing Inspired by the approach described at the bottom of https://richardstartin.github.io/posts/dont-use-protobuf-for-telemetry Given that we do a lot of varint writing as well, this should provide a small performance boost for larger/complex packets whilst not regressing hard on smaller packets. --- .../proxy/protocol/ProtocolUtils.java | 26 ++++++++++++++----- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/ProtocolUtils.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/ProtocolUtils.java index 50f6c48d0..f7424b05b 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/ProtocolUtils.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/ProtocolUtils.java @@ -62,6 +62,17 @@ 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[] VAR_INT_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) { + VAR_INT_LENGTHS[i] = (31 - i) / 7; + } + } /** * Reads a Minecraft-style VarInt from the specified {@code buf}. @@ -97,21 +108,22 @@ public enum ProtocolUtils { return Integer.MIN_VALUE; } + public static int varintBytes(int value) { + return VAR_INT_LENGTHS[Integer.numberOfLeadingZeros(value)]; + } + /** * Writes a Minecraft-style VarInt to the specified {@code buf}. * @param buf the buffer to read from * @param value the integer to write */ public static void writeVarInt(ByteBuf buf, int value) { - while (true) { - if ((value & 0xFFFFFF80) == 0) { - buf.writeByte(value); - return; - } - - buf.writeByte(value & 0x7F | 0x80); + int length = varintBytes(value); + for (int i = 0; i < length; ++i) { + buf.writeByte(((byte) ((value & 0x7F) | 0x80))); value >>>= 7; } + buf.writeByte((byte) value); } public static String readString(ByteBuf buf) {