Add BitBuf

Signed-off-by: Graham <gpe@openrs2.org>
pull/132/head
Graham 3 years ago
parent 1954f408e7
commit bd5a925f9b
  1. 265
      buffer/src/main/kotlin/org/openrs2/buffer/BitBuf.kt
  2. 624
      buffer/src/test/kotlin/org/openrs2/buffer/BitBufTest.kt

@ -0,0 +1,265 @@
package org.openrs2.buffer
import com.google.common.base.Preconditions
import io.netty.buffer.ByteBuf
import kotlin.math.min
public class BitBuf(
private val buf: ByteBuf
) : AutoCloseable {
private var readerIndex: Long = buf.readerIndex().toLong() shl 3
private set(value) {
field = value
buf.readerIndex((readerIndex shr 3).toInt())
}
private var writerIndex: Long = buf.writerIndex().toLong() shl 3
private set(value) {
field = value
buf.writerIndex((writerIndex shr 3).toInt())
}
public fun getBoolean(index: Long): Boolean {
return getBits(index, 1) != 0
}
public fun getBit(index: Long): Int {
return getBits(index, 1)
}
public fun getBits(index: Long, len: Int): Int {
Preconditions.checkArgument(len in 1..32)
if (index < 0 || (index + len) > capacity()) {
throw IndexOutOfBoundsException()
}
var value = 0
var remaining = len
var byteIndex = (index shr 3).toInt()
var bitIndex = (index and 7).toInt()
while (remaining > 0) {
val n = min(8 - bitIndex, remaining)
val shift = (8 - (bitIndex + n)) and 7
val mask = (1 shl n) - 1
val v = buf.getUnsignedByte(byteIndex).toInt()
value = value shl n
value = value or ((v shr shift) and mask)
remaining -= n
byteIndex++
bitIndex = 0
}
return value
}
public fun readBoolean(): Boolean {
return readBits(1) != 0
}
public fun readBit(): Int {
return readBits(1)
}
public fun readBits(len: Int): Int {
checkReadableBits(len)
val value = getBits(readerIndex, len)
readerIndex += len
return value
}
public fun skipBits(len: Int): BitBuf {
checkReadableBits(len)
readerIndex += len
return this
}
public fun setBoolean(index: Long, value: Boolean): BitBuf {
if (value) {
setBits(index, 1, 1)
} else {
setBits(index, 1, 0)
}
return this
}
public fun setBit(index: Long, value: Int): BitBuf {
setBits(index, 1, value)
return this
}
public fun setBits(index: Long, len: Int, value: Int): BitBuf {
Preconditions.checkArgument(len in 1..32)
if (index < 0 || (index + len) > capacity()) {
throw IndexOutOfBoundsException()
}
var remaining = len
var byteIndex = (index shr 3).toInt()
var bitIndex = (index and 7).toInt()
while (remaining > 0) {
val n = min(8 - bitIndex, remaining)
val shift = (8 - (bitIndex + n)) and 7
val mask = (1 shl n) - 1
var v = buf.getUnsignedByte(byteIndex).toInt()
v = v and (mask shl shift).inv()
v = v or (((value shr (remaining - n)) and mask) shl shift)
buf.setByte(byteIndex, v)
remaining -= n
byteIndex++
bitIndex = 0
}
return this
}
public fun writeBoolean(value: Boolean): BitBuf {
if (value) {
writeBits(1, 1)
} else {
writeBits(1, 0)
}
return this
}
public fun writeBit(value: Int): BitBuf {
writeBits(1, value)
return this
}
public fun writeBits(len: Int, value: Int): BitBuf {
ensureWritable(len.toLong())
setBits(writerIndex, len, value)
writerIndex += len
return this
}
public fun writeZero(len: Int): BitBuf {
writeBits(len, 0)
return this
}
private fun checkReadableBits(len: Int) {
Preconditions.checkArgument(len >= 0)
if ((readerIndex + len) > writerIndex) {
throw IndexOutOfBoundsException()
}
}
public fun ensureWritable(len: Long): BitBuf {
Preconditions.checkArgument(len >= 0)
if ((writerIndex + len) > maxCapacity()) {
throw IndexOutOfBoundsException()
}
val currentByteIndex = writerIndex shr 3
val nextByteIndex = (writerIndex + len + 7) shr 3
buf.ensureWritable((nextByteIndex - currentByteIndex).toInt())
return this
}
public fun readableBits(): Long {
return writerIndex - readerIndex
}
public fun writableBits(): Long {
return capacity() - writerIndex
}
public fun maxWritableBits(): Long {
return maxCapacity() - writerIndex
}
public fun capacity(): Long {
return buf.capacity().toLong() shl 3
}
public fun capacity(len: Long): BitBuf {
buf.capacity((len shr 3).toInt())
return this
}
public fun maxCapacity(): Long {
return buf.maxCapacity().toLong() shl 3
}
public fun isReadable(): Boolean {
return readerIndex < writerIndex
}
public fun isReadable(len: Long): Boolean {
Preconditions.checkArgument(len >= 0)
return (readerIndex + len) <= writerIndex
}
public fun isWritable(): Boolean {
return writerIndex < capacity()
}
public fun isWritable(len: Long): Boolean {
Preconditions.checkArgument(len >= 0)
return (writerIndex + len) <= capacity()
}
public fun readerIndex(): Long {
return readerIndex
}
public fun readerIndex(index: Long): BitBuf {
if (index < 0 || index > writerIndex) {
throw IndexOutOfBoundsException()
}
readerIndex = index
return this
}
public fun writerIndex(): Long {
return writerIndex
}
public fun writerIndex(index: Long): BitBuf {
if (index < readerIndex || index > capacity()) {
throw IndexOutOfBoundsException()
}
writerIndex = index
return this
}
public fun clear(): BitBuf {
readerIndex = 0
writerIndex = 0
return this
}
override fun close() {
val bits = (((writerIndex + 7) and 7.toLong().inv()) - writerIndex).toInt()
if (bits != 0) {
writeZero(bits)
}
readerIndex = (readerIndex + 7) and 7.toLong().inv()
}
}

@ -0,0 +1,624 @@
package org.openrs2.buffer
import io.netty.buffer.ByteBufAllocator
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertFailsWith
import kotlin.test.assertFalse
import kotlin.test.assertTrue
class BitBufTest {
@Test
fun testClear() {
ByteBufAllocator.DEFAULT.buffer().use { buf ->
buf.writeInt(1234567890)
buf.skipBytes(1)
BitBuf(buf).use { bitBuf ->
assertEquals(8, bitBuf.readerIndex())
assertEquals(32, bitBuf.writerIndex())
bitBuf.clear()
assertEquals(0, bitBuf.readerIndex())
assertEquals(0, bitBuf.writerIndex())
}
assertEquals(0, buf.readerIndex())
assertEquals(0, buf.writerIndex())
}
}
@Test
fun testReadAlignment() {
ByteBufAllocator.DEFAULT.buffer().use { buf ->
buf.writeByte(0xFF)
BitBuf(buf).use { bitBuf ->
assertEquals(0, bitBuf.readerIndex())
assertEquals(0x7F, bitBuf.readBits(7))
assertEquals(7, bitBuf.readerIndex())
assertEquals(0, buf.readerIndex())
}
assertEquals(1, buf.readerIndex())
}
}
@Test
fun testWriteAlignment() {
ByteBufAllocator.DEFAULT.buffer().use { buf ->
buf.setByte(0, 0xFF)
BitBuf(buf).use { bitBuf ->
assertEquals(0, bitBuf.writerIndex())
bitBuf.writeBits(7, 0x7F)
assertEquals(7, bitBuf.writerIndex())
assertEquals(0, buf.writerIndex())
}
assertEquals(1, buf.writerIndex())
assertEquals(0xFE, buf.getUnsignedByte(0))
}
}
@Test
fun testSkipBits() {
ByteBufAllocator.DEFAULT.buffer().use { buf ->
buf.writeInt(1234567890)
BitBuf(buf).use { bitBuf ->
assertFailsWith<IllegalArgumentException> {
bitBuf.skipBits(-1)
}
assertEquals(0, bitBuf.readerIndex())
assertEquals(0, buf.readerIndex())
bitBuf.skipBits(0)
assertEquals(0, bitBuf.readerIndex())
assertEquals(0, buf.readerIndex())
bitBuf.skipBits(7)
assertEquals(7, bitBuf.readerIndex())
assertEquals(0, buf.readerIndex())
bitBuf.skipBits(2)
assertEquals(9, bitBuf.readerIndex())
assertEquals(1, buf.readerIndex())
bitBuf.skipBits(23)
assertEquals(32, bitBuf.readerIndex())
assertEquals(4, buf.readerIndex())
bitBuf.skipBits(0)
assertEquals(32, bitBuf.readerIndex())
assertEquals(4, buf.readerIndex())
assertFailsWith<IndexOutOfBoundsException> {
bitBuf.skipBits(1)
}
}
assertEquals(4, buf.readerIndex())
}
}
@Test
fun testGetBits() {
ByteBufAllocator.DEFAULT.buffer(4, 4).use { buf ->
buf.writeInt(1234567890)
BitBuf(buf).use { bitBuf ->
assertEquals(1234567890, bitBuf.getBits(0, 32))
assertEquals(0b0100100110010110, bitBuf.getBits(0, 16))
assertEquals(0b0000001011010010, bitBuf.getBits(16, 16))
assertEquals(0b01001001, bitBuf.getBits(0, 8))
assertEquals(0b10010110, bitBuf.getBits(8, 8))
assertEquals(0b00000010, bitBuf.getBits(16, 8))
assertEquals(0b11010010, bitBuf.getBits(24, 8))
assertEquals(0b10011001, bitBuf.getBits(4, 8))
assertEquals(0b100110010110, bitBuf.getBits(4, 12))
assertEquals(0, bitBuf.getBits(0, 1))
assertEquals(0, bitBuf.getBit(0))
assertEquals(false, bitBuf.getBoolean(0))
assertEquals(1, bitBuf.getBits(1, 1))
assertEquals(1, bitBuf.getBit(1))
assertEquals(true, bitBuf.getBoolean(1))
assertFailsWith<IndexOutOfBoundsException> {
bitBuf.getBits(-1, 1)
}
assertFailsWith<IndexOutOfBoundsException> {
bitBuf.getBits(32, 1)
}
assertFailsWith<IllegalArgumentException> {
bitBuf.getBits(0, 0)
}
assertFailsWith<IllegalArgumentException> {
bitBuf.getBits(0, 33)
}
}
}
}
@Test
fun testSetBits() {
ByteBufAllocator.DEFAULT.buffer(4, 4).use { buf ->
BitBuf(buf).use { bitBuf ->
bitBuf.setBits(0, 32, 1234567890)
}
assertEquals(1234567890, buf.getInt(0))
}
ByteBufAllocator.DEFAULT.buffer(4, 4).use { buf ->
BitBuf(buf).use { bitBuf ->
bitBuf.setBits(0, 16, 0b0100100110010110)
bitBuf.setBits(16, 16, 0b0000001011010010)
}
assertEquals(1234567890, buf.getInt(0))
}
ByteBufAllocator.DEFAULT.buffer(4, 4).use { buf ->
BitBuf(buf).use { bitBuf ->
bitBuf.setBits(0, 8, 0b01001001)
bitBuf.setBits(8, 8, 0b10010110)
bitBuf.setBits(16, 8, 0b00000010)
bitBuf.setBits(24, 8, 0b11010010)
}
assertEquals(1234567890, buf.getInt(0))
}
ByteBufAllocator.DEFAULT.buffer(4, 4).use { buf ->
BitBuf(buf).use { bitBuf ->
bitBuf.setBits(0, 4, 0b0100)
bitBuf.setBits(4, 8, 0b10011001)
bitBuf.setBits(12, 4, 0b0110)
bitBuf.setBits(16, 16, 0b0000001011000101)
bitBuf.setBoolean(27, true)
bitBuf.setBits(29, 1, 0)
bitBuf.setBit(30, 1)
bitBuf.setBoolean(31, false)
}
assertEquals(1234567890, buf.getInt(0))
}
ByteBufAllocator.DEFAULT.buffer(4, 4).use { buf ->
BitBuf(buf).use { bitBuf ->
assertFailsWith<IndexOutOfBoundsException> {
bitBuf.setBits(-1, 1, 0)
}
assertFailsWith<IndexOutOfBoundsException> {
bitBuf.setBits(32, 1, 0)
}
assertFailsWith<IllegalArgumentException> {
bitBuf.setBits(0, 0, 0)
}
assertFailsWith<IllegalArgumentException> {
bitBuf.setBits(0, 33, 0)
}
}
}
}
@Test
fun testRead() {
ByteBufAllocator.DEFAULT.buffer(4, 4).use { buf ->
buf.writeInt(1234567890)
BitBuf(buf).use { bitBuf ->
assertEquals(0, bitBuf.readerIndex())
assertEquals(32, bitBuf.readableBits())
assertTrue(bitBuf.isReadable())
assertTrue(bitBuf.isReadable(0))
assertTrue(bitBuf.isReadable(1))
assertTrue(bitBuf.isReadable(31))
assertTrue(bitBuf.isReadable(32))
assertFalse(bitBuf.isReadable(33))
assertEquals(1234567890, bitBuf.readBits(32))
assertEquals(32, bitBuf.readerIndex())
assertEquals(0, bitBuf.readableBits())
assertFalse(bitBuf.isReadable())
assertTrue(bitBuf.isReadable(0))
assertFalse(bitBuf.isReadable(1))
}
assertEquals(4, buf.readerIndex())
}
ByteBufAllocator.DEFAULT.buffer(4, 4).use { buf ->
buf.writeInt(1234567890)
BitBuf(buf).use { bitBuf ->
assertEquals(0, bitBuf.readerIndex())
assertEquals(32, bitBuf.readableBits())
assertEquals(0b0100100110010110, bitBuf.readBits(16))
assertEquals(16, bitBuf.readerIndex())
assertEquals(16, bitBuf.readableBits())
assertEquals(0b0000001011010010, bitBuf.readBits(16))
assertEquals(32, bitBuf.readerIndex())
assertEquals(0, bitBuf.readableBits())
}
assertEquals(4, buf.readerIndex())
}
ByteBufAllocator.DEFAULT.buffer(4, 4).use { buf ->
buf.writeInt(1234567890)
BitBuf(buf).use { bitBuf ->
assertEquals(0, bitBuf.readerIndex())
assertEquals(32, bitBuf.readableBits())
assertEquals(0b01001001, bitBuf.readBits(8))
assertEquals(8, bitBuf.readerIndex())
assertEquals(24, bitBuf.readableBits())
assertEquals(0b10010110, bitBuf.readBits(8))
assertEquals(16, bitBuf.readerIndex())
assertEquals(16, bitBuf.readableBits())
assertEquals(0b00000010, bitBuf.readBits(8))
assertEquals(24, bitBuf.readerIndex())
assertEquals(8, bitBuf.readableBits())
assertEquals(0b11010010, bitBuf.readBits(8))
assertEquals(32, bitBuf.readerIndex())
assertEquals(0, bitBuf.readableBits())
}
assertEquals(4, buf.readerIndex())
}
ByteBufAllocator.DEFAULT.buffer(1, 1).use { buf ->
buf.writeByte(0b01010100)
BitBuf(buf).use { bitBuf ->
assertEquals(0, bitBuf.readerIndex())
assertEquals(8, bitBuf.readableBits())
assertEquals(0, bitBuf.readBits(1))
assertEquals(1, bitBuf.readerIndex())
assertEquals(7, bitBuf.readableBits())
assertEquals(1, bitBuf.readBits(1))
assertEquals(2, bitBuf.readerIndex())
assertEquals(6, bitBuf.readableBits())
assertEquals(0, bitBuf.readBit())
assertEquals(3, bitBuf.readerIndex())
assertEquals(5, bitBuf.readableBits())
assertEquals(1, bitBuf.readBit())
assertEquals(4, bitBuf.readerIndex())
assertEquals(4, bitBuf.readableBits())
assertFalse(bitBuf.readBoolean())
assertEquals(5, bitBuf.readerIndex())
assertEquals(3, bitBuf.readableBits())
assertTrue(bitBuf.readBoolean())
assertEquals(6, bitBuf.readerIndex())
assertEquals(2, bitBuf.readableBits())
bitBuf.skipBits(2)
assertEquals(8, bitBuf.readerIndex())
assertEquals(0, bitBuf.readableBits())
}
assertEquals(1, buf.readerIndex())
}
ByteBufAllocator.DEFAULT.buffer(5, 5).use { buf ->
buf.writerIndex(5)
BitBuf(buf).use { bitBuf ->
assertFailsWith<IllegalArgumentException> {
bitBuf.readBits(0)
}
assertFailsWith<IllegalArgumentException> {
bitBuf.readBits(33)
}
}
}
}
@Test
fun testWrite() {
ByteBufAllocator.DEFAULT.buffer(4, 4).use { buf ->
BitBuf(buf).use { bitBuf ->
assertEquals(0, bitBuf.writerIndex())
assertEquals(32, bitBuf.writableBits())
assertTrue(bitBuf.isWritable())
assertTrue(bitBuf.isWritable(0))
assertTrue(bitBuf.isWritable(1))
assertTrue(bitBuf.isWritable(31))
assertTrue(bitBuf.isWritable(32))
assertFalse(bitBuf.isWritable(33))
bitBuf.writeBits(32, 1234567890)
assertEquals(32, bitBuf.writerIndex())
assertEquals(0, bitBuf.writableBits())
assertFalse(bitBuf.isWritable())
assertTrue(bitBuf.isWritable(0))
assertFalse(bitBuf.isWritable(1))
}
assertEquals(1234567890, buf.getInt(0))
assertEquals(4, buf.writerIndex())
}
ByteBufAllocator.DEFAULT.buffer(4, 4).use { buf ->
BitBuf(buf).use { bitBuf ->
assertEquals(0, bitBuf.writerIndex())
assertEquals(32, bitBuf.writableBits())
bitBuf.writeBits(16, 0b0100100110010110)
assertEquals(16, bitBuf.writerIndex())
assertEquals(16, bitBuf.writableBits())
bitBuf.writeBits(16, 0b0000001011010010)
assertEquals(32, bitBuf.writerIndex())
assertEquals(0, bitBuf.writableBits())
}
assertEquals(1234567890, buf.getInt(0))
assertEquals(4, buf.writerIndex())
}
ByteBufAllocator.DEFAULT.buffer(4, 4).use { buf ->
BitBuf(buf).use { bitBuf ->
assertEquals(0, bitBuf.writerIndex())
assertEquals(32, bitBuf.writableBits())
bitBuf.writeBits(8, 0b01001001)
assertEquals(8, bitBuf.writerIndex())
assertEquals(24, bitBuf.writableBits())
bitBuf.writeBits(8, 0b10010110)
assertEquals(16, bitBuf.writerIndex())
assertEquals(16, bitBuf.writableBits())
bitBuf.writeBits(8, 0b00000010)
assertEquals(24, bitBuf.writerIndex())
assertEquals(8, bitBuf.writableBits())
bitBuf.writeBits(8, 0b11010010)
assertEquals(32, bitBuf.writerIndex())
assertEquals(0, bitBuf.writableBits())
}
assertEquals(1234567890, buf.getInt(0))
assertEquals(4, buf.writerIndex())
}
ByteBufAllocator.DEFAULT.buffer(1, 1).use { buf ->
BitBuf(buf).use { bitBuf ->
assertEquals(0, bitBuf.writerIndex())
assertEquals(8, bitBuf.writableBits())
bitBuf.writeBits(1, 0)
assertEquals(1, bitBuf.writerIndex())
assertEquals(7, bitBuf.writableBits())
bitBuf.writeBits(1, 1)
assertEquals(2, bitBuf.writerIndex())
assertEquals(6, bitBuf.writableBits())
bitBuf.writeBit(0)
assertEquals(3, bitBuf.writerIndex())
assertEquals(5, bitBuf.writableBits())
bitBuf.writeBit(1)
assertEquals(4, bitBuf.writerIndex())
assertEquals(4, bitBuf.writableBits())
bitBuf.writeBoolean(false)
assertEquals(5, bitBuf.writerIndex())
assertEquals(3, bitBuf.writableBits())
bitBuf.writeBoolean(true)
assertEquals(6, bitBuf.writerIndex())
assertEquals(2, bitBuf.writableBits())
bitBuf.writeZero(2)
assertEquals(8, bitBuf.writerIndex())
assertEquals(0, bitBuf.writableBits())
}
assertEquals(0b01010100, buf.getUnsignedByte(0))
assertEquals(1, buf.writerIndex())
}
ByteBufAllocator.DEFAULT.buffer(5, 5).use { buf ->
BitBuf(buf).use { bitBuf ->
assertFailsWith<IllegalArgumentException> {
bitBuf.writeBits(0, 0)
}
assertFailsWith<IllegalArgumentException> {
bitBuf.writeBits(33, 0)
}
assertFailsWith<IllegalArgumentException> {
bitBuf.isWritable(-1)
}
}
}
}
@Test
fun testExpand() {
ByteBufAllocator.DEFAULT.buffer(1, 2).use { buf ->
BitBuf(buf).use { bitBuf ->
assertEquals(8, bitBuf.capacity())
assertEquals(8, bitBuf.writableBits())
assertEquals(16, bitBuf.maxCapacity())
assertEquals(16, bitBuf.maxWritableBits())
bitBuf.writeBits(8, 0)
assertEquals(0, bitBuf.writableBits())
assertEquals(8, bitBuf.maxWritableBits())
bitBuf.writeBit(0)
assertEquals(7, bitBuf.writableBits())
assertEquals(7, bitBuf.maxWritableBits())
bitBuf.writeZero(7)
assertEquals(0, bitBuf.writableBits())
assertEquals(0, bitBuf.maxWritableBits())
assertFailsWith<IndexOutOfBoundsException> {
bitBuf.writeBit(0)
}
assertFailsWith<IllegalArgumentException> {
bitBuf.ensureWritable(-1)
}
}
}
}
@Test
fun testCapacity() {
ByteBufAllocator.DEFAULT.buffer(4, 16).use { buf ->
BitBuf(buf).use { bitBuf ->
assertEquals(32, bitBuf.capacity())
assertEquals(4, buf.capacity())
assertEquals(128, bitBuf.maxCapacity())
assertEquals(16, buf.maxCapacity())
bitBuf.capacity(64)
assertEquals(64, bitBuf.capacity())
assertEquals(8, buf.capacity())
}
}
}
@Test
fun testReaderIndex() {
ByteBufAllocator.DEFAULT.buffer().use { buf ->
BitBuf(buf).use { bitBuf ->
bitBuf.writerIndex(10)
assertEquals(0, bitBuf.readerIndex())
assertFailsWith<IndexOutOfBoundsException> {
bitBuf.readerIndex(-1)
}
bitBuf.readerIndex(1)
assertEquals(1, bitBuf.readerIndex())
bitBuf.readerIndex(8)
assertEquals(8, bitBuf.readerIndex())
bitBuf.readerIndex(9)
assertEquals(9, bitBuf.readerIndex())
bitBuf.readerIndex(10)
assertEquals(10, bitBuf.readerIndex())
assertFailsWith<IndexOutOfBoundsException> {
bitBuf.readerIndex(11)
}
}
}
}
@Test
fun testWriterIndex() {
ByteBufAllocator.DEFAULT.buffer(4, 4).use { buf ->
BitBuf(buf).use { bitBuf ->
bitBuf.writerIndex(20)
bitBuf.readerIndex(10)
assertEquals(20, bitBuf.writerIndex())
assertFailsWith<IndexOutOfBoundsException> {
bitBuf.writerIndex(9)
}
bitBuf.writerIndex(10)
assertEquals(10, bitBuf.writerIndex())
bitBuf.writerIndex(11)
assertEquals(11, bitBuf.writerIndex())
bitBuf.writerIndex(31)
assertEquals(31, bitBuf.writerIndex())
bitBuf.writerIndex(32)
assertEquals(32, bitBuf.writerIndex())
assertFailsWith<IndexOutOfBoundsException> {
bitBuf.writerIndex(33)
}
}
}
}
}
Loading…
Cancel
Save