Created
January 15, 2020 01:59
-
-
Save howmanysmall/e87f8f8c40ef71c9418f6715aafdf001 to your computer and use it in GitHub Desktop.
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
-- This is the original module, 7% slower with type checks on both, 14% without type checks. | |
--[[ | |
Differences from the original: | |
Using metatables instead of a function returning a table. | |
Added Vector3, Color3, Vector2, and UDim2 support. | |
Deprecated BrickColors. | |
Changed the creation method from BitBuffer.Create to BitBuffer.new. | |
OPTIMIZED! | |
Added a ::Destroy method. | |
THE API: | |
Constructor: BitBuffer.new() | |
Read/Write pairs for reading data from or writing data to the BitBuffer: | |
BitBuffer::WriteUnsigned(BitWidth: number, Value: number): void | |
BitBuffer::ReadUnsigned(BitWidth: number): number | |
Read / Write an unsigned value with a given number of bits. The | |
value must be a positive integer. For instance, if BitWidth is | |
4, then there will be 4 magnitude bits, for a value in the | |
range [0, 2 ^ 4 - 1] = [0, 15] | |
BitBuffer::WriteSigned(BitWidth: number, Value: number): void | |
BitBuffer::ReadSigned(BitWidth: number): number | |
Read / Write a a signed value with a given number of bits. For | |
instance, if BitWidth is 4 then there will be 1 sign bit and | |
3 magnitude bits, a value in the range [-2 ^ 3 + 1, 2 ^ 3 - 1] = [-7, 7] | |
BitBuffer:WriteFloat(MantissaBitWidth: number, ExponentBitWidth: number, Value: number): void | |
BitBuffer:ReadFloat(MantissaBitWidth, ExponentBitWidth): number | |
Read / Write a floating point number with a given mantissa and | |
exponent size in bits. | |
BitBuffer::WriteFloat8(Float: number): void | |
BitBuffer::ReadFloat8(): number | |
BitBuffer::WriteFloat16(Float: number): void | |
BitBuffer::ReadFloat16(): number | |
BitBuffer::WriteFloat32(Float: number): void | |
BitBuffer::ReadFloat32(): number | |
BitBuffer::WriteFloat64(Float: number): void | |
BitBuffer::ReadFloat64(): number | |
Read and write the common types of floating point number that | |
are used in code. If you want to 100% accurately save an | |
arbitrary Lua number, then you should use the Float64 format. If | |
your number is known to be smaller, or you want to save space | |
and don't need super high precision, then a Float32 will often | |
suffice. For instance, the Transparency of an object will do | |
just fine as a Float32. | |
BitBuffer::WriteBool(Boolean: boolean): void | |
BitBuffer::ReadBool(): boolean | |
Read / Write a boolean (true / false) value. Takes one bit worth of space to store. | |
BitBuffer::WriteString(String: string): void | |
BitBuffer::ReadString(): string | |
Read / Write a variable length string. The string may contain embedded nulls. | |
Only 7 bits / character will be used if the string contains no non-printable characters (greater than 0x80). | |
****** PLEASE DON'T USE THIS. USE ::WRITECOLOR3 INSTEAD. ****** | |
BitBuffer::WriteBrickColor(Color: BrickColor): void | |
BitBuffer::ReadBrickColor(): BrickColor | |
Read / Write a Roblox BrickColor. Provided as an example of reading / writing a derived data type. | |
Please don't actually use this, just use ::WriteColor3 instead. | |
BitBuffer::WriteColor3(Color: Color3): void | |
BitBuffer::ReadColor3(): Color3 | |
Read / Write a Roblox Color3. Use this over the BrickColor methods, PLEASE. | |
BitBuffer::WriteRotation(CoordinateFrame: CFrame): void | |
BitBuffer::ReadRotation(): CFrame | |
Read / Write the rotation part of a given CFrame. Encodes the | |
rotation in question into 64bits, which is a good size to get | |
a pretty dense packing, but still while having errors well within | |
the threshold that Roblox uses for stuff like MakeJoints() | |
detecting adjacency. Will also perfectly reproduce rotations which | |
are orthagonally aligned, or inverse-power-of-two rotated on only | |
a single axix. For other rotations, the results may not be | |
perfectly stable through read-write cycles (if you read/write an | |
arbitrary rotation thousands of times there may be detectable | |
"drift") | |
BitBuffer::WriteVector3(Vector: Vector3): void | |
BitBuffer::ReadVector3(): Vector3 | |
BitBuffer::WriteVector3Float32(Vector: Vector3): void | |
BitBuffer::ReadVector3Float32(): Vector3 | |
Read / write a Vector3. Encodes the vector using 32-bit precision. | |
For more precision, use BitBuffer::WriteVector3Float64 instead. | |
BitBuffer::WriteVector3Float64(Vector: Vector3): void | |
BitBuffer::ReadVector3Float64(): Vector3 | |
Read / write a Vector3. Encodes the vector using 64-bit precision. | |
For less precision, use BitBuffer::WriteVector3 instead. | |
BitBuffer::WriteVector2(Vector: Vector2): void | |
BitBuffer::ReadVector2(): Vector2 | |
BitBuffer::WriteVector2Float32(Vector: Vector2): void | |
BitBuffer::ReadVector2Float32(): Vector2 | |
Read / write a Vector2. Encodes the vector using 32-bit precision. | |
For more precision, use BitBuffer::WriteVector2Float64 instead. | |
BitBuffer::WriteVector2Float64(Vector: Vector2): void | |
BitBuffer::ReadVector2Float64(): Vector2 | |
Read / write a Vector2. Encodes the vector using 64-bit precision. | |
For less precision, use BitBuffer::WriteVector2Float32 instead. | |
BitBuffer::WriteCFrame(CoordinateFrame: CFrame): void | |
BitBuffer::ReadCFrame(): CFrame | |
Read / write the whole CFrame. This will call both ::WriteVector3Float64 and ::WriteRotation | |
to save the entire CFrame, and encodes it using 64-bit precision. | |
BitBuffer::WriteUDim2(Value: UDim2): void | |
BitBuffer::ReadUDim2(): UDim2 | |
Read / write a UDim2. Encodes the value using 32-bit precision. | |
From/To pairs for dumping out the BitBuffer to another format: | |
BitBuffer::ToString(): string | |
BitBuffer::FromString(String: string): void | |
Will replace / dump out the contents of the buffer to / from | |
a binary chunk encoded as a Lua string. This string is NOT | |
suitable for storage in the Roblox DataStores, as they do | |
not handle non-printable characters well. | |
BitBuffer::ToBase64(): string | |
BitBuffer::FromBase64(String: string): void | |
Will replace / dump out the contents of the buffer to / from | |
a set of Base64 encoded data, as a Lua string. This string | |
only consists of Base64 printable characters, so it is | |
ideal for storage in Roblox DataStores. | |
BitBuffer::ToBase128(): string | |
BitBuffer::FromBase128(String: string): void | |
Defaultio added this function. 128 characters can all be written | |
to DataStores, so this function packs more tightly than saving | |
in only 64 bit strings. Full disclosure: I have no idea what I'm | |
doing but I think this is useful. | |
Buffer / Position Manipulation | |
BitBuffer::ResetPointer(): void | |
Will Reset the point in the buffer that is being read / written | |
to back to the start of the buffer. | |
BitBuffer::Reset(): void | |
Will reset the buffer to a clean state, with no contents. | |
Example Usage: | |
local function SaveToBuffer(buffer, userData) | |
buffer:WriteString(userData.HeroName) | |
buffer:WriteUnsigned(14, userData.Score) --> 14 bits -> [0, 2^14-1] -> [0, 16383] | |
buffer:WriteBool(userData.HasDoneSomething) | |
buffer:WriteUnsigned(10, #userData.ItemList) --> [0, 1023] | |
for _, itemInfo in pairs(userData.ItemList) do | |
buffer:WriteString(itemInfo.Identifier) | |
buffer:WriteUnsigned(10, itemInfo.Count) --> [0, 1023] | |
end | |
end | |
local function LoadFromBuffer(buffer, userData) | |
userData.HeroName = buffer:ReadString() | |
userData.Score = buffer:ReadUnsigned(14) | |
userData.HasDoneSomething = buffer:ReadBool() | |
local itemCount = buffer:ReadUnsigned(10) | |
for i = 1, itemCount do | |
local itemInfo = {} | |
itemInfo.Identifier = buffer:ReadString() | |
itemInfo.Count = buffer:ReadUnsigned(10) | |
table.insert(userData.ItemList, itemInfo) | |
end | |
end | |
--... | |
local buff = BitBuffer.new() | |
SaveToBuffer(buff, someUserData) | |
myDataStore:SetAsync(somePlayer.userId, buff:ToBase64()) | |
--... | |
local data = myDataStore:GetAsync(somePlayer.userId) | |
local buff = BitBuffer.new() | |
buff:FromBase64(data) | |
LoadFromBuffer(buff, someUserData) | |
--]] | |
-- This is quite possibly the fastest BitBuffer module. | |
local BitBuffer = { | |
ClassName = "BitBuffer"; | |
__tostring = function() | |
return "BitBuffer" | |
end; | |
} | |
BitBuffer.__index = BitBuffer | |
local CHAR_0X10 = string.char(0x10) | |
local LOG_10_OF_2 = math.log10(2) | |
local DEPRECATED_WARNING = true | |
local DIGITS = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ" | |
local function ToBase(Number, Base) | |
Number = Number - Number % 1 | |
if not Base or Base == 10 then | |
return tostring(Number) | |
end | |
local Array = {} | |
local Sign = "" | |
if Number < 0 then | |
Sign = "-" | |
Number = 0 - Number | |
end | |
repeat | |
local Index = (Number % Base) + 1 | |
Number = Number / Base | |
Number = Number - Number % 1 | |
table.insert(Array, 1, string.sub(DIGITS, Index, Index)) | |
until Number == 0 | |
return Sign .. table.concat(Array) | |
end | |
local function DetermineType(Value) | |
local ActualType = typeof(Value) | |
if ActualType == "number" then | |
if Value % 1 == 0 then | |
return Value < 0 and "negative integer" or "positive integer" | |
else | |
return Value < 0 and "negative number" or "positive number" | |
end | |
elseif ActualType == "table" then | |
local Key = next(Value) | |
if DetermineType(Key) == "positive integer" then | |
return "array" | |
else | |
return "dictionary" | |
end | |
else | |
return ActualType | |
end | |
end | |
local NumberToBase64, Base64ToNumber = {}, {} | |
do | |
local CHARACTERS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" | |
for Index = 1, 64 do | |
local Character = string.sub(CHARACTERS, Index, Index) | |
NumberToBase64[Index - 1] = Character | |
Base64ToNumber[Character] = Index - 1 | |
end | |
end | |
-- Credit to Defaultio. | |
local NumberToBase128, Base128ToNumber = {}, {} | |
do | |
local CHARACTERS = "" | |
for Index = 0, 127 do | |
CHARACTERS = CHARACTERS .. string.char(Index) | |
end | |
for Index = 1, 128 do | |
local Character = string.sub(CHARACTERS, Index, Index) | |
NumberToBase128[Index - 1] = Character | |
Base128ToNumber[Character] = Index - 1 | |
end | |
end | |
local PowerOfTwo = setmetatable({}, { | |
__index = function(self, Index) | |
local Value = 2 ^ Index | |
self[Index] = Value | |
return Value | |
end; | |
}) | |
for Index = 0, 128 do | |
local _ = PowerOfTwo[Index] | |
end | |
local BrickColorToNumber, NumberToBrickColor = {}, {} | |
do | |
for Index = 0, 63 do | |
local Color = BrickColor.palette(Index) | |
BrickColorToNumber[Color.Number] = Index | |
NumberToBrickColor[Index] = Color | |
end | |
end | |
--[[** | |
Creates a new BitBuffer. | |
@returns [BitBuffer] The new BitBuffer. | |
**--]] | |
function BitBuffer.new() | |
return setmetatable({ | |
BitPointer = 0; | |
mBitBuffer = {}; | |
}, BitBuffer) | |
end | |
--[[** | |
Resets the BitBuffer's BitPointer. | |
@returns [void] | |
**--]] | |
function BitBuffer:ResetPointer() | |
self.BitPointer = 0 | |
end | |
--[[** | |
Resets the BitBuffer's BitPointer and buffer table. | |
@returns [void] | |
**--]] | |
function BitBuffer:Reset() | |
self.mBitBuffer, self.BitPointer = {}, 0 | |
end | |
--[[** | |
Reads the given string and writes to the BitBuffer accordingly. Not really useful. | |
@param [t:string] String The string. | |
@returns [void] | |
**--]] | |
function BitBuffer:FromString(String) | |
if type(String) ~= "string" then | |
error(string.format("bad argument #1 in BitBuffer::FromString (string expected, instead got %s)", typeof(String)), 2) | |
end | |
self.mBitBuffer, self.BitPointer = {}, 0 | |
local BitPointerValue = 0 | |
local mBitBuffer = self.mBitBuffer | |
for Index = 1, #String do | |
local ByteCharacter = string.byte(String, Index, Index) | |
for _ = 1, 8 do | |
BitPointerValue = BitPointerValue + 1 | |
self.BitPointer = BitPointerValue | |
mBitBuffer[BitPointerValue] = ByteCharacter % 2 | |
ByteCharacter = ByteCharacter / 2 | |
ByteCharacter = ByteCharacter - ByteCharacter % 1 | |
end | |
end | |
self.BitPointer = 0 | |
end | |
--[[** | |
Writes the BitBuffer to a string. | |
@returns [t:string] The BitBuffer string. | |
**--]] | |
function BitBuffer:ToString() | |
local String = "" | |
local Accumulator = 0 | |
local Power = 0 | |
local mBitBuffer = self.mBitBuffer | |
for Index = 1, math.ceil(#mBitBuffer / 8) * 8 do | |
Accumulator = Accumulator + PowerOfTwo[Power] * (mBitBuffer[Index] or 0) | |
Power = Power + 1 | |
if Power >= 8 then | |
String = String .. string.char(Accumulator) | |
Accumulator = 0 | |
Power = 0 | |
end | |
end | |
return String | |
end | |
--[[** | |
Reads the given Base64 string and writes to the BitBuffer accordingly. | |
@param [t:string] String The Base64 string. | |
@returns [void] | |
**--]] | |
function BitBuffer:FromBase64(String) | |
if type(String) ~= "string" then | |
error(string.format("bad argument #1 in BitBuffer::FromBase64 (string expected, instead got %s)", typeof(String)), 2) | |
end | |
self.mBitBuffer, self.BitPointer = {}, 0 | |
local BitPointerValue = 0 | |
local mBitBuffer = self.mBitBuffer | |
for Index = 1, #String do | |
local Character = string.sub(String, Index, Index) | |
local ByteCharacter = Base64ToNumber[Character] | |
if not ByteCharacter then | |
error("Bad character: 0x" .. ToBase(string.byte(Character), 16), 2) | |
end | |
for _ = 1, 6 do | |
BitPointerValue = BitPointerValue + 1 | |
self.BitPointer = BitPointerValue | |
mBitBuffer[BitPointerValue] = ByteCharacter % 2 | |
ByteCharacter = ByteCharacter / 2 | |
ByteCharacter = ByteCharacter - ByteCharacter % 1 | |
end | |
if ByteCharacter ~= 0 then | |
error("Character value 0x" .. ToBase(Base64ToNumber[Character], 16) .. " too large", 2) | |
end | |
end | |
self.BitPointer = 0 | |
end | |
--[[** | |
Writes the BitBuffer to a Base64 string. | |
@returns [t:string] The BitBuffer encoded in Base64. | |
**--]] | |
function BitBuffer:ToBase64() | |
local Array = {} | |
local Length = 0 | |
local Accumulator = 0 | |
local Power = 0 | |
local mBitBuffer = self.mBitBuffer | |
for Index = 1, math.ceil(#mBitBuffer / 6) * 6 do | |
Accumulator = Accumulator + PowerOfTwo[Power] * (mBitBuffer[Index] or 0) | |
Power = Power + 1 | |
if Power >= 6 then | |
Length = Length + 1 | |
Array[Length] = NumberToBase64[Accumulator] | |
Accumulator = 0 | |
Power = 0 | |
end | |
end | |
return table.concat(Array) | |
end | |
--[[** | |
Reads the given Base128 string and writes to the BitBuffer accordingly. Not recommended. Credit to Defaultio for the original functions. | |
@param [t:string] String The Base128 string. | |
@returns [void] | |
**--]] | |
function BitBuffer:FromBase128(String) | |
if type(String) ~= "string" then | |
error(string.format("bad argument #1 in BitBuffer::FromBase128 (string expected, instead got %s)", typeof(String)), 2) | |
end | |
self.mBitBuffer, self.BitPointer = {}, 0 | |
local BitPointerValue = 0 | |
local mBitBuffer = self.mBitBuffer | |
for Index = 1, #String do | |
local Character = string.sub(String, Index, Index) | |
local ByteCharacter = Base128ToNumber[Character] | |
if not ByteCharacter then | |
error("Bad character: 0x" .. ToBase(string.byte(Character), 16), 2) | |
end | |
for _ = 1, 7 do | |
BitPointerValue = BitPointerValue + 1 | |
self.BitPointer = BitPointerValue | |
mBitBuffer[BitPointerValue] = ByteCharacter % 2 | |
ByteCharacter = ByteCharacter / 2 | |
ByteCharacter = ByteCharacter - ByteCharacter % 1 | |
end | |
if ByteCharacter ~= 0 then | |
error("Character value 0x" .. ToBase(Base128ToNumber[Character], 16) .. " too large", 2) | |
end | |
end | |
self.BitPointer = 0 | |
end | |
--[[** | |
Writes the BitBuffer to Base128. Not recommended. Credit to Defaultio for the original functions. | |
@returns [t:string] The BitBuffer encoded in Base128. | |
**--]] | |
function BitBuffer:ToBase128() | |
local Array = {} | |
local Length = 0 | |
local Accumulator = 0 | |
local Power = 0 | |
local mBitBuffer = self.mBitBuffer | |
for Index = 1, math.ceil(#mBitBuffer / 7) * 7 do | |
Accumulator = Accumulator + PowerOfTwo[Power] * (mBitBuffer[Index] or 0) | |
Power = Power + 1 | |
if Power >= 7 then | |
Length = Length + 1 | |
Array[Length] = NumberToBase128[Accumulator] | |
Accumulator = 0 | |
Power = 0 | |
end | |
end | |
return table.concat(Array) | |
end | |
--[[** | |
Dumps the BitBuffer data and prints it. | |
@returns [void] | |
**--]] | |
function BitBuffer:Dump() | |
local String = "" | |
local String2 = "" | |
local Accumulator = 0 | |
local Power = 0 | |
local mBitBuffer = self.mBitBuffer | |
for Index = 1, math.ceil(#mBitBuffer / 8) * 8 do | |
String2 = String2 .. (mBitBuffer[Index] or 0) | |
Accumulator = Accumulator + PowerOfTwo[Power] * (mBitBuffer[Index] or 0) | |
Power = Power + 1 | |
if Power >= 8 then | |
String2 = String2 .. " " | |
String = String .. "0x" .. ToBase(Accumulator, 16) .. " " | |
Accumulator = 0 | |
Power = 0 | |
end | |
end | |
print("[Dump] Bytes:", String) | |
print("[Dump] Bits:", String2) | |
end | |
function BitBuffer:_ReadBit() | |
self.BitPointer = self.BitPointer + 1 | |
return self.mBitBuffer[self.BitPointer] | |
end | |
--[[** | |
Writes an unsigned number to the BitBuffer. | |
@param [t:integer] Width The bit width of the value. | |
@param [t:integer] Value The unsigned integer. | |
@returns [void] | |
**--]] | |
function BitBuffer:WriteUnsigned(Width, Value) | |
if type(Width) ~= "number" then | |
error(string.format("bad argument #1 in BitBuffer::WriteUnsigned (number expected, instead got %s)", DetermineType(Width)), 2) | |
end | |
if not (Value or type(Value) == "number" or Value >= 0 or Value % 1 == 0) then | |
error(string.format("bad argument #2 in BitBuffer::WriteUnsigned (positive integer expected, instead got %s)", DetermineType(Value)), 2) | |
end | |
local mBitBuffer = self.mBitBuffer | |
-- Store LSB first | |
for _ = 1, Width do | |
self.BitPointer = self.BitPointer + 1 | |
mBitBuffer[self.BitPointer] = Value % 2 | |
Value = Value / 2 | |
Value = Value - Value % 1 | |
end | |
if Value ~= 0 then | |
error("Value " .. tostring(Value) .. " has width greater than " .. Width .. " bits", 2) | |
end | |
end | |
--[[** | |
Reads an unsigned integer from the BitBuffer. | |
@param [t:integer] Width The bit width of the value. | |
@returns [t:integer] The unsigned integer. | |
**--]] | |
function BitBuffer:ReadUnsigned(Width) | |
local Value = 0 | |
for Index = 1, Width do | |
Value = Value + self:_ReadBit() * PowerOfTwo[Index - 1] | |
end | |
return Value | |
end | |
--[[** | |
Writes a signed integer to the BitBuffer. | |
@param [t:integer] Width The bit width of the value. | |
@param [t:integer] Value The signed integer. | |
@returns [void] | |
**--]] | |
function BitBuffer:WriteSigned(Width, Value) | |
if not (Width and Value) then | |
error("bad arguments in BitBuffer::WriteSigned (missing values)", 2) | |
end | |
if Value % 1 ~= 0 then | |
error("Non-integer value to BitBuffer::WriteSigned", 2) | |
end | |
local mBitBuffer = self.mBitBuffer | |
-- Write sign | |
if Value < 0 then | |
self.BitPointer = self.BitPointer + 1 | |
mBitBuffer[self.BitPointer] = 1 | |
Value = 0 - Value | |
else | |
self.BitPointer = self.BitPointer + 1 | |
mBitBuffer[self.BitPointer] = 0 | |
end | |
self:WriteUnsigned(Width - 1, Value) | |
end | |
--[[** | |
Reads a signed integer from the BitBuffer. | |
@param [t:integer] Width The bit width of the value. | |
@returns [t:integer] The signed integer. | |
**--]] | |
function BitBuffer:ReadSigned(Width) | |
self.BitPointer = self.BitPointer + 1 | |
return ((-1) ^ self.mBitBuffer[self.BitPointer]) * self:ReadUnsigned(Width - 1) | |
end | |
--[[** | |
Writes a string to the BitBuffer. | |
@param [t:string] String The string you are writing to the BitBuffer. | |
@returns [void] | |
**--]] | |
function BitBuffer:WriteString(String) | |
if type(String) ~= "string" then | |
error(string.format("bad argument #1 in BitBuffer::WriteString (string expected, instead got %s)", typeof(String)), 2) | |
end | |
-- First check if it's a 7 or 8 bit width of string | |
local StringLength = #String | |
local BitWidth = 7 | |
for Index = 1, StringLength do | |
if string.byte(String, Index, Index) > 127 then | |
BitWidth = 8 | |
break | |
end | |
end | |
-- Write the bit width flag | |
self:WriteUnsigned(1, BitWidth == 7 and 0 or 1) -- 1 for wide chars | |
-- Now write out the string, terminated with "0x10, 0b0" | |
-- 0x10 is encoded as "0x10, 0b1" | |
for Index = 1, StringLength do | |
local ByteCharacter = string.byte(String, Index, Index) | |
if ByteCharacter == 0x10 then | |
self:WriteUnsigned(BitWidth, 0x10) | |
self:WriteUnsigned(1, 1) | |
else | |
self:WriteUnsigned(BitWidth, ByteCharacter) | |
end | |
end | |
-- Write terminator | |
self:WriteUnsigned(BitWidth, 0x10) | |
self:WriteUnsigned(1, 0) | |
end | |
--[[** | |
Reads the BitBuffer for a string. | |
@returns [t:string] The string written to the BitBuffer. | |
**--]] | |
function BitBuffer:ReadString() | |
-- Get bit width | |
local BitWidth = self:ReadUnsigned(1) == 1 and 8 or 7 | |
-- Loop | |
local String = "" | |
while true do | |
local Character = self:ReadUnsigned(BitWidth) | |
if Character == 0x10 then | |
if self:ReadUnsigned(1) == 1 then | |
String = String .. CHAR_0X10 | |
else | |
break | |
end | |
else | |
String = String .. string.char(Character) | |
end | |
end | |
return String | |
end | |
--[[** | |
Writes a boolean to the BitBuffer. | |
@param [t:boolean] Boolean The value you are writing to the BitBuffer. | |
@returns [void] | |
**--]] | |
function BitBuffer:WriteBool(Boolean) | |
if type(Boolean) ~= "boolean" then | |
error(string.format("bad argument #1 in BitBuffer::WriteBool (boolean expected, instead got %s)", typeof(Boolean)), 2) | |
end | |
self:WriteUnsigned(1, Boolean and 1 or 0) | |
end | |
BitBuffer.WriteBoolean = BitBuffer.WriteBool | |
--[[** | |
Reads the BitBuffer for a boolean. | |
@returns [t:boolean] The boolean. | |
**--]] | |
function BitBuffer:ReadBool() | |
return self:ReadUnsigned(1) == 1 | |
end | |
BitBuffer.ReadBoolean = BitBuffer.ReadBool | |
-- Read / Write a floating point number with |wfrac| fraction part | |
-- bits, |wexp| exponent part bits, and one sign bit. | |
--[[** | |
Writes a float to the BitBuffer. | |
@param [t:integer] Fraction The number of bits (probably). | |
@param [t:integer] WriteExponent The number of bits for the decimal (probably). | |
@param [t:number] Float The actual number you are writing. | |
@returns [void] | |
**--]] | |
function BitBuffer:WriteFloat(Fraction, WriteExponent, Float) | |
if not (Fraction and WriteExponent and Float) then | |
error("missing argument(s)", 2) | |
end | |
-- Sign | |
local Sign = 1 | |
if Float < 0 then | |
Float = 0 - Float | |
Sign = -1 | |
end | |
-- Decompose | |
local Mantissa, Exponent = math.frexp(Float) | |
if Exponent == 0 and Mantissa == 0 then | |
self:WriteUnsigned(Fraction + WriteExponent + 1, 0) | |
return | |
else | |
Mantissa = (Mantissa - 0.5) / 0.5 * PowerOfTwo[Fraction] | |
end | |
-- Write sign | |
self:WriteUnsigned(1, Sign == -1 and 1 or 0) | |
-- Write mantissa | |
Mantissa = Mantissa + 0.5 | |
Mantissa = Mantissa - Mantissa % 1 -- Not really correct, should round up/down based on the parity of |wexp| | |
self:WriteUnsigned(Fraction, Mantissa) | |
-- Write exponent | |
local MaxExp = PowerOfTwo[WriteExponent - 1] - 1 | |
self:WriteSigned(WriteExponent, Exponent > MaxExp and MaxExp or Exponent < -MaxExp and -MaxExp or Exponent) | |
end | |
--[[** | |
Reads a float from the BitBuffer. | |
@param [t:integer] Fraction The number of bits (probably). | |
@param [t:integer] WriteExponent The number of bits for the decimal (probably). | |
@returns [t:number] The float. | |
**--]] | |
function BitBuffer:ReadFloat(Fraction, WriteExponent) | |
if not (Fraction and WriteExponent) then | |
error("missing argument(s)", 2) | |
end | |
local Sign = self:ReadUnsigned(1) == 1 and -1 or 1 | |
local Mantissa = self:ReadUnsigned(Fraction) | |
local Exponent = self:ReadSigned(WriteExponent) | |
if Exponent == 0 and Mantissa == 0 then | |
return 0 | |
end | |
Mantissa = Mantissa / PowerOfTwo[Fraction] / 2 + 0.5 | |
return Sign * math.ldexp(Mantissa, Exponent) | |
end | |
--[[** | |
Writes a float8 (quarter precision) to the BitBuffer. | |
@param [t:number] The float8. | |
@returns [void] | |
**--]] | |
function BitBuffer:WriteFloat8(Float) | |
self:WriteFloat(3, 4, Float) | |
end | |
--[[** | |
Reads a float8 (quarter precision) from the BitBuffer. | |
@returns [t:number] The float8. | |
**--]] | |
function BitBuffer:ReadFloat8() | |
local Sign = self:ReadUnsigned(1) == 1 and -1 or 1 | |
local Mantissa = self:ReadUnsigned(3) | |
local Exponent = self:ReadSigned(4) | |
if Exponent == 0 and Mantissa == 0 then | |
return 0 | |
end | |
Mantissa = Mantissa / PowerOfTwo[3] / 2 + 0.5 | |
return Sign * math.ldexp(Mantissa, Exponent) | |
end | |
--[[** | |
Writes a float16 (half precision) to the BitBuffer. | |
@param [t:number] The float16. | |
@returns [void] | |
**--]] | |
function BitBuffer:WriteFloat16(Float) | |
self:WriteFloat(10, 5, Float) | |
end | |
--[[** | |
Reads a float16 (half precision) from the BitBuffer. | |
@returns [t:number] The float16. | |
**--]] | |
function BitBuffer:ReadFloat16() | |
local Sign = self:ReadUnsigned(1) == 1 and -1 or 1 | |
local Mantissa = self:ReadUnsigned(10) | |
local Exponent = self:ReadSigned(5) | |
if Exponent == 0 and Mantissa == 0 then | |
return 0 | |
end | |
Mantissa = Mantissa / PowerOfTwo[10] / 2 + 0.5 | |
return Sign * math.ldexp(Mantissa, Exponent) | |
end | |
--[[** | |
Writes a float32 (single precision) to the BitBuffer. | |
@param [t:number] The float32. | |
@returns [void] | |
**--]] | |
function BitBuffer:WriteFloat32(Float) | |
self:WriteFloat(23, 8, Float) | |
end | |
--[[** | |
Reads a float32 (single precision) from the BitBuffer. | |
@returns [t:number] The float32. | |
**--]] | |
function BitBuffer:ReadFloat32() | |
local Sign = self:ReadUnsigned(1) == 1 and -1 or 1 | |
local Mantissa = self:ReadUnsigned(23) | |
local Exponent = self:ReadSigned(8) | |
if Exponent == 0 and Mantissa == 0 then | |
return 0 | |
end | |
Mantissa = Mantissa / PowerOfTwo[23] / 2 + 0.5 | |
return Sign * math.ldexp(Mantissa, Exponent) | |
end | |
--[[** | |
Writes a float64 (double precision) to the BitBuffer. | |
@param [t:number] The float64. | |
@returns [void] | |
**--]] | |
function BitBuffer:WriteFloat64(Float) | |
self:WriteFloat(52, 11, Float) | |
end | |
--[[** | |
Reads a float64 (double precision) from the BitBuffer. | |
@returns [t:number] The float64. | |
**--]] | |
function BitBuffer:ReadFloat64() | |
local Sign = self:ReadUnsigned(1) == 1 and -1 or 1 | |
local Mantissa = self:ReadUnsigned(52) | |
local Exponent = self:ReadSigned(11) | |
if Exponent == 0 and Mantissa == 0 then | |
return 0 | |
end | |
Mantissa = Mantissa / PowerOfTwo[52] / 2 + 0.5 | |
return Sign * math.ldexp(Mantissa, Exponent) | |
end | |
-- Roblox DataTypes | |
if DEPRECATED_WARNING then | |
--[[** | |
[DEPRECATED] Writes a BrickColor to the BitBuffer. | |
@param [t:BrickColor] Color The BrickColor you are writing to the BitBuffer. | |
@returns [void] | |
**--]] | |
function BitBuffer:WriteBrickColor(Color) | |
if typeof(Color) ~= "BrickColor" then | |
error(string.format("bad argument #1 in BitBuffer::WriteBrickColor (BrickColor expected, instead got %s)", typeof(Color)), 2) | |
end | |
warn("::WriteBrickColor is deprecated. Using ::WriteColor3 is suggested instead.") | |
local BrickColorNumber = BrickColorToNumber[Color.Number] | |
if not BrickColorNumber then | |
warn("Attempt to serialize non-pallete BrickColor \"" .. tostring(Color) .. "\" (#" .. Color.Number .. "), using Light Stone Grey instead.") | |
BrickColorNumber = BrickColorToNumber[1032] | |
end | |
self:WriteUnsigned(6, BrickColorNumber) | |
end | |
else | |
--[[** | |
[DEPRECATED] Writes a BrickColor to the BitBuffer. | |
@param [t:BrickColor] Color The BrickColor you are writing to the BitBuffer. | |
@returns [void] | |
**--]] | |
function BitBuffer:WriteBrickColor(Color) | |
if typeof(Color) ~= "BrickColor" then | |
error(string.format("bad argument #1 in BitBuffer::WriteBrickColor (BrickColor expected, instead got %s)", typeof(Color)), 2) | |
end | |
local BrickColorNumber = BrickColorToNumber[Color.Number] | |
if not BrickColorNumber then | |
warn("Attempt to serialize non-pallete BrickColor \"" .. tostring(Color) .. "\" (#" .. Color.Number .. "), using Light Stone Grey instead.") | |
BrickColorNumber = BrickColorToNumber[1032] | |
end | |
self:WriteUnsigned(6, BrickColorNumber) | |
end | |
end | |
--[[** | |
[DEPRECATED] Reads a BrickColor from the BitBuffer. | |
@returns [t:BrickColor] The BrickColor read from the BitBuffer. | |
**--]] | |
function BitBuffer:ReadBrickColor() | |
return NumberToBrickColor[self:ReadUnsigned(6)] | |
end | |
--[[** | |
Writes the rotation part of a CFrame into the BitBuffer. | |
@param [t:CFrame] CoordinateFrame The CFrame you wish to write. | |
@returns [void] | |
**--]] | |
function BitBuffer:WriteRotation(CoordinateFrame) | |
if typeof(CoordinateFrame) ~= "CFrame" then | |
error(string.format("bad argument #1 in BitBuffer::WriteRotation (CFrame expected, instead got %s)", typeof(CoordinateFrame)), 2) | |
end | |
local LookVector = CoordinateFrame.LookVector | |
local Azumith = math.atan2(-LookVector.X, -LookVector.Z) | |
local Elevation = math.atan2(LookVector.Y, math.sqrt(LookVector.X * LookVector.X + LookVector.Z * LookVector.Z)) | |
local WithoutRoll = CFrame.new(CoordinateFrame.Position) * CFrame.Angles(0, Azumith, 0) * CFrame.Angles(Elevation, 0, 0) | |
local _, _, Roll = (WithoutRoll:Inverse() * CoordinateFrame):ToEulerAnglesXYZ() | |
-- Atan2 -> in the range [-pi, pi] | |
Azumith = ((Azumith / 3.1415926535898) * 2097151) + 0.5 | |
Azumith = Azumith - Azumith % 1 | |
Roll = ((Roll / 3.1415926535898) * 1048575) + 0.5 | |
Roll = Roll - Roll % 1 | |
Elevation = ((Elevation / 1.5707963267949) * 1048575) + 0.5 | |
Elevation = Elevation - Elevation % 1 | |
self:WriteSigned(22, Azumith) | |
self:WriteSigned(21, Roll) | |
self:WriteSigned(21, Elevation) | |
end | |
--[[** | |
Reads the rotation part of a CFrame saved in the BitBuffer. | |
@returns [t:CFrame] The rotation read from the BitBuffer. | |
**--]] | |
function BitBuffer:ReadRotation() | |
local Azumith = self:ReadSigned(22) | |
local Roll = self:ReadSigned(21) | |
local Elevation = self:ReadSigned(21) | |
Azumith = 3.1415926535898 * (Azumith / 2097151) | |
Roll = 3.1415926535898 * (Roll / 1048575) | |
Elevation = 3.1415926535898 * (Elevation / 1048575) | |
local Rotation = CFrame.Angles(0, Azumith, 0) | |
Rotation = Rotation * CFrame.Angles(Elevation, 0, 0) | |
Rotation = Rotation * CFrame.Angles(0, 0, Roll) | |
return Rotation | |
end | |
--[[** | |
Writes a Color3 to the BitBuffer. | |
@param [t:Color3] Color The color you want to write into the BitBuffer. | |
@returns [void] | |
**--]] | |
function BitBuffer:WriteColor3(Color) | |
if typeof(Color) ~= "Color3" then | |
error(string.format("bad argument #1 in BitBuffer::WriteColor3 (Color3 expected, instead got %s)", typeof(Color)), 2) | |
end | |
local R, G, B = Color.R * 255, Color.G * 255, Color.B * 255 | |
self:WriteUnsigned(8, R - R % 1) | |
self:WriteUnsigned(8, G - G % 1) | |
self:WriteUnsigned(8, B - B % 1) | |
end | |
--[[** | |
Reads a Color3 from the BitBuffer. | |
@returns [t:Color3] The color read from the BitBuffer. | |
**--]] | |
function BitBuffer:ReadColor3() | |
return Color3.fromRGB(self:ReadUnsigned(8), self:ReadUnsigned(8), self:ReadUnsigned(8)) | |
end | |
--[[** | |
Writes a Vector3 to the BitBuffer. Writes with Float32 precision. | |
@param [t:Vector3] Vector The vector you want to write into the BitBuffer. | |
@returns [void] | |
**--]] | |
function BitBuffer:WriteVector3(Vector) | |
if typeof(Vector) ~= "Vector3" then | |
error(string.format("bad argument #1 in BitBuffer::WriteVector3 (Vector3 expected, instead got %s)", typeof(Vector)), 2) | |
end | |
self:WriteFloat32(Vector.X) | |
self:WriteFloat32(Vector.Y) | |
self:WriteFloat32(Vector.Z) | |
end | |
--[[** | |
Reads a Vector3 from the BitBuffer. Uses Float32 precision. | |
@returns [t:Vector3] The vector read from the BitBuffer. | |
**--]] | |
function BitBuffer:ReadVector3() | |
return Vector3.new(self:ReadFloat32(), self:ReadFloat32(), self:ReadFloat32()) | |
end | |
--[[** | |
Writes a full CFrame (position and rotation) to the BitBuffer. Uses Float64 precision. | |
@param [t:CFrame] CoordinateFrame The CFrame you are writing to the BitBuffer. | |
@returns [void] | |
**--]] | |
function BitBuffer:WriteCFrame(CoordinateFrame) | |
if typeof(CoordinateFrame) ~= "CFrame" then | |
error(string.format("bad argument #1 in BitBuffer::WriteCFrame (CFrame expected, instead got %s)", typeof(CoordinateFrame)), 2) | |
end | |
self:WriteVector3Float64(CoordinateFrame.Position) | |
self:WriteRotation(CoordinateFrame) | |
end | |
--[[** | |
Reads a full CFrame (position and rotation) from the BitBuffer. Uses Float64 precision. | |
@returns [t:CFrame] The CFrame you are reading from the BitBuffer. | |
**--]] | |
function BitBuffer:ReadCFrame() | |
local Position = CFrame.new(self:ReadVector3Float64()) | |
local Azumith = self:ReadSigned(22) | |
local Roll = self:ReadSigned(21) | |
local Elevation = self:ReadSigned(21) | |
Azumith = 3.1415926535898 * (Azumith / 2097151) | |
Roll = 3.1415926535898 * (Roll / 1048575) | |
Elevation = 3.1415926535898 * (Elevation / 1048575) | |
local Rotation = CFrame.Angles(0, Azumith, 0) | |
Rotation = Rotation * CFrame.Angles(Elevation, 0, 0) | |
Rotation = Rotation * CFrame.Angles(0, 0, Roll) | |
return Position * Rotation | |
end | |
--[[** | |
Writes a Vector2 to the BitBuffer. Writes with Float32 precision. | |
@param [t:Vector2] Vector The vector you want to write into the BitBuffer. | |
@returns [void] | |
**--]] | |
function BitBuffer:WriteVector2(Vector) | |
if typeof(Vector) ~= "Vector2" then | |
error(string.format("bad argument #1 in BitBuffer::WriteVector2 (Vector2 expected, instead got %s)", typeof(Vector)), 2) | |
end | |
self:WriteFloat32(Vector.X) | |
self:WriteFloat32(Vector.Y) | |
end | |
--[[** | |
Reads a Vector2 from the BitBuffer. Uses Float32 precision. | |
@returns [t:Vector2] The vector read from the BitBuffer. | |
**--]] | |
function BitBuffer:ReadVector2() | |
return Vector2.new(self:ReadFloat32(), self:ReadFloat32()) | |
end | |
--[[** | |
Writes a UDim2 to the BitBuffer. Uses Float32 precision for the scale. | |
@param [t:UDim2] Value The UDim2 you are writing to the BitBuffer. | |
@returns [void] | |
**--]] | |
function BitBuffer:WriteUDim2(Value) | |
if typeof(Value) ~= "UDim2" then | |
error(string.format("bad argument #1 in BitBuffer::WriteUDim2 (UDim2 expected, instead got %s)", typeof(Value)), 2) | |
end | |
self:WriteFloat32(Value.X.Scale) | |
self:WriteSigned(17, Value.X.Offset) | |
self:WriteFloat32(Value.Y.Scale) | |
self:WriteSigned(17, Value.Y.Offset) | |
end | |
--[[** | |
Reads a UDim2 from the BitBuffer. Uses Float32 precision for the scale. | |
@returns [t:UDim2] The UDim2 read from the BitBuffer. | |
**--]] | |
function BitBuffer:ReadUDim2() | |
return UDim2.new(self:ReadFloat32(), self:ReadSigned(17), self:ReadFloat32(), self:ReadSigned(17)) | |
end | |
--[[** | |
Writes a Vector3 to the BitBuffer. Writes with Float64 precision. | |
@param [t:Vector3] Vector The vector you want to write into the BitBuffer. | |
@returns [void] | |
**--]] | |
function BitBuffer:WriteVector3Float64(Vector) | |
if typeof(Vector) ~= "Vector3" then | |
error(string.format("bad argument #1 in BitBuffer::WriteVector3Float64 (Vector3 expected, instead got %s)", typeof(Vector)), 2) | |
end | |
self:WriteFloat64(Vector.X) | |
self:WriteFloat64(Vector.Y) | |
self:WriteFloat64(Vector.Z) | |
end | |
--[[** | |
Reads a Vector3 from the BitBuffer. Reads with Float64 precision. | |
@returns [t:Vector3] The vector read from the BitBuffer. | |
**--]] | |
function BitBuffer:ReadVector3Float64() | |
return Vector3.new(self:ReadFloat64(), self:ReadFloat64(), self:ReadFloat64()) | |
end | |
--[[** | |
Writes a Vector2 to the BitBuffer. Writes with Float64 precision. | |
@param [t:Vector2] Vector The vector you want to write into the BitBuffer. | |
@returns [void] | |
**--]] | |
function BitBuffer:WriteVector2Float64(Vector) | |
if typeof(Vector) ~= "Vector2" then | |
error(string.format("bad argument #1 in BitBuffer::WriteVector2Float64 (Vector2 expected, instead got %s)", typeof(Vector)), 2) | |
end | |
self:WriteFloat64(Vector.X) | |
self:WriteFloat64(Vector.Y) | |
end | |
--[[** | |
Reads a Vector2 from the BitBuffer. Reads with Float64 precision. | |
@returns [t:Vector2] The vector read from the BitBuffer. | |
**--]] | |
function BitBuffer:ReadVector2Float64() | |
return Vector2.new(self:ReadFloat64(), self:ReadFloat64()) | |
end | |
BitBuffer.WriteVector3Float32 = BitBuffer.WriteVector3 | |
BitBuffer.ReadVector3Float32 = BitBuffer.ReadVector3 | |
BitBuffer.WriteVector2Float32 = BitBuffer.WriteVector2 | |
BitBuffer.ReadVector2Float32 = BitBuffer.ReadVector2 | |
--[[** | |
Destroys the BitBuffer metatable. | |
@returns [void] | |
**--]] | |
function BitBuffer:Destroy() | |
self.mBitBuffer = nil | |
setmetatable(self, nil) | |
end | |
--[[** | |
Calculates the amount of bits needed for a given number. | |
@param [t:number] Number The number you want to use. | |
@returns [t:number] The amount of bits needed. | |
**--]] | |
function BitBuffer.BitsNeeded(Number) | |
if type(Number) ~= "number" then | |
error(string.format("bad argument #1 in BitBuffer.BitsNeeded (number expected, instead got %s)", typeof(Number)), 2) | |
end | |
local Bits = math.log10(Number + 1) / LOG_10_OF_2 | |
return Bits + (1 - Bits % 1) -- Equivalent to ceil | |
end | |
-- Lower camel case support! | |
BitBuffer.fromString = BitBuffer.FromString | |
BitBuffer.toBase64 = BitBuffer.ToBase64 | |
BitBuffer.toBase128 = BitBuffer.ToBase128 | |
BitBuffer.readVector3Float64 = BitBuffer.ReadVector3Float64 | |
BitBuffer.readUnsigned = BitBuffer.ReadUnsigned | |
BitBuffer.writeBoolean = BitBuffer.WriteBoolean | |
BitBuffer.bitsNeeded = BitBuffer.BitsNeeded | |
BitBuffer.readSigned = BitBuffer.ReadSigned | |
BitBuffer.readFloat8 = BitBuffer.ReadFloat8 | |
BitBuffer.readFloat16 = BitBuffer.ReadFloat16 | |
BitBuffer.readString = BitBuffer.ReadString | |
BitBuffer.readVector2Float64 = BitBuffer.ReadVector2Float64 | |
BitBuffer.destroy = BitBuffer.Destroy | |
BitBuffer.readColor3 = BitBuffer.ReadColor3 | |
BitBuffer.writeVector2Float64 = BitBuffer.WriteVector2Float64 | |
BitBuffer.readVector2 = BitBuffer.ReadVector2 | |
BitBuffer.readBool = BitBuffer.ReadBool | |
BitBuffer.readBrickColor = BitBuffer.ReadBrickColor | |
BitBuffer.dump = BitBuffer.Dump | |
BitBuffer.readVector3Float32 = BitBuffer.ReadVector3Float32 | |
BitBuffer.writeVector3Float32 = BitBuffer.WriteVector3Float32 | |
BitBuffer.writeVector3Float64 = BitBuffer.WriteVector3Float64 | |
BitBuffer.readRotation = BitBuffer.ReadRotation | |
BitBuffer.writeVector2 = BitBuffer.WriteVector2 | |
BitBuffer.readCFrame = BitBuffer.ReadCFrame | |
BitBuffer.writeFloat16 = BitBuffer.WriteFloat16 | |
BitBuffer.writeRotation = BitBuffer.WriteRotation | |
BitBuffer.writeVector2Float32 = BitBuffer.WriteVector2Float32 | |
BitBuffer.readUDim2 = BitBuffer.ReadUDim2 | |
BitBuffer.writeCFrame = BitBuffer.WriteCFrame | |
BitBuffer.readVector3 = BitBuffer.ReadVector3 | |
BitBuffer.readFloat = BitBuffer.ReadFloat | |
BitBuffer.writeUnsigned = BitBuffer.WriteUnsigned | |
BitBuffer.writeVector3 = BitBuffer.WriteVector3 | |
BitBuffer.readVector2Float32 = BitBuffer.ReadVector2Float32 | |
BitBuffer.writeColor3 = BitBuffer.WriteColor3 | |
BitBuffer.writeUDim2 = BitBuffer.WriteUDim2 | |
BitBuffer.writeBrickColor = BitBuffer.WriteBrickColor | |
BitBuffer.fromBase128 = BitBuffer.FromBase128 | |
BitBuffer.readBoolean = BitBuffer.ReadBoolean | |
BitBuffer.writeFloat64 = BitBuffer.WriteFloat64 | |
BitBuffer.writeBool = BitBuffer.WriteBool | |
BitBuffer.readFloat32 = BitBuffer.ReadFloat32 | |
BitBuffer.writeFloat32 = BitBuffer.WriteFloat32 | |
BitBuffer.writeFloat8 = BitBuffer.WriteFloat8 | |
BitBuffer.toString = BitBuffer.ToString | |
BitBuffer.writeSigned = BitBuffer.WriteSigned | |
BitBuffer.writeString = BitBuffer.WriteString | |
BitBuffer.writeFloat = BitBuffer.WriteFloat | |
BitBuffer.readFloat64 = BitBuffer.ReadFloat64 | |
BitBuffer.fromBase64 = BitBuffer.FromBase64 | |
BitBuffer.resetPointer = BitBuffer.ResetPointer | |
BitBuffer.reset = BitBuffer.Reset | |
return BitBuffer |
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 Utility = require(script.Utility) | |
local t = require(script.t) | |
local FastBitBuffer = {} | |
FastBitBuffer.__index = FastBitBuffer | |
local t_string = t.string | |
local t_boolean = t.boolean | |
local t_integer = t.integer | |
local t_number = t.number | |
local NonNegativeNumber = t.numberMin(0) | |
local PositiveInteger = t.intersection(t_integer, NonNegativeNumber) | |
local WriteUnsignedTuple = t.tuple(t_integer, PositiveInteger) | |
local WriteSignedTuple = t.tuple(t_integer, t_integer) | |
local WriteFloatTuple = t.tuple(t_integer, t_integer, t_number) | |
local CHAR_0X10 = string.char(0x10) | |
local LOG_10_OF_2 = math.log10(2) | |
local DEPRECATED_WARNING = true | |
local NumberToBase64 = {} | |
local Base64ToNumber = {} | |
do | |
local CHARACTERS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" | |
for Index = 1, 64 do | |
local Character = string.sub(CHARACTERS, Index, Index) | |
NumberToBase64[Index - 1] = Character | |
Base64ToNumber[Character] = Index - 1 | |
end | |
end | |
-- Credit to Defaultio. | |
local NumberToBase128 = {} | |
local Base128ToNumber = {} | |
do | |
local CHARACTERS = "" | |
for Index = 0, 127 do | |
CHARACTERS = CHARACTERS .. string.char(Index) | |
end | |
for Index = 1, 128 do | |
local Character = string.sub(CHARACTERS, Index, Index) | |
NumberToBase128[Index - 1] = Character | |
Base128ToNumber[Character] = Index - 1 | |
end | |
end | |
local PowerOfTwo = setmetatable({}, { | |
__index = function(self, Index) | |
local Value = 2 ^ Index | |
self[Index] = Value | |
return Value | |
end; | |
}) | |
for Index = 0, 128 do | |
local _ = PowerOfTwo[Index] | |
end | |
local BrickColorToNumber = {} | |
local NumberToBrickColor = {} | |
for Index = 0, 63 do | |
local Color = BrickColor.palette(Index) | |
BrickColorToNumber[Color.Number] = Index | |
NumberToBrickColor[Index] = Color | |
end | |
--[[** | |
Creates a new FastBitBuffer. | |
@returns [FastBitBuffer] The new FastBitBuffer. | |
**--]] | |
function FastBitBuffer.new() | |
return setmetatable({ | |
BitPointer = 0; | |
BitBuffer = {}; | |
}, FastBitBuffer) | |
end | |
--[[** | |
Resets the BitBuffer's BitPointer. | |
@returns [void] | |
**--]] | |
function FastBitBuffer:ResetPointer() | |
self.BitPointer = 0 | |
end | |
--[[** | |
Resets the BitBuffer's BitPointer and buffer table. | |
@returns [void] | |
**--]] | |
function FastBitBuffer:Reset() | |
self.BitBuffer, self.BitPointer = {}, 0 | |
end | |
--[[** | |
Reads the given string and writes to the BitBuffer accordingly. Not really useful. | |
@param [t:string] String The BitBuffer string you want to use. | |
@returns [void] | |
**--]] | |
function FastBitBuffer:FromString(String) | |
assert(t_string(String)) | |
self.BitBuffer, self.BitPointer = {}, 0 | |
local BitPointerValue = 0 | |
local BitBuffer = self.BitBuffer | |
for Index = 1, #String do | |
local ByteCharacter = string.byte(String, Index, Index) | |
for _ = 1, 8 do | |
BitPointerValue = BitPointerValue + 1 | |
BitBuffer[BitPointerValue] = ByteCharacter % 2 | |
ByteCharacter = ByteCharacter / 2 | |
ByteCharacter = ByteCharacter - ByteCharacter % 1 | |
end | |
end | |
end | |
--[[** | |
Writes the BitBuffer to a string. | |
@returns [t:string] The BitBuffer string. | |
**--]] | |
function FastBitBuffer:ToString() | |
local String = "" | |
local Accumulator = 0 | |
local Power = 0 | |
local BitBuffer = self.BitBuffer | |
for Index = 1, math.ceil(#BitBuffer / 8) * 8 do | |
Accumulator = Accumulator + PowerOfTwo[Power] * (BitBuffer[Index] or 0) | |
Power = Power + 1 | |
if Power >= 8 then | |
String = String .. string.char(Accumulator) | |
Accumulator = 0 | |
Power = 0 | |
end | |
end | |
return String | |
end | |
--[[** | |
Reads the given Base64 string and writes to the BitBuffer accordingly. | |
@param [t:string] String The Base64 string. | |
@returns [void] | |
**--]] | |
function FastBitBuffer:FromBase64(String) | |
assert(t_string(String)) | |
self.BitBuffer, self.BitPointer = {}, 0 | |
local BitPointerValue = 0 | |
local BitBuffer = self.BitBuffer | |
for Index = 1, #String do | |
local Character = string.sub(String, Index, Index) | |
local ByteCharacter = Base64ToNumber[Character] | |
if not ByteCharacter then | |
error("Bad character: 0x" .. Utility.ToBase(string.byte(Character), 16), 2) | |
end | |
for _ = 1, 6 do | |
BitPointerValue = BitPointerValue + 1 | |
self.BitPointer = BitPointerValue | |
BitBuffer[BitPointerValue] = ByteCharacter % 2 | |
ByteCharacter = ByteCharacter / 2 | |
ByteCharacter = ByteCharacter - ByteCharacter % 1 | |
end | |
if ByteCharacter ~= 0 then | |
error("Character value 0x" .. Utility.ToBase(Base64ToNumber[Character], 16) .. " too large", 2) | |
end | |
end | |
self.BitPointer = 0 | |
end | |
--[[** | |
Writes the BitBuffer to a Base64 string. | |
@returns [t:string] The BitBuffer encoded in Base64. | |
**--]] | |
function FastBitBuffer:ToBase64() | |
local Array = {} | |
local Length = 0 | |
local Accumulator = 0 | |
local Power = 0 | |
local BitBuffer = self.BitBuffer | |
for Index = 1, math.ceil(#BitBuffer / 6) * 6 do | |
Accumulator = Accumulator + PowerOfTwo[Power] * (BitBuffer[Index] or 0) | |
Power = Power + 1 | |
if Power >= 6 then | |
Length = Length + 1 | |
Array[Length] = NumberToBase64[Accumulator] | |
Accumulator = 0 | |
Power = 0 | |
end | |
end | |
return table.concat(Array) | |
end | |
--[[** | |
Reads the given Base128 string and writes to the BitBuffer accordingly. Not recommended. Credit to Defaultio for the original functions. | |
@param [t:string] String The Base128 string. | |
@returns [void] | |
**--]] | |
function FastBitBuffer:FromBase128(String) | |
assert(t_string(String)) | |
self.BitBuffer, self.BitPointer = {}, 0 | |
local BitPointerValue = 0 | |
local BitBuffer = self.BitBuffer | |
for Index = 1, #String do | |
local Character = string.sub(String, Index, Index) | |
local ByteCharacter = Base128ToNumber[Character] | |
if not ByteCharacter then | |
error("Bad character: 0x" .. Utility.ToBase(string.byte(Character), 16), 2) | |
end | |
for _ = 1, 7 do | |
BitPointerValue = BitPointerValue + 1 | |
BitBuffer[BitPointerValue] = ByteCharacter % 2 | |
ByteCharacter = ByteCharacter / 2 | |
ByteCharacter = ByteCharacter - ByteCharacter % 1 | |
end | |
if ByteCharacter ~= 0 then | |
error("Character value 0x" .. Utility.ToBase(Base128ToNumber[Character], 16) .. " too large", 2) | |
end | |
end | |
end | |
--[[** | |
Writes the BitBuffer to Base128. Credit to Defaultio for the original implementation. | |
@returns [t:string] The BitBuffer encoded in Base128. | |
**--]] | |
function FastBitBuffer:ToBase128() | |
local Array = {} | |
local Length = 0 | |
local Accumulator = 0 | |
local Power = 0 | |
local BitBuffer = self.BitBuffer | |
for Index = 1, math.ceil(#BitBuffer / 7) * 7 do | |
Accumulator = Accumulator + PowerOfTwo[Power] * (BitBuffer[Index] or 0) | |
Power = Power + 1 | |
if Power >= 7 then | |
Length = Length + 1 | |
Array[Length] = NumberToBase128[Accumulator] | |
Accumulator = 0 | |
Power = 0 | |
end | |
end | |
return table.concat(Array) | |
end | |
--[[** | |
Dumps the BitBuffer data and prints it. | |
@returns [void] | |
**--]] | |
function FastBitBuffer:Dump() | |
local String = "" | |
local String2 = "" | |
local Accumulator = 0 | |
local Power = 0 | |
local BitBuffer = self.BitBuffer | |
for Index = 1, math.ceil(#BitBuffer / 8) * 8 do | |
String2 = String2 .. (BitBuffer[Index] or 0) | |
Accumulator = Accumulator + PowerOfTwo[Power] * (BitBuffer[Index] or 0) | |
Power = Power + 1 | |
if Power >= 8 then | |
String2 = String2 .. " " | |
String = String .. "0x" .. Utility.ToBase(Accumulator, 16) .. " " | |
Accumulator = 0 | |
Power = 0 | |
end | |
end | |
print("[Dump] Bytes:", String) | |
print("[Dump] Bits:", String2) | |
end | |
function FastBitBuffer:_ReadBit() | |
self.BitPointer = self.BitPointer + 1 | |
return self.BitBuffer[self.BitPointer] | |
end | |
--[[** | |
Writes an unsigned integer to the BitBuffer. | |
@param [t:integer] Width The bit width of the value. | |
@param [t:intersection<t:integer, t:numberMin<0>>] Value The unsigned integer. | |
@returns [void] | |
**--]] | |
function FastBitBuffer:WriteUnsigned(Width, Value) | |
assert(WriteUnsignedTuple(Width, Value)) | |
local BitBuffer = self.BitBuffer | |
local BitPointer = self.BitPointer | |
-- Store LSB first | |
for _ = 1, Width do | |
BitPointer = BitPointer + 1 | |
BitBuffer[BitPointer] = Value % 2 | |
Value = Value / 2 | |
Value = Value - Value % 1 | |
end | |
self.BitPointer = BitPointer | |
if Value ~= 0 then | |
error("Value " .. tostring(Value) .. " has width greater than " .. Width .. " bits", 2) | |
end | |
end | |
--[[** | |
Reads an unsigned integer from the BitBuffer. | |
@param [t:integer] Width The bit width of the value. | |
@returns [t:intersection<t:integer, t:numberMin<0>>] The unsigned integer. | |
**--]] | |
function FastBitBuffer:ReadUnsigned(Width) | |
assert(t_integer(Width)) | |
local Value = 0 | |
local BitPointer = self.BitPointer | |
local BitBuffer = self.BitBuffer | |
for Index = 1, Width do | |
BitPointer = BitPointer + 1 | |
Value = Value + BitBuffer[BitPointer] * PowerOfTwo[Index - 1] | |
end | |
self.BitPointer = BitPointer | |
return Value | |
end | |
--[[** | |
Writes a signed integer to the BitBuffer. | |
@param [t:integer] Width The bit width of the value. | |
@param [t:integer] Value The signed integer. | |
@returns [void] | |
**--]] | |
function FastBitBuffer:WriteSigned(Width, Value) | |
assert(WriteSignedTuple(Width, Value)) | |
local BitBuffer = self.BitBuffer | |
local BitPointer = self.BitPointer + 1 | |
-- Write sign | |
if Value < 0 then | |
BitBuffer[BitPointer] = 1 | |
Value = 0 - Value | |
else | |
BitBuffer[BitPointer] = 0 | |
end | |
-- WriteUnsigned call | |
for _ = 1, Width - 1 do | |
BitPointer = BitPointer + 1 | |
BitBuffer[BitPointer] = Value % 2 | |
Value = Value / 2 | |
Value = Value - Value % 1 | |
end | |
self.BitPointer = BitPointer | |
if Value ~= 0 then | |
error("Value " .. tostring(Value) .. " has width greater than " .. Width .. " bits", 2) | |
end | |
end | |
--[[** | |
Reads a signed integer from the BitBuffer. | |
@param [t:integer] Width The bit width of the value. | |
@returns [t:integer] The signed integer. | |
**--]] | |
function FastBitBuffer:ReadSigned(Width) | |
assert(t_integer(Width)) | |
local BitBuffer = self.BitBuffer | |
local BitPointer = self.BitPointer + 1 | |
local SignedValue = (-1) ^ BitBuffer[BitPointer] | |
-- ReadUnsigned call | |
local Value = 0 | |
for Index = 1, Width - 1 do | |
BitPointer = BitPointer + 1 | |
Value = Value + BitBuffer[BitPointer] * PowerOfTwo[Index - 1] | |
end | |
self.BitPointer = BitPointer | |
return SignedValue * Value | |
end | |
--[[** | |
Writes a string to the BitBuffer. | |
@param [t:string] String The string you are writing to the BitBuffer. | |
@returns [void] | |
**--]] | |
function FastBitBuffer:WriteString(String) | |
assert(t_string(String)) | |
local BitBuffer = self.BitBuffer | |
local BitPointer = self.BitPointer | |
-- First check if it's a 7 or 8 bit width of string | |
local StringLength = #String | |
local BitWidth = 7 | |
for Index = 1, StringLength do | |
if string.byte(String, Index, Index) > 127 then | |
BitWidth = 8 | |
break | |
end | |
end | |
-- Write the bit width flag | |
-- WriteUnsigned(1, BitWidth == 7 and 0 or 1) | |
local Value = BitWidth == 7 and 0 or 1 -- 1 for wide chars | |
BitPointer = BitPointer + 1 | |
BitBuffer[BitPointer] = Value % 2 | |
Value = Value / 2 | |
Value = Value - Value % 1 | |
if Value ~= 0 then | |
error("Value " .. tostring(Value) .. " has width greater than 1 bit", 2) | |
end | |
-- Now write out the string, terminated with "0x10, 0b0" | |
-- 0x10 is encoded as "0x10, 0b1" | |
for Index = 1, StringLength do | |
local ByteCharacter = string.byte(String, Index, Index) | |
if ByteCharacter == 0x10 then | |
-- WriteUnsigned(BitWidth, 0x10) | |
Value = 0x10 | |
for _ = 1, BitWidth do | |
BitPointer = BitPointer + 1 | |
BitBuffer[BitPointer] = Value % 2 | |
Value = Value / 2 | |
Value = Value - Value % 1 | |
end | |
if Value ~= 0 then | |
error("Value " .. tostring(Value) .. " has width greater than " .. BitWidth .. " bits", 2) | |
end | |
-- WriteUnsigned(1, 1) | |
Value = 1 | |
BitPointer = BitPointer + 1 | |
BitBuffer[BitPointer] = Value % 2 | |
Value = Value / 2 | |
Value = Value - Value % 1 | |
if Value ~= 0 then | |
error("Value " .. tostring(Value) .. " has width greater than 1 bit", 2) | |
end | |
else | |
-- WriteUnsigned(BitWidth, ByteCharacter) | |
Value = ByteCharacter | |
for _ = 1, BitWidth do | |
BitPointer = BitPointer + 1 | |
BitBuffer[BitPointer] = Value % 2 | |
Value = Value / 2 | |
Value = Value - Value % 1 | |
end | |
if Value ~= 0 then | |
error("Value " .. tostring(Value) .. " has width greater than " .. BitWidth .. " bits", 2) | |
end | |
end | |
end | |
-- Write terminator | |
-- WriteUnsigned(BitWidth, 0x10) | |
Value = 0x10 | |
for _ = 1, BitWidth do | |
BitPointer = BitPointer + 1 | |
BitBuffer[BitPointer] = Value % 2 | |
Value = Value / 2 | |
Value = Value - Value % 1 | |
end | |
if Value ~= 0 then | |
error("Value " .. tostring(Value) .. " has width greater than " .. BitWidth .. " bits", 2) | |
end | |
-- WriteUnsigned(1, 0) | |
Value = 0 | |
BitPointer = BitPointer + 1 | |
BitBuffer[BitPointer] = Value % 2 | |
Value = Value / 2 | |
Value = Value - Value % 1 | |
if Value ~= 0 then | |
error("Value " .. tostring(Value) .. " has width greater than 1 bit", 2) | |
end | |
self.BitPointer = BitPointer | |
end | |
--[[** | |
Reads the BitBuffer for a string. | |
@returns [t:string] The string written to the BitBuffer. | |
**--]] | |
function FastBitBuffer:ReadString() | |
-- Get bit width | |
-- ReadUnsigned(1) | |
local Value = 0 | |
local BitPointer = self.BitPointer + 1 | |
local BitBuffer = self.BitBuffer | |
Value = Value + BitBuffer[BitPointer] * PowerOfTwo[0] | |
local BitWidth = Value == 1 and 8 or 7 | |
-- Loop | |
local String = "" | |
while true do | |
-- ReadUnsigned(BitWidth) | |
Value = 0 | |
for Index = 1, BitWidth do | |
BitPointer = BitPointer + 1 | |
Value = Value + BitBuffer[BitPointer] * PowerOfTwo[Index - 1] | |
end | |
self.BitPointer = BitPointer | |
local Character = Value | |
if Character == 0x10 then | |
-- ReadUnsigned(1) | |
Value = 0 | |
BitPointer = BitPointer + 1 | |
Value = Value + BitBuffer[BitPointer] * PowerOfTwo[0] | |
self.BitPointer = BitPointer | |
if Value == 1 then | |
String = String .. CHAR_0X10 | |
else | |
break | |
end | |
else | |
String = String .. string.char(Character) | |
end | |
end | |
return String | |
end | |
--[[** | |
Writes a boolean to the BitBuffer. | |
@param [t:boolean] Boolean The value you are writing to the BitBuffer. | |
@returns [void] | |
**--]] | |
function FastBitBuffer:WriteBool(Boolean) | |
assert(t_boolean(Boolean)) | |
local Value = Boolean and 1 or 0 | |
-- WriteUnsigned(1, Boolean and 1 or 0) | |
self.BitPointer = self.BitPointer + 1 | |
self.BitBuffer[self.BitPointer] = Value % 2 | |
Value = Value / 2 | |
Value = Value - Value % 1 | |
if Value ~= 0 then | |
error("Value " .. tostring(Value) .. " has width greater than 1 bit", 2) | |
end | |
end | |
FastBitBuffer.WriteBoolean = FastBitBuffer.WriteBool | |
--[[** | |
Reads the BitBuffer for a boolean. | |
@returns [t:boolean] The boolean. | |
**--]] | |
function FastBitBuffer:ReadBool() | |
-- ReadUnsigned(1) | |
local Value = 0 | |
local BitPointer = self.BitPointer + 1 | |
Value = Value + self.BitBuffer[BitPointer] * PowerOfTwo[0] | |
self.BitPointer = BitPointer | |
return Value == 1 | |
end | |
FastBitBuffer.ReadBoolean = FastBitBuffer.ReadBool | |
--[[** | |
Writes a float to the BitBuffer. | |
@param [t:integer] Fraction The number of bits (probably). | |
@param [t:integer] WriteExponent The number of bits for the decimal (probably). | |
@param [t:number] Float The actual number you are writing. | |
@returns [void] | |
**--]] | |
function FastBitBuffer:WriteFloat(Fraction, WriteExponent, Float) | |
assert(WriteFloatTuple(Fraction, WriteExponent, Float)) | |
local BitBuffer = self.BitBuffer | |
local BitPointer = self.BitPointer | |
local Value = 0 | |
-- Sign | |
local Sign = 1 | |
if Float < 0 then | |
Float = 0 - Float | |
Sign = -1 | |
end | |
-- Decompose | |
local Mantissa, Exponent = math.frexp(Float) | |
if Exponent == 0 and Mantissa == 0 then | |
-- WriteUnsigned(Fraction + WriteExponent + 1, 0) | |
for _ = 1, Fraction + WriteExponent + 1 do | |
BitPointer = BitPointer + 1 | |
BitBuffer[BitPointer] = Value % 2 | |
Value = Value / 2 | |
Value = Value - Value % 1 | |
end | |
self.BitPointer = BitPointer | |
if Value ~= 0 then | |
error("Value " .. tostring(Value) .. " has width greater than " .. Fraction + WriteExponent + 1 .. " bits", 2) | |
end | |
return | |
else | |
Mantissa = (Mantissa - 0.5) / 0.5 * PowerOfTwo[Fraction] | |
end | |
-- Write sign | |
-- WriteUnsigned(1, Sign == -1 and 1 or 0) | |
Value = Sign == -1 and 1 or 0 | |
BitPointer = BitPointer + 1 | |
BitBuffer[BitPointer] = Value % 2 | |
Value = Value / 2 | |
Value = Value - Value % 1 | |
self.BitPointer = BitPointer | |
if Value ~= 0 then | |
error("Value " .. tostring(Value) .. " has width greater than 1 bit", 2) | |
end | |
-- Write mantissa | |
Mantissa = Mantissa + 0.5 | |
Mantissa = Mantissa - Mantissa % 1 -- Not really correct, should round up/down based on the parity of |wexp| | |
Value = Mantissa | |
-- WriteUnsigned(Fraction, Mantissa) | |
for _ = 1, Fraction do | |
BitPointer = BitPointer + 1 | |
BitBuffer[BitPointer] = Value % 2 | |
Value = Value / 2 | |
Value = Value - Value % 1 | |
end | |
self.BitPointer = BitPointer | |
if Value ~= 0 then | |
error("Value " .. tostring(Value) .. " has width greater than " .. Fraction .. " bits", 2) | |
end | |
-- Write exponent | |
-- WriteSigned(WriteExponent, Exponent) | |
local MaxExp = PowerOfTwo[WriteExponent - 1] - 1 | |
Value = Exponent > MaxExp and MaxExp or Exponent < -MaxExp and -MaxExp or Exponent | |
if Value < 0 then | |
BitPointer = BitPointer + 1 | |
BitBuffer[BitPointer] = 1 | |
Value = 0 - Value | |
else | |
BitPointer = BitPointer + 1 | |
BitBuffer[BitPointer] = 0 | |
end | |
for _ = 1, WriteExponent - 1 do | |
BitPointer = BitPointer + 1 | |
BitBuffer[BitPointer] = Value % 2 | |
Value = Value / 2 | |
Value = Value - Value % 1 | |
end | |
self.BitPointer = BitPointer | |
if Value ~= 0 then | |
error("Value " .. tostring(Value) .. " has width greater than " .. WriteExponent - 1 .. " bits", 2) | |
end | |
end | |
--[[** | |
Reads a float from the BitBuffer. | |
@param [t:integer] Fraction The number of bits (probably). | |
@param [t:integer] WriteExponent The number of bits for the decimal (probably). | |
@returns [t:number] The float. | |
**--]] | |
function FastBitBuffer:ReadFloat(Fraction, WriteExponent) | |
assert(WriteSignedTuple(Fraction, WriteExponent)) | |
local Value = 0 | |
local BitPointer = self.BitPointer + 1 | |
local BitBuffer = self.BitBuffer | |
-- ReadUnsigned(1) | |
local Sign = (BitBuffer[BitPointer] * PowerOfTwo[0]) == 1 and -1 or 1 | |
-- ReadUnsigned(Fraction) | |
for Index = 1, Fraction do | |
BitPointer = BitPointer + 1 | |
Value = Value + BitBuffer[BitPointer] * PowerOfTwo[Index - 1] | |
end | |
local Mantissa = Value | |
-- ReadSigned(WriteExponent) | |
Value = 0 | |
BitPointer = BitPointer + 1 | |
local SignedValue = (-1) ^ BitBuffer[BitPointer] | |
for Index = 1, WriteExponent - 1 do | |
BitPointer = BitPointer + 1 | |
Value = Value + BitBuffer[BitPointer] * PowerOfTwo[Index - 1] | |
end | |
local Exponent = SignedValue * Value | |
if Exponent == 0 and Mantissa == 0 then | |
return 0 | |
else | |
Mantissa = Mantissa / PowerOfTwo[Fraction] / 2 + 0.5 | |
self.BitPointer = BitPointer | |
return Sign * math.ldexp(Mantissa, Exponent) | |
end | |
end | |
--[[** | |
Writes a float8 (quarter precision) to the BitBuffer. | |
@param [t:number] Float The float8. | |
@returns [void] | |
**--]] | |
function FastBitBuffer:WriteFloat8(Float) | |
assert(t_number(Float)) | |
local BitBuffer = self.BitBuffer | |
local BitPointer = self.BitPointer | |
local Value = 0 | |
-- Sign | |
local Sign = 1 | |
if Float < 0 then | |
Float = 0 - Float | |
Sign = -1 | |
end | |
-- Decompose | |
local Mantissa, Exponent = math.frexp(Float) | |
if Exponent == 0 and Mantissa == 0 then | |
-- WriteUnsigned(Fraction + WriteExponent + 1, 0) | |
for _ = 1, 8 do | |
BitPointer = BitPointer + 1 | |
BitBuffer[BitPointer] = Value % 2 | |
Value = Value / 2 | |
Value = Value - Value % 1 | |
end | |
self.BitPointer = BitPointer | |
if Value ~= 0 then | |
error("Value " .. tostring(Value) .. " has width greater than 8 bits", 2) | |
end | |
return | |
else | |
Mantissa = (Mantissa - 0.5) / 0.5 * PowerOfTwo[3] | |
end | |
-- Write sign | |
-- WriteUnsigned(1, Sign == -1 and 1 or 0) | |
Value = Sign == -1 and 1 or 0 | |
BitPointer = BitPointer + 1 | |
BitBuffer[BitPointer] = Value % 2 | |
Value = Value / 2 | |
Value = Value - Value % 1 | |
self.BitPointer = BitPointer | |
if Value ~= 0 then | |
error("Value " .. tostring(Value) .. " has width greater than 1 bit", 2) | |
end | |
-- Write mantissa | |
Mantissa = Mantissa + 0.5 | |
Mantissa = Mantissa - Mantissa % 1 -- Not really correct, should round up/down based on the parity of |wexp| | |
Value = Mantissa | |
-- WriteUnsigned(Fraction, Mantissa) | |
for _ = 1, 3 do | |
BitPointer = BitPointer + 1 | |
BitBuffer[BitPointer] = Value % 2 | |
Value = Value / 2 | |
Value = Value - Value % 1 | |
end | |
self.BitPointer = BitPointer | |
if Value ~= 0 then | |
error("Value " .. tostring(Value) .. " has width greater than 3 bits", 2) | |
end | |
-- Write exponent | |
-- WriteSigned(WriteExponent, Exponent) | |
local MaxExp = PowerOfTwo[3] - 1 | |
Value = Exponent > MaxExp and MaxExp or Exponent < -MaxExp and -MaxExp or Exponent | |
if Value < 0 then | |
BitPointer = BitPointer + 1 | |
BitBuffer[BitPointer] = 1 | |
Value = 0 - Value | |
else | |
BitPointer = BitPointer + 1 | |
BitBuffer[BitPointer] = 0 | |
end | |
for _ = 1, 3 do | |
BitPointer = BitPointer + 1 | |
BitBuffer[BitPointer] = Value % 2 | |
Value = Value / 2 | |
Value = Value - Value % 1 | |
end | |
self.BitPointer = BitPointer | |
if Value ~= 0 then | |
error("Value " .. tostring(Value) .. " has width greater than 3 bits", 2) | |
end | |
end | |
--[[** | |
Reads a float8 (quarter precision) from the BitBuffer. | |
@returns [t:number] The float8. | |
**--]] | |
function FastBitBuffer:ReadFloat8() | |
local Value = 0 | |
local BitPointer = self.BitPointer + 1 | |
local BitBuffer = self.BitBuffer | |
-- ReadUnsigned(1) | |
local Sign = (BitBuffer[BitPointer] * PowerOfTwo[0]) == 1 and -1 or 1 | |
-- ReadUnsigned(Fraction) | |
for Index = 1, 3 do | |
BitPointer = BitPointer + 1 | |
Value = Value + BitBuffer[BitPointer] * PowerOfTwo[Index - 1] | |
end | |
local Mantissa = Value | |
-- ReadSigned(WriteExponent) | |
Value = 0 | |
BitPointer = BitPointer + 1 | |
local SignedValue = (-1) ^ BitBuffer[BitPointer] | |
for Index = 1, 3 do | |
BitPointer = BitPointer + 1 | |
Value = Value + BitBuffer[BitPointer] * PowerOfTwo[Index - 1] | |
end | |
local Exponent = SignedValue * Value | |
if Exponent == 0 and Mantissa == 0 then | |
return 0 | |
else | |
Mantissa = Mantissa / PowerOfTwo[3] / 2 + 0.5 | |
self.BitPointer = BitPointer | |
return Sign * math.ldexp(Mantissa, Exponent) | |
end | |
end | |
--[[** | |
Writes a float16 (half precision) to the BitBuffer. | |
@param [t:number] Float The float16. | |
@returns [void] | |
**--]] | |
function FastBitBuffer:WriteFloat16(Float) | |
assert(t_number(Float)) | |
local BitBuffer = self.BitBuffer | |
local BitPointer = self.BitPointer | |
local Value = 0 | |
-- Sign | |
local Sign = 1 | |
if Float < 0 then | |
Float = 0 - Float | |
Sign = -1 | |
end | |
-- Decompose | |
local Mantissa, Exponent = math.frexp(Float) | |
if Exponent == 0 and Mantissa == 0 then | |
-- WriteUnsigned(Fraction + WriteExponent + 1, 0) | |
for _ = 1, 16 do | |
BitPointer = BitPointer + 1 | |
BitBuffer[BitPointer] = Value % 2 | |
Value = Value / 2 | |
Value = Value - Value % 1 | |
end | |
self.BitPointer = BitPointer | |
if Value ~= 0 then | |
error("Value " .. tostring(Value) .. " has width greater than 16 bits", 2) | |
end | |
return | |
else | |
Mantissa = (Mantissa - 0.5) / 0.5 * PowerOfTwo[10] | |
end | |
-- Write sign | |
-- WriteUnsigned(1, Sign == -1 and 1 or 0) | |
Value = Sign == -1 and 1 or 0 | |
BitPointer = BitPointer + 1 | |
BitBuffer[BitPointer] = Value % 2 | |
Value = Value / 2 | |
Value = Value - Value % 1 | |
self.BitPointer = BitPointer | |
if Value ~= 0 then | |
error("Value " .. tostring(Value) .. " has width greater than 1 bit", 2) | |
end | |
-- Write mantissa | |
Mantissa = Mantissa + 0.5 | |
Mantissa = Mantissa - Mantissa % 1 -- Not really correct, should round up/down based on the parity of |wexp| | |
Value = Mantissa | |
-- WriteUnsigned(Fraction, Mantissa) | |
for _ = 1, 10 do | |
BitPointer = BitPointer + 1 | |
BitBuffer[BitPointer] = Value % 2 | |
Value = Value / 2 | |
Value = Value - Value % 1 | |
end | |
self.BitPointer = BitPointer | |
if Value ~= 0 then | |
error("Value " .. tostring(Value) .. " has width greater than 10 bits", 2) | |
end | |
-- Write exponent | |
-- WriteSigned(WriteExponent, Exponent) | |
local MaxExp = PowerOfTwo[4] - 1 | |
Value = Exponent > MaxExp and MaxExp or Exponent < -MaxExp and -MaxExp or Exponent | |
if Value < 0 then | |
BitPointer = BitPointer + 1 | |
BitBuffer[BitPointer] = 1 | |
Value = 0 - Value | |
else | |
BitPointer = BitPointer + 1 | |
BitBuffer[BitPointer] = 0 | |
end | |
for _ = 1, 4 do | |
BitPointer = BitPointer + 1 | |
BitBuffer[BitPointer] = Value % 2 | |
Value = Value / 2 | |
Value = Value - Value % 1 | |
end | |
self.BitPointer = BitPointer | |
if Value ~= 0 then | |
error("Value " .. tostring(Value) .. " has width greater than 4 bits", 2) | |
end | |
end | |
--[[** | |
Reads a float16 (half precision) from the BitBuffer. | |
@returns [t:number] The float16. | |
**--]] | |
function FastBitBuffer:ReadFloat16() | |
local Value = 0 | |
local BitPointer = self.BitPointer + 1 | |
local BitBuffer = self.BitBuffer | |
-- ReadUnsigned(1) | |
local Sign = (BitBuffer[BitPointer] * PowerOfTwo[0]) == 1 and -1 or 1 | |
-- ReadUnsigned(Fraction) | |
for Index = 1, 10 do | |
BitPointer = BitPointer + 1 | |
Value = Value + BitBuffer[BitPointer] * PowerOfTwo[Index - 1] | |
end | |
local Mantissa = Value | |
-- ReadSigned(WriteExponent) | |
Value = 0 | |
BitPointer = BitPointer + 1 | |
local SignedValue = (-1) ^ BitBuffer[BitPointer] | |
for Index = 1, 4 do | |
BitPointer = BitPointer + 1 | |
Value = Value + BitBuffer[BitPointer] * PowerOfTwo[Index - 1] | |
end | |
local Exponent = SignedValue * Value | |
if Exponent == 0 and Mantissa == 0 then | |
return 0 | |
else | |
Mantissa = Mantissa / PowerOfTwo[10] / 2 + 0.5 | |
self.BitPointer = BitPointer | |
return Sign * math.ldexp(Mantissa, Exponent) | |
end | |
end | |
--[[** | |
Destroys the BitBuffer metatable. | |
@returns [void] | |
**--]] | |
function FastBitBuffer:Destroy() | |
self.BitBuffer = nil | |
setmetatable(self, nil) | |
end | |
return FastBitBuffer |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment