Long story short this is a buffer that manipulates bits rather than bytes (what the usual buffer does)
it has everything a bit buffer would need except writing/reading floats, which I find unnecessary because you wouldn’t find yourself doing that
The BitBuffer uses buffer as the base rather than the old method of using string methods
Documentation
BitBufferLibrary methods
BitBufferLibrary.new(size: number) : BitBuffer
- Creates a BitBuffer based on given size (in bits), the number can be any positive integer, if the size is not divisible by 8 it would just round it to the closest possible byte
BitBufferLibrary.fromBuffer(buffer: buffer) : BitBuffer
- Creates a BitBuffer from a buffer
BitBuffer
BitBuffer.tostring(i: number?, j: number?) : string
- Reads out the bits from the BitBuffer or from given range from the BitBuffer
BitBuffer.getRawBuffer() : buffer
- Returns you the BitBuffer as a buffer
BitBuffer.canRead(bit: number) : boolean
- Tells you if the buffer can read certian amounts of bits, this method is primary used to detect when the BitBuffer reaches it end since there is no manual reading
BitBuffer.readBit() : number
- Reads the current bit
BitBuffer.readUnsignedInteger(size: number) : number
- Reads unsigned integer from given size in bits
BitBuffer.readSignedInteger(size: number) : number
- Reads signed integer from given size in bits
BitBuffer.rawReadBit(offset: number) : number
- Reads the current bit at given offset
BitBuffer.rawReadUnsignedInteger(size: number, offset: number) : number
- Reads unsigned integer from given size in bits at given offset
BitBuffer.rawReadSignedInteger(size: number, offset: number) : number
- Reads signed integer from given size in bits at given offset
BitBuffer.setReaderOffset(offset: number?)
- Either resets or sets the reading offset
BitBuffer.writeBit(offset: number, value: number)
- Writes a bit at certain offset
BitBuffer.writeUnsignedInteger(offset: number, size: number, value: number)
- Writes a unsigned integer from given bit size at certain offset
BitBuffer.writeSignedInteger(offset: number, size: number, value: number)
- Writes a signed integer from given bit size at certain offset
Important information
- The BitBuffer works exactly how the buffer does, meaning it uses little endian.
- Just as buffers the first element starts from 0 meaning its not the traditional way (for lua at least) of accessing data (from tables as example where you access the first element with index 1 and not 0)
- This currently does not support floats but its in the works
Code
export type BitBuffer = {
tostring: (from: number?, to: number?) -> string,
canRead: (Bits: number?) -> boolean,
getRawBuffer: () -> buffer,
readBit: () -> (number),
readUnsignedInteger: (IntegerSize: number) -> (number),
readSignedInteger: (IntegerSize: number) -> (number),
rawReadBit: (Offset: number) -> (number),
rawReadUnsignedInteger: (IntegerSize: number, Offset: number) -> (number),
rawReadSignedInteger: (IntegerSize: number, Offset: number) -> (number),
setReaderOffset: (Offset: number?) -> (),
writeBit: (Offset: number, Value: number) -> (),
writeUnsignedInteger: (Offset: number, IntegerSize: number, Value: number) -> (),
writeSignedInteger: (Offset: number, IntegerSize: number, Value: number) -> (),
}
local BitBufferLibrary = {}
local function ToBase2(number)
local base2 = ""
while number > 0 do
base2 = (number % 2) .. base2
number //= 2
end
return base2
end
local function LeftPad(str, len)
return string.rep(0, len - #str) .. str
end
local function readBit(Buffer, Offset)
local Byte = Offset // 8
local Num = buffer.readu8(Buffer, Byte)
return bit32.extract(Num, 7 - Offset % 8)
end
local function writeBit(Buffer, Offset, Value)
local Byte = Offset // 8
local Num = bit32.replace(buffer.readu8(Buffer, Byte), Value, 7 - Offset % 8)
buffer.writeu8(Buffer, Byte, Num)
end
local function readSignedInteger(Buffer, Offset, Size)
local ByteSize = Size // 8
local Num = 2 ^ Size * -readBit(Buffer, Offset + ByteSize * 8 - 8)
for j = 0, ByteSize - 1 do
local ByteOffset = j * 8
for i = 7, 0, -1 do
local Bit = readBit(Buffer, Offset + ByteOffset + i)
Num += Bit * 2 ^ (j * 8 + 7 - i)
end
end
return Num
end
local function writeSignedInteger(Buffer, Offset, Size, Value)
local ByteSize = Size // 8
if Value < 0 then
writeBit(Buffer, Offset + ByteSize * 8 - 7, 1)
else
writeBit(Buffer, Offset + ByteSize * 8 - 7, 0)
end
for j = 0, ByteSize do
local ByteOffset = j * 8
for i = 7, 0, -1 do
writeBit(Buffer, Offset + ByteOffset + i, Value % 2)
Value //= 2
end
end
end
local function readUnsignedInteger(Buffer, Offset, Size)
local ByteSize = Size // 8
local Num = 0
for j = 0, ByteSize - 1 do
local ByteOffset = j * 8
for i = 7, 0, -1 do
local Bit = readBit(Buffer, Offset + ByteOffset + i)
Num += Bit * 2 ^ (j * 8 + 7 - i)
end
end
return Num
end
local function writeUnsignedInteger(Buffer, Offset, Size, Value)
local ByteSize = Size // 8
for j = 0, ByteSize do
local ByteOffset = j * 8
for i = 7, 0, -1 do
writeBit(Buffer, Offset + ByteOffset + i, Value % 2)
Value //= 2
end
end
end
local function CreateBitBuffer(Buffer: buffer) : BitBuffer
local BitBufferLength = buffer.len(Buffer) * 8
local OffsetReader = 0
return {
tostring = function(i, j)
i = i or 0
j = j and j - 1 or BitBufferLength - 1
local String = ""
local StartByte, EndByte = i // 8, j // 8
for k = StartByte, EndByte do
String ..= LeftPad(ToBase2(buffer.readu8(Buffer, k)), 8)
end
return String
end,
getRawBuffer = function()
return Buffer
end,
canRead = function(Bit)
return OffsetReader + Bit <= BitBufferLength
end,
readBit = function()
local Bit = readBit(Buffer, OffsetReader)
OffsetReader += 1
return Bit
end,
readUnsignedInteger = function(Size)
local IntSize = readUnsignedInteger(Buffer, OffsetReader, Size)
OffsetReader += Size
return IntSize
end,
readSignedInteger = function(Size)
local IntSize = readSignedInteger(Buffer, OffsetReader, Size)
OffsetReader += Size
return IntSize
end,
rawReadBit = function(Offset)
return readBit(Buffer, Offset)
end,
rawReadUnsignedInteger = function(Size , Offset)
return readUnsignedInteger(Buffer, OffsetReader, Size)
end,
rawReadSignedInteger = function(Size, Offset)
return readSignedInteger(Buffer, Offset, Size)
end,
setReaderOffset = function(Offset)
OffsetReader = Offset or 0
end,
writeBit = function(Offset, Value)
writeBit(Buffer, Offset, Value)
end,
writeUnsignedInteger = function(Offset, Size, Value)
writeUnsignedInteger(Buffer, Offset, Size, Value)
end,
writeSignedInteger = function(Offset, Size, Value)
writeSignedInteger(Buffer, Offset, Size, Value)
end,
}
end
function BitBufferLibrary.fromBuffer(Buffer: buffer) : BitBuffer
return CreateBitBuffer(Buffer)
end
function BitBufferLibrary.new(BitSize: number) : BitBuffer
local Buffer = buffer.create(BitSize // 8 + 1)
buffer.fill(Buffer, 0, 0)
return CreateBitBuffer(Buffer)
end
return BitBufferLibrary
Example
local BitBufferLibrary = require(script.BitBufferLibrary)
local Buffer = buffer.create(32)
local BitBuffer = BitBufferLibrary.fromBuffer(Buffer)
buffer.writeu8(Buffer, 0, 231)
buffer.writei8(Buffer, 1, -67)
buffer.writeu16(Buffer, 2, 32561)
buffer.writei16(Buffer, 4, -23561)
buffer.writeu32(Buffer, 6, 2365236126)
buffer.writei32(Buffer, 10, -1235235235)
print(BitBuffer.readUnsignedInteger(8))
print(BitBuffer.readSignedInteger(8))
print(BitBuffer.readUnsignedInteger(16))
print(BitBuffer.readSignedInteger(16))
print(BitBuffer.readUnsignedInteger(32))
print(BitBuffer.readSignedInteger(32))
Images
Thats all