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 | 
