Compare commits

...

4 commits

Author SHA1 Message Date
Valaphee The Meerkat
6488683b4d
Merge cb596f1105 into 8150091888 2024-11-11 06:34:14 +01:00
Eclipse
8150091888
Fix equippable component writing (#868)
Some checks failed
Java CI with Gradle / build (push) Has been cancelled
Deploy / build (push) Has been cancelled
2024-11-07 19:03:03 +08:00
valaphee
cb596f1105 Simplify readVarInt/Long, Fix and simplify writeVarInt/Long, add some simple tests to ensure functionality, VarInt/Long wider than 5/10 bytes will now take precedence over index out of bounds 2024-09-16 16:02:09 +02:00
valaphee
c2795892fd Fix readVarLong, remove multiplication in readVarInt 2024-08-19 14:37:45 +02:00
3 changed files with 84 additions and 110 deletions

View file

@ -9,135 +9,58 @@ public class BasePacketCodecHelper implements PacketCodecHelper {
@Override
public void writeVarInt(ByteBuf buf, int value) {
this.writeVarLong(buf, value & 0xFFFFFFFFL);
while ((value & ~0x7F) != 0) {
buf.writeByte(value & 0x7F | 0x80);
value >>>= 7;
}
buf.writeByte(value);
}
@Override
public int readVarInt(ByteBuf buf) {
int value = 0;
int size = 0;
int b;
while (((b = buf.readByte()) & 0x80) == 0x80) {
value |= (b & 0x7F) << (size++ * 7);
if (size > 5) {
throw new IllegalArgumentException("VarInt too long (length must be <= 5)");
}
}
return value | ((b & 0x7F) << (size * 7));
byte b;
do {
if (size >= 35) {
throw new RuntimeException("VarInt wider than 5 bytes");
}
b = buf.readByte();
value |= (b & 0x7F) << size;
size += 7;
} while ((b & 0x80) == 0x80);
return value;
}
// Based off of Andrew Steinborn's blog post:
// https://steinborn.me/posts/performance/how-fast-can-you-write-a-varint/
@Override
public void writeVarLong(ByteBuf buf, long value) {
// Peel the one and two byte count cases explicitly as they are the most common VarInt sizes
// that the server will write, to improve inlining.
if ((value & ~0x7FL) == 0) {
buf.writeByte((byte) value);
} else if ((value & ~0x3FFFL) == 0) {
int w = (int) ((value & 0x7FL | 0x80L) << 8 |
(value >>> 7));
buf.writeShort(w);
} else {
writeVarLongFull(buf, value);
while ((value & ~0x7FL) != 0) {
buf.writeByte((int) (value & 0x7F | 0x80));
value >>>= 7;
}
}
private static void writeVarLongFull(ByteBuf buf, long value) {
if ((value & ~0x7FL) == 0) {
buf.writeByte((byte) value);
} else if ((value & ~0x3FFFL) == 0) {
int w = (int) ((value & 0x7FL | 0x80L) << 8 |
(value >>> 7));
buf.writeShort(w);
} else if ((value & ~0x1FFFFFL) == 0) {
int w = (int) ((value & 0x7FL | 0x80L) << 16 |
((value >>> 7) & 0x7FL | 0x80L) << 8 |
(value >>> 14));
buf.writeMedium(w);
} else if ((value & ~0xFFFFFFFL) == 0) {
int w = (int) ((value & 0x7F | 0x80) << 24 |
(((value >>> 7) & 0x7F | 0x80) << 16) |
((value >>> 14) & 0x7F | 0x80) << 8 |
(value >>> 21));
buf.writeInt(w);
} else if ((value & ~0x7FFFFFFFFL) == 0) {
int w = (int) ((value & 0x7F | 0x80) << 24 |
((value >>> 7) & 0x7F | 0x80) << 16 |
((value >>> 14) & 0x7F | 0x80) << 8 |
((value >>> 21) & 0x7F | 0x80));
buf.writeInt(w);
buf.writeByte((int) (value >>> 28));
} else if ((value & ~0x3FFFFFFFFFFL) == 0) {
int w = (int) ((value & 0x7F | 0x80) << 24 |
((value >>> 7) & 0x7F | 0x80) << 16 |
((value >>> 14) & 0x7F | 0x80) << 8 |
((value >>> 21) & 0x7F | 0x80));
int w2 = (int) (((value >>> 28) & 0x7FL | 0x80L) << 8 |
(value >>> 35));
buf.writeInt(w);
buf.writeShort(w2);
} else if ((value & ~0x1FFFFFFFFFFFFL) == 0) {
int w = (int) ((value & 0x7F | 0x80) << 24 |
((value >>> 7) & 0x7F | 0x80) << 16 |
((value >>> 14) & 0x7F | 0x80) << 8 |
((value >>> 21) & 0x7F | 0x80));
int w2 = (int) ((((value >>> 28) & 0x7FL | 0x80L) << 16 |
((value >>> 35) & 0x7FL | 0x80L) << 8) |
(value >>> 42));
buf.writeInt(w);
buf.writeMedium(w2);
} else if ((value & ~0xFFFFFFFFFFFFFFL) == 0) {
long w = (value & 0x7F | 0x80) << 56 |
((value >>> 7) & 0x7F | 0x80) << 48 |
((value >>> 14) & 0x7F | 0x80) << 40 |
((value >>> 21) & 0x7F | 0x80) << 32 |
((value >>> 28) & 0x7FL | 0x80L) << 24 |
((value >>> 35) & 0x7FL | 0x80L) << 16 |
((value >>> 42) & 0x7FL | 0x80L) << 8 |
(value >>> 49);
buf.writeLong(w);
} else if ((value & ~0x7FFFFFFFFFFFFFFFL) == 0) {
long w = (value & 0x7F | 0x80) << 56 |
((value >>> 7) & 0x7F | 0x80) << 48 |
((value >>> 14) & 0x7F | 0x80) << 40 |
((value >>> 21) & 0x7F | 0x80) << 32 |
((value >>> 28) & 0x7FL | 0x80L) << 24 |
((value >>> 35) & 0x7FL | 0x80L) << 16 |
((value >>> 42) & 0x7FL | 0x80L) << 8 |
(value >>> 49);
buf.writeLong(w);
buf.writeByte((byte) (value >>> 56));
} else {
long w = (value & 0x7F | 0x80) << 56 |
((value >>> 7) & 0x7F | 0x80) << 48 |
((value >>> 14) & 0x7F | 0x80) << 40 |
((value >>> 21) & 0x7F | 0x80) << 32 |
((value >>> 28) & 0x7FL | 0x80L) << 24 |
((value >>> 35) & 0x7FL | 0x80L) << 16 |
((value >>> 42) & 0x7FL | 0x80L) << 8 |
(value >>> 49);
int w2 = (int) (((value >>> 56) & 0x7FL | 0x80L) << 8 |
(value >>> 63));
buf.writeLong(w);
buf.writeShort(w2);
}
buf.writeByte((int) value);
}
@Override
public long readVarLong(ByteBuf buf) {
int value = 0;
long value = 0;
int size = 0;
int b;
while (((b = buf.readByte()) & 0x80) == 0x80) {
value |= (b & 0x7F) << (size++ * 7);
if (size > 10) {
throw new IllegalArgumentException("VarLong too long (length must be <= 10)");
}
}
return value | ((b & 0x7FL) << (size * 7));
byte b;
do {
if (size >= 70) {
throw new RuntimeException("VarLong wider than 10 bytes");
}
b = buf.readByte();
value |= (b & 0x7FL) << size;
size += 7;
} while ((b & 0x80) == 0x80);
return value;
}
@Override

View file

@ -158,6 +158,7 @@ public class ItemCodecHelper extends MinecraftCodecHelper {
}
this.writeNullable(buf, equippable.model(), this::writeResourceLocation);
this.writeNullable(buf, equippable.cameraOverlay(), this::writeResourceLocation);
this.writeNullable(buf, equippable.allowedEntities(), this::writeHolderSet);
buf.writeBoolean(equippable.dispensable());
buf.writeBoolean(equippable.swappable());

View file

@ -0,0 +1,50 @@
package org.geysermc.mcprotocollib.protocol.codec;
import io.netty.buffer.Unpooled;
import org.geysermc.mcprotocollib.network.codec.BasePacketCodecHelper;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
public class BasePacketCodecHelperTest {
private static final BasePacketCodecHelper codecHelper = new BasePacketCodecHelper();
@Test
public void readVarInt() {
assertEquals(0x80808080, codecHelper.readVarInt(Unpooled.wrappedBuffer(new byte[]{
(byte) 0x80, (byte) 0x81, (byte) 0x82, (byte) 0x84, (byte) 0x08
})));
}
@Test
public void readVarIntTooLong() {
try {
// VarInt too long error should take precedence over IndexOutOfBoundsException
codecHelper.readVarInt(Unpooled.wrappedBuffer(new byte[]{
(byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80
}));
} catch (RuntimeException ex) {
// (assertThrow doesn't work as IndexOutOfBoundsException is also a RuntimeException)
assertEquals("VarInt wider than 5 bytes", ex.getMessage());
}
}
@Test
public void readVarLong() {
assertEquals(0x8080808080808080L, codecHelper.readVarLong(Unpooled.wrappedBuffer(new byte[]{
(byte) 0x80, (byte) 0x81, (byte) 0x82, (byte) 0x84, (byte) 0x88, (byte) 0x90, (byte) 0xa0, (byte) 0xc0, (byte) 0x80, (byte) 0x01
})));
}
@Test
public void readVarLongTooLong() {
try {
// VarLong too long error should take precedence over IndexOutOfBoundsException
codecHelper.readVarLong(Unpooled.wrappedBuffer(new byte[]{
(byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80
}));
} catch (RuntimeException ex) {
assertEquals("VarLong wider than 10 bytes", ex.getMessage());
}
}
}