Skip to content

Instantly share code, notes, and snippets.

@Reselim
Last active August 29, 2020 14:21
Show Gist options
  • Save Reselim/71ede2c81694b9b16d99f73ec918704b to your computer and use it in GitHub Desktop.
Save Reselim/71ede2c81694b9b16d99f73ec918704b to your computer and use it in GitHub Desktop.
Relatively fast buffer module
local powersOfTwo = setmetatable({}, {
__index = function(self, exponent)
local value = 2 ^ exponent
self[exponent] = value
return value
end
})
local function encodeFloat(value, mantissaBits, exponentBits)
if value == 0 then
return 0, 0, 0
end
local sign = value < 0 and 1 or 0
local mantissa, exponent = math.frexp(value)
mantissa = (math.abs(mantissa) - 0.5) * 2 * (powersOfTwo[mantissaBits] - 1)
mantissa = math.floor(mantissa + 0.5) -- May not be correct, but good enough
local exponentSign = exponent < 0 and 1 or 0
local maxExponent = powersOfTwo[exponentBits - 1]
-- Cap at maximum exponent
exponent = math.clamp(exponent, -maxExponent, maxExponent - 1)
if exponent < 0 then
-- Underflow
exponent = exponent + maxExponent
end
-- Add exponent sign
exponent = exponent + bit32.lshift(exponentSign, exponentBits - 1)
return sign, mantissa, exponent
end
local function decodeFloat(sign, mantissa, exponent, mantissaBits, exponentBits)
mantissa = 0.5 + (mantissa / (powersOfTwo[mantissaBits] - 1)) * 0.5
local exponentSign = bit32.rshift(exponent, exponentBits - 1)
if exponentSign == 1 then
exponent = exponent - (powersOfTwo[exponentBits - 1] * 2)
end
if sign == 1 then
mantissa = -mantissa
end
return math.ldexp(mantissa, exponent)
end
local function createWriteBuffer()
local data = {}
local pointer = 1
local function writeUInt(byteWidth, value)
for index = 0, byteWidth - 1 do
data[pointer + index] = bit32.rshift(value, index * 8) % 256
end
pointer = pointer + byteWidth
end
local function writeInt(byteWidth, value)
if value < 0 then
-- Underflow
value = value + powersOfTwo[byteWidth * 8]
end
writeUInt(byteWidth, value)
end
local function writeFloat16(value)
local sign, mantissa, exponent = encodeFloat(value, 10, 5)
data[pointer] = bit32.lshift(sign, 7) + bit32.lshift(exponent, 2) + bit32.rshift(mantissa, 8)
data[pointer + 1] = mantissa % 256
pointer = pointer + 2
end
local function writeFloat32(value)
local sign, mantissa, exponent = encodeFloat(value, 23, 8)
data[pointer] = bit32.lshift(sign, 7) + bit32.rshift(exponent, 1)
data[pointer + 1] = bit32.lshift(exponent, 7) % 256 + bit32.rshift(mantissa, 16)
data[pointer + 2] = bit32.rshift(mantissa, 8) % 256
data[pointer + 3] = mantissa % 256
pointer = pointer + 4
end
local function writeFloat64(value)
error("writeFloat64 disabled, as bit32 can't handle 64 bit numbers")
local sign, mantissa, exponent = encodeFloat(value, 52, 11)
data[pointer] = bit32.lshift(sign, 7) + bit32.rshift(exponent, 4)
data[pointer + 1] = bit32.lshift(exponent, 4) % 256 + math.floor(mantissa * powersOfTwo[48])
data[pointer + 2] = bit32.rshift(mantissa, 40) % 256
data[pointer + 3] = bit32.rshift(mantissa, 32) % 256
data[pointer + 4] = bit32.rshift(mantissa, 24) % 256
data[pointer + 5] = bit32.rshift(mantissa, 16) % 256
data[pointer + 6] = bit32.rshift(mantissa, 8) % 256
data[pointer + 7] = mantissa % 256
pointer = pointer + 8
end
local function writeBool(value)
data[pointer] = value and 1 or 0
pointer = pointer + 1
end
local function writeString(value)
local length = string.len(value)
writeUInt(2, length)
for index = 1, length do
data[pointer + index - 1] = string.byte(value, index)
end
pointer = pointer + length
end
local function writeColor3(value)
data[pointer] = math.floor(value.r * 255)
data[pointer + 1] = math.floor(value.g * 255)
data[pointer + 2] = math.floor(value.b * 255)
pointer = pointer + 3
end
local function asString()
local fragments = {}
for index = 1, #data, 4096 do
table.insert(fragments, string.char(
unpack(data, index, math.min(index + 4096 - 1, #data))
))
end
return table.concat(fragments, "")
end
local function getPointer()
return pointer
end
local function setPointer(index)
pointer = index
end
return {
writeUInt = writeUInt,
writeInt = writeInt,
writeFloat16 = writeFloat16,
writeFloat32 = writeFloat32,
writeFloat64 = writeFloat64,
writeBool = writeBool,
writeString = writeString,
writeColor3 = writeColor3,
asString = asString,
getPointer = getPointer,
setPointer = setPointer
}
end
local function createReadBuffer(data)
local pointer = 1
local function read(byteCount)
local rangeStart = pointer
pointer = pointer + byteCount
local rangeEnd = pointer - 1
return string.byte(data, rangeStart, rangeEnd)
end
local function readUInt(byteWidth)
local value = 0
for index = 0, byteWidth - 1 do
local byte = string.byte(data, pointer + index)
value = value + bit32.lshift(byte, index * 8)
end
pointer = pointer + byteWidth
return value
end
local function readInt(byteWidth)
local value = readUInt(byteWidth)
local firstBit = powersOfTwo[byteWidth * 8 - 1]
if value >= firstBit then
-- Overflow
value = value - firstBit * 2
end
return value
end
local function readFloat16()
local b1, b2 = read(2)
local sign = bit32.rshift(b1, 7)
local exponent = bit32.rshift(b1, 2) % powersOfTwo[5]
local mantissa = bit32.lshift(b1 % powersOfTwo[2], 8) + b2
return decodeFloat(sign, mantissa, exponent, 10, 5)
end
local function readFloat32()
local b1, b2, b3, b4 = read(4)
local sign = bit32.rshift(b1, 7)
local exponent = bit32.lshift(b1, 1) % 256 + bit32.rshift(b2, 7)
local mantissa = bit32.lshift(b2 % powersOfTwo[7], 16) + bit32.lshift(b3, 8) + b4
return decodeFloat(sign, mantissa, exponent, 23, 8)
end
local function readFloat64()
error("readFloat64 disabled, as bit32 can't handle 64 bit numbers")
local b1, b2, b3, b4, b5, b6, b7, b8 = read(8)
local sign = bit32.rshift(b1, 7)
local exponent = bit32.lshift(b1 % powersOfTwo[7], 4) + bit32.rshift(b2, 4)
local mantissa = bit32.lshift(b2 % powersOfTwo[4], 48) + bit32.lshift(b3, 40) + bit32.lshift(b4, 32) + bit32.lshift(b5, 24) + bit32.lshift(b6, 16) + bit32.lshift(b7, 8) + b8
return decodeFloat(sign, mantissa, exponent, 52, 11)
end
local function readBool()
local value = string.byte(data, pointer)
pointer = pointer + 1
return value == 1
end
local function readString()
local length = readUInt(2)
local rangeStart = pointer
pointer = pointer + length
local rangeEnd = pointer - 1
return string.sub(data, rangeStart, rangeEnd)
end
local function readColor3()
local R, G, B = read(3)
return Color3.fromRGB(R, G, B)
end
local function getPointer()
return pointer
end
local function setPointer(index)
pointer = index
end
return {
read = read,
readUInt = readUInt,
readInt = readInt,
readFloat16 = readFloat16,
readFloat32 = readFloat32,
readFloat64 = readFloat64,
readBool = readBool,
readString = readString,
readColor3 = readColor3,
getPointer = getPointer,
setPointer = setPointer
}
end
return {
createWriteBuffer = createWriteBuffer,
createReadBuffer = createReadBuffer
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment