Last active
August 29, 2020 14:21
-
-
Save Reselim/71ede2c81694b9b16d99f73ec918704b to your computer and use it in GitHub Desktop.
Relatively fast buffer module
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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