diff --git a/buffer/src/main/java/dev/openrs2/buffer/ByteBufExtensions.kt b/buffer/src/main/java/dev/openrs2/buffer/ByteBufExtensions.kt new file mode 100644 index 00000000..c8dedf95 --- /dev/null +++ b/buffer/src/main/java/dev/openrs2/buffer/ByteBufExtensions.kt @@ -0,0 +1,71 @@ +package dev.openrs2.buffer + +import io.netty.buffer.ByteBuf + +public fun ByteBuf.readShortSmart(): Int { + val peek = getUnsignedByte(readerIndex()).toInt() + return if ((peek and 0x80) == 0) { + readUnsignedByte().toInt() - 0x40 + } else { + (readUnsignedShort() and 0x7FFF) - 0x4000 + } +} + +public fun ByteBuf.writeShortSmart(v: Int) { + when (v) { + in -0x40..0x3F -> writeByte(v + 0x40) + in -0x4000..0x3FFF -> writeShort(0x8000 or (v + 0x4000)) + else -> throw IllegalArgumentException() + } +} + +public fun ByteBuf.readUnsignedShortSmart(): Int { + val peek = getUnsignedByte(readerIndex()).toInt() + return if ((peek and 0x80) == 0) { + readUnsignedByte().toInt() + } else { + readUnsignedShort() and 0x7FFF + } +} + +public fun ByteBuf.writeUnsignedShortSmart(v: Int) { + when (v) { + in 0..0x7F -> writeByte(v) + in 0..0x7FFF -> writeShort(0x8000 or v) + else -> throw IllegalArgumentException() + } +} + +public fun ByteBuf.readIntSmart(): Int { + val peek = getUnsignedByte(readerIndex()).toInt() + return if ((peek and 0x80) == 0) { + readUnsignedShort() - 0x4000 + } else { + (readInt() and 0x7FFFFFFF) - 0x40000000 + } +} + +public fun ByteBuf.writeIntSmart(v: Int) { + when (v) { + in -0x4000..0x3FFF -> writeShort(v + 0x4000) + in -0x40000000..0x3FFFFFFF -> writeInt(0x80000000.toInt() or (v + 0x40000000)) + else -> throw IllegalArgumentException() + } +} + +public fun ByteBuf.readUnsignedIntSmart(): Int { + val peek = getUnsignedByte(readerIndex()).toInt() + return if ((peek and 0x80) == 0) { + readUnsignedShort() + } else { + readInt() and 0x7FFFFFFF + } +} + +public fun ByteBuf.writeUnsignedIntSmart(v: Int) { + when (v) { + in 0..0x7FFF -> writeShort(v) + in 0..0x7FFFFFFF -> writeInt(0x80000000.toInt() or v) + else -> throw IllegalArgumentException() + } +} diff --git a/buffer/src/test/java/dev/openrs2/buffer/ByteBufExtensionsTest.kt b/buffer/src/test/java/dev/openrs2/buffer/ByteBufExtensionsTest.kt new file mode 100644 index 00000000..97e65add --- /dev/null +++ b/buffer/src/test/java/dev/openrs2/buffer/ByteBufExtensionsTest.kt @@ -0,0 +1,393 @@ +package dev.openrs2.buffer + +import io.netty.buffer.Unpooled +import org.junit.jupiter.api.assertThrows +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertFalse + +object ByteBufExtensionsTest { + @Test + fun testReadShortSmart() { + Unpooled.wrappedBuffer(byteArrayOf(0x00)).use { buf -> + assertEquals(-0x40, buf.readShortSmart()) + assertFalse(buf.isReadable) + } + + Unpooled.wrappedBuffer(byteArrayOf(0x40)).use { buf -> + assertEquals(0, buf.readShortSmart()) + assertFalse(buf.isReadable) + } + + Unpooled.wrappedBuffer(byteArrayOf(0x7F)).use { buf -> + assertEquals(0x3F, buf.readShortSmart()) + assertFalse(buf.isReadable) + } + + Unpooled.wrappedBuffer(byteArrayOf(0x80.toByte(), 0x00)).use { buf -> + assertEquals(-0x4000, buf.readShortSmart()) + assertFalse(buf.isReadable) + } + + Unpooled.wrappedBuffer(byteArrayOf(0xBF.toByte(), 0xBF.toByte())).use { buf -> + assertEquals(-0x41, buf.readShortSmart()) + assertFalse(buf.isReadable) + } + + Unpooled.wrappedBuffer(byteArrayOf(0xC0.toByte(), 0x40.toByte())).use { buf -> + assertEquals(0x40, buf.readShortSmart()) + assertFalse(buf.isReadable) + } + + Unpooled.wrappedBuffer(byteArrayOf(0xFF.toByte(), 0xFF.toByte())).use { buf -> + assertEquals(0x3FFF, buf.readShortSmart()) + assertFalse(buf.isReadable) + } + } + + @Test + fun testWriteShortSmart() { + Unpooled.wrappedBuffer(byteArrayOf(0x00)).use { expected -> + Unpooled.buffer().use { actual -> + actual.writeShortSmart(-0x40) + assertEquals(expected, actual) + } + } + + Unpooled.wrappedBuffer(byteArrayOf(0x40)).use { expected -> + Unpooled.buffer().use { actual -> + actual.writeShortSmart(0) + assertEquals(expected, actual) + } + } + + Unpooled.wrappedBuffer(byteArrayOf(0x7F)).use { expected -> + Unpooled.buffer().use { actual -> + actual.writeShortSmart(0x3F) + assertEquals(expected, actual) + } + } + + Unpooled.wrappedBuffer(byteArrayOf(0x80.toByte(), 0x00)).use { expected -> + Unpooled.buffer().use { actual -> + actual.writeShortSmart(-0x4000) + assertEquals(expected, actual) + } + } + + Unpooled.wrappedBuffer(byteArrayOf(0xBF.toByte(), 0xBF.toByte())).use { expected -> + Unpooled.buffer().use { actual -> + actual.writeShortSmart(-0x41) + assertEquals(expected, actual) + } + } + + Unpooled.wrappedBuffer(byteArrayOf(0xC0.toByte(), 0x40.toByte())).use { expected -> + Unpooled.buffer().use { actual -> + actual.writeShortSmart(0x40) + assertEquals(expected, actual) + } + } + + Unpooled.wrappedBuffer(byteArrayOf(0xFF.toByte(), 0xFF.toByte())).use { expected -> + Unpooled.buffer().use { actual -> + actual.writeShortSmart(0x3FFF) + assertEquals(expected, actual) + } + } + + Unpooled.buffer().use { buf -> + assertThrows { + buf.writeShortSmart(-0x4001) + } + } + + Unpooled.buffer().use { buf -> + assertThrows { + buf.writeShortSmart(0x4000) + } + } + } + + @Test + fun testReadUnsignedShortSmart() { + Unpooled.wrappedBuffer(byteArrayOf(0x00)).use { buf -> + assertEquals(0, buf.readUnsignedShortSmart()) + assertFalse(buf.isReadable) + } + + Unpooled.wrappedBuffer(byteArrayOf(0x40)).use { buf -> + assertEquals(0x40, buf.readUnsignedShortSmart()) + assertFalse(buf.isReadable) + } + + Unpooled.wrappedBuffer(byteArrayOf(0x7F)).use { buf -> + assertEquals(0x7F, buf.readUnsignedShortSmart()) + assertFalse(buf.isReadable) + } + + Unpooled.wrappedBuffer(byteArrayOf(0x80.toByte(), 0x80.toByte())).use { buf -> + assertEquals(0x80, buf.readUnsignedShortSmart()) + assertFalse(buf.isReadable) + } + + Unpooled.wrappedBuffer(byteArrayOf(0xC0.toByte(), 0x00)).use { buf -> + assertEquals(0x4000, buf.readUnsignedShortSmart()) + assertFalse(buf.isReadable) + } + + Unpooled.wrappedBuffer(byteArrayOf(0xFF.toByte(), 0xFF.toByte())).use { buf -> + assertEquals(0x7FFF, buf.readUnsignedShortSmart()) + assertFalse(buf.isReadable) + } + } + + @Test + fun testWriteUnsignedShortSmart() { + Unpooled.wrappedBuffer(byteArrayOf(0x00)).use { expected -> + Unpooled.buffer().use { actual -> + actual.writeUnsignedShortSmart(0) + assertEquals(expected, actual) + } + } + + Unpooled.wrappedBuffer(byteArrayOf(0x40)).use { expected -> + Unpooled.buffer().use { actual -> + actual.writeUnsignedShortSmart(0x40) + assertEquals(expected, actual) + } + } + + Unpooled.wrappedBuffer(byteArrayOf(0x7F)).use { expected -> + Unpooled.buffer().use { actual -> + actual.writeUnsignedShortSmart(0x7F) + assertEquals(expected, actual) + } + } + + Unpooled.wrappedBuffer(byteArrayOf(0x80.toByte(), 0x80.toByte())).use { expected -> + Unpooled.buffer().use { actual -> + actual.writeUnsignedShortSmart(0x80) + assertEquals(expected, actual) + } + } + + Unpooled.wrappedBuffer(byteArrayOf(0xC0.toByte(), 0x00)).use { expected -> + Unpooled.buffer().use { actual -> + actual.writeUnsignedShortSmart(0x4000) + assertEquals(expected, actual) + } + } + + Unpooled.wrappedBuffer(byteArrayOf(0xFF.toByte(), 0xFF.toByte())).use { expected -> + Unpooled.buffer().use { actual -> + actual.writeUnsignedShortSmart(0x7FFF) + assertEquals(expected, actual) + } + } + + Unpooled.buffer().use { buf -> + assertThrows { + buf.writeUnsignedShortSmart(-0x1) + } + } + + Unpooled.buffer().use { buf -> + assertThrows { + buf.writeUnsignedShortSmart(0x10000) + } + } + } + + @Test + fun testReadIntSmart() { + Unpooled.wrappedBuffer(byteArrayOf(0x00, 0x00)).use { buf -> + assertEquals(-0x4000, buf.readIntSmart()) + assertFalse(buf.isReadable) + } + + Unpooled.wrappedBuffer(byteArrayOf(0x40, 0x00)).use { buf -> + assertEquals(0, buf.readIntSmart()) + assertFalse(buf.isReadable) + } + + Unpooled.wrappedBuffer(byteArrayOf(0x7F, 0xFF.toByte())).use { buf -> + assertEquals(0x3FFF, buf.readIntSmart()) + assertFalse(buf.isReadable) + } + + Unpooled.wrappedBuffer(byteArrayOf(0x80.toByte(), 0x00, 0x00, 0x00)).use { buf -> + assertEquals(-0x40000000, buf.readIntSmart()) + assertFalse(buf.isReadable) + } + + Unpooled.wrappedBuffer(byteArrayOf(0xBF.toByte(), 0xFF.toByte(), 0xBF.toByte(), 0xFF.toByte())).use { buf -> + assertEquals(-0x4001, buf.readIntSmart()) + assertFalse(buf.isReadable) + } + + Unpooled.wrappedBuffer(byteArrayOf(0xC0.toByte(), 0x00, 0x40.toByte(), 0x00)).use { buf -> + assertEquals(0x4000, buf.readIntSmart()) + assertFalse(buf.isReadable) + } + + Unpooled.wrappedBuffer(byteArrayOf(0xFF.toByte(), 0xFF.toByte(), 0xFF.toByte(), 0xFF.toByte())).use { buf -> + assertEquals(0x3FFFFFFF, buf.readIntSmart()) + assertFalse(buf.isReadable) + } + } + + @Test + fun testWriteIntSmart() { + Unpooled.wrappedBuffer(byteArrayOf(0x00, 0x00)).use { expected -> + Unpooled.buffer().use { actual -> + actual.writeIntSmart(-0x4000) + assertEquals(expected, actual) + } + } + + Unpooled.wrappedBuffer(byteArrayOf(0x40, 0x00)).use { expected -> + Unpooled.buffer().use { actual -> + actual.writeIntSmart(0) + assertEquals(expected, actual) + } + } + + Unpooled.wrappedBuffer(byteArrayOf(0x7F, 0xFF.toByte())).use { expected -> + Unpooled.buffer().use { actual -> + actual.writeIntSmart(0x3FFF) + assertEquals(expected, actual) + } + } + + Unpooled.wrappedBuffer(byteArrayOf(0x80.toByte(), 0x00, 0x00, 0x00)).use { expected -> + Unpooled.buffer().use { actual -> + actual.writeIntSmart(-0x40000000) + assertEquals(expected, actual) + } + } + + Unpooled.wrappedBuffer( + byteArrayOf(0xBF.toByte(), 0xFF.toByte(), 0xBF.toByte(), 0xFF.toByte()) + ).use { expected -> + Unpooled.buffer().use { actual -> + actual.writeIntSmart(-0x4001) + assertEquals(expected, actual) + } + } + + Unpooled.wrappedBuffer(byteArrayOf(0xC0.toByte(), 0x00, 0x40.toByte(), 0x00)).use { expected -> + Unpooled.buffer().use { actual -> + actual.writeIntSmart(0x4000) + assertEquals(expected, actual) + } + } + + Unpooled.wrappedBuffer( + byteArrayOf(0xFF.toByte(), 0xFF.toByte(), 0xFF.toByte(), 0xFF.toByte()) + ).use { expected -> + Unpooled.buffer().use { actual -> + actual.writeIntSmart(0x3FFFFFFF) + assertEquals(expected, actual) + } + } + + Unpooled.buffer().use { buf -> + assertThrows { + buf.writeIntSmart(-0x40000001) + } + } + + Unpooled.buffer().use { buf -> + assertThrows { + buf.writeIntSmart(0x40000000) + } + } + } + + @Test + fun testReadUnsignedIntSmart() { + Unpooled.wrappedBuffer(byteArrayOf(0x00, 0x00)).use { buf -> + assertEquals(0, buf.readUnsignedIntSmart()) + assertFalse(buf.isReadable) + } + + Unpooled.wrappedBuffer(byteArrayOf(0x40, 0x00)).use { buf -> + assertEquals(0x4000, buf.readUnsignedIntSmart()) + assertFalse(buf.isReadable) + } + + Unpooled.wrappedBuffer(byteArrayOf(0x7F, 0xFF.toByte())).use { buf -> + assertEquals(0x7FFF, buf.readUnsignedIntSmart()) + assertFalse(buf.isReadable) + } + + Unpooled.wrappedBuffer(byteArrayOf(0x80.toByte(), 0x00, 0x80.toByte(), 0x00)).use { buf -> + assertEquals(0x8000, buf.readUnsignedIntSmart()) + assertFalse(buf.isReadable) + } + + Unpooled.wrappedBuffer(byteArrayOf(0xC0.toByte(), 0x00, 0x00, 0x00)).use { buf -> + assertEquals(0x40000000, buf.readUnsignedIntSmart()) + assertFalse(buf.isReadable) + } + + Unpooled.wrappedBuffer(byteArrayOf(0xFF.toByte(), 0xFF.toByte(), 0xFF.toByte(), 0xFF.toByte())).use { buf -> + assertEquals(0x7FFFFFFF, buf.readUnsignedIntSmart()) + assertFalse(buf.isReadable) + } + } + + @Test + fun testWriteUnsignedIntSmart() { + Unpooled.wrappedBuffer(byteArrayOf(0x00, 0x00)).use { expected -> + Unpooled.buffer().use { actual -> + actual.writeUnsignedIntSmart(0) + assertEquals(expected, actual) + } + } + + Unpooled.wrappedBuffer(byteArrayOf(0x40, 0x00)).use { expected -> + Unpooled.buffer().use { actual -> + actual.writeUnsignedIntSmart(0x4000) + assertEquals(expected, actual) + } + } + + Unpooled.wrappedBuffer(byteArrayOf(0x7F, 0xFF.toByte())).use { expected -> + Unpooled.buffer().use { actual -> + actual.writeUnsignedIntSmart(0x7FFF) + assertEquals(expected, actual) + } + } + + Unpooled.wrappedBuffer(byteArrayOf(0x80.toByte(), 0x00, 0x80.toByte(), 0x00)).use { expected -> + Unpooled.buffer().use { actual -> + actual.writeUnsignedIntSmart(0x8000) + assertEquals(expected, actual) + } + } + + Unpooled.wrappedBuffer(byteArrayOf(0xC0.toByte(), 0x00, 0x00, 0x00)).use { expected -> + Unpooled.buffer().use { actual -> + actual.writeUnsignedIntSmart(0x40000000) + assertEquals(expected, actual) + } + } + + Unpooled.wrappedBuffer( + byteArrayOf(0xFF.toByte(), 0xFF.toByte(), 0xFF.toByte(), 0xFF.toByte()) + ).use { expected -> + Unpooled.buffer().use { actual -> + actual.writeUnsignedIntSmart(0x7FFFFFFF) + assertEquals(expected, actual) + } + } + + Unpooled.buffer().use { buf -> + assertThrows { + buf.writeUnsignedIntSmart(0x80000000.toInt()) + } + } + } +}