forked from Simnation/Main
155 lines
No EOL
5 KiB
Lua
155 lines
No EOL
5 KiB
Lua
-- Credit: https://github.com/citizenfx/lua/blob/luaglm-dev/cfx/libs/scripts/examples/dataview.lua
|
|
local dataView = setmetatable({
|
|
EndBig = ">",
|
|
EndLittle = "<",
|
|
Types = {
|
|
Int8 = { code = "i1" },
|
|
Uint8 = { code = "I1" },
|
|
Int16 = { code = "i2" },
|
|
Uint16 = { code = "I2" },
|
|
Int32 = { code = "i4" },
|
|
Uint32 = { code = "I4" },
|
|
Int64 = { code = "i8" },
|
|
Uint64 = { code = "I8" },
|
|
Float32 = { code = "f", size = 4 }, -- a float (native size)
|
|
Float64 = { code = "d", size = 8 }, -- a double (native size)
|
|
|
|
LuaInt = { code = "j" }, -- a lua_Integer
|
|
UluaInt = { code = "J" }, -- a lua_Unsigned
|
|
LuaNum = { code = "n" }, -- a lua_Number
|
|
String = { code = "z", size = -1, }, -- zero terminated string
|
|
},
|
|
|
|
FixedTypes = {
|
|
String = { code = "c" }, -- a fixed-sized string with n bytes
|
|
Int = { code = "i" }, -- a signed int with n bytes
|
|
Uint = { code = "I" }, -- an unsigned int with n bytes
|
|
},
|
|
}, {
|
|
__call = function(_, length)
|
|
return dataView.ArrayBuffer(length)
|
|
end
|
|
})
|
|
dataView.__index = dataView
|
|
|
|
--[[ Create an ArrayBuffer with a size in bytes --]]
|
|
function dataView.ArrayBuffer(length)
|
|
return setmetatable({
|
|
blob = string.blob(length),
|
|
length = length,
|
|
offset = 1,
|
|
cangrow = true,
|
|
}, dataView)
|
|
end
|
|
|
|
--[[ Wrap a non-internalized string --]]
|
|
function dataView.Wrap(blob)
|
|
return setmetatable({
|
|
blob = blob,
|
|
length = blob:len(),
|
|
offset = 1,
|
|
cangrow = true,
|
|
}, dataView)
|
|
end
|
|
|
|
--[[ Return the underlying bytebuffer --]]
|
|
function dataView:Buffer() return self.blob end
|
|
function dataView:ByteLength() return self.length end
|
|
function dataView:ByteOffset() return self.offset end
|
|
function dataView:SubView(offset, length)
|
|
return setmetatable({
|
|
blob = self.blob,
|
|
length = length or self.length,
|
|
offset = 1 + offset,
|
|
cangrow = false,
|
|
}, dataView)
|
|
end
|
|
|
|
--[[ Return the Endianness format character --]]
|
|
function ef(big) return (big and dataView.EndBig) or dataView.EndLittle end
|
|
|
|
--[[ Helper function for setting fixed datatypes within a buffer --]]
|
|
function packblob(self, offset, value, code)
|
|
-- If cangrow is false the dataview represents a subview, i.e., a subset
|
|
-- of some other string view. Ensure the references are the same before
|
|
-- updating the subview
|
|
local packed = self.blob:blob_pack(offset, code, value)
|
|
if self.cangrow or packed == self.blob then
|
|
self.blob = packed
|
|
self.length = packed:len()
|
|
return true
|
|
else
|
|
return false
|
|
end
|
|
end
|
|
|
|
--[[
|
|
Create the API by using dataView.Types
|
|
--]]
|
|
for label,datatype in pairs(dataView.Types) do
|
|
if not datatype.size then -- cache fixed encoding size
|
|
datatype.size = string.packsize(datatype.code)
|
|
elseif datatype.size >= 0 and string.packsize(datatype.code) ~= datatype.size then
|
|
local msg = "Pack size of %s (%d) does not match cached length: (%d)"
|
|
error(msg:format(label, string.packsize(datatype.code), datatype.size))
|
|
return nil
|
|
end
|
|
|
|
dataView["Get" .. label] = function(self, offset, endian)
|
|
offset = offset or 0
|
|
if offset >= 0 then
|
|
local o = self.offset + offset
|
|
local v,_ = self.blob:blob_unpack(o, ef(endian) .. datatype.code)
|
|
return v
|
|
end
|
|
return nil
|
|
end
|
|
|
|
dataView["Set" .. label] = function(self, offset, value, endian)
|
|
if offset >= 0 and value then
|
|
local o = self.offset + offset
|
|
local v_size = (datatype.size < 0 and value:len()) or datatype.size
|
|
if self.cangrow or ((o + (v_size - 1)) <= self.length) then
|
|
if not packblob(self, o, value, ef(endian) .. datatype.code) then
|
|
error("cannot grow subview")
|
|
end
|
|
else
|
|
error("cannot grow dataview")
|
|
end
|
|
end
|
|
return self
|
|
end
|
|
end
|
|
|
|
for label,datatype in pairs(dataView.FixedTypes) do
|
|
datatype.size = -1 -- Ensure cached encoding size is invalidated
|
|
|
|
dataView["GetFixed" .. label] = function(self, offset, typelen, endian)
|
|
if offset >= 0 then
|
|
local o = self.offset + offset
|
|
if (o + (typelen - 1)) <= self.length then
|
|
local code = ef(endian) .. "c" .. tostring(typelen)
|
|
local v,_ = self.blob:blob_unpack(o, code)
|
|
return v
|
|
end
|
|
end
|
|
return nil -- Out of bounds
|
|
end
|
|
|
|
dataView["SetFixed" .. label] = function(self, offset, typelen, value, endian)
|
|
if offset >= 0 and value then
|
|
local o = self.offset + offset
|
|
if self.cangrow or ((o + (typelen - 1)) <= self.length) then
|
|
local code = ef(endian) .. "c" .. tostring(typelen)
|
|
if not packblob(self, o, value, code) then
|
|
error("cannot grow subview")
|
|
end
|
|
else
|
|
error("cannot grow dataview")
|
|
end
|
|
end
|
|
return self
|
|
end
|
|
end
|
|
|
|
return dataView |