1134 lines
21 KiB
Lua
1134 lines
21 KiB
Lua
|
|
-- Copyright (C) 2017-2020 DBotThePony
|
|
|
|
-- Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
-- of this software and associated documentation files (the "Software"), to deal
|
|
-- in the Software without restriction, including without limitation the rights
|
|
-- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
|
-- of the Software, and to permit persons to whom the Software is furnished to do so,
|
|
-- subject to the following conditions:
|
|
|
|
-- The above copyright notice and this permission notice shall be included in all copies
|
|
-- or substantial portions of the Software.
|
|
|
|
-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
|
-- INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
|
-- PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
|
|
-- FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
|
-- OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
|
-- DEALINGS IN THE SOFTWARE.
|
|
|
|
|
|
jit.on()
|
|
local DLib = DLib
|
|
local meta = FindMetaTable('LBytesBuffer') or {}
|
|
debug.getregistry().LBytesBuffer = meta
|
|
DLib.BytesBufferMeta = meta
|
|
|
|
local bitworker = DLib.bitworker
|
|
|
|
local type = type
|
|
local math = math
|
|
local assert = assert
|
|
local table = table
|
|
local rawget = rawget
|
|
local rawset = rawset
|
|
local setmetatable = setmetatable
|
|
local string = string
|
|
local bit = bit
|
|
local lshift = bit.lshift
|
|
local rshift = bit.rshift
|
|
local band = bit.band
|
|
local bor = bit.bor
|
|
local bxor = bit.bxor
|
|
|
|
meta.__index = meta
|
|
|
|
--[[
|
|
@doc
|
|
@fname DLib.BytesBuffer
|
|
@args string binary = ''
|
|
|
|
@desc
|
|
entry point of BytesBuffer creation
|
|
you can pass a string to it to construct bytes array from it
|
|
**BUFFER ONLY WORK WITH BIG ENDIAN BYTES**
|
|
@descdesc
|
|
|
|
@returns
|
|
BytesBuffer: newly created object
|
|
]]
|
|
DLib.BytesBuffer = setmetatable({proto = meta, meta = meta}, {__call = function(self, stringIn)
|
|
local obj = setmetatable({}, meta)
|
|
obj.bytes = {}
|
|
obj.pointer = 0
|
|
obj.length = 0
|
|
|
|
if type(stringIn) == 'string' then
|
|
obj.bytes = DLib.string.bbyte(stringIn, 1, #stringIn)
|
|
obj.length = #obj.bytes
|
|
end
|
|
|
|
-- obj:Seek(0)
|
|
|
|
return obj
|
|
end})
|
|
|
|
--[[
|
|
@doc
|
|
@fname BytesBuffer:Seek
|
|
@args number position
|
|
|
|
@returns
|
|
BytesBuffer: self
|
|
]]
|
|
-- Operations
|
|
function meta:Seek(moveTo)
|
|
if moveTo < 0 or moveTo > self.length then
|
|
error('Seek - invalid position (' .. moveTo .. '; ' .. self.length .. ')', 2)
|
|
end
|
|
|
|
self.pointer = moveTo
|
|
return self
|
|
end
|
|
|
|
--[[
|
|
@doc
|
|
@fname BytesBuffer:Tell
|
|
@alias BytesBuffer:Ask
|
|
|
|
@returns
|
|
number: pointer position
|
|
]]
|
|
function meta:Tell()
|
|
return self.pointer
|
|
end
|
|
|
|
meta.Ask = meta.Tell
|
|
|
|
--[[
|
|
@doc
|
|
@fname BytesBuffer:Move
|
|
@alias BytesBuffer:Walk
|
|
@args number delta
|
|
|
|
@returns
|
|
BytesBuffer: self
|
|
]]
|
|
function meta:Move(moveBy)
|
|
return self:Seek(self.pointer + moveBy)
|
|
end
|
|
|
|
meta.Walk = meta.Move
|
|
|
|
--[[
|
|
@doc
|
|
@fname BytesBuffer:Reset
|
|
|
|
@desc
|
|
alias of BytesBuffer:Seek(0)
|
|
@enddesc
|
|
|
|
@returns
|
|
BytesBuffer: self
|
|
]]
|
|
function meta:Reset()
|
|
return self:Seek(0)
|
|
end
|
|
|
|
--[[
|
|
@doc
|
|
@fname BytesBuffer:Release
|
|
|
|
@desc
|
|
sets pointer to 0 and removes internal bytes array
|
|
@enddesc
|
|
|
|
@returns
|
|
BytesBuffer: self
|
|
]]
|
|
function meta:Release()
|
|
self.pointer = 0
|
|
self.bytes = {}
|
|
return self
|
|
end
|
|
|
|
--[[
|
|
@doc
|
|
@fname BytesBuffer:GetBytes
|
|
|
|
@internal
|
|
|
|
@returns
|
|
table: of integers (for optimization purpose). editing this array will affect the object! be careful
|
|
]]
|
|
function meta:GetBytes()
|
|
return self.bytes
|
|
end
|
|
|
|
local function wrap(num, maximal)
|
|
if num >= 0 then
|
|
return num
|
|
end
|
|
|
|
return maximal * 2 + num
|
|
end
|
|
|
|
local function unwrap(num, maximal)
|
|
if num < maximal then
|
|
return num
|
|
end
|
|
|
|
return num - maximal * 2
|
|
end
|
|
|
|
local function assertType(valueIn, desiredType, funcName)
|
|
if type(valueIn) == desiredType then return end
|
|
error(funcName .. ' - input is not a ' .. desiredType .. '! typeof ' .. type(valueIn), 3)
|
|
end
|
|
|
|
local function assertRange(valueIn, min, max, funcName)
|
|
if valueIn >= min and valueIn <= max then return end
|
|
error(funcName .. ' - size overflow (' .. min .. ' -> ' .. max .. ' vs ' .. valueIn .. ')', 3)
|
|
end
|
|
|
|
|
|
--[[
|
|
@doc
|
|
@fname BytesBuffer:EndOfStream
|
|
|
|
@returns
|
|
boolean
|
|
]]
|
|
function meta:EndOfStream()
|
|
return self.pointer >= self.length
|
|
end
|
|
|
|
--[[
|
|
@doc
|
|
@fname BytesBuffer:WriteUByte
|
|
@args number value
|
|
|
|
@returns
|
|
BytesBuffer: self
|
|
]]
|
|
function meta:WriteUByte(valueIn)
|
|
assertType(valueIn, 'number', 'WriteUByte')
|
|
assertRange(valueIn, 0, 0xFF, 'WriteUByte')
|
|
|
|
valueIn = math.floor(valueIn)
|
|
|
|
self.bytes[self.pointer + 1] = valueIn
|
|
self.pointer = self.pointer + 1
|
|
self.length = #self.bytes
|
|
|
|
return self
|
|
end
|
|
|
|
--[[
|
|
@doc
|
|
@fname BytesBuffer:WriteByte_2
|
|
@args number value
|
|
|
|
@desc
|
|
with value shift
|
|
@enddesc
|
|
|
|
@returns
|
|
BytesBuffer: self
|
|
]]
|
|
|
|
--[[
|
|
@doc
|
|
@fname BytesBuffer:WriteByte
|
|
@args number value
|
|
|
|
@desc
|
|
with negative number overflow
|
|
@enddesc
|
|
|
|
@returns
|
|
BytesBuffer: self
|
|
]]
|
|
|
|
--[[
|
|
@doc
|
|
@fname BytesBuffer:WriteCHar
|
|
@args string char
|
|
|
|
@returns
|
|
BytesBuffer: self
|
|
]]
|
|
-- Primitive read/write
|
|
-- wrap overflow
|
|
function meta:WriteByte_2(valueIn)
|
|
assertType(valueIn, 'number', 'WriteByte')
|
|
assertRange(valueIn, -0x80, 0x7F, 'WriteByte')
|
|
return self:WriteUByte(math.floor(valueIn) + 0x80)
|
|
end
|
|
|
|
-- one's component
|
|
function meta:WriteByte(valueIn)
|
|
assertType(valueIn, 'number', 'WriteByte')
|
|
assertRange(valueIn, -0x80, 0x7F, 'WriteByte')
|
|
return self:WriteUByte(wrap(math.floor(valueIn), 0x80))
|
|
end
|
|
|
|
meta.WriteInt8 = meta.WriteByte
|
|
meta.WriteUInt8 = meta.WriteUByte
|
|
|
|
function meta:WriteChar(char)
|
|
assertType(char, 'string', 'WriteChar')
|
|
assert(#char == 1, 'Input is not a single char!')
|
|
self:WriteUByte(string.byte(char))
|
|
return self
|
|
end
|
|
|
|
--[[
|
|
@doc
|
|
@fname BytesBuffer:WriteShort_2
|
|
@alias BytesBuffer:WriteInt16_2
|
|
@args number value
|
|
|
|
@desc
|
|
with value shift
|
|
@enddesc
|
|
|
|
@returns
|
|
BytesBuffer: self
|
|
]]
|
|
|
|
--[[
|
|
@doc
|
|
@fname BytesBuffer:WriteShort
|
|
@alias BytesBuffer:WriteInt16
|
|
@args number value
|
|
|
|
@desc
|
|
with negative number overflow
|
|
@enddesc
|
|
|
|
@returns
|
|
BytesBuffer: self
|
|
]]
|
|
|
|
--[[
|
|
@doc
|
|
@fname BytesBuffer:WriteUShort
|
|
@alias BytesBuffer:WriteUInt16
|
|
@args number value
|
|
|
|
@returns
|
|
BytesBuffer: self
|
|
]]
|
|
function meta:WriteInt16_2(valueIn)
|
|
assertType(valueIn, 'number', 'WriteInt16')
|
|
assertRange(valueIn, -0x8000, 0x7FFF, 'WriteInt16')
|
|
return self:WriteUInt16(math.floor(valueIn) + 0x8000)
|
|
end
|
|
|
|
function meta:WriteInt16(valueIn)
|
|
assertType(valueIn, 'number', 'WriteInt16')
|
|
assertRange(valueIn, -0x8000, 0x7FFF, 'WriteInt16')
|
|
return self:WriteUInt16(wrap(math.floor(valueIn), 0x8000))
|
|
end
|
|
|
|
function meta:WriteUInt16(valueIn)
|
|
assertType(valueIn, 'number', 'WriteUInt16')
|
|
assertRange(valueIn, 0, 0xFFFF, 'WriteUInt16')
|
|
|
|
self.bytes[self.pointer + 1] = band(rshift(valueIn, 8), 0xFF)
|
|
self.bytes[self.pointer + 2] = band(valueIn, 0xFF)
|
|
self.pointer = self.pointer + 2
|
|
self.length = #self.bytes
|
|
|
|
return self
|
|
end
|
|
|
|
meta.WriteShort = meta.WriteInt16
|
|
meta.WriteShort_2 = meta.WriteInt16_2
|
|
meta.WriteUShort = meta.WriteUInt16
|
|
|
|
--[[
|
|
@doc
|
|
@fname BytesBuffer:WriteInt_2
|
|
@alias BytesBuffer:WriteInt32_2
|
|
@args number value
|
|
|
|
@desc
|
|
with value shift
|
|
@enddesc
|
|
|
|
@returns
|
|
BytesBuffer: self
|
|
]]
|
|
|
|
--[[
|
|
@doc
|
|
@fname BytesBuffer:WriteInt
|
|
@alias BytesBuffer:WriteInt32
|
|
@args number value
|
|
|
|
@desc
|
|
with negative number overflow
|
|
@enddesc
|
|
|
|
@returns
|
|
BytesBuffer: self
|
|
]]
|
|
|
|
--[[
|
|
@doc
|
|
@fname BytesBuffer:WriteUInt
|
|
@alias BytesBuffer:WriteUInt32
|
|
@args number value
|
|
|
|
@returns
|
|
BytesBuffer: self
|
|
]]
|
|
function meta:WriteInt32_2(valueIn)
|
|
assertType(valueIn, 'number', 'WriteInt32')
|
|
assertRange(valueIn, -0x80000000, 0x7FFFFFFF, 'WriteInt32')
|
|
return self:WriteUInt32(math.floor(valueIn) + 0x80000000)
|
|
end
|
|
|
|
function meta:WriteInt32(valueIn)
|
|
assertType(valueIn, 'number', 'WriteInt32')
|
|
assertRange(valueIn, -0x80000000, 0x7FFFFFFF, 'WriteInt32')
|
|
return self:WriteUInt32(wrap(math.floor(valueIn), 0x80000000))
|
|
end
|
|
|
|
function meta:WriteUInt32(valueIn)
|
|
assertType(valueIn, 'number', 'WriteUInt32')
|
|
assertRange(valueIn, 0, 0xFFFFFFFF, 'WriteUInt32')
|
|
|
|
self.bytes[self.pointer + 1] = band(rshift(valueIn, 24), 0xFF)
|
|
self.bytes[self.pointer + 2] = band(rshift(valueIn, 16), 0xFF)
|
|
self.bytes[self.pointer + 3] = band(rshift(valueIn, 8), 0xFF)
|
|
self.bytes[self.pointer + 4] = band(valueIn, 0xFF)
|
|
self.pointer = self.pointer + 4
|
|
self.length = #self.bytes
|
|
|
|
return self
|
|
end
|
|
|
|
meta.WriteInt = meta.WriteInt32
|
|
meta.WriteInt_2 = meta.WriteInt32_2
|
|
meta.WriteUInt = meta.WriteUInt32
|
|
|
|
--[[
|
|
@doc
|
|
@fname BytesBuffer:WriteLong_2
|
|
@alias BytesBuffer:WriteInt64_2
|
|
@args number value
|
|
|
|
@desc
|
|
with value shift
|
|
due to precision errors, this not actually accurate operation
|
|
@enddesc
|
|
|
|
@returns
|
|
BytesBuffer: self
|
|
]]
|
|
|
|
--[[
|
|
@doc
|
|
@fname BytesBuffer:WriteLong
|
|
@alias BytesBuffer:WriteInt64
|
|
@args number value
|
|
|
|
@desc
|
|
with negative number overflow
|
|
due to precision errors, this not actually accurate operation
|
|
@enddesc
|
|
|
|
@returns
|
|
BytesBuffer: self
|
|
]]
|
|
|
|
--[[
|
|
@doc
|
|
@fname BytesBuffer:WriteULong
|
|
@alias BytesBuffer:WriteUInt64
|
|
@args number value
|
|
|
|
@desc
|
|
due to precision errors, this not actually accurate operation
|
|
@enddesc
|
|
|
|
@returns
|
|
BytesBuffer: self
|
|
]]
|
|
function meta:WriteInt64_2(valueIn)
|
|
self:WriteUInt64(valueIn + 0x100000000)
|
|
return self
|
|
end
|
|
|
|
function meta:WriteInt64(valueIn)
|
|
self:WriteUInt64(wrap(valueIn, 0x100000000))
|
|
return self
|
|
end
|
|
|
|
function meta:WriteUInt64(valueIn)
|
|
self:WriteUInt32((valueIn - valueIn % 0xFFFFFFFF) / 0xFFFFFFFF)
|
|
valueIn = valueIn % 0xFFFFFFFF
|
|
self:WriteUInt32(valueIn)
|
|
return self
|
|
end
|
|
|
|
function meta:CheckOverflow(name, moveBy)
|
|
if self.pointer + moveBy > self.length then
|
|
error('Read' .. name .. ' - bytes amount overflow (' .. self.pointer .. ' + ' .. moveBy .. ' vs ' .. self.length .. ')', 3)
|
|
end
|
|
end
|
|
|
|
meta.WriteLong = meta.WriteInt64
|
|
meta.WriteLong_2 = meta.WriteInt64_2
|
|
meta.WriteULong = meta.WriteUInt64
|
|
|
|
--[[
|
|
@doc
|
|
@fname BytesBuffer:ReadByte_2
|
|
|
|
@desc
|
|
with value shift
|
|
@enddesc
|
|
|
|
@returns
|
|
number
|
|
]]
|
|
|
|
--[[
|
|
@doc
|
|
@fname BytesBuffer:ReadByte
|
|
|
|
@desc
|
|
with negative number overflow
|
|
@enddesc
|
|
|
|
@returns
|
|
number
|
|
]]
|
|
|
|
--[[
|
|
@doc
|
|
@fname BytesBuffer:ReadChar
|
|
|
|
@returns
|
|
string
|
|
]]
|
|
function meta:ReadByte_2()
|
|
return self:ReadUByte() - 0x80
|
|
end
|
|
|
|
function meta:ReadByte()
|
|
return unwrap(self:ReadUByte(), 0x80)
|
|
end
|
|
|
|
function meta:ReadUByte()
|
|
self:CheckOverflow('UByte', 1)
|
|
local value = self.bytes[self.pointer + 1]
|
|
self.pointer = self.pointer + 1
|
|
|
|
return value
|
|
end
|
|
|
|
meta.ReadInt8 = meta.ReadByte
|
|
meta.ReadUInt8 = meta.ReadUByte
|
|
|
|
--[[
|
|
@doc
|
|
@fname BytesBuffer:ReadInt16_2
|
|
|
|
@desc
|
|
with value shift
|
|
@enddesc
|
|
|
|
@returns
|
|
number
|
|
]]
|
|
|
|
--[[
|
|
@doc
|
|
@fname BytesBuffer:ReadInt16
|
|
|
|
@desc
|
|
with negative number overflow
|
|
@enddesc
|
|
|
|
@returns
|
|
number
|
|
]]
|
|
|
|
--[[
|
|
@doc
|
|
@fname BytesBuffer:ReadUInt16
|
|
|
|
@returns
|
|
number
|
|
]]
|
|
function meta:ReadInt16_2()
|
|
return self:ReadUInt16() - 0x8000
|
|
end
|
|
|
|
function meta:ReadInt16()
|
|
return unwrap(self:ReadUInt16(), 0x8000)
|
|
end
|
|
|
|
function meta:ReadUInt16()
|
|
self:CheckOverflow('UInt16', 2)
|
|
|
|
local value =
|
|
lshift(self.bytes[self.pointer + 1], 8) +
|
|
self.bytes[self.pointer + 2]
|
|
|
|
self.pointer = self.pointer + 2
|
|
return value
|
|
end
|
|
|
|
--[[
|
|
@doc
|
|
@fname BytesBuffer:ReadInt32_2
|
|
|
|
@desc
|
|
with value shift
|
|
@enddesc
|
|
|
|
@returns
|
|
number
|
|
]]
|
|
|
|
--[[
|
|
@doc
|
|
@fname BytesBuffer:ReadInt32
|
|
|
|
@desc
|
|
with negative number overflow
|
|
@enddesc
|
|
|
|
@returns
|
|
number
|
|
]]
|
|
|
|
--[[
|
|
@doc
|
|
@fname BytesBuffer:ReadUInt32
|
|
|
|
@returns
|
|
number
|
|
]]
|
|
function meta:ReadInt32_2()
|
|
return self:ReadUInt32() - 0x80000000
|
|
end
|
|
|
|
function meta:ReadInt32()
|
|
return unwrap(self:ReadUInt32(), 0x80000000)
|
|
end
|
|
|
|
function meta:ReadUInt32()
|
|
self:CheckOverflow('UInt32', 4)
|
|
|
|
local value =
|
|
lshift(self.bytes[self.pointer + 1], 24) +
|
|
lshift(self.bytes[self.pointer + 2], 16) +
|
|
lshift(self.bytes[self.pointer + 3], 8) +
|
|
self.bytes[self.pointer + 4]
|
|
|
|
self.pointer = self.pointer + 4
|
|
return value
|
|
end
|
|
|
|
--[[
|
|
@doc
|
|
@fname BytesBuffer:ReadInt64_2
|
|
|
|
@desc
|
|
with value shift
|
|
due to precision errors, this not actually accurate operation
|
|
@enddesc
|
|
|
|
@returns
|
|
number
|
|
]]
|
|
|
|
--[[
|
|
@doc
|
|
@fname BytesBuffer:ReadInt64
|
|
|
|
@desc
|
|
with negative number overflow
|
|
due to precision errors, this not actually accurate operation
|
|
@enddesc
|
|
|
|
@returns
|
|
number
|
|
]]
|
|
|
|
--[[
|
|
@doc
|
|
@fname BytesBuffer:ReadUInt64
|
|
|
|
@desc
|
|
due to precision errors, this not actually accurate operation
|
|
@enddesc
|
|
|
|
@returns
|
|
number
|
|
]]
|
|
function meta:ReadInt64_2()
|
|
return self:ReadUInt64() - 0x100000000
|
|
end
|
|
|
|
function meta:ReadInt64()
|
|
return unwrap(self:ReadUInt64(), 0x100000000)
|
|
end
|
|
|
|
function meta:ReadUInt64()
|
|
self:CheckOverflow('UInt64', 8)
|
|
return
|
|
self:ReadUByte() * 0x100000000000000 +
|
|
self:ReadUByte() * 0x1000000000000 +
|
|
self:ReadUByte() * 0x10000000000 +
|
|
self:ReadUByte() * 0x100000000 +
|
|
self:ReadUByte() * 0x1000000 +
|
|
self:ReadUByte() * 0x10000 +
|
|
self:ReadUByte() * 0x100 +
|
|
self:ReadUByte()
|
|
end
|
|
|
|
--[[
|
|
@doc
|
|
@fname BytesBuffer:WriteFloatSlow
|
|
@args number float
|
|
|
|
@desc
|
|
due to precision errors, this is a slightly inaccurate operation
|
|
*This function internally utilize tables, so it can hog memory*
|
|
@enddesc
|
|
|
|
@returns
|
|
BytesBuffer: self
|
|
]]
|
|
function meta:WriteFloatSlow(valueIn)
|
|
assertType(valueIn, 'number', 'WriteFloat')
|
|
local bits = bitworker.FloatToBinaryIEEE(valueIn, 8, 23)
|
|
local bitsInNumber = bitworker.BinaryToUInteger(bits)
|
|
return self:WriteUInt32(bitsInNumber)
|
|
end
|
|
|
|
--[[
|
|
@doc
|
|
@fname BytesBuffer:WriteFloat
|
|
@args number float
|
|
|
|
@desc
|
|
due to precision errors, this is a slightly inaccurate operation
|
|
@enddesc
|
|
|
|
@returns
|
|
BytesBuffer: self
|
|
]]
|
|
function meta:WriteFloat(valueIn)
|
|
assertType(valueIn, 'number', 'WriteFloat')
|
|
return self:WriteInt32(bitworker.FastFloatToBinaryIEEE(valueIn))
|
|
end
|
|
|
|
--[[
|
|
@doc
|
|
@fname BytesBuffer:ReadFloatSlow
|
|
|
|
@desc
|
|
due to precision errors, this is a slightly inaccurate operation
|
|
*This function internally utilize tables, so it can hog memory*
|
|
@enddesc
|
|
|
|
@returns
|
|
number
|
|
]]
|
|
function meta:ReadFloatSlow()
|
|
local bitsInNumber = self:ReadUInt32()
|
|
local bits = bitworker.UIntegerToBinary(bitsInNumber, 32)
|
|
return bitworker.BinaryToFloatIEEE(bits, 8, 23)
|
|
end
|
|
|
|
--[[
|
|
@doc
|
|
@fname BytesBuffer:ReadFloat
|
|
|
|
@desc
|
|
due to precision errors, this is a slightly inaccurate operation
|
|
@enddesc
|
|
|
|
@returns
|
|
number
|
|
]]
|
|
function meta:ReadFloat()
|
|
return bitworker.FastBinaryToFloatIEEE(self:ReadUInt32())
|
|
end
|
|
|
|
--[[
|
|
@doc
|
|
@fname BytesBuffer:WriteDoubleSlow
|
|
@args number double
|
|
|
|
@desc
|
|
due to precision errors, this is a inaccurate operation
|
|
*This function internally utilize tables, so it can hog memory*
|
|
@enddesc
|
|
|
|
@returns
|
|
BytesBuffer: self
|
|
]]
|
|
function meta:WriteDoubleSlow(valueIn)
|
|
assertType(valueIn, 'number', 'WriteDouble')
|
|
local bits = bitworker.FloatToBinaryIEEE(valueIn, 11, 52)
|
|
local bytes = bitworker.BitsToBytes(bits)
|
|
|
|
self.bytes[self.pointer + 1] = bytes[1]
|
|
self.bytes[self.pointer + 2] = bytes[2]
|
|
self.bytes[self.pointer + 3] = bytes[3]
|
|
self.bytes[self.pointer + 4] = bytes[4]
|
|
self.bytes[self.pointer + 5] = bytes[5]
|
|
self.bytes[self.pointer + 6] = bytes[6]
|
|
self.bytes[self.pointer + 7] = bytes[7]
|
|
self.bytes[self.pointer + 8] = bytes[8]
|
|
self.pointer = self.pointer + 8
|
|
self.length = #self.bytes
|
|
|
|
return self
|
|
end
|
|
|
|
--[[
|
|
@doc
|
|
@fname BytesBuffer:WriteDouble
|
|
@args number double
|
|
|
|
@desc
|
|
due to precision errors, this is a inaccurate operation
|
|
@enddesc
|
|
|
|
@returns
|
|
BytesBuffer: self
|
|
]]
|
|
function meta:WriteDouble(valueIn)
|
|
assertType(valueIn, 'number', 'WriteDouble')
|
|
local int1, int2 = bitworker.FastDoubleToBinaryIEEE(valueIn)
|
|
self:WriteInt32(int1)
|
|
self:WriteInt32(int2)
|
|
return self
|
|
end
|
|
|
|
--[[
|
|
@doc
|
|
@fname BytesBuffer:ReadDoubleSlow
|
|
|
|
@desc
|
|
due to precision errors, this is a slightly inaccurate operation
|
|
*This function internally utilize tables, so it can hog memory*
|
|
@enddesc
|
|
|
|
@returns
|
|
number
|
|
]]
|
|
function meta:ReadDoubleSlow()
|
|
local bytes1 = self:ReadUInt32()
|
|
local bytes2 = self:ReadUInt32()
|
|
local bits = {}
|
|
table.append(bits, bitworker.UIntegerToBinary(bytes1, 32))
|
|
table.append(bits, bitworker.UIntegerToBinary(bytes2, 32))
|
|
return bitworker.BinaryToFloatIEEE(bits, 11, 52)
|
|
end
|
|
|
|
--[[
|
|
@doc
|
|
@fname BytesBuffer:ReadDouble
|
|
|
|
@desc
|
|
due to precision errors, this is a slightly inaccurate operation
|
|
@enddesc
|
|
|
|
@returns
|
|
number
|
|
]]
|
|
function meta:ReadDouble()
|
|
return bitworker.FastBinaryToDoubleIEEE(self:ReadUInt32(), self:ReadUInt32())
|
|
end
|
|
|
|
--[[
|
|
@doc
|
|
@fname BytesBuffer:WriteString
|
|
@args string data
|
|
|
|
@desc
|
|
writes NUL terminated string to buffer
|
|
errors if NUL is present in string
|
|
@enddesc
|
|
|
|
@returns
|
|
BytesBuffer: self
|
|
]]
|
|
-- String
|
|
function meta:WriteString(stringIn)
|
|
assertType(stringIn, 'string', 'WriteString')
|
|
if #stringIn == 0 then return self end
|
|
local bytes = DLib.string.bbyte(stringIn, 1, #stringIn)
|
|
local i = 0
|
|
local len = #bytes
|
|
|
|
::loop::
|
|
i = i + 1
|
|
|
|
if bytes[i] == 0 then
|
|
error('Binary data in a string?!')
|
|
end
|
|
|
|
self:WriteUByte(bytes[i])
|
|
|
|
if i < len then
|
|
goto loop
|
|
end
|
|
|
|
self:WriteUByte(0)
|
|
|
|
return self
|
|
end
|
|
|
|
--[[
|
|
@doc
|
|
@fname BytesBuffer:ReadString
|
|
@args string data
|
|
|
|
@desc
|
|
reads buffer until it hits NUL symbol
|
|
errors if buffer depleted before NUL is found
|
|
@enddesc
|
|
|
|
@returns
|
|
string
|
|
]]
|
|
function meta:ReadString()
|
|
self:CheckOverflow('ReadString', 1)
|
|
local readNext = self:ReadUByte()
|
|
local output = {}
|
|
|
|
if readNext == 0 then return '' end
|
|
|
|
::loop::
|
|
|
|
table.insert(output, readNext)
|
|
|
|
if self:EndOfStream() then
|
|
error('No NUL terminator was found, buffer overflow!')
|
|
end
|
|
|
|
readNext = self:ReadUByte()
|
|
|
|
if readNext ~= 0 and readNext ~= nil then
|
|
goto loop
|
|
end
|
|
|
|
return DLib.string.bcharTable(output)
|
|
end
|
|
|
|
-- Binary Data
|
|
|
|
--[[
|
|
@doc
|
|
@fname BytesBuffer:WriteBinary
|
|
@alias BytesBuffer:WriteData
|
|
@args string binary
|
|
|
|
@returns
|
|
BytesBuffer: self
|
|
]]
|
|
function meta:WriteBinary(binaryString)
|
|
assertType(binaryString, 'string', 'WriteBinary')
|
|
if #binaryString == 0 then return self end
|
|
|
|
for i = 1, #binaryString do
|
|
self:WriteUByte(binaryString:byte(i, i))
|
|
end
|
|
|
|
return self
|
|
end
|
|
|
|
--[[
|
|
@doc
|
|
@fname BytesBuffer:ReadBinary
|
|
@alias BytesBuffer:ReadData
|
|
@args number bytesToRead
|
|
|
|
@returns
|
|
string
|
|
]]
|
|
function meta:ReadBinary(readAmount)
|
|
assert(readAmount >= 0, 'Read amount must be positive')
|
|
if readAmount == 0 then return '' end
|
|
self:CheckOverflow('Binary', readAmount)
|
|
|
|
local output = {}
|
|
|
|
local i = 0
|
|
|
|
::loop::
|
|
|
|
i = i + 1
|
|
table.insert(output, self:ReadUByte())
|
|
|
|
if i < readAmount then
|
|
goto loop
|
|
end
|
|
|
|
return DLib.string.bcharTable(output)
|
|
end
|
|
|
|
meta.WriteData = meta.WriteBinary
|
|
meta.ReadData = meta.ReadBinary
|
|
|
|
function meta:ReadChar()
|
|
return string.char(self:ReadUByte())
|
|
end
|
|
|
|
--[[
|
|
@doc
|
|
@fname BytesBuffer:ToString
|
|
|
|
@deprecated
|
|
|
|
@returns
|
|
string
|
|
]]
|
|
function meta:ToString()
|
|
local pointer = self.pointer
|
|
self:Seek(0)
|
|
local str = self:ReadBinary(self.length)
|
|
self:Seek(pointer)
|
|
return str
|
|
end
|
|
|
|
--[[
|
|
@doc
|
|
@fname BytesBuffer:ToFileStream
|
|
@args File stream
|
|
|
|
@returns
|
|
File
|
|
]]
|
|
function meta:ToFileStream(fileStream)
|
|
local bytes = #self.bytes
|
|
if bytes == 0 then return fileStream end
|
|
|
|
for i, byte in ipairs(self.bytes) do
|
|
fileStream:WriteByte(byte)
|
|
end
|
|
|
|
return fileStream
|
|
end
|
|
|
|
--[[
|
|
@doc
|
|
@fname DLib.BytesBuffer.CompileStructure
|
|
@args string structureDef, table customTypes
|
|
|
|
@desc
|
|
Reads a structure from current pointer position
|
|
this is somewhat fancy way of reading typical headers and stuff
|
|
The string is in next format:
|
|
```
|
|
type identifier with any symbols
|
|
type on_next_line
|
|
uint32 thing
|
|
```
|
|
Supported types:
|
|
`int8`
|
|
`int16`
|
|
`int32`
|
|
`int64`
|
|
`bigint`
|
|
`float`
|
|
`double`
|
|
`uint8`
|
|
`uint16`
|
|
`uint32`
|
|
`uint64`
|
|
`ubigint`
|
|
`string` - NUL terminated string
|
|
@enddesc
|
|
|
|
@returns
|
|
function: a function to pass `BytesBuffer` to get readed structure
|
|
]]
|
|
function DLib.BytesBuffer.CompileStructure(structureDef, callbacks)
|
|
local output = {}
|
|
|
|
for i, line in ipairs(structureDef:split('\n')) do
|
|
line = line:trim()
|
|
--assert(line ~= '', 'Invalid line definition at ' .. i)
|
|
|
|
if line ~= '' then
|
|
local findSpace = assert(line:find('%s'), 'Can\'t find variable name at line ' .. i)
|
|
local rtype2 = line:sub(1, findSpace):trim()
|
|
local rtype = rtype2:lower()
|
|
local rname = line:sub(findSpace + 1):trim()
|
|
local findCommentary = rname:find('%s//')
|
|
|
|
if findCommentary then
|
|
rname = rname:sub(1, findCommentary):trim()
|
|
end
|
|
|
|
if rtype == 'int8' or rtype == 'byte' then
|
|
table.insert(output, {rname, meta.ReadByte})
|
|
elseif rtype == 'int16' or rtype == 'short' then
|
|
table.insert(output, {rname, meta.ReadInt16})
|
|
elseif rtype == 'int32' or rtype == 'long' or rtype == 'int' then
|
|
table.insert(output, {rname, meta.ReadInt32})
|
|
elseif rtype == 'int64' or rtype == 'longlong' or rtype == 'bigint' then
|
|
table.insert(output, {rname, meta.ReadInt64})
|
|
elseif rtype == 'uint8' or rtype == 'ubyte' then
|
|
table.insert(output, {rname, meta.ReadUByte})
|
|
elseif rtype == 'uint16' or rtype == 'ushort' then
|
|
table.insert(output, {rname, meta.ReadUInt16})
|
|
elseif rtype == 'uint32' or rtype == 'ulong' or rtype == 'uint' then
|
|
table.insert(output, {rname, meta.ReadUInt32})
|
|
elseif rtype == 'uint64' or rtype == 'ulong64' or rtype == 'biguint' or rtype == 'ubigint' then
|
|
table.insert(output, {rname, meta.ReadUInt64})
|
|
elseif rtype == 'float' then
|
|
table.insert(output, {rname, meta.ReadFloat})
|
|
elseif rtype == 'double' then
|
|
table.insert(output, {rname, meta.ReadDouble})
|
|
elseif rtype == 'variable' or rtype == 'string' then
|
|
table.insert(output, {rname, meta.ReadString})
|
|
elseif callbacks and callbacks[rtype2] then
|
|
table.insert(output, {rname, callbacks[rtype2]})
|
|
else
|
|
DLib.MessageError(debug.traceback('Undefined type: ' .. rtype))
|
|
end
|
|
end
|
|
end
|
|
|
|
return function(self)
|
|
local read = {}
|
|
|
|
for i, data in ipairs(output) do
|
|
read[data[1]] = data[2](self, read)
|
|
end
|
|
|
|
return read
|
|
end
|
|
end
|
|
|
|
--[[
|
|
@doc
|
|
@fname BytesBuffer:ReadStructure
|
|
@args string structureDef, table customTypes
|
|
|
|
@desc
|
|
`DLib.BytesBuffer.CompileStructure(structureDef, customTypes)(self)`
|
|
@enddesc
|
|
|
|
@returns
|
|
table: the read structure
|
|
]]
|
|
function meta:ReadStructure(structureDef, callbacks)
|
|
return DLib.BytesBuffer.CompileStructure(structureDef, callbacks)(self)
|
|
end
|