Skip to content

Instantly share code, notes, and snippets.

@ultraviolet-jordan
Created June 19, 2023 01:51
Show Gist options
  • Save ultraviolet-jordan/1fcebf56192ec2d0dfb5c94ecc4309e9 to your computer and use it in GitHub Desktop.
Save ultraviolet-jordan/1fcebf56192ec2d0dfb5c94ecc4309e9 to your computer and use it in GitHub Desktop.
fun main() {
val buffer = ByteBuffer.allocate(100)
buffer.withBitAccess {
pBit(2, 3)
pBit(1, 0)
}
}
inline fun ByteBuffer.withBitAccess(function: ByteBuffer.() -> Unit) {
position(position() * 8)
mark()
function.invoke(this)
val position = position()
reset()
// The marked starting byte index to calculate from the ending position.
val marked = position()
val index = marked + (position - marked)
position((index + 7) / 8)
}
fun ByteBuffer.pBit(count: Int, value: Int) {
val position = position()
// Constantly mark and reset.
reset()
val marked = position()
// Keeps the mark positioned at the starting byte index.
mark()
val index = marked + (position - marked)
pBit(value, count, index shr 3, index % 8)
position(position + count)
}
private tailrec fun ByteBuffer.pBit(value: Int, remainingBits: Int, byteIndex: Int, bitIndex: Int) {
if (remainingBits == 0) return
val bitOffset = 8 - bitIndex
// The maximum number of bits that can be written to the current byte.
val bitsToWrite = min(remainingBits, bitOffset)
val max = (1 shl bitsToWrite) - 1
// The relevant bits from the value.
val byteValue = (value ushr (remainingBits - bitsToWrite)) and max
// The relevant bits in the current byte.
// The runescape client pre generates this array.
val mask = max shl (bitOffset - bitsToWrite)
// The current byte from the buffer.
val currentValue = get(byteIndex).toInt()
// The current byte with the new bits.
val newValue = currentValue and mask.inv() or (byteValue shl (bitOffset - bitsToWrite))
put(byteIndex, newValue.toByte())
return pBit(value, remainingBits - bitsToWrite, byteIndex + 1, 0)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment