diff --git a/buffer/src/main/kotlin/org/openrs2/buffer/ByteBufExtensions.kt b/buffer/src/main/kotlin/org/openrs2/buffer/ByteBufExtensions.kt index 8f0a5a41..785fc01c 100644 --- a/buffer/src/main/kotlin/org/openrs2/buffer/ByteBufExtensions.kt +++ b/buffer/src/main/kotlin/org/openrs2/buffer/ByteBufExtensions.kt @@ -128,6 +128,40 @@ public fun ByteBuf.writeVersionedString(s: CharSequence): ByteBuf { return this } +public fun ByteBuf.readVarInt(): Int { + var value = 0 + + var byte: Int + do { + byte = readUnsignedByte().toInt() + value = (value shl 7) or (byte and 0x7F) + } while ((byte and 0x80) != 0) + + return value +} + +public fun ByteBuf.writeVarInt(v: Int): ByteBuf { + if ((v and 0x7F.inv()) != 0) { + if ((v and 0x3FFF.inv()) != 0) { + if ((v and 0x1FFFFF.inv()) != 0) { + if ((v and 0xFFFFFFF.inv()) != 0) { + writeByte(((v ushr 28) and 0x7F) or 0x80) + } + + writeByte(((v ushr 21) and 0x7F) or 0x80) + } + + writeByte(((v ushr 14) and 0x7F) or 0x80) + } + + writeByte(((v ushr 7) and 0x7F) or 0x80) + } + + writeByte(v and 0x7F) + + return this +} + public fun ByteBuf.crc32(): Int { return crc32(readerIndex(), readableBytes()) } diff --git a/buffer/src/test/kotlin/org/openrs2/buffer/ByteBufExtensionsTest.kt b/buffer/src/test/kotlin/org/openrs2/buffer/ByteBufExtensionsTest.kt index f745b5de..2e254b00 100644 --- a/buffer/src/test/kotlin/org/openrs2/buffer/ByteBufExtensionsTest.kt +++ b/buffer/src/test/kotlin/org/openrs2/buffer/ByteBufExtensionsTest.kt @@ -586,6 +586,108 @@ class ByteBufExtensionsTest { } } + @Test + fun testReadVarInt() { + wrappedBuffer(0x7F).use { buf -> + assertEquals(0x7F, buf.readVarInt()) + } + + wrappedBuffer(0x81.toByte(), 0x00).use { buf -> + assertEquals(0x80, buf.readVarInt()) + } + + wrappedBuffer(0xFF.toByte(), 0x7F).use { buf -> + assertEquals(0x3FFF, buf.readVarInt()) + } + + wrappedBuffer(0x81.toByte(), 0x80.toByte(), 0x00).use { buf -> + assertEquals(0x4000, buf.readVarInt()) + } + + wrappedBuffer(0xFF.toByte(), 0xFF.toByte(), 0x7F.toByte()).use { buf -> + assertEquals(0x1FFFFF, buf.readVarInt()) + } + + wrappedBuffer(0x81.toByte(), 0x80.toByte(), 0x80.toByte(), 0x00).use { buf -> + assertEquals(0x200000, buf.readVarInt()) + } + + wrappedBuffer(0xFF.toByte(), 0xFF.toByte(), 0xFF.toByte(), 0x7F).use { buf -> + assertEquals(0xFFFFFFF, buf.readVarInt()) + } + + wrappedBuffer(0x81.toByte(), 0x80.toByte(), 0x80.toByte(), 0x80.toByte(), 0x00).use { buf -> + assertEquals(0x10000000, buf.readVarInt()) + } + } + + @Test + fun testWriteVarInt() { + ByteBufAllocator.DEFAULT.buffer().use { actual -> + actual.writeVarInt(0x7F) + + wrappedBuffer(0x7F).use { expected -> + assertEquals(expected, actual) + } + } + + ByteBufAllocator.DEFAULT.buffer().use { actual -> + actual.writeVarInt(0x80) + + wrappedBuffer(0x81.toByte(), 0x00).use { expected -> + assertEquals(expected, actual) + } + } + + ByteBufAllocator.DEFAULT.buffer().use { actual -> + actual.writeVarInt(0x3FFF) + + wrappedBuffer(0xFF.toByte(), 0x7F).use { expected -> + assertEquals(expected, actual) + } + } + + ByteBufAllocator.DEFAULT.buffer().use { actual -> + actual.writeVarInt(0x4000) + + wrappedBuffer(0x81.toByte(), 0x80.toByte(), 0x00).use { expected -> + assertEquals(expected, actual) + } + } + + ByteBufAllocator.DEFAULT.buffer().use { actual -> + actual.writeVarInt(0x1FFFFF) + + wrappedBuffer(0xFF.toByte(), 0xFF.toByte(), 0x7F).use { expected -> + assertEquals(expected, actual) + } + } + + ByteBufAllocator.DEFAULT.buffer().use { actual -> + actual.writeVarInt(0x200000) + + wrappedBuffer(0x81.toByte(), 0x80.toByte(), 0x80.toByte(), 0x00).use { expected -> + assertEquals(expected, actual) + } + } + + ByteBufAllocator.DEFAULT.buffer().use { actual -> + actual.writeVarInt(0xFFFFFFF) + + wrappedBuffer(0xFF.toByte(), 0xFF.toByte(), 0xFF.toByte(), 0x7F).use { expected -> + assertEquals(expected, actual) + } + } + + ByteBufAllocator.DEFAULT.buffer().use { actual -> + actual.writeVarInt(0x10000000) + + wrappedBuffer(0x81.toByte(), 0x80.toByte(), 0x80.toByte(), 0x80.toByte(), 0x00).use { expected -> + assertEquals(expected, actual) + } + } + } + @Test fun testCrc32() { val s = "AAThe quick brown fox jumps over the lazy dogA"